Spring Core Technologies - 1.12. Java-based Container Configuration
- 1.12. Java-based Container Configuration
- 1.12.1. Basic Concepts: @Bean and @Configuration
- 1.12.2. Instantiating the Spring Container by Using
AnnotationConfigApplicationContext
- 1.12.3. Using the
@Bean
Annotation - 1.12.4. Using the
@Configuration
annotation - 1.12.5. Composing Java-based Configurations
- 함께 읽기
- 목록으로 - [[/spring/document/core]]{Spring Core Technologies}
- 이전 문서 - [[/spring/document/core/01-11-using-jsr-330-standard-annotations]]{1.11. Using JSR 330 Standard Annotations}
- 다음 문서 - [[/spring/document/core/01-13-env-abstraction]]{1.13. Environment Abstraction}
1.12. Java-based Container Configuration
This section covers how to use annotations in your Java code to configure the Spring container. It includes the following topics:
이 섹션에서는 Java 코드에서 애노테이션을 사용해 Spring 컨테이너를 configure하는 방법을 다룹니다. 다음 주제를 다루게 됩니다.
- Basic Concepts: @Bean and @Configuration
- Instantiating the Spring Container by Using AnnotationConfigApplicationContext
- Using the @Bean Annotation
- Using the @Configuration annotation
- Composing Java-based Configurations
- Bean Definition Profiles
- PropertySource Abstraction
- Using @PropertySource
- Placeholder Resolution in Statements
1.12.1. Basic Concepts: @Bean and @Configuration
The central artifacts in Spring’s new Java-configuration support are
@Configuration
-annotated classes and@Bean
-annotated methods.
Spring의 새로운 Java configuration support의 핵심 아티팩트는 @Configuration
애노테이션을 붙인 클래스와 @Bean
애노테이션을 붙인 메소드입니다.
The
@Bean
annotation is used to indicate that a method instantiates, configures, and initializes a new object to be managed by the Spring IoC container. For those familiar with Spring’s<beans/>
XML configuration, the@Bean
annotation plays the same role as the<bean/>
element. You can use@Bean
-annotated methods with any Spring@Component
. However, they are most often used with@Configuration
beans.
@Bean
애노테이션은 Spring IoC 컨테이너가 관리할 새로운 객체를 인스턴스화하고, configure하고, 초기화한다는 것을 나타내는 데 사용됩니다.
Spring의 XML 설정에서 <beans/>
에 익숙한 사람이라면, @Bean
이 <bean/>
과 같은 역할을 한다고 생각하면 됩니다.
@Bean
애노테이션을 붙인 메소드는 Spring @Component
와 함께 사용할 수 있지만, 대체로 @Configuration
bean과 사용하는 경우가 많습니다.
Annotating a class with
@Configuration
indicates that its primary purpose is as a source of bean definitions. Furthermore,@Configuration
classes let inter-bean dependencies be defined by calling other@Bean
methods in the same class. The simplest possible@Configuration
class reads as follows:
클래스에 @Configuration
애노테이션을 붙이는 것은 해당 클래스의 목적이 bean definition 소스라는 것을 나타내는 것입니다.
그리고 @Configuration
클래스는 같은 클래스 안에 있는 @Bean
메소드들끼리 서로를 호출하여 bean들 사이의 dependency를 정의할 수 있게 합니다.
다음은 간단한 @Configuration
클래스의 예제입니다.
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
The preceding
AppConfig
class is equivalent to the following Spring<beans/>
XML:
위의 AppConfig
클래스는 다음 Spring XML의 <beans/>
와 똑같습니다.
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
Full @Configuration vs “lite” @Bean mode?
When
@Bean
methods are declared within classes that are not annotated with@Configuration
, they are referred to as being processed in a “lite” mode. Bean methods declared in a@Component
or even in a plain old class are considered to be “lite”, with a different primary purpose of the containing class and a@Bean
method being a sort of bonus there. For example, service components may expose management views to the container through an additional@Bean
method on each applicable component class. In such scenarios,@Bean
methods are a general-purpose factory method mechanism.Unlike full
@Configuration
, lite@Bean
methods cannot declare inter-bean dependencies. Instead, they operate on their containing component’s internal state and, optionally, on arguments that they may declare. Such a@Bean
method should therefore not invoke other@Bean
methods. Each such method is literally only a factory method for a particular bean reference, without any special runtime semantics. The positive side-effect here is that no CGLIB subclassing has to be applied at runtime, so there are no limitations in terms of class design (that is, the containing class may befinal
and so forth).In common scenarios,
@Bean
methods are to be declared within@Configuration
classes, ensuring that “full” mode is always used and that cross-method references therefore get redirected to the container’s lifecycle management. This prevents the same@Bean
method from accidentally being invoked through a regular Java call, which helps to reduce subtle bugs that can be hard to track down when operating in “lite” mode.
Full @Configuration vs “lite” @Bean mode?
@Configuration
애노테이션이 붙지 않은 클래스에서@Bean
메소드를 선언하면, 그런 메소드들은 "lite" 모드로 처리가 됩니다.@Component
나 순수한 클래스 안에서 선언된 bean 메소드는 "lite"로 간주됩니다.- 일종의 보너스에 해당하는 목적이 있기 때문입니다.
- 예를 들어, 서비스 컴포넌트는 컨테이너에 대한 관리적인 관점에서 적용 가능한 각 컴포넌트 클래스에 대해 추가적인
@Bean
메소드를 사용할 수 있습니다. - 이런 시나리오에서
@Bean
메소드는 범용적인 팩토리 메소드 메커니즘이 됩니다.
- full
@Configuration
과 달리 lite@Bean
메소드는 bean과 bean 사이의 dependencies를 선언할 수 없습니다.- 그 대신, lite
@Bean
메소드를 포함하는 컴포넌트의 내부 상태와, 옵셔널하게 선언할 수 있는 인자에 따라 작동하게 됩니다. - 따라서 이런 종류의 (lite)
@Bean
메소드는 다른@Bean
메소드를 호출하지 않아야 합니다. - 이런 메소드들은 특별한 런타임상의 의미가 없고, 말 그대로 특정 bean 참조에 대한 팩토리 메소드일 뿐입니다.
- lite
@Bean
의 긍정적인 사이드 이펙트는 클래스 디자인 측면에서 제안이 별로 없다는 것입니다.- (예를 들어 포함하는 클래스가
final
이어도 가능합니다) - 런타임에 CGLIB 서브클래싱을 적용할 필요가 없기 때문입니다.
- (예를 들어 포함하는 클래스가
- 그 대신, lite
- 일반적인 시나리오에서
@Bean
메소드는@Configuration
클래스 내에서 선언되어 항상 "full" 모드가 적용됩니다.- 따라서 교차 메소드 참조(cross-method references)가 있다면 컨테이너의 생명주기 관리로 리다이렉션됩니다.
- 이러한 구조 때문에 평범한 Java 호출을 통해 똑같은
@Bean
메소드가 우발적으로 호출되는 것이 방지되어 미묘한 버그를 줄이는 데에 도움이 됩니다.- (lite 모드에서는 이것이 방지되지 않기 때문)
The
@Bean
and@Configuration
annotations are discussed in depth in the following sections. First, however, we cover the various ways of creating a spring container using by Java-based configuration.
@Bean
과 @Configuration
애노테이션은 다음 섹션에서 자세히 설명합니다.
일단은 Java 기반 configuration을 사용하여 Spring 컨테이너를 생성하는 다양한 방법을 알아봅시다.
1.12.2. Instantiating the Spring Container by Using AnnotationConfigApplicationContext
The following sections document Spring’s
AnnotationConfigApplicationContext
, introduced in Spring 3.0. This versatileApplicationContext
implementation is capable of accepting not only@Configuration
classes as input but also plain@Component
classes and classes annotated with JSR-330 metadata.When
@Configuration
classes are provided as input, the@Configuration
class itself is registered as a bean definition and all declared@Bean
methods within the class are also registered as bean definitions.When
@Component
and JSR-330 classes are provided, they are registered as bean definitions, and it is assumed that DI metadata such as@Autowired
or@Inject
are used within those classes where necessary.
다음 섹션부터는 Spring 3.0에 도입된 Spring AnnotationConfigApplicationContext
를 다룹니다.
AnnotationConfigApplicationContext
는 ApplicationContext
의 구현체이며, @Configuration
클래스 뿐만 아니라 일반적인 @Component
클래스와 JSR-330 메타데이터 애노테이션이 붙은 클래스도 입력받을 수 있습니다.
@Configuration
클래스가 입력으로 제공되면 @Configuration
클래스 자체가 bean definition으로 등록되고, 해당 클래스 내에 선언된 모든 @Bean
메소드도 bean definition으로 등록됩니다.
@Component
와 JSR-330 클래스들이 제공되면, 얘네들도 bean definition으로 등록되고, 필요에 따라 해당 클래스 내에 있는 @Autowired
와 @Inject
등의 DI 메타데이터를 사용하게 됩니다.
Simple Construction
In much the same way that Spring XML files are used as input when instantiating a
ClassPathXmlApplicationContext
, you can use@Configuration
classes as input when instantiating anAnnotationConfigApplicationContext
. This allows for completely XML-free usage of the Spring container, as the following example shows:
ClassPathXmlApplicationContext
를 인스턴스화할 때 Spring XML 파일을 입력받는 것처럼, AnnotationConfigApplicationContext
를 인스턴스화할 때에는 @Configuration
클래스를 입력으로 사용하게 됩니다.
이를 통해 다음 예제와 같이 XML 없이 Spring 컨테이너를 사용할 수 있게 됩니다.
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
As mentioned earlier,
AnnotationConfigApplicationContext
is not limited to working only with@Configuration
classes. Any@Component
or JSR-330 annotated class may be supplied as input to the constructor, as the following example shows:
위에서 언급한 바와 같이, AnnotationConfigApplicationContext
는 @Configuration
만 사용하게끔 제한되어 있지는 않습니다.
다음 예제와 같이 @Component
또는 JSR-330 애노테이션 클래스를 AnnotationConfigApplicationContext
의 생성자에 입력할 수 있습니다.
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
The preceding example assumes that
MyServiceImpl
,Dependency1
, andDependency2
use Spring dependency injection annotations such as@Autowired
.
위의 예제에서는 MyServiceImpl
, Dependency1
, Dependency2
가 @Autowired
와 같은 Spring dependency injection 애노테이션을 사용한다고 전제한 것입니다.
Building the Container Programmatically by Using register(Class<?>…)
You can instantiate an
AnnotationConfigApplicationContext
by using a no-arg constructor and then configure it by using theregister()
method. This approach is particularly useful when programmatically building anAnnotationConfigApplicationContext
. The following example shows how to do so:
인자 없는 생성자를 사용하여 AnnotationConfigApplicationContext
를 인스턴스화한 다음에 register()
메소드를 사용해 configure하는 것도 가능합니다.
이런 접근 방법은 AnnotationConfigApplicationContext
를 프로그래밍 방식으로 빌드할 때 유용합니다.
다음 예제를 봅시다.
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
Enabling Component Scanning with scan(String…)
To enable component scanning, you can annotate your
@Configuration
class as follows:
컴포넌트 스캔을 활성화하려면, @Configuration
클래스에 다음과 같이 애노테이션을 달아줄 수 있습니다.
@Configuration
@ComponentScan(basePackages = "com.acme") // (1)
public class AppConfig {
...
}
(1) This annotation enables component scanning.
(1) 이 애노테이션이 컴포넌트 스캔을 가능하게 합니다.
Experienced Spring users may be familiar with the XML declaration equivalent from Spring’s context: namespace, shown in the following example:
- 경험이 풍부한 Spring 사용자는 다음과 같은 XML 선언에 익숙할 수 있습니다.
<beans>
<context:component-scan base-package="com.acme"/>
</beans>
In the preceding example, the
com.acme
package is scanned to look for any@Component
-annotated classes, and those classes are registered as Spring bean definitions within the container.AnnotationConfigApplicationContext
exposes thescan(String…)
method to allow for the same component-scanning functionality, as the following example shows:
위의 예제에서 com.acme
패키지는 @Component
애노테이션이 달린 클래스를 찾기 위해 스캔되며, 해당 클래스는 컨테이너에 Spring bean definition으로 등록됩니다.
AnnotationConfigApplicationContext
에는 이와 똑같은 컴포넌트 스캔 기능을 사용할 수 있도록 scan(String...)
메소드가 제공됩니다.
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
(i) Remember that
@Configuration
classes are meta-annotated with@Component
, so they are candidates for component-scanning. In the preceding example, assuming thatAppConfig
is declared within thecom.acme
package (or any package underneath), it is picked up during the call toscan()
. Uponrefresh()
, all its@Bean
methods are processed and registered as bean definitions within the container.
- (i)
@Configuration
클래스에는 메타 애노테이션으로@Component
가 붙어 있습니다. 즉,@Configuration
도 컴포넌트 스캔의 대상이라는 것을 기억해 두세요.- 위의 예제에서
AppConfig
가com.acme
패키지(또는 그 하위 패키지) 내부에서 선언되었다고 가정하면,scan()
호출 중에AppConfig
도 선택이 됩니다.
- 위의 예제에서
refresh()
가 호출될 때에는 모든@Bean
메소드가 처리되고, 컨테이너 내에 bean definition으로 등록됩니다.
Support for Web Applications with AnnotationConfigWebApplicationContext
A
WebApplicationContext
variant ofAnnotationConfigApplicationContext
is available withAnnotationConfigWebApplicationContext
. You can use this implementation when configuring the SpringContextLoaderListener
servlet listener, Spring MVCDispatcherServlet
, and so forth. The followingweb.xml
snippet configures a typical Spring MVC web application (note the use of thecontextClass
context-param and init-param):
AnnotationConfigApplicationContext
의 WebApplicationContext
변형은 AnnotationConfigWebApplicationContext
와 함께 사용할 수 있습니다.
Spring ContextLoaderListener
servlet listener, Spring MVC DispatcherServlet
등을 configuring할 때 이 구현을 사용할 수 있습니다.
다음 web.xml
스니펫은 일반적인 Spring MVC 웹 애플리케이션을 configure 합니다.(contextClass
context-param과 init-param을 참고하세요)
<web-app>
<!-- Configure ContextLoaderListener to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<!-- Configuration locations must consist of one or more comma- or space-delimited
fully-qualified @Configuration classes. Fully-qualified packages may also be
specified for component-scanning -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.AppConfig</param-value>
</context-param>
<!-- Bootstrap the root application context as usual using ContextLoaderListener -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Declare a Spring MVC DispatcherServlet as usual -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- Configure DispatcherServlet to use AnnotationConfigWebApplicationContext
instead of the default XmlWebApplicationContext -->
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<!-- Again, config locations must consist of one or more comma- or space-delimited
and fully-qualified @Configuration classes -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.acme.web.MvcConfig</param-value>
</init-param>
</servlet>
<!-- map all requests for /app/* to the dispatcher servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
1.12.3. Using the @Bean
Annotation
@Bean
is a method-level annotation and a direct analog of the XML<bean/>
element. The annotation supports some of the attributes offered by<bean/>
, such as: * init-method * destroy-method * autowiring *name
.You can use the
@Bean
annotation in a@Configuration
-annotated or in a@Component
-annotated class.
@Bean
은 메소드 레벨 애노테이션이며, XML의 <bean/>
엘리먼트의 직접적으로 대응됩니다.
이 애노테이션은 <bean/>
이 제공하는 다음과 같은 이름을 가진 속성을 지원합니다.
- init-method, destroy-method, autowiring
@Bean
애노테이션은 @Configuration
이나 @Component
애노테이션이 붙은 클래스에서 사용할 수 있습니다.
Declaring a Bean
To declare a bean, you can annotate a method with the
@Bean
annotation. You use this method to register a bean definition within anApplicationContext
of the type specified as the method’s return value. By default, the bean name is the same as the method name. The following example shows a@Bean
method declaration:
bean을 선언하기 위해 @Bean
애노테이션을 메소드에 붙일 수 있습니다.
이 메소드의 리턴값으로 지정된 타입이 ApplicationContext
에 bean definition으로 등록됩니다.
기본적으로 bean 이름은 메소드 이름과 동일합니다.
다음 예제는 @Bean
메소드 선언을 보여줍니다.
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
The preceding configuration is exactly equivalent to the following Spring XML:
위의 configuration은 다음의 Spring XML과 완전히 똑같습니다.
<beans>
<bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>
Both declarations make a bean named
transferService
available in theApplicationContext
, bound to an object instance of typeTransferServiceImpl
, as the following text image shows:
두 선언 모두 ApplicationContext
에서 사용할 수 있는 transferService
라는 이름의 bean을 만듭니다. 이 bean의 객체 인스턴스 타입은 다음과 같이 TransferServiceImpl
입니다.
transferService -> com.acme.TransferServiceImpl
You can also declare your
@Bean
method with an interface (or base class) return type, as the following example shows:
다음 예제와 같이 인터페이스(또는 base class) 리턴 타입을 사용해 @Bean
메소드를 선언할 수도 있습니다.
@Configuration
public class AppConfig {
@Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
However, this limits the visibility for advance type prediction to the specified interface type (
TransferService
). Then, with the full type (TransferServiceImpl
) known to the container only once, the affected singleton bean has been instantiated. Non-lazy singleton beans get instantiated according to their declaration order, so you may see different type matching results depending on when another component tries to match by a non-declared type (such as@Autowired TransferServiceImpl
, which resolves only once thetransferService
bean has been instantiated).
그러나 이 방법은 고급 타입 추론의 범위를 지정된 인터페이스 타입(TransferService
)으로 제한합니다.
그러고나서, 컨테이너에 한 번만 알려진 full 타입(TransferServiceImpl
)의 영향을 받은 싱글톤 bean이 인스턴스화됩니다.
non-lazy 싱글톤 bean은 선언 순서에 따라 인스턴스화되므로, 다른 컴포넌트가 선언되지 않은 타입(@Autowired TransferServiceImpl
처럼 transferService
bean이 인스턴스화되면 resolve되는 것들)과 매칭되려고 하는 시기에 따라 다른 타입 매칭 결과를 볼 수도 있습니다.
If you consistently refer to your types by a declared service interface, your
@Bean
return types may safely join that design decision. However, for components that implement several interfaces or for components potentially referred to by their implementation type, it is safer to declare the most specific return type possible (at least as specific as required by the injection points that refer to your bean).
- 선언된 서비스 인터페이스에서 일관성있게 타입을 참조하는 경우,
@Bean
의 리턴 타입이 설계 결정에 안전하게 사용될 수 있습니다. - 그러나 여러 개의 인터페이스를 구현하는 컴포넌트나 구현 타입에 잠재적으로 참조되는 컴포넌트의 경우, 가능한 한 가장 구체적인 리턴 타입을 선언하는 것이 더 안전합니다.
- (적어도 bean을 참조하는 주입 지점에서 요구하는 정도로 구체적일 필요가 있습니다.)
Bean Dependencies
A
@Bean
-annotated method can have an arbitrary number of parameters that describe the dependencies required to build that bean. For instance, if ourTransferService
requires anAccountRepository
, we can materialize that dependency with a method parameter, as the following example shows:
@Bean
애노테이션이 붙은 메소드는 해당 bean을 빌드하는 데에 필요한 dependencies를 명시하기 위한 파라미터를 여러 개 가질 수 있습니다.
예를 들어 TransferService
가 AccountRepository
를 필요로 한다면, 다음 예제와 같이 메소드 파라미터를 사용해 dependency를 구체화할 수 있습니다.
@Configuration
public class AppConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
The resolution mechanism is pretty much identical to constructor-based dependency injection. See the relevant section for more details.
결정 메커니즘은 생성자 기반 dependency injection과 거의 같습니다. 자세한 내용은 관련 섹션을 참고하세요.
Receiving Lifecycle Callbacks
Any classes defined with the
@Bean
annotation support the regular lifecycle callbacks and can use the@PostConstruct
and@PreDestroy
annotations from JSR-250. See JSR-250 annotations for further details.
@Bean
애노테이션을 통해 정의된 모든 클래스는 일반적인 생명주기 콜백을 지원하고, JSR-250의 @PostConstruct
와 @PreDestroy
애노테이션을 사용할 수 있습니다.
자세한 내용은 JSR-250 annotations 문서를 참고하세요.
The regular Spring lifecycle callbacks are fully supported as well. If a bean implements
InitializingBean
,DisposableBean
, orLifecycle
, their respective methods are called by the container.
일반적인 Spring의 생명주기 콜백도 완벽하게 지원됩니다.
만약 bean 이 InitializingBean
, DisposableBean
, Lifecycle
의 구현체라면 생명주기와 관련된 메소드는 컨테이너에서 호출합니다.
The standard set of
*Aware
interfaces (such as BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware, and so on) are also fully supported.
*Aware
인터페이스의 표준 세트(BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware 등등)도 완전히 지원됩니다.
The
@Bean
annotation supports specifying arbitrary initialization and destruction callback methods, much like Spring XML’sinit-method
anddestroy-method
attributes on thebean
element, as the following example shows:
@Bean
애노테이션은 Spring XML의 bean
엘리먼트 속성인 init-method
, 그리고 destroy-method
와 마찬가지로 초기화 및 파괴 용도의 콜백 메소드를 지정할 수 있습니다.
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();
}
}
(i) By default, beans defined with Java configuration that have a public
close
orshutdown
method are automatically enlisted with a destruction callback. If you have a publicclose
orshutdown
method and you do not wish for it to be called when the container shuts down, you can add@Bean(destroyMethod="")
to your bean definition to disable the default(inferred)
mode.You may want to do that by default for a resource that you acquire with JNDI, as its lifecycle is managed outside the application. In particular, make sure to always do it for a
DataSource
, as it is known to be problematic on Java EE application servers.The following example shows how to prevent an automatic destruction callback for a
DataSource
:
- (i)
- 기본적으로
close
이나shutdown
이라는 이름을 가진 public 메소드를 갖고 있는 Java configuration으로 정의된 bean은 자동으로 destruction 콜백에 참여하게 됩니다.- 만약 public
close
이나shutdown
메소드가 있지만, 컨테이너가 종료(shuts down)될 때 호출하기를 원하지 않는다면@Bean(destroyMethod="")
와 같이 추가해서 기본(inferred)
모드를 disable할 수 있습니다.
- 만약 public
- JNDI를 통해 얻은 리소스의 생명주기가 애플리케이션 외부에서 관리되기 때문에, 이런 설정을 기본값으로 설정하고 싶다면 그렇게 할 수 있습니다.
- 특히 Java EE 애플리케이션 서버에서
DataSource
에 대해 문제가 있는 것으로 알려져 있으므로 항상 설정해 주도록 합니다.
- 특히 Java EE 애플리케이션 서버에서
- 다음 예제는
DataSource
에 대해 자동으로 destruction 콜백을 호출하는 것을 막는 방법을 보여줍니다.
- 기본적으로
@Bean(destroyMethod="")
public DataSource dataSource() throws NamingException {
return (DataSource) jndiTemplate.lookup("MyDS");
}
Also, with
@Bean
methods, you typically use programmatic JNDI lookups, either by using Spring’sJndiTemplate
orJndiLocatorDelegate
helpers or straight JNDIInitialContext
usage but not theJndiObjectFactoryBean
variant (which would force you to declare the return type as theFactoryBean
type instead of the actual target type, making it harder to use for cross-reference calls in other@Bean
methods that intend to refer to the provided resource here).
- (i) (이어서)
- 또한
@Bean
메소드를 사용할 때에는 일반적으로 프로그래밍 방식의 JNDI 룩업을 사용하게 됩니다.- Spring의
JndiTemplate
또는JndiLocatorDelegate
helpers를 사용하거나JndiObjectFactoryBean
의 변형체를 사용하지 않고 JNDIInitialContext
를 사용하여 프로그래밍 방식의 JNDI 조회를 사용합니다.
- Spring의
- 이는 리턴 타입을 실제 대상의 타입 대신
FactoryBean
타입으로 선언하도록 하여, 여기에 제공된 리소스를 참조하려는 다른@Bean
메소드에서 상호 참조 호출(cross-reference calls)에 사용하기 어렵게 만듭니다.
- 또한
In the case of
BeanOne
from the example above the preceding note, it would be equally valid to call theinit()
method directly during construction, as the following example shows:
위의 예제에서 BeanOne
의 경우 다음 예제와 같이 생성 중에 init()
메소드를 호출하는 것도 똑같이 유효한 방법입니다.
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
BeanOne beanOne = new BeanOne();
beanOne.init();
return beanOne;
}
// ...
}
When you work directly in Java, you can do anything you like with your objects and do not always need to rely on the container lifecycle.
- Java로 직접 작업할 때, 여러분의 객체만으로 여러분이 원하는 모든 작업을 수행할 수 있습니다. 항상 컨테이너의 생명주기에 의존하지 않아도 됩니다.
Specifying Bean Scope
Spring includes the
@Scope
annotation so that you can specify the scope of a bean.
Spring은 @Scope
애노테이션을 포함하고 있어, bean의 스코프를 지정할 수 있습니다.
Using the @Scope
Annotation
You can specify that your beans defined with the
@Bean
annotation should have a specific scope. You can use any of the standard scopes specified in the Bean Scopes section.
@Bean
애노테이션으로 정의된 bean이 특정 스코프를 갖도록 지정할 수 있습니다.
Bean Scopes 섹션에서 명시한 표준 스코프를 사용하면 됩니다.
The default scope is
singleton
, but you can override this with the@Scope
annotation, as the following example shows:
기본 스코프는 singleton
입니다. 그러나 다음 예제와 같이 @Scope
애노테이션을 사용해 이를 오버라이드할 수 있습니다.
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
@Scope
and scoped-proxy
Spring offers a convenient way of working with scoped dependencies through scoped proxies. The easiest way to create such a proxy when using the XML configuration is the
<aop:scoped-proxy/>
element. Configuring your beans in Java with a@Scope
annotation offers equivalent support with theproxyMode
attribute. The default isScopedProxyMode.DEFAULT
, which typically indicates that no scoped proxy should be created unless a different default has been configured at the component-scan instruction level. You can specifyScopedProxyMode.TARGET_CLASS
,ScopedProxyMode.INTERFACES
orScopedProxyMode.NO
.
Spring은 스코프가 지정된 프록시를 통해 scoped dependency로 작업하는 편리한 방법을 제공합니다.
XML configuration을 사용한다면 이런 프록시를 만드는 가장 쉬운 방법은 <aop:scoped-proxy/>
엘리먼트를 쓰는 것입니다.
@Scope
애노테이션을 써서 Java bean을 구성하면, XML 에서 proxyMode
속성을 사용한 것과 똑같이 지원됩니다.
기본값은 ScopedProxyMode.DEFAULT
이며, 이 기본값은 일반적으로 컴포넌트 스캔 레벨에서 다른 기본값이 configured되지 않은 경우에는 스코프 프록시를 생성하지 않아야 한다는 것을 보여줍니다.
지정할 수 있는 값은 ScopedProxyMode.TARGET_CLASS
, ScopedProxyMode.INTERFACES
or ScopedProxyMode.NO
입니다.
If you port the scoped proxy example from the XML reference documentation (see scoped proxies) to our
@Bean
using Java, it resembles the following:
XML 레퍼런스 문서(scoped proxies를 읽어보세요)에 나오는 스코프 프록시 에제를 @Bean
을 사용한 Java 코드로 보여준다면 다음과 같을 것입니다.
// 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
By default, configuration classes use a
@Bean
method’s name as the name of the resulting bean. This functionality can be overridden, however, with thename
attribute, as the following example shows:
기본적으로 configuration 클래스는 @Bean
메소드의 이름을 bean의 이름으로 사용합니다.
그러나 다음 예제와 같이 name
속성을 사용하면 이 기능을 오버라이드할 수 있습니다.
@Configuration
public class AppConfig {
@Bean(name = "myThing")
public Thing thing() {
return new Thing();
}
}
Bean Aliasing
As discussed in Naming Beans, it is sometimes desirable to give a single bean multiple names, otherwise known as bean aliasing. The
name
attribute of the@Bean
annotation accepts a String array for this purpose. The following example shows how to set a number of aliases for a bean:
Naming Beans에서 살펴본 바와 같이, 때때로 하나의 bean에 여러 개의 이름을 붙여주는 것이 바람직하기도 합니다.
이 기능은 bean aliasing이라 부릅니다.
@Bean
애노테이션의 name
속성은 이런 경우를 위해 String 배열을 받기도 합니다.
다음 예제는 하나의 bean에 여러 알리아스를 지정하는 방법을 보여줍니다.
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
Bean Description
Sometimes, it is helpful to provide a more detailed textual description of a bean. This can be particularly useful when beans are exposed (perhaps through JMX) for monitoring purposes.
때로는 bean에 자세한 텍스트 설명을 남겨두는 것이 도움이 되기도 합니다. 이렇게 하면 모니터링 목적으로 bean이 노출되었을 때(아마도 JMX를 통해서) 유용할 수 있습니다.
To add a description to a
@Bean
, you can use the @Description annotation, as the following example shows:
@Bean
에 설명을 남기려면 @Description
애노테이션을 사용하면 됩니다.
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
1.12.4. Using the @Configuration
annotation
@Configuration
is a class-level annotation indicating that an object is a source of bean definitions.@Configuration
classes declare beans through@Bean
annotated methods. Calls to@Bean
methods on@Configuration
classes can also be used to define inter-bean dependencies. See Basic Concepts: @Bean and @Configuration for a general introduction.
@Configuration
은 해당 객체가 bean definition 소스라는 것을 보여주기 위해 붙여주는 클래스 레벨 애노테이션입니다.
@Configuration
클래스는 @Bean
애노테이션을 붙인 메소드를 통해 bean을 선언합니다.
@Configuration
클래스에서 @Bean
메소드를 호출하여 bean과 bean 사이의 dependencies를 정의할 수 있습니다.
이에 대해서는 Basic Concepts: @Bean and @Configuration 문서를 참고하세요.
Injecting Inter-bean Dependencies
When beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another, as the following example shows:
bean들이 서로 서로 dependency를 갖고 있다는 것은 다음과 같이 bean 메소드가 다른 bean 메소드를 호출하는 코드로 간단하게 표현할 수 있습니다.
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
In the preceding example,
beanOne
receives a reference tobeanTwo
through constructor injection.
위의 예제에서 beanOne
은 생성자 주입을 통해 beanTwo
에 대한 참조를 넘겨받습니다.
(i) This method of declaring inter-bean dependencies works only when the
@Bean
method is declared within a@Configuration
class. You cannot declare inter-bean dependencies by using plain@Component
classes.
- (i)
- bean과 bean 사이의 dependencies를 선언하는 이런 메소드는
@Configuration
클래스 내에@Bean
메소드가 선언된 경우에만 동작합니다. - 평범한
@Component
클래스로는 bean 과 bean 사이의 dependencies를 선언할 수 없습니다.
- bean과 bean 사이의 dependencies를 선언하는 이런 메소드는
Lookup Method Injection
As noted earlier, lookup method injection is an advanced feature that you should use rarely. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this type of configuration provides a natural means for implementing this pattern. The following example shows how to use lookup method injection:
앞에서 언급한 바와 같이 lookup method injection은 거의 사용되지 않는 고급 기능입니다. 이 기능은 싱글톤 스코프의 bean이 프로토타입 스코프의 bean에 의존하는 경우에 유용합니다. Java로 작업을 하면 이런 종류의 configuration이 필요할 때 자연스럽게 작업할 수 있습니다. 다음 예제는 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();
}
By using Java configuration, you can create a subclass of
CommandManager
where the abstractcreateCommand()
method is overridden in such a way that it looks up a new (prototype) command object. The following example shows how to do so:
Java configuration을 사용하면 추상 메소드인 createCommand()
를 오버라이드한 메소드를 갖는 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();
}
}
}
Further Information About How Java-based Configuration Works Internally
Java 기반의 configuration이 내부적으로 어떻게 동작하는지에 대한 추가 정보 원문
Consider the following example, which shows a
@Bean
annotated method being called twice:
다음 예제는 @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()
has been called once inclientService1()
and once inclientService2()
. Since this method creates a new instance ofClientDaoImpl
and returns it, you would normally expect to have two instances (one for each service). That definitely would be problematic: In Spring, instantiated beans have asingleton
scope by default. This is where the magic comes in: All@Configuration
classes are subclassed at startup-time withCGLIB
. In the subclass, the child method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new instance.
clientDao()
는 clientService1()
에서 한 번 호출되고 clientService2()
에서도 한 번 호출됩니다.
이 메소드는 ClientDaoImpl
의 new instance를 생성해 리턴하므로, 일반적으로는 위의 코드에서 이 메소드는 clientService1
과 clientService2
각각을 위해 두 개의 인스턴스를 만들었을 것으로 생각할 수 있습니다.
이건 확실히 좀 이상해 보입니다. 왜냐하면 Spring에서 인스턴스화된 bean은 기본적으로 singleton
스코프를 갖게 되기 때문입니다.
그러나 여기에서 마법이 일어납니다. 모든 @Configuration
클래스는 startup-time 에 CGLIB를 이용해 서브 클래스화 되는 것입니다.
그리고 그 서브 클래스에서 자식 메소드는 부모 메소드를 호출하고, 새로운 인스턴스를 만들기 전에 먼저 캐시된(스코프가 지정된) bean이 컨테이너에 등록되었는지를 확인합니다.
(i) The behavior could be different according to the scope of your bean. We are talking about singletons here.
- (i)
- 이런 동작은 bean의 스코프에 따라 달라질 수 있습니다. 여기에서는 싱글톤에 대해서만 이야기하고 있습니다.
(i) As of Spring 3.2, it is no longer necessary to add CGLIB to your classpath because CGLIB classes have been repackaged under
org.springframework.cglib
and included directly within the spring-core JAR.
- (i)
- Spring 3.2 부터는 CGLIB 클래스가
org.springframework.cglib
의 하위에 다시 패키징되고 spring-core JAR에 직접 포함됩니다. 따라서 classpath에 CGLIB를 추가하지 않아도 됩니다.
- Spring 3.2 부터는 CGLIB 클래스가
There are a few restrictions due to the fact that CGLIB dynamically adds features at startup-time. In particular, configuration classes must not be final. However, as of 4.3, any constructors are allowed on configuration classes, including the use of
@Autowired
or a single non-default constructor declaration for default injection.If you prefer to avoid any CGLIB-imposed limitations, consider declaring your
@Bean
methods on non-@Configuration
classes (for example, on plain@Component
classes instead). Cross-method calls between@Bean
methods are not then intercepted, so you have to exclusively rely on dependency injection at the constructor or method level there.
- CGLIB가 startup-time에 몇몇 기능들을 동적으로 추가한다는 사실 때문에 몇 가지 제약 사항이 있을 수 있습니다.
- configuration 클래스가
final
이면 안 됩니다.
- configuration 클래스가
- 하지만 다음은 허용됩니다.
- Spring 4.3 부터는 configuration 클래스에 모든 종류의 생성자가 허용되기 때문에, default injection을 위해
@Autowired
방식과 기본 생성자가 아닌 단일 생성자(single non-default constructor)를 사용할 수도 있습니다.
- Spring 4.3 부터는 configuration 클래스에 모든 종류의 생성자가 허용되기 때문에, default injection을 위해
- 만약 CGLIB 때문에 발생하는 제약을 피하고 싶다면
@Configuration
이 아닌 클래스에서@Bean
메소드를 선언하는 것도 고려해 보세요.- 예를 들어
@Component
클래스라던가… - 이렇게 하면
@Bean
메소드들 사이의 교차적인 호출을 인터셉트하지 않으므로, 생성자나 메소드 레벨에서의 dependency injection만 사용해야 합니다.
- 예를 들어
1.12.5. Composing Java-based Configurations
Spring’s Java-based configuration feature lets you compose annotations, which can reduce the complexity of your configuration.
Spring의 Java 기반 configuration 기능을 사용하면, 여러분만의 애노테이션을 만들 수 있으므로 configuration의 복잡도를 줄일 수 있습니다.
Using the @Import
Annotation
Much as the
<import/>
element is used within Spring XML files to aid in modularizing configurations, the@Import
annotation allows for loading@Bean
definitions from another configuration class, as the following example shows:
Spring XML 파일에서 configuration을 모듈화하는데 <import/>
엘리먼트를 사용하는 것처럼, @Import
애노테이션을 사용하면 다른 configuration 클래스에서 @Bean
definition을 로딩할 수 있습니다.
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
Now, rather than needing to specify both
ConfigA.class
andConfigB.class
when instantiating the context, onlyConfigB
needs to be supplied explicitly, as the following example shows:
이제 컨텍스트를 인스턴스화할 때 ConfigA.class
와 ConfigB.class
를 모두 지정할 필요 없이, 다음 예제와 같이 ConfigB
만 명시적으로 제공하면 됩니다.
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}
This approach simplifies container instantiation, as only one class needs to be dealt with, rather than requiring you to remember a potentially large number of
@Configuration
classes during construction.
이 방법은 애플리케이션을 생성하는 동안 많은 수의 @Configuration
클래스를 기억할 필요 없이 한 개의 클래스만 처리하면 되므로 컨테이너 인스턴스를 단순화합니다.
As of Spring Framework 4.2,
@Import
also supports references to regular component classes, analogous to theAnnotationConfigApplicationContext.register
method. This is particularly useful if you want to avoid component scanning, by using a few configuration classes as entry points to explicitly define all your components.
- Spring 프레임워크 4.2 부터
@Import
는AnnotationConfigApplicationContext.register
메소드처럼, 일반적인 컴포넌트 클래스에 대한 참조도 지원합니다. - 이 방법은 컴포넌트 스캐닝을 고의로 피할 일이 있을 떄 유용합니다.
Injecting Dependencies on Imported @Bean Definitions
The preceding example works but is simplistic. In most practical scenarios, beans have dependencies on one another across configuration classes. When using XML, this is not an issue, because no compiler is involved, and you can declare
ref="someBean"
and trust Spring to work it out during container initialization. When using@Configuration
classes, the Java compiler places constraints on the configuration model, in that references to other beans must be valid Java syntax.
앞의 예는 작동하긴 하지만 단순하다는 문제가 있습니다.
실제로는 대부분의 경우에 bean은 여러 configuration 클래스와 의존 관계를 갖게 됩니다.
XML을 사용할 때에는 컴파일러가 XML에 관여하지 않기도 하고, ref="someBean"
을 선언하면 컨테이너가 초기화할 때 Spring이 알아서 잘 처리하기 때문에 그런 의존 관계는 문제가 되지 않습니다.
하지만 @Configuration
클래스를 사용할 때에는 Java 컴파일러 때문에 다른 bean에 대한 참조가 valid한 Java 신택스여야 한다는 점에서 configuration 모델에 제약이 생길 수 밖에 없습니다.
Fortunately, solving this problem is simple. As we already discussed, a
@Bean
method can have an arbitrary number of parameters that describe the bean dependencies. Consider the following more real-world scenario with several@Configuration
classes, each depending on beans declared in the others:
다행히, 이런 문제를 해결하는 방법은 간단합니다.
앞에서 이야기한 바에 따르면 @Bean
메소드는 bean 의존관계를 표현하는 여러 개의 파라미터를 가질 수 있었죠.
다음 예제를 봅시다. 각각 다른 클래스에서 선언된 bean에 의존하는 여러 개의 @Configuration
클래스가 있는 실제 가능한 시나리오입니다.
@Configuration
public class ServiceConfig {
@Bean
public TransferService transferService(AccountRepository accountRepository) {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
@Bean
public AccountRepository accountRepository(DataSource dataSource) {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
There is another way to achieve the same result. Remember that
@Configuration
classes are ultimately only another bean in the container: This means that they can take advantage of@Autowired
and@Value
injection and other features the same as any other bean.
이와 동일한 결과를 얻는 다른 방법도 있습니다.
@Configuration
클래스도 결국엔 컨테이너에 들어가는 또다른 bean일 뿐이라는 것을 떠올려 봅시다.
즉, @Autowired
와 @Value
주입 및 다른 bean과 동일한 다양한 기능들을 사용할 수 있다는 것입니다.
(!) Make sure that the dependencies you inject that way are of the simplest kind only.
@Configuration
classes are processed quite early during the initialization of the context, and forcing a dependency to be injected this way may lead to unexpected early initialization. Whenever possible, resort to parameter-based injection, as in the preceding example.Also, be particularly careful with
BeanPostProcessor
andBeanFactoryPostProcessor
definitions through@Bean
. Those should usually be declared asstatic @Bean
methods, not triggering the instantiation of their containing configuration class. Otherwise,@Autowired
and@Value
may not work on the configuration class itself, since it is possible to create it as a bean instance earlier than AutowiredAnnotationBeanPostProcessor.
- (!)
- 위와 같은 방법으로 주입하는 dependencies가 가장 단순한 종류의 dependencies인지 확인하도록 하세요.
Configuration
클래스는 컨텍스트 초기화 중에 상당히 일ㄹ찍 처리되기 때문에, 이런 방식으로 dependency를 강제로 주입하면 예상하지 못한 조기 초기화가 발생할 수 있습니다.- 가능하면 앞의 예제처럼 파라미터 기반의 주입에 의존하도록 합시다.
- 또한
@Bean
을 통한BeanPostProcessor
와BeanFactoryPostProcessor
의 definition에 특별히 주의하도록 합시다.- 이것들은 일반적으로 포함하는 configuration 클래스의 인스턴스화를 트리거하지 않고
static @Bean
메소드로 선언되어야 합니다. - 이렇게 하지 않으면
AutowiredAnnotationBeanPostProcessor
보다 먼저 bean 인스턴스로 생성될 수 있기 때문에@Autowired
와@Value
가 configuration 클래스 자체에서 작동하지 않을 수도 있습니다.
- 이것들은 일반적으로 포함하는 configuration 클래스의 인스턴스화를 트리거하지 않고
- 위와 같은 방법으로 주입하는 dependencies가 가장 단순한 종류의 dependencies인지 확인하도록 하세요.
The following example shows how one bean can be autowired to another bean:
다음 예제는 하나의 bean이 다른 bean에 어떻게 autowired 될 수 있는지 보여줍니다.
@Configuration
public class ServiceConfig {
@Autowired
private AccountRepository accountRepository;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(accountRepository);
}
}
@Configuration
public class RepositoryConfig {
private final DataSource dataSource;
public RepositoryConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
}
@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return new DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
// everything wires up across configuration classes...
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
Constructor injection in
@Configuration
classes is only supported as of Spring Framework 4.3. Note also that there is no need to specify@Autowired
if the target bean defines only one constructor.
@Configuration
클래스의 생성자 주입은 Spring 프레임워크 4.3 부터 지원됩니다.
타깃 bean에 딱 한 개의 생성자만 정의된 경우라면 @Autowired
애노테이션을 붙일 필요가 없다는 점도 참고해 둡시다.
Fully-qualifying imported beans for ease of navigation
In the preceding scenario, using
@Autowired
works well and provides the desired modularity, but determining exactly where the autowired bean definitions are declared is still somewhat ambiguous. For example, as a developer looking atServiceConfig
, how do you know exactly where the@Autowired AccountRepository
bean is declared? It is not explicit in the code, and this may be just fine. Remember that the Spring Tools for Eclipse provides tooling that can render graphs showing how everything is wired, which may be all you need. Also, your Java IDE can easily find all declarations and uses of theAccountRepository
type and quickly show you the location of@Bean
methods that return that type.
위의 시나리오를 통해 @Autowired
가 잘 작동하고 필요한 모듈화를 잘 제공한다는 것은 알 수 있었지만,
autowired bean definition이 선언된 정확한 위치를 결정하는 것은 아직 모호하게 느껴집니다.
예를 들어, ServiceConfig
를 읽고 있는 개발자가 있을 때, 이 개발자가 어떻게 @Autowired AccountRepository
bean이 선언된 위치를 정확히 알 수 있을까요?
이것은 코드에 명시된 정보는 아니지만, 큰 문제는 아닙니다.
STS는 모든 것들이 서로 어떻게 연결되어 있는지 보여주는 그래프 도구를 제공하며, 여러분에게는 이것만으로도 충분할 수 있습니다.
또한, Java IDE는 AccountRepository
타입의 모든 선언과 사용처를 쉽게 찾아주고, 해당 타입을 리턴하는 @Bean
메소드의 위치도 빠르게 찾아 보여줄 수 있을 겁니다.
In cases where this ambiguity is not acceptable and you wish to have direct navigation from within your IDE from one
@Configuration
class to another, consider autowiring the configuration classes themselves. The following example shows how to do so:
이러한 애매모호함이 싫고, IDE 내에서 @Configuration
클래스들 사이를 직접 탐색하려 한다면, configuration 클래스들 자체를 autowiring하는 것을 고려해볼 수 있습니다.
다음 예제는 그 방법을 보여줍니다.
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
// navigate 'through' the config class to the @Bean method!
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
In the preceding situation, where
AccountRepository
is defined is completely explicit. However,ServiceConfig
is now tightly coupled toRepositoryConfig
. That is the tradeoff. This tight coupling can be somewhat mitigated by using interface-based or abstract class-based@Configuration
classes. Consider the following example:
위와 같은 상황에서 AccountRepository
가 정의된 위치는 완전히 명시적입니다.
하지만 이제 ServiceConfig
는 RepositoryConfig
와 강하게 커플링이 생겨버렸습니다. 이것이 바로 이 방법의 트레이드 오프입니다.
이런 강한 결합은 인터페이스 기반 또는 추상 클래스 기반의 @Configuration
클래스를 사용해서 어느 정도 완화할 수 있기는 합니다.
다음 예를 봅시다.
@Configuration
public class ServiceConfig {
@Autowired
private RepositoryConfig repositoryConfig;
@Bean
public TransferService transferService() {
return new TransferServiceImpl(repositoryConfig.accountRepository());
}
}
@Configuration
public interface RepositoryConfig {
@Bean
AccountRepository accountRepository();
}
@Configuration
public class DefaultRepositoryConfig implements RepositoryConfig {
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(...);
}
}
@Configuration
@Import({ServiceConfig.class, DefaultRepositoryConfig.class}) // import the concrete config!
public class SystemTestConfig {
@Bean
public DataSource dataSource() {
// return DataSource
}
}
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
transferService.transfer(100.00, "A123", "C456");
}
Now
ServiceConfig
is loosely coupled with respect to the concreteDefaultRepositoryConfig
, and built-in IDE tooling is still useful: You can easily get a type hierarchy ofRepositoryConfig
implementations. In this way, navigating@Configuration
classes and their dependencies becomes no different than the usual process of navigating interface-based code.
이제 ServiceConfig
는 구체적인 DefaultRepositoryConfig
와는 느슨하게 결합되게 되었습니다. 게다가 편리한 IDE 빌트인 도구도 사용할 수 있으니까 RepositoryConfig
구현 계층 구조를 쉽게 확인할 수 있습니다.
이런 방식으로 @Configuration
클래스와 그와 관련된 dependencies를 탐색하는 것은 인터페이스 기반의 코드를 탐색하는 일반적인 과정과 그리 다르지 않습니다.
If you want to influence the startup creation order of certain beans, consider declaring some of them as
@Lazy
(for creation on first access instead of on startup) or as@DependsOn
certain other beans (making sure that specific other beans are created before the current bean, beyond what the latter’s direct dependencies imply).
- 특정한 bean의 시작 생성 순서를 제어하고 싶다면, 그런 bean을
@Lazy
로 선언하거나(이렇게 하면 시작할 때 생성되지 않고, 처음으로 객체에 엑세스할 때 생성이 됩니다), 다른 특정 bean을@DependsOn
로 지정하는 방법이 있습니다(이렇게 하면@DependsOn
으로 설정한 다른 bean이 먼저 생성됩니다)
Conditionally Include @Configuration
Classes or @Bean
Methods
It is often useful to conditionally enable or disable a complete
@Configuration
class or even individual@Bean
methods, based on some arbitrary system state. One common example of this is to use the@Profile
annotation to activate beans only when a specific profile has been enabled in the SpringEnvironment
(see Bean Definition Profiles for details).
시스템 상태에 따라 전체적인 @Configuration
클래스나 개별 @Bean
메소드를 조건부로 enable 하거나 disable하는 유용한 방법도 있습니다.
예를 들어 @Profile
애노테이션을 써서 특정 프로파일이 Spring 환경에서 활성화된 경우에만 bean을 enable할 수 있습니다. (자세한 내용은 Bean Definition Profiles를 참고하세요)
The
@Profile
annotation is actually implemented by using a much more flexible annotation called @Conditional. The@Conditional
annotation indicates specificorg.springframework.context.annotation.Condition
implementations that should be consulted before a@Bean
is registered.
@Profile
애노테이션은 실제로는 @Conditional
이라는 훨씬 더 유연한 애노테이션을 사용하여 구현됩니다.
@Conditional
애노테이션은 @Bean
이 등록되기 전에 참조해야 하는 org.springframework.context.annotation.Condition
의 구현을 나타냅니다.
Implementations of the
Condition
interface provide amatches(…)
method that returnstrue
orfalse
. For example, the following listing shows the actualCondition
implementation used for@Profile
:
Condition
인터페이스의 구현은 match(...)
메소드를 제공하며, 이 메소드는 true
, 나 false
를 리턴합니다.
예를 들어 다음 목록은 @Profile
에 사용된 실제 Condition
의 구현을 보여줍니다.
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// Read the @Profile annotation attributes
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
See the @Conditional javadoc for more detail.
더 자세한 내용은 @Conditional
의 javadoc을 참고하세요.
Combining Java and XML Configuration
Spring’s @Configuration class support does not aim to be a 100% complete replacement for Spring XML. Some facilities, such as Spring XML namespaces, remain an ideal way to configure the container. In cases where XML is convenient or necessary, you have a choice: either instantiate the container in an “XML-centric” way by using, for example, ClassPathXmlApplicationContext, or instantiate it in a “Java-centric” way by using AnnotationConfigApplicationContext and the @ImportResource annotation to import XML as needed.
XML-centric Use of @Configuration
Classes
It may be preferable to bootstrap the Spring container from XML and include
@Configuration
classes in an ad-hoc fashion. For example, in a large existing codebase that uses Spring XML, it is easier to create@Configuration
classes on an as-needed basis and include them from the existing XML files. Later in this section, we cover the options for using@Configuration
classes in this kind of “XML-centric” situation.
XML에서 Spring 컨테이너를 부트스트랩하고, @Configuration
클래스를 포함시키는 일이 바람직할 수도 있습니다.
예를 들어 Spring XML을 사용하는 대규모의 코드베이스에서는 @Configuration
클래스를 먼저 만들고, 필요에 따라 이미 존재하는 XML 파일을 include하는 것이 더 쉽습니다.
이 섹션의 뒷부분에서는 이런 종류의 "XML-위주"의 상황에서 @Configuration
클래스를 사용하는 옵션을 다룹니다.
Declaring @Configuration
classes as plain Spring <bean/>
elements
Remember that
@Configuration
classes are ultimately bean definitions in the container. In this series examples, we create a@Configuration
class namedAppConfig
and include it withinsystem-test-config.xml
as a<bean/>
definition. Because<context:annotation-config/>
is switched on, the container recognizes the@Configuration
annotation and processes the@Bean
methods declared inAppConfig
properly.The following example shows an ordinary configuration class in Java:
@Configuration
클래스는 결국엔 컨테이너에 들어갈 bean definition들이라는 것을 잊지 마세요.
다음의 예제들은 AppConfig
라는 @Configuration
클래스를 만들고, system-test-config.xml
파일에 <bean/>
definition으로 include 합니다.
왜냐하면 <context:annotation-config/>
가 on 으로 켜져 있기 때문에, 컨테이너는 @Configuration
애노테이션을 인식하고, AppConfig
에 선언된 @Bean
메소드를 제대로 처리합니다.
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
The following example shows part of a sample
system-test-config.xml
file:
<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
The following example shows a possible
jdbc.properties
file:
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
(i) In
system-test-config.xml
file, theAppConfig
<bean/>
does not declare anid
element. While it would be acceptable to do so, it is unnecessary, given that no other bean ever refers to it, and it is unlikely to be explicitly fetched from the container by name. Similarly, theDataSource
bean is only ever autowired by type, so an explicit beanid
is not strictly required.
- (i)
system-test-config.xml
파일에서,AppConfig
<bean/>
은id
엘리먼트를 선언하지 않았습니다.id
를 선언할 수도 있었지만, 다른 bean이 그id
를 참조하지도 않고, 컨테이너에서 이름으로 가져갈 가능성도 없기 때문에 선언이 필요하지 않습니다.- 그와 마찬가지로
DataSource
bean도 명시적인 beanid
가 필요하지 않은데, 타입에 의해 autowired 되기 때문입니다.
Using <context:component-scan/>
to pick up @Configuration
classes
Because
@Configuration
is meta-annotated with@Component
,@Configuration
-annotated classes are automatically candidates for component scanning. Using the same scenario as describe in the previous example, we can redefinesystem-test-config.xml
to take advantage of component-scanning. Note that, in this case, we need not explicitly declare<context:annotation-config/>
, because<context:component-scan/>
enables the same functionality.The following example shows the modified
system-test-config.xml
file:
@Configuration
에는 메타 애노테이션으로 @Component
가 붙어있기 때문에, @configuration
애노테이션이 붙은 클래스들은 자동으로 컴포넌트 스캔의 후보가 됩니다.
앞의 예제에서 설명한 것과 똑같은 시나리오로 컴포넌트 스캔이 작동하도록 system-test-config.xml
을 재정의할 수 있습니다.
이런 경우에는 <context:component-scan/>
이 같은 기능을 enable하므로 <context:annotation-config/>
를 명시적으로 선언하지 않아도 됩니다.
다음 예제는 수정된 system-test-config.xml
파일을 보여줍니다.
<beans>
<!-- picks up and registers AppConfig as a bean definition -->
<context:component-scan base-package="com.acme"/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
@Configuration
Class-centric Use of XML with @ImportResource
In applications where
@Configuration
classes are the primary mechanism for configuring the container, it is still likely necessary to use at least some XML. In these scenarios, you can use@ImportResource
and define only as much XML as you need. Doing so achieves a “Java-centric” approach to configuring the container and keeps XML to a bare minimum. The following example (which includes a configuration class, an XML file that defines a bean, a properties file, and themain
class) shows how to use the@ImportResource
annotation to achieve “Java-centric” configuration that uses XML as needed:
@Configuration
클래스가 컨테이너 confire를 위한 primary 메커니즘인 애플리케이션에서는, 어느 정도의 최소한의 XML을 사용할 수 밖에 없습니다.
이런 시나리오에서는 @ImportResource
를 사용해서 필요한 만큼의 XML만을 정의하는 방법을 쓸 수 있습니다.
이렇게 하면 "Java-중심"의 방법으로 컨테이너를 configure할 수 있으며, XML은 최소한으로 관리할 수 있습니다.
다음 예제(configuration 클래스와 bean을 정의하는 XML, properties 파일, main
클래스가 포함된 예제)는 @ImportResource
애노테이션을 사용해 "Java-중심" configuration을 달성하는 방법을 보여줍니다.
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}
properties-config.xml
<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
TransferService transferService = ctx.getBean(TransferService.class);
// ...
}
함께 읽기
- 목록으로 - [[/spring/document/core]]{Spring Core Technologies}
- 이전 문서 - [[/spring/document/core/01-11-using-jsr-330-standard-annotations]]{1.11. Using JSR 330 Standard Annotations}
- 다음 문서 - [[/spring/document/core/01-13-env-abstraction]]{1.13. Environment Abstraction}