스프링에서 수동으로 빈을 등록 할때 @Configuration 클래스 안에 @Bean을 사용한다. 왜 그런지 살펴보자.
● @Configuration 안에 @Bean을 사용하는 이유, proxyBeanMethods
- @Bean 어노테이션을 이용한 수동 빈 등록
스프링에선 일반적으로 컴포넌트 스캔을 이용해 자동으로 빈을 등록하는 방법을 이용한다. 하지만 @Bean 어노테이션을 사용해 수동으로 빈을 등록해야 할 때도 있다.
- 개발자가 직접 제어가 불가능한 라이브러리를 활용할 때
- 애플리케이션 전 범위적으로 사용되는 클래스를 등록할 때
- 다형성을 활용하여 여러 구현체를 등록해야 할 때
@Bean을 이용한 수동 빈 메소드는 스프링 빈 안에만 구현되어 있다면 모두 동작한다. 하지만 스프링은 @Bean은 반드시 @Configuration 어노테이션 활용하도록 하는데, 그 이유는 @Configuration에 특별한 부가 기능이 적용되기 때문이다.
● @Configuration 에 적용되는 프록시 패턴
- @Configuration 어노테이션 안에는 @Component 어노테이션이 붙어 있어 @Configuration이 붙어있는 클래스 역시 스프링 빈으로 등록이 된다. 그럼에도 스프링이 @Configuration을 만든 이유는 CGLib으로 프록시 패턴을 적용해 수동으로 등록하는 스프링 빈이 반드시 싱글톤으로 생성됨을 보장하기 위해서다.
public class Resource {}
위 클래스를 @Component를 이용해 자동으로 빈 등록을 한다면 스프링이 해당 크래스의 객체의 생성을 제어하게 되고(제어의 역전, IoC) 하나의 객체만 생성되도록 컨트롤 할 수 있다. 하지만 위의 클래스를 @Bean을 이용해 직접 빈으로 등록해준다고 하면, 우리는 다음과 같이 해당 빈 등록 메소드를 여러 번 호출할 수 있게 된다.
@Configuration
public class MyBeanConfiguration {
@Bean
public Resource resource() {
return new Resource();
}
@Bean
public MyFirstBean myFirstBean() {
return new MyFirstBean(resource());
}
@Bean
public MySecondBean mySecondBean() {
return new MySecondBean(resource());
}
}
실수로 위와 같이 빈을 생성하는 메소드를 여러 번 호출 했다면 불필요하게 여러 개의 빈이 생성이 된다. 스프링은 이러한 문제를 방지하고자 @Configuration이 있는 클래스를 객체로 생성할 때 CGLib을 사용해 프록시 패턴을 적용한다. 그래서 @Bean이 있는 메소드를 여러 번 호출하여도 항상 동일한 객체를 반환하여 싱글톤을 보장한다.
@Configuration
public class MyBeanConfigurationProxy extends MyBeanConfiguration {
private Object source;
@Override
public Resource resource() {
if (resource == null) {
source = super.resource();
}
return source;
}
@Override
public MyFirstBean myFirstBean() {
return super.myFirstBean();
}
@Override
public MySecondBean mySecondBean() {
return super.mySecondBean();
}
}
CGLib은 상속을 통해 프록시를 구현하므로 위와 같이 프록시가 구현 됬다고 이해 할 수 있다. 물론 이렇게 생성이 되는건 아니라 내부 클래스를 사용하는 등의 차이가 있으므로 이해를 돕기 위한 코드로 보면 된다.
● 싱글톤 여부를 제어하기 위한 proxyBeanMethods
대부분 @Bean에 의한 수동 빈 등록을 할 때 싱글톤으로 생성되기를 원한다. 하지만 @Bean 메소드를 호출할 때 의도적으로 매번 다른 객체가 생성되기를 바랄 수 있다. 그럴 경우엔 @Configuration 어노테이션이 갖고 있는 proxyBeanMethods를 false로 주면 된다.
@Configuration(proxyBeanMethods = false)
public class MyBeanConfigurationProxy extends MyBeanConfiguration {
private Object source;
@Override
public Resource resource() {
if (resource == null) {
source = super.resource();
}
return source;
}
@Override
public MyFirstBean myFirstBean() {
return super.myFirstBean();
}
@Override
public MySecondBean mySecondBean() {
return super.mySecondBean();
}
}
https://mangkyu.tistory.com/234
'Programming > Spring' 카테고리의 다른 글
빈 등록을 위한 어노테이션 (0) | 2022.10.30 |
---|---|
@Controller와 @RestController (0) | 2022.10.30 |
@RequestBody, @ModelAttribute, @RequestParam (0) | 2022.10.30 |
서블릿(Servlet) 이란? (0) | 2022.10.30 |
IoC, 스프링 컨테이너(Container), 스프링 빈(Bean)이란? (0) | 2022.10.29 |
댓글