- 제어의 역전 : 객체의 생성에서부터 생명주기의 관리까지 모든 객체에 대한 제어권이 바뀌었다는 것을 의미
1) DI : Dependency Injection
생성자, 필드, setter
동적으로 의존성 주입 -> 응집도 낮춤
디버깅 어려움 + 테스트 코드 짜기 어려움
2) DL : Depnedency LookUp
의존관계가 필요한 객체에서 직접 검색하는 방식
1) 생성자 주입
- 객체를 생성할 때, 의존성을 강제할 수 있음
private final MasterService;
MentorController(MasterService masterService ){
this.masterService = masterService;
}
- 순환참조 예방
2) 필드 주입
3) setter
- 필요할 때만 외부에서 받아서 사용 가능
1) 전역적으로 객체간의 의존관계를 설정
2) 반복적인 객체 생성 x
BeanFactory의 하위 인터페이스가 ApplicationContext가 Spring에서 말하는 IoC Container라고 생각하면 된다
객체 의존성을 역전시켜 객체 간의 결합도를 줄이고, 유연한 코드를 작성할 수 있게 하여
가독성 및 코드 중복, 유지 보수를 편하게 할 수 있음
ApplicationContext의 또다른 기능으로는 AOP와의 쉬운 통합, 메시지 리소스 처리(국제화), 이벤트 게시가 있다
ApplicationContext 의 상속 클래스 로 인해 자바코드, XML 등으로 설정 가능
1) AnnotationConfigApplicationContext(AppConfig.class)
2) GenericXmlApplicationContext(appConfig.xml)
IoC Container 생성 -> 빈 생성 -> 의존 관계 주입 -> 초기화 콜백 메소드 호출@PostConstruct -> 사용 -> 소멸전 콜백 메소드 호출@PreDestroy -> 스프링 종료
싱글톤 객체는 상태를 유지하게 설계하면 안된다
무상태로 설계해야 한다
1) 특정 클라이언트가 의존적인 필드가 있으면 안된다
2) 값을 변경할 수 있는 필드가 있으면 안된다
public class StatefulService {
private int price; // 상태를 유지하는 필드
public void order(String name, int price){
System.out.println("name= "+ name + " price = " + price);
this.price = price; // 여기가 문제!
}
public int getPrice(){
return price;
}
}
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);
statefulService.order("userA", 10000);
statefulService2.order("userB", 20000);
int price = statefulService.getPrice();
System.out.println(price); // 20000
Assertions.assertThat(statefulService.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService(){
return new StatefulService();
}
}
}
@Configuration을 적용한 AppConfig클래스는
CGLIB가 바이트코드를 조작하여 싱글톤으로 보장한다
@Bean
public MemberRepository memberRepository() {
if(memberRepository가 이미 스프링 컨테이너에 등록되어 있음){
return 스프링 컨테이너에서 찾아서 반환;
} else {
기존 로직을 호출해서 생성하고 스프링 컨테이너에 등록
return 반환;
}
}
@ComponentScan
@Component 가 붙은 모든 클래스를 스프링 빈으로 등록
basePackages : 필요한 위치부터 탐색하도록 시작 위치 지정
@SpringBootApplication 안에 @ComponentScan이 들어있음
@Controller : 스프링 MVC 컨트롤러
@Repository : 스프링 데이터 접근 계층으로 인식, 데이터 계층의 예외를 스프링 예외로 변환
@Configuration : 스프링 설정 정보로 인식, 스프링 빈이 싱글톤을 유지하도록 추가 처리
@Service : 비즈니스 계층 인식 도움
컴포넌트 스캔 제외
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {}
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type= FilterType.ANNOTATION, classess = MyExcludeComponent.class)
)
@Autowired
자동으로 타입이 같은 스프링 빈을 찾아서 주입
우선순위
1. 필드명 매핑
2. @Qualifier 끼리 매칭 > 빈 이름 매칭
3. @Primary : 우선순위 정하기
수동빈 등록
-> 업무 로직 빈 : 비즈니스 요구사항
-> 기술 지원 빈 : AOP나 기술적인 문제 처리할 때, 공통 로그 처리, 데이터베이스 연결
- 인터페이스 (InitializingBean, DisposableBean)
상속받아서 afterPropertiesSet()메서드로 초기화 지원, destroy()메서드로 소멸 지원
- 설정 정보에 초기화 메서드, 종료 메서드 지정
@Bean의 destroyMethod 속성에서 close, shutdown 이름의 종료 메서드 사용
@Bean(initMethod="init", destroyMethod="close")
- @PostConstruct @PreDestroy 어노테이션 지원
외부 라이브러리 적용 x
1) 싱글톤 : 기본 스코프, 시작-종료
@Test
void singletonBeanFind() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicatiohnContext(Singleton.class);
SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
assertThat(singletonBean1).isSameAs(singletonBean2);
ac.close();
}
@Scope("singleton")
static class SingletonBean {
@PostConstruct
public void init(){
System.out.println("init");
}
@PreDestroy
public void destroy(){
System.out.println("destroy");
}
}
2) 프로토타입 : 빈의 생성과 의존관계 주입까지만 관여, 더는 관리 x, @PreDistory 같은 종료 메서드가 호출되지 않음
주입 받는 시점에 각각 새로운 프로토타입 빈이 생성됨
프로토타입 스코프 + 싱글톤 빈과 함께 사용시 Provider로 문제 해결
의존관계를 주입받는게 아니라 직접 의존관계 찾는 것을 Dependency Lookup(DL)이라고 한다
ObjectFactory, ObjectProvider
3) 웹 관련 스코프 : request(요청들어오고나갈때까지), session(웹세션), application(서블릿컨텍스트와 같은 범위)
스프링 애플리케이션을 실행하는 시점에 request 스코프 빈은 생성되지 않음 -> 실제 고객의 요청이 들어와야 생성됨
그래서, 미리 Proxy 클래스를 다른 빈에 미리 주입함
@Component
@Scope(value="request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {
private String uuid, requestURL;
public void log(String message){
System.out.println(uuid+" "+requestURL+" "+message);
}
@PostConstruct
public void init(){
}
@PreDestroy
public void close() {
}
}
프록시
- CGLIB 라이브러리로 클래스 상속 받은 가짜 프록시 객체
- 프록시 객체는 실제 request scope와는 관계 없음
[스프링] OSIV, AOP (0) | 2024.11.27 |
---|---|
[스프링] 동시성 문제, ThreadLocal (0) | 2024.11.25 |
[자바] Collections API : TreeMap (0) | 2024.10.22 |
[Java] Java 메모리 구조, Reflection (0) | 2024.01.06 |
[Java] Exception, try-catch-finally, try-with-resources (0) | 2023.12.24 |