https://sundaland.tistory.com/480
▶ Using the @Configuration annotation
@Configuration은 객체가 빈 정의의 소스임을 나타내는 클래스 레벨 어노테이션이다. @Configuration 클래스는 @Bean 어노테이션이 달린 메서드를 통해 빈을 선언한다. @Configuration 클래스에서 @Bean 메서드에 대한 호출은 빈 간 종속성을 정의하는 데에도 사용할 수 있다.
▷ Injecting Inter-bean Dependencies
빈이 서로 종속성을 가질 때, 그 종속성을 표현하는 것은 다음 예제에서 보듯이 한 빈 메서드가 다른 빈 메서드를 호출하는 것만큼 간단하다.
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
앞의 예제에서 beanOne은 생성자 주입을 통해 beanTwo에 대한 참조를 받는다.
이 빈 간 종속성 선언 방법은 @Bean 메서드가 @Configuration 클래스 내에서 선언될 때만 작동한다. 일반 @Component 클래스를 사용하여 빈 간 종속성을 선언할 수 없다.
▷ Lookup Method Injection
앞서 언급했듯이, lookup method injection은 드물게 사용해야 하는 고급 기능이다. 싱글톤 스코프의 빈이 프로토타입 스코프의 빈에 종속되어 있는 경우에 유용하다. 이러한 유형의 구성에 Java를 사용하면 이 패턴을 구현하는 자연스러운 수단이 제공된다.
▼ lookup method injection을 사용하는 방법
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
Java 구성을 사용하면 abstract createCommand() 메서드가 새(프로토타입) Command 클래스 객체를 찾는 방식으로 재정의되는 CommandManager의 하위 클래스를 만들 수 있다. 아래 예제는 이를 수행하는 방법을 보여준다.
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// inject dependencies here as required
return command;
}
@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
// SpringLookupInjection 참조
▷ Further Information About How Java-based Configuration Works Internally
아래 예제에서는 @Bean 메서드가 두 번 호출되는 것을 보여준다.
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
clientDao()는 clientService1()에서 한 번, clientService2()에서 한 번 호출되었다. 이 메서드는 ClientDaoImpl의 새 인스턴스를 생성하여 리턴하므로 일반적으로 두 개의 인스턴스(각 서비스당 하나씩)가 있을 것으로 예상한다. Spring에서 인스턴스화된 빈은 기본적으로 싱글톤 스코프를 갖는다. 모든 @Configuration 클래스는 시작 시 CGLIB로 서브클래싱된다. 서브클래스에서 자식 메서드는 부모 메서드를 호출하고 새 인스턴스를 만들기 전에 먼저 컨테이너에서 캐시된(범위가 지정된) 빈을 확인한다.
동작은 빈의 스코프에 따라 다를 수 있습니다. 여기서는 싱글톤에 대해 이야기하고 있다.
CGLIB 클래스는 org.springframework.cglib 패키지로 다시 패키징되어 spring-core JAR에 직접 포함되므로 클래스 경로에 CGLIB를 추가할 필요가 없다.
CGLIB이 시작 시점에 동적으로 기능을 추가하기 때문에 몇 가지 제한 사항이 있다. 특히, 구성 클래스는 final로 선언되어서는 안 된다. 그러나 구성 클래스에서 생성자는 @Autowired를 사용하거나 디폴트가 아닌 생성자를 선언하여 디폴트 주입을 위한 단일 생성자를 포함하여 모든 생성자가 허용된다.
만약 CGLIB에 의해 부과되는 제한을 피하고 싶다면, @Bean 메소드를 non-@Configuration 클래스(일반 @Component 클래스)에서 선언하거나, 구성 클래스에 @Configuration(proxyBeanMethods = false)로 애노테이션을 지정하는 것을 고려해야한다. 이 경우, @Bean 메소드 간의 교차 호출이 인터셉트되지 않으므로 생성자나 메소드 수준의 의존성 주입에만 의존해야 한다.
'스프링 프레임워크 > IoC (Inversion of Control)' 카테고리의 다른 글
Composing Java-based Configurations (0) | 2024.11.19 |
---|---|
Using the @Bean Annotation (0) | 2024.11.19 |
Instantiating the Spring Container by Using AnnotationConfigApplicationContext (0) | 2024.11.19 |
Fine-tuning Annotation-based Autowiring with Qualifiers (0) | 2024.11.19 |
Annotation-based Container Configuration (0) | 2024.11.19 |