Using the @Bean Annotation

2024. 11. 14. 14:15Spring Framework/Spring IoC

Using the @Bean Annotation

@Bean은 메서드 레벨 어노테이션이며 XML <bean/> 엘리먼트와 직접적으로 대응됩니다. 이 어노테이션은 <bean/>에서 제공하는 다음과 같은 일부 속성을 지원합니다.

  • init-method
  • destroy-method
  • autowiring
  • name

@Bean 애노테이션은 @Configuration 어노테이션이 붙은 클래스나 @Component 어노테이션이 붙은 클래스에서 사용할 수 있습니다.

 

Declaring a Bean

빈을 선언하려면 @Bean 어노테이션으로 메서드에 어노테이션을 달 수 있습니다. 이 메서드를 사용하여 메서드의 리턴 값으로 지정된 타입의 ApplicationContext 내에 빈 정의를 등록합니다. 기본적으로 빈 이름은 메서드 이름과 동일합니다. 다음 예제는 @Bean 메서드 선언을 보여줍니다. 

@Configuration
public class AppConfig {

	@Bean
	public TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}

 

위 선언은 다음 텍스트 이미지에서 볼 수 있듯이 TransferServiceImpl 타입의 객체 인스턴스에 바인딩된 transferService라는 이름의 빈을 ApplicationContext에서 사용할 수 있게 합니다.

transferService -> com.acme.TransferServiceImpl

 

디폴트 메서드를 사용하여 빈을 정의할 수도 있습니다. 이를 통해 디폴트 메서드에서 빈 정의가 있는 인터페이스를 구현하여 빈 구성을 구성할 수 있습니다.

public interface BaseConfig {

	@Bean
	default TransferServiceImpl transferService() {
		return new TransferServiceImpl();
	}
}

@Configuration
public class AppConfig implements BaseConfig {

}

 

다음 예제와 같이 인터페이스(또는 베이스 클래스) 리턴 유형으로 @Bean 메서드를 선언할 수도 있습니다.

@Configuration
public class AppConfig {

	@Bean
	public TransferService transferService() {
		return new TransferServiceImpl();
	}
}

 

그러나, 이는 고급 타입 예측의 가시성을 지정된 인터페이스 타입(TransferService)으로 제한합니다. 전체 타입(TransferServiceImpl)은 해당 싱글톤 빈이 인스턴스화된 후에야 컨테이너에 알려지게 됩니다. 비지연(non-lazy) 싱글톤 빈들은 선언된 순서에 따라 인스턴스화되기 때문에, 다른 컴포넌트가 선언되지 않은 타입(예: @Autowired TransferServiceImpl)을 기준으로 매칭을 시도할 때, 이 빈이 인스턴스화된 시점에 따라 다른 타입 매칭 결과가 나타날 수 있습니다.

선언된 서비스 인터페이스로 타입을 일관되게 참조하는 경우 @Bean 리턴 타입이 해당 디자인 결정에 안전하게 결합될 수 있습니다. 그러나 여러 인터페이스를 구현하는 컴포넌트나 구현 타입으로 잠재적으로 참조되는 컴포넌트의 경우 가능한 가장 구체적인 리턴 타입을 선언하는 것이 더 안전합니다(적어도 빈을 참조하는 주입 지점에서 요구하는 만큼 구체적)

 

Bean Dependencies

@Bean 어노테이션이 붙은 메서드는 해당 빈을 빌드하는 데 필요한 종속성을 설명하는 임의의 수의 파라미터를 가질 수 있습니다. 예를 들어, TransferService에 AccountRepository가 필요한 경우 다음 예제와 같이 메서드 파라미터로 해당 종속성을 구체화할 수 있습니다.

@Configuration
public class AppConfig {

	@Bean
	public TransferService transferService(AccountRepository accountRepository) {
		return new TransferServiceImpl(accountRepository);
	}
}

 

해결 메커니즘은 생성자 기반 종속성 주입과 거의 동일합니다. 자세한 내용은 관련 섹션을 참조하세요.

 

Receiving Lifecycle Callbacks

@Bean 어노테이션으로 정의된 모든 클래스는 일반 라이프사이클 콜백을 지원하고 JSR-250의 @PostConstruct 및 @PreDestroy 주석을 사용할 수 있습니다. 자세한 내용은 JSR-250 어노테이션을 참조하세요.

 

일반적인 Spring 라이프사이클 콜백도 완벽하게 지원됩니다. 빈이 InitializingBean, DisposableBean 또는 Lifecycle을 구현하는 경우 해당 메서드는 컨테이너에서 호출됩니다.

BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware 등과 같은 표준 *Aware 인터페이스 세트도 완벽하게 지원됩니다.

@Bean 어노테이션은 다음 예제에서 볼 수 있듯이 빈 요소에 Spring XML의 init-method 및 destroy-method 속성과 매우 유사하게 임의의 init 및 destroy 콜백 메서드를 지정하는 것을 지원합니다.

public class BeanOne {

	public void init() {
		// initialization logic
	}
}

public class BeanTwo {

	public void cleanup() {
		// destruction logic
	}
}

@Configuration
public class AppConfig {

	@Bean(initMethod = "init")
	public BeanOne beanOne() {
		return new BeanOne();
	}

	@Bean(destroyMethod = "cleanup")
	public BeanTwo beanTwo() {
		return new BeanTwo();
	}
}

 

 

기본적으로 Java 구성으로 정의된 빈은 public close 또는 shutdown 메서드가 있고 자동으로 destroy 콜백에 등록됩니다.  public close 또는 shutdown 메서드가 있고 컨테이너가 종료될 때 호출되지 않도록 하려면 빈 정의에 @Bean(destroyMethod = "")을 추가하여 디폴트(추론된) 모드를 비활성화할 수 있습니다.

JNDI로 획득한 리소스의 경우 기본적으로 이를 수행할 수 있습니다. 해당 리소스의 수명 주기는 애플리케이션 외부에서 관리되기 때문입니다. 특히 Jakarta EE 애플리케이션 서버에서 문제가 되는 것으로 알려져 있으므로 DataSource의 경우 항상 이를 수행해야 합니다.

다음 예제는 DataSource에 대한 자동 destruction 콜백을 방지하는 방법을 보여줍니다.
@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
	return (DataSource) jndiTemplate.lookup("MyDS");
}


또한 @Bean 메서드에서는 일반적으로 Spring의 JndiTemplate 또는 JndiLocatorDelegate 헬퍼를 사용하거나 직접 JNDI InitialContext를 사용하지만 JndiObjectFactoryBean 변형은 사용하지 않는 프로그램적 JNDI 조회를 사용합니다(이 경우 실제 대상 유형 대신 FactoryBean 유형으로 반환 유형을 선언해야 하므로 여기에 제공된 리소스를 참조하려는 다른 @Bean 메서드에서 교차 참조 호출에 사용하기 어렵습니다).

 

위의 예에서 BeanOne의 경우, 다음 예와 같이 생성 중에 init() 메서드를 직접 호출하는 것도 마찬가지로 유효합니다.

@Configuration
public class AppConfig {

	@Bean
	public BeanOne beanOne() {
		BeanOne beanOne = new BeanOne();
		beanOne.init();
		return beanOne;
	}

	// ...
}

 

Java에서 직접 작업하는 경우 객체를 원하는 대로 조작할 수 있으며 항상 컨테이너 수명 주기에 의존할 필요가 없습니다.

 

Specifying Bean Scope

Spring에는 @Scope 어노테이션이 포함되어 있어 빈의 범위를 지정할 수 있습니다.

 

Using the @Scope Annotation

@Bean 어노테이션으로 정의된 빈이 특정 scope를 가져야 한다고 지정할 수 있습니다. Bean Scopes 섹션에 지정된 표준 범위를 사용할 수 있습니다.

디폴트 스코프는 싱글톤이지만 다음 예제와 같이 @Scope 어노테이션으로 이를 재정의할 수 있습니다.

@Configuration
public class MyConfiguration {

	@Bean
	@Scope("prototype")
	public Encryptor encryptor() {
		// ...
	}
}

 

@Scope and scoped-proxy

Spring은 스코프가 지정된 프록시를 통해 스코프가 지정된 종속성을 처리하는 편리한 방법을 제공합니다. XML 구성을 사용할 때 이러한 프록시를 만드는 가장 쉬운 방법은 <aop:scoped-proxy/> 엘리먼트입니다. @Scope 어노테이션으로 Java에서 빈을 구성하면 proxyMode 특성으로 동일한 지원을 제공합니다. 기본값은 ScopedProxyMode.DEFAULT이며, 일반적으로 컴포넌트 스캔 지시 레벨에서 다른 디폴트 값이 구성되지 않는 한 스코프가 지정된 프록시를 만들지 않아야 함을 나타냅니다. ScopedProxyMode.TARGET_CLASS, ScopedProxyMode.INTERFACES 또는 ScopedProxyMode.NO를 지정할 수 있습니다.

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@SessionScope
public UserPreferences userPreferences() {
	return new UserPreferences();
}

@Bean
public Service userService() {
	UserService service = new SimpleUserService();
	// a reference to the proxied userPreferences bean
	service.setUserPreferences(userPreferences());
	return service;
}

 

Customizing Bean Naming

기본적으로 구성 클래스는 @Bean 메서드의 이름을 결과 빈의 이름으로 사용합니다. 그러나 이 기능은 다음 예제에서 보여지는 것처럼 name 속성을 사용하여 재정의할 수 있습니다.

@Configuration
public class AppConfig {

	@Bean("myThing")
	public Thing thing() {
		return new Thing();
	}
}

 

Bean Aliasing

Naming Beans에서 논의했듯이, 때로는 단일 빈에 여러 이름을 부여하는 것이 바람직한데, 이를 빈 별칭이라고도 합니다. @Bean 어노테이션의 name 속성은 이 목적을 위해 문자열 배열을 허용합니다. 다음 예는 빈에 대한 여러 별칭을 설정하는 방법을 보여줍니다.

@Configuration
public class AppConfig {

	@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
	public DataSource dataSource() {
		// instantiate, configure and return DataSource bean...
	}
}

 

Bean Description

때로는 빈에 대한 보다 자세한 텍스트 설명을 제공하는 것이 도움이 됩니다. 이는 모니터링 목적으로 빈이 노출될 때(아마도 JMX를 통해) 특히 유용할 수 있습니다.

@Bean에 설명을 추가하려면 다음 예와 같이 @Description 어노테이션을 사용할 수 있습니다.

@Configuration
public class AppConfig {

	@Bean
	@Description("Provides a basic example of a bean")
	public Thing thing() {
		return new Thing();
	}
}