Using the @Bean Annotation

Dev.Hammy·2024년 2월 24일
0

@Bean은 메소드 수준 어노테이션이며 XML <bean/> 요소와 직접적으로 유사합니다. 어노테이션은 다음과 같이 <bean/>에서 제공하는 일부 속성(attribute)을 지원합니다.

@Configuration-annotated 또는 @Component-annotated 클래스에서 @Bean 어노테이션을 사용할 수 있습니다.

Declaring a Bean

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

@Configuration
public class AppConfig {

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

앞의 구성은 다음 Spring XML과 정확히 동일합니다.

<beans>
	<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

두 선언 모두 다음 텍스트 이미지에 표시된 것처럼 TransferServiceImpl 타입의 객체 인스턴스에 바인딩되어 ApplicationContext에서 사용 가능한 transferService라는 빈을 만듭니다.

transferService -> com.acme.TransferServiceImpl

default 메소드를 사용하여 Bean을 정의할 수도 있습니다. 이를 통해 default 메소드에 Bean 정의가 포함된 인터페이스를 구현하여 Bean 구성(configuration)을 구성(compose)할 수 있습니다.

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();
	}
}

그러나 이는 고급 type prediction의 가시성을 지정된 인터페이스 타입(TransferService)으로 제한합니다. 그런 다음 영향을 받은 싱글톤 Bean이 인스턴스화되면 컨테이너에 전체 타입(TransferServiceImpl)이 알려집니다. Non-lazy 싱글톤 Bean은 선언 순서에 따라 인스턴스화 됩니다. 따라서 다른 구성 요소가 선언되지 않은 유형(예: transferService 빈이 인스턴스화된 후에만 resolve되는 @Autowired TransferServiceImpl)으로 일치(match)를 시도하는 경우에 따라 다른 타입 match 결과가 표시될 수 있습니다.

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

Bean Dependencies

@Bean 어노테이션이 달린 메소드에는 해당 Bean을 빌드하는 데 필요한 종속성을 설명하는 임의의 수의 매개변수가 있을 수 있습니다. 예를 들어 TransferServiceAccountRepository가 필요한 경우 다음 예제와 같이 메서드 매개 변수를 사용하여 해당 종속성을 구체화(materialize)할 수 있습니다.

@Configuration
public class AppConfig {

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

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

Receiving Lifecycle Callbacks

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

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

*Aware 인터페이스(예: BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware 등)의 표준 세트도 완벽하게 지원됩니다.

@Bean 어노테이션은 다음 예제와 같이 Bean 요소에 대한 Spring XML의 init-methoddestroy-method 속성(attribute)과 매우 유사한 임의의 초기화 및 소멸 콜백 메소드 지정을 지원합니다.

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();
	}
}

[Note]
기본적으로 public close 또는 shutdown 메소드가 있는 Java 구성으로 정의된 Bean은 자동으로 소멸 콜백에 등록(enlisted)됩니다. public close 또는 shutdown 메소드가 있고, 컨테이너가 종료될 때 호출되는 것을 원하지 않는 경우 Bean 정의에 @Bean(destroyMethod = "")를 추가하여 기본(inferred) 모드를 비활성화할 수 있습니다.

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

다음 예에서는 DataSource에 대한 자동 소멸 콜백을 방지하는 방법을 보여줍니다.

@Bean(destroyMethod = "")
public DataSource dataSource() throws NamingException {
	return (DataSource) jndiTemplate.lookup("MyDS");
}

또한 @Bean 메소드를 사용하면 일반적으로 Spring의 JndiTemplate 또는 JndiLocatorDelegate 도우미를 사용하거나 JNDI InitialContext을 사용하여 프로그래밍 방식의 JNDI 조회(look up)를 사용하지만 JndiObjectFactoryBean 변형은 사용하지 않습니다(이 경우 실제 대상 타입 대신 FactoryBean 타입으로 반환 타입 선언을 강제하며, 이로 인해 여기에 제공된 리소스를 참조하려는 다른 @Bean 메서드의 상호 참조 호출에 사용하기가 더 어려워집니다).

이전 참고 사항 위의 예제에서 BeanOne의 경우 다음 예제와 같이 생성 중에 직접 init() 메서드를 호출하는 것도 동일하게 유효합니다.

@Configuration
public class AppConfig {

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

	// ...
}

[Tip]
Java에서 직접 작업할 때 객체를 사용하여 원하는 모든 작업을 수행할 수 있으며 항상 컨테이너 수명 주기에 의존할 필요는 없습니다.

Spedifying Bean Scope

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

Using the @Scope Annotation

@Bean 어노테이션으로 정의된 Bean이 특정 범위를 갖도록 지정할 수 있습니다. Bean 범위 섹션에 지정된 표준 범위를 사용할 수 있습니다.

기본 범위는 singleton이지만 다음 예제와 같이 @Scope 어노테이션으로 이를 재정의할 수 있습니다.

@Configuration
public class MyConfiguration {

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

@Scope and scoped-proxy

Spring은 범위가 지정된 프록시(scoped-proxy)를 통해 범위가 지정된 종속성을 작업하는 편리한 방법을 제공합니다. XML 구성을 사용할 때 이러한 프록시를 생성하는 가장 쉬운 방법은 <aop:scoped-proxy/> 요소입니다. @Scope 어노테이션을 사용하여 Java에서 Bean을 구성하면 proxyMode 속성(attribute)과 동등한 지원이 제공됩니다. 기본값은 ScopedProxyMode.DEFAULT입니다. 이는 일반적으로 컴포넌트-스캔 명령 수준에서 다른 기본값(default)이 구성되지 않은 한 범위가 지정된 프록시를 생성해서는 안 됨을 나타냅니다. ScopedProxyMode.TARGET_CLASS, ScopedProxyMode.INTERFACES 또는 ScopedProxyMode.NO를 지정할 수 있습니다.

Java를 사용하여 XML 참조 문서(범위가 지정된 프록시 참조)의 범위가 지정된 프록시 예제를 @Bean으로 포팅하는 경우 다음과 유사합니다.

// 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 메소드 이름을 결과 Bean의 이름으로 사용합니다. 그러나 이 기능은 다음 예제와 같이 name 속성(attribute)으로 재정의될 수 있습니다.

@Configuration
public class AppConfig {

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

Bean Aliasing

Bean 이름 지정에서 설명한 것처럼 단일 Bean에 여러 이름을 지정하는 것이 바람직할 때도 있습니다. 이는 Bean 별명 지정이라고도 알려져 있습니다. @Bean 어노테이션의 name 속성(attribute)은 이 목적을 위해 String 배열을 허용합니다. 다음 예에서는 Bean에 대한 여러 별칭을 설정하는 방법을 보여줍니다.

@Configuration
public class AppConfig {

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

Bean Description

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

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

@Configuration
public class AppConfig {

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

0개의 댓글