Customizing the Nature of a Bean
Spring 프레임워크는 빈의 특성을 사용자 정의하는 데 사용할 수 있는 다양한 인터페이스를 제공합니다. 이 섹션에서는 이러한 인터페이스를 다음과 같이 그룹화합니다:
Lifecycle Callbacks
컨테이너의 빈 라이프사이클 관리와 상호 작용하기 위해 Spring InitializingBean 및 DisposableBean 인터페이스를 구현할 수 있습니다. 컨테이너는 빈의 초기화 및 소멸 시 빈이 특정 작업을 수행하도록 하기 위해 전자의 경우afterPropertiesSet(), 후자의 경우 destroy( )를 호출합니다.
JSR-250 @PostConstruct 및 @PreDestroy 어노테이션은 일반적으로 최신 Spring 애플리케이션에서 수명 주기 콜백을 수신하기 위한 모범 사례로 간주됩니다. 이러한 어노테이션을 사용한다는 것은 빈이 Spring 전용 인터페이스에 연결되지 않는다는 것을 의미합니다. 자세한 내용은 @PostConstruct 및 @PreDestroy 사용을 참조하세요.
JSR-250 어노테이션을 사용하고 싶지 않지만 여전히 결합을 제거하려는 경우 init-method 및 destroy-method 빈 정의 메타데이터를 고려하세요.
|
내부적으로 Spring 프레임워크는 BeanPostProcessor 구현을 사용하여 찾을 수 있는 모든 콜백 인터페이스를 처리하고 적절한 메서드를 호출합니다. Spring이 기본적으로 제공하지 않는 사용자 정의 기능이나 기타 라이프사이클 동작이 필요한 경우 BeanPostProcessor를 직접 구현할 수 있습니다. 자세한 내용은컨테이너 확장 포인트를 참조하세요.
초기화 및 소멸 콜백 외에도 Spring 관리 객체는 컨테이너의 자체 라이프사이클에 따라 해당 객체가 시작 및 종료 프로세스에 참여할 수 있도록 라이프사이클 인터페이스를 구현할 수도 있습니다.
이 섹션에서는 라이프사이클 콜백 인터페이스에 대해 설명합니다.
초기화 콜백
Org.springframework.beans.factory.InitializingBean 인터페이스는 컨테이너가 빈에 필요한 모든 속성을 설정한 후 빈이 초기화 작업을 수행할 수 있도록 합니다. InitializingBean 인터페이스는 단일 메서드를 지정합니다:
void afterPropertiesSet() throws Exception;
불필요하게 Spring에 코드를 결합하므로 InitializingBean 인터페이스를 사용하지 않는 것이 좋습니다. 또는 @PostConstruct 어노테이션을 사용하거나 POJO 초기화 메서드를 지정하는 것이 좋습니다. XML 기반 구성 메타데이터의 경우, init-method 속성을 사용하여 무효 인수 없음 서명이 있는 메서드의 이름을 지정할 수 있습니다. Java 구성의 경우@Bean의 initMethod 속성을 사용할 수 있습니다. 수명 주기 콜백 받기를 참조하세요. 다음 예제를 살펴보세요:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean {
public void init() {
// do some initialization work
}
}
앞의 예는 다음 예(두 개의 목록으로 구성됨)와 거의 동일한 효과를 가져옵니다:
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
그러나 앞의 두 예제 중 첫 번째 예제는 코드를 Spring에 결합하지 않습니다.
일반적으로 @PostConstruct와 초기화 메서드는 컨테이너의 싱글톤 생성 잠금 내에서 실행된다는 점에 유의하세요. 빈 인스턴스는@PostConstruct 메서드에서 반환된 후에만 완전히 초기화되고 다른 사용자에게 게시할 준비가 된 것으로 간주됩니다. 이러한 개별 초기화 메서드는 구성 상태를 확인하고 주어진 구성을 기반으로 일부 데이터 구조를 준비하기 위한 것일 뿐 외부 빈 액세스를 통한 추가 작업은 수행하지 않습니다. 그렇지 않으면 초기화 데드락이 발생할 위험이 있습니다.
비동기 데이터베이스 준비 단계와 같이 비용이 많이 드는 초기화 후 활동이 트리거되는 시나리오의 경우, 빈은SmartInitializingSingleton.afterSingletonsInstantiated() 를 구현하거나 컨텍스트 새로 고침 이벤트에 의존해야 합니다. ApplicationListener<ContextRefreshedEvent>를 구현하거나 그에 해당하는 어노테이션 @EventListener(ContextRefreshedEvent.class)를 선언하는 것입니다. 이러한 변종은 모든 일반 싱글톤 초기화 후에 제공되므로 싱글톤 생성 잠금 외부에서 이루어집니다.
또는 (스마트)라이프사이클 인터페이스를 구현하고 자동 시작 메커니즘, 파괴 전 중지 단계, 잠재적인 중지/재시작 콜백을 포함한 컨테이너의 전반적인 라이프사이클 관리와 통합할 수도 있습니다(아래 참조).
|
파기 콜백
Org.springframework.beans.factory.DisposableBean 인터페이스를 구현하면 빈이 포함된 컨테이너가 소멸될 때 콜백을 받을 수 있습니다.DisposableBean 인터페이스는 단일 메서드를 지정합니다:
void destroy() throws Exception;
DisposableBean 콜백 인터페이스는 코드를 Spring에 불필요하게 결합하므로 사용하지 않는 것이 좋습니다. 또는 @PreDestroy 어노테이션을 사용하거나 빈 정의에서 지원되는 일반 메서드를 지정하는 것이 좋습니다. XML 기반 구성 메타데이터를 사용하는 경우 <bean/>에서 destroy-method 속성을 사용할 수 있습니다 . Java 구성을 사용하는 경우 @Bean의 destroyMethod 속성을 사용할 수 있습니다.라이프사이클 콜백 받기를 참조하세요. 다음 정의를 고려해 보세요:
<bean id="exampleDestructionBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
앞의 정의는 다음 정의와 거의 동일한 효과를 가져옵니다:
<bean id="exampleDestructionBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
그러나 앞의 두 정의 중 첫 번째 정의는 코드를 Spring에 결합하지 않습니다.
Spring은 공개 닫기 또는종료 메서드를 감지하는 파괴 메서드 추론도 지원합니다. 이는 Java 구성 클래스에서 @Bean 메서드의 기본 동작이며 java.lang.AutoCloseable 또는 java.io.Closeable구현과 자동으로 일치하며, 소멸 로직도 Spring에 결합하지 않습니다.
XML을 사용한 소멸 메서드 추론의 경우, 특정 빈 정의에 대한 빈 클래스에서 공용 닫기 또는 종료 메서드를 자동으로 감지하도록 Spring에 지시하는 특수 (추 론) 값을 <bean> 요소의 destroy-method 속성에 지정할 수 있습니다. 이 특수 (추론 ) 값을 <beans> 요소의 default-destroy-method 속성에 설정하여 전체 빈 정의 집합에 이 동작을 적용할 수도 있습니다(기본 초기화 및 소멸 메서드 참조) |
확장된 종료 단계의 경우, 수명 주기 인터페이스를 구현하고 싱글톤 빈의 삭제 메서드가 호출되기 전에 조기 중지 신호를 받을 수 있습니다. 또한 컨테이너가 이러한 모든 중지 처리가 완료될 때까지 기다렸다가 삭제 메서드로 이동하는 시간 제한 중지 단계에 대해 SmartLifecycle을 구현할 수도 있습니다.
|
기본 초기화 및 소멸 메서드
Spring 전용 InitializingBean 및 DisposableBean 콜백 인터페이스를 사용하지 않는 초기화 및 삭제 메서드 콜백을 작성할 때는 일반적으로 init(), initialize(), dispose() 등과 같은 이름을 가진 메서드를 작성합니다. 이러한 라이프사이클 콜백 메서드의 이름은 프로젝트 전체에서 표준화되어 모든 개발자가 동일한 메서드 이름을 사용하고 일관성을 보장하는 것이 이상적입니다.
모든 빈에서 명명된 초기화 및 삭제 콜백 메서드 이름을 "찾도록" Spring 컨테이너를 구성할 수 있습니다. 즉, 애플리케이션 개발자는 각 빈 정의에 init-method="init" 속성을 구성할 필요 없이 애플리케이션 클래스를 작성하고 init()라는 초기화 콜백을 사용할 수 있습니다. Spring IoC 컨테이너는 빈이 생성될 때(그리고 앞서 설명한 표준 수명 주기 콜백 계약에 따라) 해당 메서드를 호출합니다. 이 기능은 또한 초기화 및 삭제 메서드 콜백에 대해 일관된 명명 규칙을 적용합니다.
초기화 콜백 메서드의 이름이 init(), 소멸 콜백 메서드의 이름이 destroy()라고 가정해 보겠습니다. 그러면 여러분의 클래스는 다음 예제의 클래스와 비슷해집니다:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
그런 다음 다음과 유사한 빈에서 해당 클래스를 사용할 수 있습니다:
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
최상위 <beans/> 요소 속성에 default-init-method 속성이 있으면 Spring IoC 컨테이너가 빈 클래스에서 init이라는 메서드를 초기화 메서드 콜백으로 인식하게 됩니다. 빈이 생성되고 어셈블될 때 빈 클래스에 이러한 메서드가 있는 경우 적절한 시점에 호출됩니다.
XML에서는 최상위 <beans/> 요소에default-destroy-method 속성을 사용하여 이와 유사하게 메서드 소멸 콜백을 구성할 수 있습니다(즉, XML에서는).
기존 빈 클래스에 이미 규칙과 다르게 명명된 콜백 메서드가 있는 경우, <bean/>자체의 init-method 및 destroy-method 속성을 사용하여 메서드 이름을 지정하여(즉, XML에서) 기본값을 재정의할 수 있습니다.
Spring 컨테이너는 빈에 모든 종속성이 제공된 직후에 구성된 초기화 콜백이 호출되도록 보장합니다. 따라서 초기화 콜백은 원시 빈 참조에서 호출되며, 이는 AOP 인터셉터 등이 아직 빈에 적용되지 않았음을 의미합니다. 대상 빈이 먼저 완전히 생성된 다음 해당 인터셉터 체인이 포함된 AOP 프록시(예:)가 적용됩니다. 대상 빈과 프록시가 별도로 정의된 경우, 코드가 프록시를 우회하여 원시 대상 빈과 상호 작용할 수도 있습니다. 따라서 인터셉터를 초기화 메서드에 적용하면 대상 빈의 라이프사이클이 프록시 또는 인터셉터에 결합되고 코드가 원시 대상 빈과 직접 상호 작용할 때 이상한 의미가 남게 되므로 일관성이 떨어집니다.
라이프사이클 메커니즘 결합
Spring 2.5부터는 빈 라이프사이클 동작을 제어하기 위한 세 가지 옵션이 있습니다:
- InitializingBean 및DisposableBean 콜백 인터페이스
- 사용자 정의 init() 및 destroy() 메서드
- PostConstruct 및 @PreDestroy 어노테이션
-
- 이러한 메커니즘을 결합하여 주어진 빈을 제어할 수 있습니다.
하나의 빈에 대해 여러 수명 주기 메커니즘이 구성되고 각 메커니즘이 다른 메서드 이름으로 구성된 경우, 구성된 각 메서드는 이 메모 뒤에 나열된 순서대로 실행됩니다. 그러나 동일한 메서드 이름(예: 초기화 메서드의 경우init()) 이 하나 이상의 수명 주기 메커니즘에 대해 구성된 경우이전 섹션에서 설명한 대로 해당 메서드가 한 번 실행됩니다 |
동일한 빈에 대해 서로 다른 초기화 메서드를 사용하여 구성된 여러 라이프사이클 메커니즘은 다음과 같이 호출됩니다:
- PostConstruct로 어노테이션된 메서드
- afterPropertiesSet() 으로 주석 처리된 메서드, InitializingBean 콜백 인터페이스에 정의된 메서드
- 사용자 정의 구성된 init() 메서드
파괴 메서드는 동일한 순서로 호출됩니다:
- PreDestroy로 주석이 달린 메서드
- destroy( )로 주석 처리된 메서드, DisposableBean 콜백 인터페이스에 정의된 메서드
- 사용자 정의 구성된 destroy() 메서드
시작 및 종료 콜백
라이프사이클 인터페이스는 자체 라이프사이클 요구 사항(예: 일부 백그라운드 프로세스 시작 및 중지)이 있는 모든 객체에 대한 필수 메서드를 정의합니다:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
모든 Spring 관리 객체는 라이프사이클 인터페이스를 구현할 수 있습니다. 그런 다음애플리케이션 컨텍스트 자체가 시작 및 중지 신호를 수신하면(예: 런타임에 중지/재시작 시나리오) 해당 컨텍스트 내에 정의된 모든 라이프사이클 구현으로 해당 호출을 캐스케이드합니다. 이 작업은 다음 목록에 표시된 LifecycleProcessor에 위임하여 수행합니다:
public interface LifecycleProcessor extends Lifecycle {
void onRefresh();
void onClose();
}
라이프사이클프로세서는 그 자체로 라이프사이클인터페이스의 확장입니다. 또한 새로 고침 및 닫히는 컨텍스트에 반응하는 두 가지 다른 메서드도 추가합니다.
일반 org.springframework.context.Lifecycle 인터페이스는 명시적인 시작 및 중지 알림을 위한 일반 컨트랙트이며 컨텍스트 새로 고침 시간에 자동 시작을 의미하지 않는다는 점에 유의하세요. 자동 시작을 세밀하게 제어하고 특정 빈(시작 및 중지 단계 포함)을 우아하게 중지하려면 확장된 org.springframework.context.SmartLifecycle 인터페이스를 대신 구현하는 것을 고려하세요.
또한 중지 알림이 반드시 소멸 전에 수신되는 것은 아닙니다. 정기 종료 시 모든 라이프사이클 빈은 일반적인 소멸 콜백이 전파되기 전에 먼저 중지 알림을 받습니다. 그러나 컨텍스트의 수명 중 핫 새로 고침 또는 중지된 새로 고침 시도에서는 소멸 메서드만 호출됩니다.
|
시작 및 종료 호출의 순서는 중요할 수 있습니다. 두 객체 사이에 "종속" 관계가 존재하면 종속된 쪽이 종속된 쪽보다 먼저 시작하고 종속된 쪽이 종속된 쪽보다 먼저 중지합니다. 그러나 때로는 직접적인 종속성을 알 수 없는 경우도 있습니다. 특정 유형의 객체가 다른 유형의 객체보다 먼저 시작되어야 한다는 것만 알 수 있습니다. 이러한 경우 SmartLifecycle 인터페이스는 다른 옵션, 즉 상위 인터페이스인Phased에 정의된 getPhase() 메서드를 정의합니다. 다음 목록은 Phased 인터페이스의 정의를 보여줍니다:
public interface Phased {
int getPhase();
}
다음 목록은 SmartLifecycle 인터페이스의 정의를 보여줍니다:
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
시작할 때는 위상이 가장 낮은 객체가 먼저 시작됩니다. 중지할 때는 그 반대 순서를 따릅니다. 따라서 SmartLifecycle을 구현하고 getPhase() 메서드가 Integer.MIN_VALUE를 반환하는 객체는 가장 먼저 시작하고 가장 늦게 중지합니다. 반대로 위상 값이Integer.MAX_VALUE인 개체는 실행 중인 다른 프로세스에 따라 달라지므로 가장 마지막에 시작하고 가장 먼저 중지해야 합니다(아마도). 위상 값을 고려할 때 SmartLifecycle을 구현하지 않는 "일반"수명 주기 개체의 기본 위상은 0이라는 점도 중요합니다. 따라서 음수 위상 값은 개체가 표준 구성 요소보다 먼저 시작되고 그 다음에 중지되어야 함을 나타냅니다. 양수 위상 값은 그 반대입니다.
SmartLifecycle에 정의된 중지 메서드는 콜백을 허용합니다. 모든 구현은 해당 구현의 종료 프로세스가 완료된 후 해당 콜백의 실행() 메서드를 호출해야 합니다. 이렇게 하면 필요한 경우 비동기 종료가 가능한데, LifecycleProcessor 인터페이스의 기본 구현인DefaultLifecycleProcessor는 각 단계 내의 개체 그룹에 대해 해당 콜백을 호출할 때까지 시간 초과 값까지 대기하기 때문입니다. 기본 단계별 시간 제한은 30초입니다. 컨텍스트 내에서lifecycleProcessor라는 이름의 빈을 정의하여 기본 수명 주기 프로세서 인스턴스를 재정의할 수 있습니다. 타임아웃만 수정하려는 경우 다음을 정의하면 충분합니다:
<bean id="lifecycleProcessor" class="org.springframework.context.support.DefaultLifecycleProcessor">
<!-- timeout value in milliseconds -->
<property name="timeoutPerShutdownPhase" value="10000"/>
</bean>
앞서 언급했듯이 LifecycleProcessor 인터페이스는 컨텍스트 새로 고침 및 종료를 위한 콜백 메서드도 정의합니다. 후자는 stop() 이 명시적으로 호출된 것처럼 종료 프로세스를 구동하지만, 컨텍스트가 닫힐 때 발생합니다. 반면에 'refresh' 콜백은SmartLifecycle 빈의 또 다른 기능을 활성화합니다. 컨텍스트가 새로 고쳐지면(모든 객체가 인스턴스화되고 초기화된 후) 해당 콜백이 호출됩니다. 이때 기본 수명 주기 프로세서는 각SmartLifecycle 객체의 isAutoStartup() 메서드가 반환하는 부울 값을 확인합니다. True이면 컨텍스트 또는 자체 start() 메서드의 명시적 호출을 기다리지 않고 해당 시점에 해당 객체가 시작됩니다(컨텍스트 새로 고침과 달리 표준 컨텍스트 구현에서는 컨텍스트 시작이 자동으로 발생하지 않음). 위상 값과 "의존성" 관계는 앞서 설명한 대로 시작 순서를 결정합니다.
웹이 아닌 애플리케이션에서 Spring IoC 컨테이너를 정상적으로 종료하기
이 섹션은 비웹 애플리케이션에만 적용됩니다. Spring의 웹 기반ApplicationContext 구현에는 관련 웹 애플리케이션이 종료될 때 Spring IoC 컨테이너를 정상적으로 종료하는 코드가 이미 마련되어 있습니다.
|
웹 애플리케이션이 아닌 환경(예: 리치 클라이언트 데스크톱 환경)에서 Spring의 IoC 컨테이너를 사용하는 경우, 종료 훅을 JVM에 등록하세요. 이렇게 하면 정상적으로 종료되고 싱글톤 빈에서 관련 삭제 메서드를 호출하여 모든 리소스가 해제됩니다. 이러한 종료 콜백을 올바르게 구성하고 구현해야 합니다.
종료 훅을 등록하려면 다음 예시와 같이 ConfigurableApplicationContext 인터페이스에 선언된 registerShutdownHook() 메서드를 호출합니다:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
스레드 안전 및 가시성
Spring 코어 컨테이너는 생성된 싱글톤 인스턴스를 스레드 안전 방식으로 게시하여 싱글톤 잠금을 통해 액세스를 보호하고 다른 스레드에서 가시성을 보장합니다.
따라서 애플리케이션이 제공하는 빈 클래스는 초기화 상태의 가시성에 대해 걱정할 필요가 없습니다. 일반 구성 필드는 초기화 단계 중에만 변경되는 한 휘발성으로 표시할 필요가 없으며, 초기 단계에서 변경 가능한 세터 기반 구성 상태의 경우에도 최종 상태와 유사한 가시성을 보장합니다. 이러한 필드가 빈 생성 단계 및 후속 초기 게시 이후에 변경되는 경우, 해당 필드는휘발성으로 선언하거나 액세스할 때마다 공통 잠금으로 보호해야 합니다.
컨트롤러 인스턴스나 리포지토리 인스턴스 등 싱글톤 빈 인스턴스에서 이러한 구성 상태에 대한 동시 액세스는 컨테이너 측에서 이러한 안전한 초기 게시 이후에는 완벽하게 스레드 안전하다는 점에 유의하세요. 여기에는 일반 싱글톤 잠금 내에서 처리되는 일반 싱글톤FactoryBean 인스턴스도 포함됩니다.
소멸 콜백의 경우, 구성 상태는 스레드 안전하지만 초기화와 소멸 사이에 누적된 모든 런타임 상태는 일반적인 Java 지침에 따라 스레드 안전 구조(또는 간단한 경우 휘발성 필드)에 보관해야 합니다.
위와 같이 보다 심층적인 수명 주기 통합에는 실행 가능한 필드와 같이 런타임에 변경 가능한 상태가 포함되며, 이러한 상태는 휘발성으로 선언되어야 합니다. 일반적인 수명 주기 콜백은 특정 순서를 따르지만(예: 시작 콜백은 전체 초기화 후에만 발생하고 중지 콜백은 초기 시작 후에만 발생하도록 보장), 일반적인 중지 전 소멸 배열에는 특별한 경우가 있습니다: 이러한 빈의 내부 상태는 취소된 부트스트랩 후 특별한 종료 중에 발생하거나 다른 빈으로 인한 중지 시간 초과가 발생할 수 있으므로 선행 중지 없이 즉각적인 파괴 콜백을 허용할 것을 강력히 권장합니다.
ApplicationContextAware and BeanNameAware
ApplicationContext가org.springframework.context.ApplicationContextAware 인터페이스를 구현하는 객체 인스턴스를 생성하면 인스턴스에는 해당 ApplicationContext에 대한 참조가 제공됩니다. 다음 목록은 ApplicationContextAware 인터페이스의 정의를 보여줍니다:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
따라서 빈은 ApplicationContext 인터페이스를 통해 또는 이 인터페이스의 알려진 하위 클래스(예: 추가 기능을 노출하는 ConfigurableApplicationContext)에 대한 참조를 캐스팅하여 자신을 생성한 ApplicationContext를 프로그래밍 방식으로 조작할 수 있습니다. 한 가지 용도는 다른 빈을 프로그래밍 방식으로 검색하는 것입니다. 이 기능이 유용할 때도 있습니다. 그러나 일반적으로는 코드를 Spring에 결합하고 공동 작업자가 빈에 속성으로 제공되는 제어의 반전 스타일을 따르지 않기 때문에 피해야 합니다.ApplicationContext의 다른 메서드는 파일 리소스에 대한 액세스, 애플리케이션 이벤트 게시 및 MessageSource 액세스를 제공합니다. 이러한 추가 기능은 애플리케이션 컨텍스트의 추가 기능에 설명되어 있습니다.
자동 배선은ApplicationContext에 대한 참조를 얻기 위한 또 다른 대안입니다. 기존의 생성자 및 byType 자동 배선 모드( 자동 배선 협력자에 설명된 대로)는 각각 생성자 인수 또는 설정자 메서드 매개변수에 대해ApplicationContext 유형의 종속성을 제공할 수 있습니다. 필드 및 여러 매개변수 메서드를 자동 배선하는 기능 등 더 많은 유연성을 원한다면 어노테이션 기반 자동 배선 기능을 사용하세요. 이 경우 해당 필드, 생성자 또는 메서드에 @Autowired 어노테이션이 있는 경우 ApplicationContext 유형이 예상되는 필드, 생성자 인수 또는 메서드 매개변수에 ApplicationContext가 자동 연결됩니다. 자세한 내용은 @Autowired 사용을 참조하세요.
ApplicationContext가org.springframework.beans.factory.BeanNameAware 인터페이스를 구현하는 클래스를 생성하면 해당 클래스에는 연관된 객체 정의에 정의된 이름에 대한 참조가 제공됩니다. 다음 목록은 BeanNameAware 인터페이스의 정의를 보여줍니다:
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
콜백은 일반 빈 프로퍼티를 채운 후에 호출되지만 InitializingBean.afterPropertiesSet( )과 같은 초기화 콜백이나 사용자 정의 init 메서드 전에 호출됩니다.
Other Aware Interfaces
앞서 설명한 애플리케이션 컨텍스트 인식 및 BeanName 인식 외에도 Spring은 빈이 특정 인프라 종속성이 필요하다는 것을 컨테이너에 표시할 수 있는 광범위한 인식 콜백 인터페이스를 제공합니다. 일반적으로 이름은 종속성 유형을 나타냅니다. 다음 표에는 가장 중요한 Aware 인터페이스가 요약되어 있습니다:
ApplicationContextAware | ApplicationContext. | ApplicationContextAware 및 BeanNameAware |
ApplicationEventPublisherAware | 둘러싸는 ApplicationContext의 이벤트 게시자 인식. | 애플리케이션 컨텍스트의 추가 기능 |
BeanClassLoaderAware | 빈 클래스를 로드하는 데 사용되는 클래스 로더. | 빈 인스턴스화 |
BeanFactoryAware | BeanFactory 선언. | BeanFactory API |
BeanNameAware | 선언하는 빈의 이름입니다. | 애플리케이션 컨텍스트 인식 및 빈 이름 인식 |
LoadTimeWeaverAware | 로드 시 클래스 정의를 처리하기 위한 정의된 위버입니다. | Spring 프레임워크에서 AspectJ를 사용한 로드 시간 위빙 |
MessageSourceAware | 메시지 확인을 위한 구성된 전략(매개변수화 및 국제화 지원 포함). | 애플리케이션 컨텍스트의 추가 기능 |
NotificationPublisherAware | Spring JMX 알림 게시자. | 알림 |
ResourceLoaderAware | 리소스에 대한 저수준 액세스를 위해 구성된 로더. | Resources |
ServletConfigAware | 컨테이너가 실행되는 현재 ServletConfig. 웹 인식 스프링 애플리케이션 컨텍스트에서만 유효합니다. | Spring MVC |
ServletContextAware | 컨테이너가 실행되는 현재 서블릿 컨텍스트. 웹 인식 스프링 애플리케이션 컨텍스트에서만 유효합니다. | Spring MVC |
이러한 인터페이스를 사용하면 코드가 Spring API에 연결되며 제어의 반전 스타일을 따르지 않는다는 점에 다시 한 번 유의하세요. 따라서 컨테이너에 대한 프로그래밍 방식의 액세스가 필요한 인프라 빈에 사용하는 것을 권장합니다.
빈 정의 상속
빈 정의에는 생성자 인수, 속성 값, 초기화 메서드, 정적 팩토리 메서드 이름과 같은 컨테이너별 정보 등 많은 구성 정보가 포함될 수 있습니다. 자식 빈 정의는 부모 정의에서 구성 데이터를 상속합니다. 자식 정의는 필요에 따라 일부 값을 재정의하거나 다른 값을 추가할 수 있습니다. 부모 및 자식 빈 정의를 사용하면 많은 타이핑을 절약할 수 있습니다. 사실상 이것은 템플릿의 한 형태입니다.
프로그래밍 방식으로 ApplicationContext 인터페이스로 작업하는 경우, 자식 빈 정의는 ChildBeanDefinition 클래스로 표현됩니다. 대부분의 사용자는 이 수준에서 작업하지 않습니다. 대신 ClassPathXmlApplicationContext와 같은 클래스에서 선언적으로 빈 정의를 구성합니다. XML 기반 구성 메타데이터를 사용하는 경우 부모 속성을 사용하여 부모 빈을 이 속성의 값으로 지정하여 자식 빈 정의를 나타낼 수 있습니다. 다음 예제는 그 방법을 보여줍니다:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
부모 속성에 주목합니다. |
자식 빈 정의는 부모 정의가 지정되지 않은 경우 부모 정의의 빈 클래스를 사용하지만 재정의할 수도 있습니다. 후자의 경우, 자식 빈 클래스는 부모와 호환되어야 합니다(즉, 부모의 속성 값을 허용해야 합니다).
자식 빈 정의는 부모로부터 범위, 생성자 인수 값, 속성 값 및 메서드 재정의와 함께 새 값을 추가할 수 있는 옵션을 상속받습니다. 지정하는 모든 범위, 초기화 메서드, 소멸 메서드 또는 정적 팩토리 메서드 설정은 해당 부모 설정을 재정의합니다.
나머지 설정은 항상 자식 정의에서 가져옵니다: 의존성, 자동 와이어 모드, 의존성 검사, 싱글톤 및 지연 초기화.
앞의 예에서는 추상 속성을 사용하여 부모 빈 정의를 명시적으로 추상으로 표시합니다. 상위 정의에 클래스가 지정되지 않은 경우 다음 예제에서 볼 수 있듯이 상위 빈 정의를 추상적으로 명시적으로 표시해야 합니다:
<bean id="inheritedTestBeanWithoutClass" abstract="true">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBeanWithoutClass" init-method="initialize">
<property name="name" value="override"/>
<!-- age will inherit the value of 1 from the parent bean definition-->
</bean>
부모 빈은 불완전하기 때문에 자체적으로 인스턴스화할 수 없으며, 또한 명시적으로 추상적으로 표시되어 있습니다. 정의가 추상적인 경우, 자식 정의의 부모 정의 역할을 하는 순수한 템플릿 빈 정의로만 사용할 수 있습니다. 이러한 추상적인 부모 빈을 다른 빈의 참조 속성으로 참조하거나 부모 빈 ID로 명시적으로 getBean() 호출을 수행하여 자체적으로 사용하려고 시도하면 오류가 반환됩니다. 마찬가지로 컨테이너의 내부사전 인스턴스화싱글톤() 메서드는 추상적으로 정의된 빈 정의를 무시합니다.
ApplicationContext는 기본적으로 모든 싱글톤을 사전 인스턴스화합니다. 따라서 (적어도 싱글톤 빈의 경우) 템플릿으로만 사용하려는 (부모) 빈 정의가 있고 이 정의가 클래스를 지정하는 경우, 추상 속성을 true로 설정해야 하며, 그렇지 않으면 애플리케이션 컨텍스트가 실제로 추상 빈을 사전 인스턴스화하려고 시도합니다(시도) |
Container Extension Points
일반적으로 애플리케이션 개발자는 ApplicationContext구현 클래스를 서브클래싱할 필요가 없습니다. 대신 특수 통합 인터페이스의 구현을 연결하여 Spring IoC 컨테이너를 확장할 수 있습니다. 다음 몇 섹션에서는 이러한 통합 인터페이스에 대해 설명합니다.
Customizing Beans by Using a BeanPostProcessor
BeanPostProcessor 인터페이스는 자체 인스턴스화 로직, 의존성 해결 로직 등을 제공하기 위해(또는 컨테이너의 기본값을 재정의하기 위해) 구현할 수 있는 콜백 메서드를 정의합니다. Spring 컨테이너가 빈 인스턴스화, 구성 및 초기화를 마친 후 사용자 정의 로직을 구현하려는 경우 하나 이상의 사용자 정의 BeanPostProcessor 구현을 연결할 수 있습니다.
여러 개의 BeanPostProcessor 인스턴스를 구성할 수 있으며, order 속성을 설정하여 이러한 BeanPostProcessor 인스턴스가 실행되는 순서를 제어할 수 있습니다. 이 속성은 BeanPostProcessor가 Ordered인터페이스를 구현하는 경우에만 설정할 수 있습니다. BeanPostProcessor를 직접 작성하는 경우 Ordered 인터페이스 구현도 고려해야 합니다. 자세한 내용은BeanPostProcessor및 Ordered 인터페이스의 자바독을 참조하세요. BeanPostProcessor 인스턴스의 프로그래밍 방식 등록에 대한 참고 사항도 참조하세요.
BeanPostProcessor 인스턴스는 빈(또는 객체) 인스턴스에서 작동합니다. 즉, Spring IoC 컨테이너가 빈 인스턴스를 인스턴스화한 다음 BeanPostProcessor인스턴스가 작업을 수행합니다.
BeanPostProcessor 인스턴스는 컨테이너별로 범위가 지정됩니다. 이는 컨테이너 계층 구조를 사용하는 경우에만 관련이 있습니다. 하나의 컨테이너에 BeanPostProcessor를 정의하면 해당 컨테이너에 있는 빈만 사후 처리합니다. 즉, 한 컨테이너에 정의된 빈은 두 컨테이너가 동일한 계층에 속해 있더라도 다른 컨테이너에 정의된 BeanPostProcessor에 의해 사후 처리되지 않습니다.
실제 빈 정의(즉, 빈을 정의하는 블루프린트)를 변경하려면 BeanFactoryPostProcessor로 구성 메타데이터 사용자 지정에 설명된 대로 BeanFactoryPostProcessor를 대신 사용해야 합니다.
|
Org.springframework.beans.factory.config.BeanPostProcessor 인터페이스는 정확히 두 개의 콜백 메서드로 구성됩니다. 이러한 클래스가 컨테이너에 포스트 프로세서로 등록되면 컨테이너에 의해 생성되는 각 빈 인스턴스에 대해 포스트 프로세서는 컨테이너 초기화 메서드(예: InitializingBean.afterPropertiesSet( ) 또는 선언된 init 메서드)가 호출되기 전과 모든 빈 초기화 콜백 이후에 컨테이너로부터 콜백을 받습니다. 포스트 프로세서는 빈 인스턴스로 모든 작업을 수행할 수 있으며, 콜백을 완전히 무시하는 것도 포함해서 어떤 작업도 수행할 수 있습니다. 빈 포스트 프로세서는 일반적으로 콜백 인터페이스를 확인하거나 프록시로 빈을 래핑할 수 있습니다. 일부 Spring AOP 인프라 클래스는 프록시 래핑 로직을 제공하기 위해 빈 포스트 프로세서로 구현됩니다.
애플리케이션 컨텍스트는 구성 메타데이터에 정의된 BeanPostProcessor 인터페이스를 구현하는 모든 빈을 자동으로 감지합니다.애플리케이션 컨텍스트는 이러한 빈을 포스트 프로세서로 등록하여 나중에 빈 생성 시 호출할 수 있도록 합니다. 빈 포스트프로세서는 다른 빈과 동일한 방식으로 컨테이너에 배포할 수 있습니다.
구성 클래스에서 @Bean 팩토리 메서드를 사용하여 BeanPostProcessor를 선언할 때, 팩토리 메서드의 반환 유형은 구현 클래스 자체 또는 최소한 org.springframework.beans.factory.config.BeanPostProcessor인터페이스여야 해당 빈의 포스트프로세서 특성을 명확하게 나타낼 수 있다는 점에 유의하세요. 그렇지 않으면ApplicationContext가 완전히 생성되기 전에 유형별로 자동 감지할 수 없습니다. 컨텍스트에서 다른 빈의 초기화에 적용하려면 BeanPostProcessor를 조기에 인스턴스화해야 하므로 이 초기 유형 감지는 매우 중요합니다.
프로그래밍 방식으로 BeanPostProcessor 인스턴스 등록하기
앞서 설명한 대로ApplicationContext 자동 감지를 사용하는 것이 권장되지만, 추가BeanPostProcessor메서드를 사용하여 ConfigurableBeanFactory에 대해 프로그래밍 방식으로 등록할 수도 있습니다. 이는 등록 전에 조건부 로직을 평가해야 하거나 계층 구조의 컨텍스트 간에 빈 포스트 프로세서를 복사해야 할 때 유용할 수 있습니다. 그러나 프로그래밍 방식으로 추가된 BeanPostProcessor 인스턴스는 Ordered 인터페이스를 따르지 않는다는 점에 유의하세요. 여기서 실행 순서를 결정하는 것은 등록 순서입니다. 또한 프로그래밍 방식으로 등록된 BeanPostProcessor 인스턴스는 명시적인 순서에 관계없이 자동 감지를 통해 등록된 인스턴스보다 항상 먼저 처리된다는 점에 유의하세요 |
BeanPostProcessor 인스턴스와 AOP 자동 프록시링
BeanPostProcessor 인터페이스를 구현하는 클래스는 특별하며 컨테이너에서 다르게 취급됩니다. 모든 BeanPostProcessor 인스턴스와 이들이 직접 참조하는 빈은 시작 시 ApplicationContext의 특수 시작 단계의 일부로 인스턴스화됩니다. 그 다음, 모든 BeanPostProcessor 인스턴스가 정렬된 방식으로 등록되고 컨테이너의 모든 추가 빈에 적용됩니다. AOP 자동 프록시는 BeanPostProcessor 자체로 구현되기 때문에, BeanPostProcessor인스턴스나 인스턴스가 직접 참조하는 빈은 자동 프록시를 사용할 수 없으며, 따라서 그 안에 측면이 짜여져 있지 않습니다.
이러한 빈의 경우 정보 로그 메시지가 표시되어야 합니다: Bean someBean은 모든 BeanPostProcessor 인터페이스에서 처리할 수 없습니다(예: 자동 프록시에 적합하지 않음).
자동 배선 또는@Resource (자동 배선으로 되돌아갈 수 있음)를 사용하여 BeanPostProcessor에 빈을 연결한 경우, Spring이 유형 일치 종속성 후보를 검색할 때 예기치 않은 빈에 액세스하여 자동 프록시 또는 다른 종류의 빈 사후 처리에 사용할 수 없게 만들 수 있습니다. 예를 들어, 필드 또는 설정자 이름이 선언된 빈의 이름과 직접 일치하지 않고 이름 속성이 사용되지 않는 @Resource로 주석이 달린 종속성이 있는 경우 Spring은 유형별로 일치시키기 위해 다른 빈에 액세스합니다.
|
다음 예제는 ApplicationContext에서 BeanPostProcessor 인스턴스를 작성, 등록 및 사용하는 방법을 보여줍니다.
예제: Hello World, BeanPostProcessor 스타일
이 첫 번째 예시는 기본적인 사용법을 보여줍니다. 이 예는 컨테이너에서 생성될 때 각 빈의 toString() 메서드를 호출하고 결과 문자열을 시스템 콘솔에 인쇄하는 사용자 지정BeanPostProcessor 구현을 보여줍니다.
다음 목록은 사용자 정의 BeanPostProcessor 구현 클래스 정의를 보여줍니다:
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
다음 빈 요소는 인스턴스화 추적 빈 포스트 프로세서를 사용합니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
https://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
인스턴스화트레이싱빈포스트프로세서가 어떻게 단순히 정의되어 있는지 주목하세요. 이름도 없으며, 빈이기 때문에 다른 빈과 마찬가지로 의존성을 주입할 수 있습니다. (앞의 구성은 Groovy 스크립트로 지원되는 빈도 정의하고 있습니다. Spring 동적 언어 지원은동적 언어 지원 장에 자세히 설명되어 있습니다
다음 Java 애플리케이션은 앞의 코드와 구성을 실행합니다:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = ctx.getBean("messenger", Messenger.class);
System.out.println(messenger);
}
}
앞의 애플리케이션의 출력은 다음과 유사합니다:
생성된 빈 '메신저' : org.springframework.scripting.groovy.GroovyMessenger@272961 org.springframework.scripting.groovy.GroovyMessenger@272961
예제: 자동 와이어드 어노테이션 빈 포스트 프로세서
콜백 인터페이스 또는 어노테이션을 사용자 정의 BeanPostProcessor구현과 함께 사용하는 것은 Spring IoC 컨테이너를 확장하는 일반적인 수단입니다. 예를 들어 Spring 배포와 함께 제공되며 자동 와이어 어노테이션 필드, 세터 메서드 및 임의의 구성 메서드와 함께 제공되는 BeanPostProcessor 구현인 Spring의 AutowiredAnnotationBeanPostProcessor를들 수 있습니다.
Customizing Configuration Metadata with a BeanFactoryPostProcessor
다음으로 살펴볼 확장 포인트는org.springframework.beans.factory.config.BeanFactoryPostProcessor입니다. 이 인터페이스의 의미는 BeanPostProcessor의 의미와 유사하지만 한 가지 큰 차이점이 있습니다: BeanFactoryPostProcessor는 빈 구성 메타데이터에서 작동합니다. 즉, Spring IoC 컨테이너는 컨테이너가 BeanFactoryPostProcessor 인스턴스 이외의 다른 빈을 인스턴스화하기 전에 BeanFactoryPostProcessor가 구성 메타데이터를 읽고 잠재적으로 변경할 수 있게 해줍니다.
여러 BeanFactoryPostProcessor 인스턴스를 구성할 수 있으며, order 속성을 설정하여 이러한 BeanFactoryPostProcessor 인스턴스가 실행되는 순서를 제어할 수 있습니다. 그러나 이 속성은 BeanFactoryPostProcessor가Ordered 인터페이스를 구현하는 경우에만 설정할 수 있습니다. BeanFactoryPostProcessor를 직접 작성하는 경우 Ordered 인터페이스 구현도 고려해야 합니다. 자세한 내용은BeanFactoryPostProcessor및 Ordered 인터페이스의 자바독을 참조하세요.
실제 빈 인스턴스(즉, 구성 메타데이터에서 생성되는 객체)를 변경하려면 BeanPostProcessor를대신 사용해야 합니다(앞서 BeanPostProcessor를 사용하여 빈 사용자 지정하기에서 설명). 기술적으로 BeanFactoryPostProcessor내에서 빈 인스턴스로 작업하는 것이 가능하지만(예: BeanFactory.getBean() 사용), 그렇게 하면 표준 컨테이너 수명 주기를 위반하여 조기 빈 인스턴스화가 발생할 수 있습니다. 이로 인해 빈 사후 처리를 우회하는 등의 부작용이 발생할 수 있습니다.
또한 BeanFactoryPostProcessor 인스턴스는 컨테이너별로 범위가 지정됩니다. 이는 컨테이너 계층 구조를 사용하는 경우에만 관련이 있습니다. 하나의 컨테이너에서 BeanFactoryPostProcessor를 정의하면 해당 컨테이너의 빈 정의에만 적용됩니다. 한 컨테이너의 빈 정의는 두 컨테이너가 동일한 계층 구조에 속해 있더라도 다른 컨테이너의 BeanFactoryPostProcessor 인스턴스에 의해 사후 처리되지 않습니다.
|
빈 팩토리 포스트 프로세서는 컨테이너를 정의하는 구성 메타데이터에 변경 사항을 적용하기 위해애플리케이션 컨텍스트 내부에서 선언될 때 자동으로 실행됩니다. Spring에는 PropertyOverrideConfigurer 및PropertySourcesPlaceholderConfigurer와 같이 미리 정의된 여러 빈 팩토리 포스트 프로세서가 포함되어 있습니다. 예를 들어 사용자 정의 속성 편집기를 등록하기 위해 사용자 정의 BeanFactoryPostProcessor를사용할 수도 있습니다.
애플리케이션 컨텍스트는 그 안에 배포된 BeanFactoryPostProcessor 인터페이스를 구현하는 모든 빈을 자동으로 감지합니다. 그리고 적절한 시점에 이러한 빈을 빈 팩토리 포스트 프로세서로 사용합니다. 이러한 포스트프로세서 빈은 다른 빈과 마찬가지로 배포할 수 있습니다.
BeanPostProcessors와 마찬가지로, 일반적으로 지연 초기화를 위해BeanFactoryPostProcessors를 구성하고 싶지 않을 것입니다. 다른 빈이Bean(Factory)포스트프로세서를 참조하지 않으면 해당 포스트프로세서는 전혀 인스턴스화되지 않습니다. 따라서 지연 초기화를 위해 표시하는 것은 무시되며, <beans /> 요소의 선언에서default-lazy-init 속성을 true로 설정하더라도Bean(Factory)포스트프로세서는 열심히 인스턴스화됩니다 |
예시: 클래스 이름 대체 PropertySourcesPlaceholderConfigurer
PropertySourcesPlaceholderConfigurer를 사용하여 표준 Java 속성 형식을 사용하여 별도의 파일에 있는 빈 정의의 속성 값을 외부화할 수 있습니다. 이렇게 하면 애플리케이션을 배포하는 사람이 컨테이너의 기본 XML 정의 파일을 수정하는 복잡성이나 위험 없이 데이터베이스 URL 및 비밀번호와 같은 환경별 속성을 사용자 지정할 수 있습니다.
플레이스홀더 값을 가진 데이터소스가정의되어 있는 다음 XML 기반 구성 메타데이터 조각을 살펴보세요:
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
이 예는 외부 속성 파일에서 구성된 속성을 보여줍니다. 런타임 시, 데이터소스의 일부 프로퍼티를 대체하는 메타데이터에 PropertySourcesPlaceholderConfigurer가 적용됩니다. 대체할 값은 ${property-name} 형식의 플레이스홀더로 지정되며, 이는 Ant 및 log4j와 JSP EL 스타일을 따릅니다.
실제 값은 표준 Java 속성 형식의 다른 파일에서 가져옵니다:
jdbc.driverClassName=org.hsqldb.jdbcDriver jdbc.url=jdbc:hsqldb:hsql://production:9002 jdbc.username=sa jdbc.password=root
따라서 ${jdbc.username} 문자열은 런타임에 'sa' 값으로 대체되며 속성 파일의 키와 일치하는 다른 자리 표시자 값에도 동일하게 적용됩니다. PropertySourcesPlaceholderConfigurer는 빈 정의의 대부분의 속성 및 속성에서 자리 표시자를 확인합니다. 또한 플레이스홀더 접두사와 접미사를 사용자 지정할 수 있습니다.
Spring 2.5에 도입된 컨텍스트 네임스페이스를 사용하면 전용 구성 요소로 속성 플레이스홀더를 구성할 수 있습니다. 다음 예시와 같이 위치 속성에 하나 이상의 위치를 쉼표로 구분된 목록으로 제공할 수 있습니다:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
PropertySourcesPlaceholderConfigurer는 사용자가 지정한 속성파일에서 속성만 찾지 않습니다. 기본적으로 지정된 속성 파일에서 속성을 찾을 수 없는 경우 Spring 환경 속성 및 일반 Java 시스템 속성을 확인합니다.
지정된 애플리케이션에 대해 필요한 속성을 가진 이러한 요소는 하나만 정의해야 합니다. 고유한 자리 표시자 구문(${...})이 있는 한 여러 개의 속성 자리 표시자를 구성할 수 있습니다.
대체에 사용되는 프로퍼티의 소스를 모듈화해야 하는 경우 여러 개의 프로퍼티 자리 표시자를 만들면 안 됩니다. 그 대신 사용할 프로퍼티를 수집하는 고유한PropertySourcesPlaceholderConfigurer 빈을 만들어야 합니다.
|
런타임에 특정 구현 클래스를 선택해야 할 때 유용하게 사용할 수 있는 속성 소스 플레이스홀더 컨피규레이터를 사용하여 클래스 이름을 대체할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:
런타임에 클래스를 유효한 클래스로 확인할 수 없는 경우, 빈이 생성되려고 할 때, 즉 비래지 초기화 빈에 대한 ApplicationContext의 preInstantiateSingletons()단계 중에 빈 확인에 실패합니다.
|
예시: 속성 오버라이드 컨피규레이터
또 다른 빈 팩토리 포스트프로세서인 PropertyOverrideConfigurer는PropertySourcesPlaceholderConfigurer와 유사하지만, 후자와 달리 원래 정의에 빈 속성에 대한 기본값이 있거나 값이 전혀 없을 수 있습니다. 재정의하는속성 파일에 특정 빈 속성에 대한 항목이 없는 경우 기본 컨텍스트 정의가 사용됩니다.
빈 정의는 재정의되고 있다는 것을 인식하지 못하므로 XML 정의 파일에서 재정의 구성자가 사용되고 있다는 것을 즉시 알 수 없습니다. 동일한 빈 프로퍼티에 대해 서로 다른 값을 정의하는 PropertyOverrideConfigurer 인스턴스가 여러 개 있는 경우, 재정의 메커니즘으로 인해 마지막 인스턴스가 우선합니다.
속성 파일 구성 줄의 형식은 다음과 같습니다:
beanName.property=값
다음 목록은 형식의 예를 보여줍니다:
dataSource.driverClassName=com.mysql.jdbc.Driver dataSource.url=jdbc:mysql:mydb
이 예제 파일은 driverClassName 및 url 속성이 있는dataSource라는 빈이 포함된 컨테이너 정의와 함께 사용할 수 있습니다.
재정의되는 최종 속성을 제외한 경로의 모든 구성 요소가 이미 null이 아닌 경우(아마도 생성자에 의해 초기화된 것으로 추정됨) 복합 속성 이름도 지원됩니다. 다음 예제에서는 tom 콩의 fred 프로퍼티의 bob프로퍼티의 sammy 프로퍼티가 스칼라 값 123으로 설정되어 있습니다:
tom.fred.bob.sammy=123
지정된 오버라이드 값은 항상 리터럴 값입니다. 이 값은 빈 참조로 변환되지 않습니다. 이 규칙은 XML 빈 정의의 원래 값이 빈 참조를 지정하는 경우에도 적용됩니다 |
Spring 2.5에 도입된 컨텍스트 네임스페이스를 사용하면 다음 예제에서 볼 수 있듯이 전용 구성 요소를 사용하여 속성 재정의 기능을 구성할 수 있습니다:
<context:property-override location="classpath:override.properties"/>
Customizing Instantiation Logic with a FactoryBean
그 자체로 팩토리인 객체에 대해 org.springframework.beans.factory.FactoryBean 인터페이스를 구현할 수 있습니다.
FactoryBean 인터페이스는 Spring IoC 컨테이너의 인스턴스화 로직에 플러그할 수 있는 지점입니다. (잠재적으로) 장황한 양의 XML 대신 Java로 더 잘 표현되는 복잡한 초기화 코드가 있는 경우, 자체FactoryBean을 생성하고 해당 클래스 내부에 복잡한 초기화를 작성한 다음 사용자 정의 FactoryBean을 컨테이너에 플러그할 수 있습니다.
FactoryBean<T> 인터페이스는 세 가지 메서드를 제공합니다:
- T getObject(): 이 팩토리가 생성한 객체의 인스턴스를 반환합니다. 이 팩토리가 싱글톤을 반환하는지 프로토타입을 반환하는지에 따라 인스턴스를 공유할 수 있습니다.
- boolean isSingleton(): 이 팩토리 빈이 싱글 톤을 리턴하면 참을 리턴하고, 그렇지 않으면거짓을 리턴합니다. 이 메서드의 기본 구현은 참을 리턴합니다.
- Class<? > getObjectType(): GetObject() 메서드가 반환한 객체 유형을 리턴하거나, 유형을 미리 알 수 없는 경우 null을 리턴합니다.
FactoryBean 개념과 인터페이스는 Spring 프레임워크 내 여러 곳에서 사용됩니다. 50개 이상의 FactoryBean 인터페이스 구현이 Spring 자체와 함께 제공됩니다.
컨테이너가 생성하는 빈 대신 실제 FactoryBean 인스턴스 자체를 컨테이너에 요청해야 하는 경우, 애플리케이션 컨텍스트의 getBean() 메서드를 호출할 때 빈의 ID 앞에 앰퍼샌드 기호(&)를 붙이면 됩니다. 따라서 id가 myBean인 주어진 FactoryBean의경우 컨테이너에서 getBean("myBean")을 호출하면 FactoryBean의 제품이 반환되는 반면, getBean("&myBean ")을 호출하면FactoryBean 인스턴스 자체가 반환됩니다.
어노테이션 기반 컨테이너 구성
Spring은 관련 클래스, 메서드 또는 필드 선언에 대한 주석을 사용하여 구성 요소 클래스 자체의 메타데이터에서 작동하는 주석 기반 구성을 포괄적으로 지원합니다.예제에서 언급했듯이 에서 언급했듯이, Spring은 어노테이션과 함께 BeanPostProcessor를 사용하여 핵심 IOC 컨테이너가 특정 어노테이션을 인식하도록 합니다.
예를 들어, @Autowired어노테이션은자동 배선 공동 작업 자에 설명된 것과 동일한 기능을 제공하지만 더 세분화된 제어와 더 넓은 적용 가능성을 제공합니다. 또한 Spring은 @PostConstruct 및 @PreDestroy와 같은 JSR-250 어노테이션을 지원할 뿐만 아니라 @Inject 및 @Named와 같은jakarta.inject 패키지에 포함된 JSR-330(Java용 의존성 주입) 어노테이션도 지원합니다. 이러한 어노테이션에 대한 자세한 내용은 관련 섹션에서 확인할 수 있습니다.
어노테이션 주입은 외부 프로퍼티 주입 전에 수행됩니다. 따라서 혼합 접근 방식을 통해 연결할 때 외부 구성(예: XML 지정 빈 속성)이 속성에 대한 어노테이션을 효과적으로 재정의합니다.
|
기술적으로는 포스트프로세서를 개별 빈 정의로 등록할 수 있지만, 이미 AnnotationConfigApplicationContext에 암시적으로 등록되어 있습니다.
XML 기반 Spring 설정에서 다음 구성 태그를 포함하면 어노테이션 기반 구성과 혼합 및 매칭할 수 있습니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
</beans>
<context:annotation-config/> 요소는 다음과 같은 포스트 프로세서를 암시적으로 등록합니다:
<context:annotation-config/>는 어노테이션이 정의된 동일한 애플리케이션 컨텍스트에서 빈에 대한 어노테이션만 찾습니다. 즉, DispatcherServlet에 대한 WebApplicationContext에<context:annotation-config/>를 넣으면 서비스가 아닌 컨트롤러에서 @Autowired 빈만 확인합니다. 자세한 내용은디스패처서블릿을 참조하세요.
|
오토와이어드 사용
이 섹션에 포함된 예제에서는 JSR 330의 @Inject 어노테이션을 Spring의 @Autowired 어노테이션 대신 사용할 수 있습니다. 자세한 내용은 여기를 참조하세요.
|
다음 예제에서 볼 수 있듯이 생성자에 @Autowired 어노테이션을 적용할 수 있습니다:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
Spring 프레임워크 4.3부터, 대상 빈이 처음부터 하나의 생성자만 정의하는 경우 이러한 생성자에 대한 @Autowired 어노테이션은 더 이상 필요하지 않습니다. 그러나 여러 생성자를 사용할 수 있고 기본/기본 생성자가 없는 경우 컨테이너에 어떤 생성자를 사용할지 지시하려면 생성자 중 하나 이상에 @Autowired 어 노테이션을 추가해야 합니다. 자세한 내용은 생성자해결에 대한 토론을 참조하세요.
|
다음 예제에서 볼 수 있듯이 기존 설정자 메서드에 @Autowired 어노테이션을 적용할 수도 있습니다:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
다음 예제에서 보는 것처럼 임의의 이름과 여러 인수가 있는 메서드에도 이 어노테이션을 적용할 수 있습니다:
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
다음 예시처럼 필드에도 @Autowired를 적용할 수 있으며 생성자와 혼합하여 사용할 수도 있습니다:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
대상 컴포넌트(예: MovieCatalog 또는 CustomerPreferenceDao)가 @Autowired 주석 처리된 주입 지점에 사용하는 유형에 따라 일관되게 선언되어 있는지 확인하세요. 그렇지 않으면 런타임에 "일치하는 유형을 찾을 수 없음" 오류로 인해 주입이 실패할 수 있습니다.
클래스 경로 검색을 통해 찾은 XML 정의 빈 또는 컴포넌트 클래스의 경우, 컨테이너는 일반적으로 구체적인 유형을 미리 알고 있습니다. 그러나 @Bean 팩토리 메서드의 경우, 선언된 반환 유형이 충분히 표현력이 있는지 확인해야 합니다. 여러 인터페이스를 구현하는 컴포넌트나 구현 유형에 의해 잠재적으로 참조되는 컴포넌트의 경우, 팩토리 메서드에 가장 구체적인 반환 유형을 선언하는 것을 고려하세요(최소한 빈을 참조하는 주입 지점에서 요구하는 만큼 구체적이어야 함).
|
4.3부터 @Autowired는 주입에 대한 자체 참조(즉, 현재 주입된 빈에 대한 참조)도 고려합니다. 자체 주입은 대체 수단입니다. 실제로는 자체 참조를 최후의 수단으로만 사용해야 합니다(예: 빈의 트랜잭션 프록시를 통해 동일한 인스턴스에서 다른 메서드를 호출하는 경우). 이러한 시나리오에서는 영향을 받는 메서드를 별도의 델리게이트 빈으로 분리하는 것을 고려하세요.
|
다음 예제에서 볼 수 있듯이 해당 유형의 배열을 기대하는 필드 또는 메서드에 @Autowired 주석을 추가하여 Spring이ApplicationContext에서 특정 유형의 모든 빈을 제공하도록 지시할 수도 있습니다:
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}
다음 예제에서 볼 수 있듯이 유형이 지정된 컬렉션에도 동일하게 적용됩니다:
public class MovieRecommender {
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
배열 또는 목록의 항목을 특정 순서로 정렬하려면 대상 빈이 org.springframework.core.Ordered 인터페이스를 구현하거나 @Order 또는 표준 @Priority 어노테이션을 사용할 수 있습니다. 그렇지 않으면 컨테이너에 있는 해당 대상 빈 정의의 등록 순서를 따릅니다.
대상 클래스 수준과 @Bean 메서드에서, 잠재적으로 개별 빈 정의(동일한 빈 클래스를 사용하는 여러 정의의 경우)에 대해 @Order 어노테이션을 선언할 수 있습니다. order 값은 주입 지점의 우선순위에 영향을 줄 수 있지만 종속성 관계 및 @DependsOn 선언에 의해 결정되는 직교 문제인 싱글톤 시작 순서에는 영향을 미치지 않는다는 점에 유의하세요.
구성 클래스의 @Order 어노테이션은 시작 시 전체 구성 클래스 집합 내의 평가 순서에만 영향을 줍니다. 이러한 구성 수준 순서 값은 포함된 @Bean 메서드에는 전혀 영향을 미치지 않습니다. 빈 수준 순서의 경우, 각 @Bean 메서드에는 특정 빈 유형(팩토리 메서드에서 반환된 대로)에 대한 여러 일치 집합 내에 적용되는 자체 @Order 어노테이션이 있어야 합니다.
메서드에서 선언할 수 없기 때문에@Bean 수준에서는 표준 jakarta.annotation.Priority 어노테이션을 사용할 수 없다는 점에 유의하세요. 그 의미는 각 유형에 대해 단일 빈에서 @Order 값을 @Primary와 함께 사용하여 모델링할 수 있습니다.
|
예상 키 유형이 String인 경우 유형이 지정된 Map 인스턴스도 자동 배선할 수 있습니다. 다음 예제에서 보듯이 맵 값에는 예상 유형의 모든 빈이 포함되고 키에는 해당 빈 이름이 포함됩니다:
public class MovieRecommender {
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}
기본적으로 자동 배선은 지정된 주입 지점에 대해 일치하는 후보 빈이 없는 경우 실패합니다. 선언된 배열, 컬렉션 또는 맵의 경우 하나 이상의 일치하는 요소가 예상됩니다.
기본 동작은 주석이 달린 메서드와 필드를 필수 종속성을 나타내는 것으로 처리하는 것입니다. 다음 예시에서와 같이 이 동작을 변경하여 프레임워크가 비필수로 표시하여(즉, @Autowired의 필수 속성을 false로 설정하여) 충족되지 않는 주입 지점을 건너뛸 수 있도록 할 수 있습니다:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required = false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
비필수 메서드는 해당 종속성(또는 인수가 여러 개인 경우 종속성 중 하나)을 사용할 수 없는 경우 전혀 호출되지 않습니다. 이러한 경우 비필수 필드는 전혀 채워지지 않고 기본값이 그대로 유지됩니다.
즉, 필수 속성을 false로 설정하면 해당 속성이 자동 배선을 위한 선택 사항임을 나타내며, 자동 배선할 수 없는 경우 해당 속성은 무시됩니다. 이렇게 하면 종속성 주입을 통해 선택적으로 재정의할 수 있는 기본값을 프로퍼티에 할당할 수 있습니다.
|
주입된 생성자 및 팩토리 메서드 인수는 여러 생성자를 잠재적으로 처리할 수 있는 Spring의 생성자 확인 알고리즘으로 인해 @Autowired의 필수속성이 다소 다른 의미를 갖기 때문에 특별한 경우입니다. 생성자 및 팩토리 메서드 인수는 기본적으로 필수이지만 단일 생성자 시나리오에서는 다중 요소 주입 지점(배열, 컬렉션, 맵)이 일치하는 빈이 없는 경우 빈 인스턴스로 해결되는 등 몇 가지 특수한 규칙이 적용됩니다. 이를 통해 모든 종속성을 고유한 다중 인수 생성자에서 선언할 수 있는 일반적인 구현 패턴(예: @Autowired 어노테이션 없이 단일 공용 생성자로 선언)을 사용할 수 있습니다.
주어진 빈 클래스의 생성자는 필수속성이 true로 설정된 @Autowired를 선언하여 Spring 빈으로 사용될 때 생성자가 자동 연결됨을 나타내는 단 하나의 생성자만 선언할 수 있습니다. 결과적으로 필수 속성이 기본값 참으로 유지되는 경우 단일 생성자만 @Autowired로 주석을 달 수 있습니다. 여러 생성자가 어노테이션을 선언하는 경우, 자동 배선 후보로 간주되려면 모두 required=false를 선언해야 합니다(XML의 autowire=생성자와 유사). Spring 컨테이너에서 일치하는 빈으로 만족할 수 있는 종속성 수가 가장 많은 생성자가 선택됩니다. 후보 중 어느 것도 만족할 수 없는 경우 기본/기본 생성자(있는 경우)가 사용됩니다. 마찬가지로, 클래스가 여러 생성자를 선언하지만 그 중 @Autowired로 주석을 달지 않은 경우 기본/기본 생성자(있는 경우)가 사용됩니다. 클래스가 처음부터 하나의 생성자만 선언하는 경우에는 주석을 달지 않더라도 항상 이 생성자가 사용됩니다. 주석이 달린 생성자는 반드시 공개적일 필요는 없습니다.
|
또는 다음 예제에서 볼 수 있듯이 Java 8의 java.util.Optional을 통해 특정 종속성의 비필수성을 표현할 수도 있습니다:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
Spring Framework 5.0부터는 @Nullable 어노테이션(모든 패키지의 모든 종류 - 예: JSR-305의 javax.annotation.Nullable )을 사용하거나 Kotlin에 내장된 널 안전 지원을 활용할 수도 있습니다:
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
또한 잘 알려진 해결 가능한 종속성인 인터페이스에 @Autowired를 사용할 수도 있습니다: BeanFactory, ApplicationContext, Environment, ResourceLoader,ApplicationEventPublisher 및 MessageSource. 이러한 인터페이스와 그 확장 인터페이스(예: ConfigurableApplicationContext 또는 ResourcePatternResolver)는 특별한 설정이 필요 없이 자동으로 해결됩니다. 다음 예제는 ApplicationContext 객체를 자동 와이어링합니다:
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
Autowired, @Inject, @Value 및 @Resource 어노테이션은 SpringBeanPostProcessor 구현에서 처리됩니다. 즉, 자체 BeanPostProcessor 또는 BeanFactoryPostProcessor 유형(있는 경우) 내에서 이러한 어노테이션을 적용할 수 없습니다. 이러한 유형은 XML 또는 Spring @Bean 메서드를 사용하여 명시적으로 '와이어링'해야 합니다.
|
Primary로 어노테이션 기반 자동 배선 미세 조정하기
유형별 자동 배선은 여러 후보로 이어질 수 있으므로 선택 프로세스를 더 잘 제어해야 하는 경우가 많습니다. 이를 위한 한 가지 방법은 Spring의@Primary 어노테이션을 사용하는 것입니다. primary는 여러 빈이 단일 값 종속성에 자동 연결될 후보일 때 특정 빈에 우선순위를 부여해야 함을 나타냅니다. 후보 중 정확히 하나의 기본 빈이 존재하면 해당 빈이 자동 연결된 값이 됩니다.
FirstMovieCatalog를 기본 MovieCatalog로 정의하는 다음 구성을 고려해 보겠습니다:
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
앞의 구성을 사용하면 다음 MovieRecommender가firstMovieCatalog와 자동 연결됩니다:
public class MovieRecommender {
@Autowired
private MovieCatalog movieCatalog;
// ...
}
해당 빈 정의는 다음과 같습니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog" primary="true">
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
한정자를 사용하여 어노테이션 기반 자동 배선 미세 조정하기
primary는 하나의 기본 후보를 결정할 수 있는 경우 여러 인스턴스에서 유형별 자동 배선을 사용하는 효과적인 방법입니다. 선택 프로세스에 대한 보다 세밀한 제어가 필요한 경우 Spring의 @Qualifier 어노테이션을 사용할 수 있습니다. 한정자 값을 특정 인수와 연결하여 각 인수에 대해 특정 빈이 선택되도록 유형 일치 집합을 좁힐 수 있습니다. 가장 간단한 경우에는 다음 예제와 같이 단순한 설명 값일 수 있습니다:
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
다음 예와 같이 개별 생성자 인수 또는 메서드 매개변수에 @Qualifier 어노테이션을 지정할 수도 있습니다:
public class MovieRecommender {
private final MovieCatalog movieCatalog;
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
다음 예는 해당 빈 정의를 보여줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
주 한정자 값을 가진 빈은 동일한 값으로 한정된 생성자 인수와 연결됩니다. | |
액션 한정자 값을 가진 빈은 동일한 값으로 한정된 생성자 인수와 연결됩니다. |
폴백 일치의 경우 빈 이름은 기본 한정자 값으로 간주됩니다. 따라서 중첩된 한정자 요소 대신 ID가 main인 빈을 정의하여 동일한 일치 결과를 얻을 수 있습니다. 그러나 이 규칙을 사용하여 특정 빈을 이름으로 참조할 수는 있지만, @Autowired는 기본적으로 선택적 의미 한정자를 사용한 유형 중심 주입에 관한 것입니다. 즉, 한정자 값은 빈 이름 폴백을 사용하더라도 유형 일치 집합 내에서 항상 좁은 의미론을 갖습니다. 고유한 빈 ID에 대한 참조를 의미적으로 표현하지 않습니다. 좋은 한정자 값은 메인또는 EMEA 또는 영구적이며, 앞의 예에서와 같이 익명 빈 정의의 경우 자동 생성될 수 있는 빈 ID와 독립적인 특정 구성 요소의 특성을 표현합니다.
한정자는 앞서 설명한 것처럼 유형화된 컬렉션(예:Set<MovieCatalog>)에도 적용됩니다. 이 경우, 선언된 한정자에 따라 일치하는 모든 빈이 컬렉션으로 주입됩니다. 이는 한정자가 고유할 필요가 없음을 의미합니다. 오히려 필터링 기준을 구성합니다. 예를 들어, 동일한 한정자 값 "action"을 가진 여러 MovieCatalog 빈을 정의할 수 있으며, 이 모든 빈은 @Qualifier("action")로 주석이 달린 Set<MovieCatalog>에 주입됩니다.
한정자 값이 유형 일치 후보 내에서 대상 빈 이름에 대해 선택되도록 하면 주입 지점에 @Qualifier 어노테이션이 필요하지 않습니다. 다른 해결 표시기(한정자 또는 기본 마커 등)가 없는 경우, 고유하지 않은 종속성 상황에서 Spring은 주입 지점 이름(즉, 필드 이름 또는 매개 변수 이름)을 대상 빈 이름과 일치시키고 동일한 이름의 후보가 있는 경우(빈 이름 또는 관련 별칭으로) 선택합니다.
버전 6.1부터는 -parameters Java 컴파일러 플래그가 있어야 합니다.
|
이름에 의한 주입의 대안으로, 특정 대상 컴포넌트를 고유한 이름으로 식별하도록 의미론적으로 정의되고 선언된 유형은 일치 프로세스와 관련이 없는 JSR-250 @Resource 어노테이션을 고려해 보세요. 자동화된 @Autowired는 다소 다른 의미를 가집니다: 유형별로 후보 빈을 선택한 후에는 지정된 String한정자 값이 해당 유형으로 선택된 후보 내에서만 고려됩니다(예: 동일한 한정자 레이블로 표시된 빈에 대해 계정 한정자를 일치시키는 경우).
그 자체가 컬렉션, Map 또는 배열 유형으로 정의된 빈의 경우, 특정 컬렉션 또는 배열 빈을 고유 이름으로 참조하는 @Resource가좋은 해결책입니다. 즉, 4.3부터는 요소 유형 정보가 @Bean 반환 유형 서명 또는 컬렉션 상속 계층에 보존되어 있는 한 Spring의@Autowired 유형 일치 알고리즘을 통해 컬렉션, Map 및 배열 유형을 일치시킬 수도 있습니다. 이 경우 이전 단락에 설명한 대로 한정자 값을 사용하여 동일한 유형의 컬렉션 중에서 선택할 수 있습니다.
4.3부터 @Autowired는 주입에 대한 자체 참조(즉, 현재 주입된 빈에 대한 참조)도 고려합니다. 자체 주입은 대체 수단입니다. 다른 컴포넌트에 대한 일반 종속성은 항상 우선순위를 갖습니다. 그런 의미에서 자체 참조는 정기적인 후보 선택에 참여하지 않으므로 특히 기본이 되지 않습니다. 오히려 항상 가장 낮은 우선순위를 갖습니다. 실제로는 자체 참조를 최후의 수단으로만 사용해야 합니다(예: 빈의 트랜잭션 프록시를 통해 동일한 인스턴스에서 다른 메서드를 호출하는 경우). 이러한 시나리오에서는 영향을 받는 메서드를 별도의 위임 빈으로 분리하는 것을 고려하세요. 또는 고유 이름으로 현재 빈의 프록시를 다시 가져올 수 있는 @Resource를 사용할 수 있습니다.
동일한 구성 클래스에서 @Bean 메서드의 결과를 주입하려고 시도하는 것도 사실상 자체 참조 시나리오입니다. 실제로 필요한 경우(구성 클래스의 자동 와이어드 필드 대신) 메서드 서명에서 이러한 참조를 느리게 해결하거나 영향을 받는 @Bean 메서드를 정적으로 선언하여 포함된 구성 클래스 인스턴스 및 해당 수명 주기에서 분리합니다. 그렇지 않으면 이러한 빈은 폴백 단계에서만 고려되며 다른 구성 클래스에서 일치하는 빈이 대신 기본 후보로 선택(가능한 경우)됩니다.
|
autowired는 필드, 생성자 및 다중 인수 메서드에 적용되므로 매개변수 수준에서 한정자 주석을 통해 범위를 좁힐 수 있습니다. 반면 @Resource는단일 인수가 있는 필드 및 빈 속성 설정자 메서드에만 지원됩니다. 따라서 주입 대상이 생성자 또는 다중 인수 메서드인 경우 한정자를 사용해야 합니다.
사용자 지정 한정자 어노테이션을 직접 만들 수 있습니다. 이렇게 하려면 다음 예제에서 보는 것처럼 어노테이션을 정의하고 정의 내에 @Qualifier 어노테이션을 제공하세요:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
그런 다음 다음 예제와 같이 자동 연결 필드 및 매개변수에 사용자 지정 한정자를 제공할 수 있습니다:
public class MovieRecommender {
@Autowired
@Genre("Action")
private MovieCatalog actionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
this.comedyCatalog = comedyCatalog;
}
// ...
}
다음으로, 후보 빈 정의에 대한 정보를 제공할 수 있습니다.<qualifier/> 태그를 <bean/> 태그의 하위 요소로 추가한 다음 사용자 지정 한정자 주석과 일치하도록 유형과값을 지정할 수 있습니다. 유형은 어노테이션의 정규화된 클래스 이름과 일치합니다. 또는 이름이 충돌할 위험이 없는 경우 편의를 위해 짧은 클래스 이름을 사용할 수 있습니다. 다음 예에서는 두 가지 접근 방식을 모두 보여줍니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
</beans>
클래스 경로 검색 및 관리되는 컴포넌트에서 한정자 메타데이터를 XML로 제공하는 대신 어노테이션을 기반으로 하는 대안을 확인할 수 있습니다. 구체적으로 어노테이션으로 한정자 메타데이터 제공하기를 참조하세요.
경우에 따라서는 값 없이 어노테이션을 사용하는 것으로 충분할 수도 있습니다. 이는 어노테이션이 보다 일반적인 용도로 사용되며 여러 유형의 종속성 전반에 걸쳐 적용될 수 있는 경우에 유용할 수 있습니다. 예를 들어 인터넷에 연결할 수 없을 때 검색할 수 있는 오프라인 카탈로그를 제공할 수 있습니다. 먼저 다음 예시와 같이 간단한 어노테이션을 정의합니다:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Offline {
}
그런 다음 다음 예시와 같이 자동 연결할 필드 또는 속성에 어노테이션을 추가합니다:
public class MovieRecommender {
@Autowired
@Offline
private MovieCatalog offlineCatalog;
// ...
}
이 줄은 @Offline 어노테이션을 추가합니다. |
이제 빈 정의에는 다음 예와 같이 한정자 유형만 필요합니다:
<bean class="example.SimpleMovieCatalog">
<qualifier type="Offline"/>
<!-- inject any dependencies required by this bean -->
</bean>
이 요소는 한정자를 지정합니다. |
단순 값 속성 외에 또는 대신에 명명된 속성을 허용하는 사용자 지정 한정자 어노테이션을 정의할 수도 있습니다. 그런 다음 자동 와이어링할 필드 또는 매개 변수에 여러 속성 값이 지정된 경우 빈 정의가 이러한 모든 속성 값과 일치해야 자동 와이어 후보로 간주됩니다. 예를 들어 다음 어노테이션 정의를 생각해 보겠습니다:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface MovieQualifier {
String genre();
Format format();
}
이 경우 Format은 다음과 같이 정의된 열거형입니다:
public enum Format {
VHS, DVD, BLURAY
}
자동 와이어링될 필드에는 사용자 지정 한정자가 어노테이션되며 다음 예제에서 볼 수 있듯이 장르 및 형식 두 속성에 대한 값이 모두 포함됩니다:
public class MovieRecommender {
@Autowired
@MovieQualifier(format=Format.VHS, genre="Action")
private MovieCatalog actionVhsCatalog;
@Autowired
@MovieQualifier(format=Format.VHS, genre="Comedy")
private MovieCatalog comedyVhsCatalog;
@Autowired
@MovieQualifier(format=Format.DVD, genre="Action")
private MovieCatalog actionDvdCatalog;
@Autowired
@MovieQualifier(format=Format.BLURAY, genre="Comedy")
private MovieCatalog comedyBluRayCatalog;
// ...
}
마지막으로, 빈 정의에는 일치하는 한정자 값이 포함되어야 합니다. 이 예는 또한<qualifier/> 요소 대신 빈 메타 속성을 사용할 수 있음을 보여줍니다. 가능한 경우 <qualifier/> 요소와 해당 속성이 우선하지만, 다음 예의 마지막 두 빈 정의에서와 같이 해당 한정자가 없는 경우 자동 배선 메커니즘은<meta/> 태그 내에 제공된 값을 다시 사용합니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Action"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Comedy"/>
</qualifier>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="DVD"/>
<meta key="genre" value="Action"/>
<!-- inject any dependencies required by this bean -->
</bean>
<bean class="example.SimpleMovieCatalog">
<meta key="format" value="BLURAY"/>
<meta key="genre" value="Comedy"/>
<!-- inject any dependencies required by this bean -->
</bean>
</beans>
제네릭을 자동 배선 한정자로 사용하기
Qualifier 어노테이션 외에도 Java 제네릭 유형을 암시적 한정자로 사용할 수 있습니다. 예를 들어 다음과 같은 구성이 있다고 가정해 보겠습니다:
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
앞의 빈이 제네릭 인터페이스(즉, Store<String> 및Store<Integer>)를 구현한다고 가정하면 다음 예제에서 보는 것처럼 Store 인터페이스를 @자동 연결 하고 제네릭을 한정자로 사용할 수 있습니다:
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
목록, 맵 인스턴스 및 배열을 자동 와이어링할 때도 일반 한정자가 적용됩니다. 다음 예는 일반 목록을 자동 와이어링하는 예제입니다:
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
CustomAutowireConfigurer 사용
CustomAutowireConfigurer는Spring의 @Qualifier 어노테이션이 아니더라도 사용자 정의 한정자 어노테이션 유형을 등록할 수 있는 BeanFactoryPostProcessor입니다. 다음 예는 CustomAutowireConfigurer를 사용하는 방법을 보여줍니다:
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
AutowireCandidateResolver는 오토와이어 후보를 결정합니다:
- 각 빈 정의의 자동 와이어 후보 값
- <beans/> 요소에서 사용 가능한 모든 기본 자동 와이어 후보 패턴
- 한정자 어노테이션 및 CustomAutowireConfigurer에 등록된 모든 사용자 정의 어노테이션의 존재 여부
여러 개의 빈이 자동 와이어 후보로 자격이 되는 경우 "기본"의 결정은 다음과 같습니다: 후보 중 정확히 하나의 빈 정의에 기본속성이 true로 설정되어 있는 경우 해당 정의가 선택됩니다.
Resource를 사용한 주입
Spring은 필드 또는 빈 속성 설정자 메서드에 JSR-250 @Resource 어노테이션(jakarta.annotation.Resource)을 사용하는 주입도 지원합니다. 이는 JSF 관리 빈 및 JAX-WS 엔드포인트와 같은 Jakarta EE에서 흔히 볼 수 있는 패턴입니다. Spring은 Spring 관리 객체에서도 이 패턴을 지원합니다.
resource는 이름 속성을 사용합니다. 기본적으로 Spring은 해당 값을 주입할 빈 이름으로 해석합니다. 즉, 다음 예제에서 볼 수 있듯이 이름별 시맨틱을 따릅니다:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
이 줄은 @Resource를 주입합니다. |
이름이 명시적으로 지정되지 않은 경우 기본 이름은 필드 이름 또는 설정자 메서드에서 파생됩니다. 필드의 경우 필드 이름을 사용합니다. 세터 메서드의 경우 빈 속성 이름을 사용합니다. 다음 예제에서는 movieFinder라는 이름의 빈을 세터 메서드에 주입합니다:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
어노테이션과 함께 제공된 이름은 CommonAnnotationBeanPostProcessor가 인식하는ApplicationContext에 의해 빈 이름으로 확인됩니다. Spring의SimpleJndiBeanFactory를명시적으로 구성하는 경우 JNDI를 통해 이름을 확인할 수 있습니다. 그러나 기본 동작에 의존하고 Spring의 JNDI 조회 기능을 사용하여 간접 지시 수준을 유지하는 것이 좋습니다 |
명시적인 이름이 지정되지 않은 @Resource 사용의 경우 @Autowired와 유사하게 @Resource는 특정 이름의 빈 대신 기본 유형이 일치하는 것을 찾고 잘 알려진 해결 가능한 종속성인 BeanFactory,ApplicationContext, ResourceLoader, ApplicationEventPublisher 및 MessageSource인터페이스를 해결합니다.
따라서 다음 예제에서 customerPreferenceDao 필드는 먼저 "customerPreferenceDao"라는 이름의 빈을 찾은 다음CustomerPreferenceDao 유형에 대한 기본 유형 일치로 되돌아갑니다:
public class MovieRecommender {
@Resource
private CustomerPreferenceDao customerPreferenceDao;
@Resource
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
컨텍스트 필드는 알려진 확인 가능한 종속성 유형인ApplicationContext를 기반으로 주입됩니다. |
Value 사용
value는 일반적으로 외부화된 프로퍼티를 주입하는 데 사용됩니다:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
다음 구성을 사용합니다:
@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }
그리고 다음 application.properties 파일을 사용합니다:
catalog.name=MovieCatalog
이 경우 카탈로그 매개변수 및 필드는 MovieCatalog 값과 동일합니다.
Spring에서는 기본적으로 관대한 임베디드 값 해석기를 제공합니다. 속성 값을 확인하려고 시도하고 확인할 수 없는 경우 속성 이름(예: ${catalog.name})이 값으로 주입됩니다. 존재하지 않는 값에 대한 엄격한 제어를 유지하려면 다음 예제에서와 같이 PropertySourcesPlaceholderConfigurer 빈을 선언해야 합니다:
@Configuration
public class AppConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
JavaConfig를 사용하여 PropertySourcesPlaceholderConfigurer를 구성하는 경우@Bean 메서드는 정적이어야 합니다 |
위의 구성을 사용하면 ${}자리 표시자를 확인할 수 없는 경우 Spring 초기화 실패를 방지할 수 있습니다. 또한setPlaceholderPrefix, setPlaceholderSuffix 또는 setValueSeparator와 같은 메서드를 사용하여 자리 표시자를 사용자 지정할 수 있습니다.
Spring Boot는 기본적으로 application.properties 및 application.yml 파일에서 속성을 가져오는 PropertySourcesPlaceholderConfigurer 빈을 구성합니다 |
Spring에서 제공하는 기본 제공 변환기 지원을 통해 간단한 유형 변환(예: Integer또는 int로 )을 자동으로 처리할 수 있습니다. 쉼표로 구분된 여러 값을 추가 작업 없이 문자열 배열로 자동 변환할 수 있습니다.
다음과 같이 기본값을 제공할 수 있습니다:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
this.catalog = catalog;
}
}
Spring BeanPostProcessor는 @Value의 String 값을 대상 유형으로 변환하는 프로세스를 처리하기 위해 뒤에서 ConversionService를 사용합니다. 사용자 정의 유형에 대한 변환 지원을 제공하려는 경우 다음 예제와 같이 자체ConversionService 빈 인스턴스를 제공할 수 있습니다:
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
Value에 SpEL 표현식이 포함된 경우 다음 예제와 같이 런타임에 값이 동적으로 계산됩니다:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
SpEL을 사용하면 보다 복잡한 데이터 구조도 사용할 수 있습니다:
@Component
public class MovieRecommender {
private final Map<String, Integer> countOfMoviesPerCatalog;
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
포스트컨스트럭트 및 프리데스트로이 사용
CommonAnnotationBeanPostProcessor는 @Resource 어노테이션뿐만 아니라 JSR-250 라이프사이클 어노테이션인 jakarta.annotation.PostConstruct와jakarta.annotation.PreDestroy도 인식합니다. Spring 2.5에 도입된 이러한 어노테이션에 대한 지원은초기화 콜백 및소멸 콜백에 설명된 수명 주기 콜백 메커니즘에 대한 대안을 제공합니다.CommonAnnotationBeanPostProcessor가 Spring ApplicationContext 내에 등록되어 있다면, 이러한 어노테이션 중 하나를 포함하는 메서드는 해당 Spring 라이프사이클 인터페이스 메서드 또는 명시적으로 선언된 콜백 메서드와 라이프사이클의 동일한 지점에서 호출됩니다. 다음 예제에서는 초기화 시 캐시가 미리 채워지고 소멸 시 지워집니다:
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}
다양한 라이프사이클 메커니즘을 결합하는 효과에 대한 자세한 내용은라이프사이클 메커니즘 결합하기를 참조하세요.
Resource와 마찬가지로 @PostConstruct 및 @PreDestroy 어노테이션 유형은 JDK 6부터 8까지 표준 Java 라이브러리의 일부였습니다. 그러나 JDK 9에서는 전체 javax.annotation패키지가 핵심 Java 모듈에서 분리되었고 결국 JDK 11에서 제거되었습니다. Jakarta EE 9부터 패키지는 이제 jakarta.annotation에 있습니다. 필요한 경우 이제 다른 라이브러리와 마찬가지로 애플리케이션의 클래스 경로에 추가하기 위해 Maven Central을 통해 jakarta.annotation-api 아티팩트를 가져와야 합니다.
|
Classpath Scanning and Managed Components
이 장의 대부분의 예제에서는 XML을 사용하여 Spring 컨테이너 내에서 각 BeanDefinition을 생성하는 구성 메타데이터를 지정합니다. 이전 섹션(어노테이션 기반 컨테이너 구성)에서는 소스 수준 어노테이션을 통해 많은 구성 메타데이터를 제공하는 방법을 보여드렸습니다. 그러나 이러한 예제에서도 "기본" 빈 정의는 XML 파일에 명시적으로 정의되어 있고 어노테이션은 종속성 주입만 구동합니다. 이 섹션에서는 클래스 경로를 스캔하여 후보 컴포넌트를 암시적으로 감지하는 옵션에 대해 설명합니다. 후보 컴포넌트는 필터 기준과 일치하고 컨테이너에 등록된 해당 빈 정의가 있는 클래스입니다. 이렇게 하면 XML을 사용하여 빈 등록을 수행할 필요가 없습니다. 대신 어노테이션(예: @Component), AspectJ 유형 표현식 또는 사용자 지정 필터 기준을 사용하여 컨테이너에 등록된 빈 정의가 있는 클래스를 선택할 수 있습니다.
XML 파일 대신 Java를 사용하여 빈을 정의할 수 있습니다. 이러한 기능을 사용하는 방법에 대한 예는@Configuration, @Bean, @Import 및 @DependsOn 어노테이션을 참조하세요.
|
@Component and Further Stereotype Annotations
리포지토리 어노테이션은 리포지토리의 역할 또는고정관념을 충족하는 모든 클래스(데이터 액세스 개체 또는 DAO라고도 함)에 대한 마커입니다. 이 마커의 용도 중에는예외 번역에 설명된 대로 예외의 자동 번역이 있습니다.
Spring은 추가적인 스테레오타입 주석을 제공합니다: 컴포넌트, @서비스,@컨트롤러. 컴포넌트는 모든 Spring 관리 컴포넌트에 대한 일반적인 스테레오타입입니다. 리포지토리, @서비스 및 @컨트롤러는 각각 지속성, 서비스 및 프레젠테이션 레이어에서 보다 구체적인 사용 사례를 위한 @컴포넌트의 전문화입니다. 따라서 컴포넌트 클래스에@Component로 주석을 달 수 있지만, 대신 @Repository, @Service 또는 @Controller로주석을 달면 도구로 처리하거나 측면과 연결하는 데 더 적합하게 클래스를 만들 수 있습니다. 예를 들어, 이러한 스테레오타입 어노테이션은 포인트컷에 이상적인 타겟이 됩니다. 리포지토리, @서비스, @컨트롤러는 Spring 프레임워크의 향후 릴리스에서 추가적인 의미를 가질 수도 있습니다. 따라서 서비스 레이어에 @Component와 @Service 중 하나를 선택해야 하는 경우 @Service가 더 나은 선택입니다. 마찬가지로 앞서 설명한 것처럼 @Repository는 지속성 계층에서 자동 예외 변환을 위한 마커로 이미 지원되고 있습니다.
Using Meta-annotations and Composed Annotations
Spring에서 제공하는 많은 어노테이션을 자신의 코드에서 메타 어노테이션으로 사용할 수 있습니다. 메타 어노테이션은 다른 어노테이션에 적용할 수 있는 어노테이션입니다. 예를 들어 앞서언급한 @Service 어노테이션은 다음 예제에서 볼 수 있듯이 @Component로 메타 어노테이션되어 있습니다:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
// ...
}
컴포넌트는 @서비스가 @컴포넌트와 같은 방식으로 처리되도록 합니다. |
메타 어노테이션을 결합하여 "구성된 어노테이션"을 만들 수도 있습니다. 예를 들어 Spring MVC의 @RestController 어노테이션은 @Controller와@ResponseBody로 구성됩니다.
또한 구성된 어노테이션은 선택적으로 메타 어노테이션에서 어트리뷰트를 다시 선언하여 사용자 정의할 수 있습니다. 이는 메타 어노테이션 속성의 하위 집합만 노출하려는 경우에 특히 유용할 수 있습니다. 예를 들어 Spring의@SessionScope 어노테이션은 범위 이름을 세션에 하드 코딩하지만 여전히 proxyMode의 사용자 지정은 허용합니다. 다음 목록은SessionScope 어노테이션의 정의를 보여줍니다:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
/**
* Alias for {@link Scope#proxyMode}.
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
그런 다음 다음과 같이 proxyMode를 선언하지 않고 @SessionScope를 사용할 수 있습니다:
@Service
@SessionScope
public class SessionScopedService {
// ...
}
다음 예제에서 볼 수 있듯이 proxyMode의 값을 재정의할 수도 있습니다:
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
자세한 내용은Spring 어노테이션 프로그래밍 모델위키 페이지를 참조하세요.
Automatically Detecting Classes and Registering Bean Definitions
Spring은 정형화된 클래스를 자동으로 감지하고 해당BeanDefinition 인스턴스를 ApplicationContext에 등록할 수 있습니다. 예를 들어, 다음 두 클래스는 이러한 자동 감지를 사용할 수 있습니다:
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
이러한 클래스를 자동 감지하고 해당 빈을 등록하려면 @Configuration 클래스에@ComponentScan을 추가해야 하며, 여기서 basePackages 속성은 두 클래스의 공통 상위 패키지입니다. (또는 각 클래스의 부모 패키지를 포함하는 쉼표 또는 세미콜론 또는 공백으로 구분된 목록을 지정할 수 있습니다
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
간결성을 위해 앞의 예에서는 어노테이션의 값 속성(즉, @ComponentScan("org.example")을 사용했을 수 있습니다 |
다음 대안은 XML을 사용합니다:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
<context:component-scan>을 사용하면<context:annotation-config>의 기능이 암시적으로 활성화됩니다. 일반적으로 <context :component-scan>을 사용할 때는<context:annotation-config> 요소를 포함할 필요가 없습니다 |
클래스 경로 패키지를 스캔하려면 클래스 경로에 해당 디렉토리 항목이 있어야 합니다. Ant로 JAR을 빌드할 때 JAR 작업의 파일 전용 스위치를 활성화하지 않았는지 확인하세요. 또한 일부 환경에서는 보안 정책에 따라 클래스 경로 디렉터리가 노출되지 않을 수 있습니다(예: JDK 1.7.0_45 이상의 독립 실행형 앱 - 매니페스트에서 'Trusted-Library' 설정 필요 -stackoverflow.com/questions/19394570/java-jre-7u45-breaks-classloader-getresources 참조).
JDK 9의 모듈 경로(Jigsaw)에서 Spring의 클래스 경로 검색은 일반적으로 예상대로 작동합니다. 그러나 구성 요소 클래스가 모듈 정보설명자에 내보내져 있는지 확인하세요. Spring이 클래스의 비공개 멤버를 호출할 것으로 예상되는 경우, 해당 클래스가 '열린' 상태인지(즉, 모듈 정보 설명자에서exports 선언 대신 opens 선언을 사용하는지) 확인해야 합니다.
|
또한, 구성 요소 스캔 요소를 사용할 때 AutowiredAnnotationBeanPostProcessor와CommonAnnotationBeanPostProcessor가 모두 암시적으로 포함됩니다. 즉, 두 구성 요소는 XML로 제공되는 빈 구성 메타데이터 없이도 자동 감지되어 함께 연결됩니다.
Annotation-config 속성을 false 값으로 포함하면 AutowiredAnnotationBeanPostProcessor 및CommonAnnotationBeanPostProcessor의 등록을 비활성화할 수 있습니다 |
Using Filters to Customize Scanning
기본적으로 @Component, @Repository, @Service, @Controller,@Configuration으로 주석이 달린 클래스 또는 자체에 @Component로 주석이 달린 사용자 정의 어노테이션이 유일하게 감지되는 후보 컴포넌트입니다. 그러나 사용자 정의 필터를 적용하여 이 동작을 수정하고 확장할 수 있습니다. 사용자 정의 필터를 @ComponentScan 어노테이션의 포함 필터 또는 제외 필터 속성으로 추가하거나 XML 구성에서 <context :component-scan> 요소의 <context : include-filter /> 또는<context:exclude-filter /> 자식 요소로 추가합니다(또는 XML 구성에서 <context :component-scan> 요소의 <context: 포함 필터 /> 로). 각 필터 요소에는 유형 및 표현식 속성이 필요합니다. 다음 표에서는 필터링 옵션에 대해 설명합니다:
어노테이션(기본값) | org.example.SomeAnnotation | 대상 컴포넌트의 유형 수준에서 존재하거나 메타적으로 존재할 어노테이션입니다. |
할당 가능 | org.example.SomeClass | 대상 컴포넌트가 어사인(확장 또는 구현)할 수 있는 클래스(또는 인터페이스). |
aspectj | org.example..*Service+ | 대상 컴포넌트가 일치시킬 AspectJ 타입 표현식입니다. |
regex | org\.example\.Default.* | 대상 컴포넌트의 클래스 이름으로 일치시킬 정규식입니다. |
custom | org.example.MyTypeFilter | Org.springframework.core.type.TypeFilter 인터페이스의 사용자 정의 구현. |
다음 예제는 모든 @Repository 어노테이션을 무시하고 대신 "스텁" 리포지토리를 사용하는 구성을 보여줍니다:
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
// ...
}
다음 목록은 이에 해당하는 XML을 보여줍니다:
<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>
어노테이션에 useDefaultFilters=false를 설정하거나<component-scan/> 요소의 속성으로 use-default-filters="false "를 제공하여 기본 필터를 비활성화할 수도 있습니다. 이렇게 하면 @Component, @Repository, @Service, @Controller,@RestController 또는 @Configuration으로 어노테이션되거나 메타 어노테이션된 클래스의 자동 감지를 효과적으로 비활성화할 수 있습니다 |
Defining Bean Metadata within Components
Spring 컴포넌트는 컨테이너에 빈 정의 메타데이터를 제공할 수도 있습니다. 구성어노테이션 클래스 내에서 빈 메타데이터를 정의하는 데 사용되는 것과 동일한 @Bean 어노테이션을 사용하여 이 작업을 수행할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
앞의 클래스는doWork() 메서드에 애플리케이션별 코드가 있는 Spring 컴포넌트입니다. 그러나 이 클래스는 publicInstance() 메서드를 참조하는 팩토리 메서드가 있는 빈 정의도 제공합니다. Bean 어노테이션은 팩토리 메서드와 @Qualifier 어노테이션을 통해 한정자 값과 같은 기타 빈 정의 속성을 식별합니다. 지정할 수 있는 다른 메서드 수준 어노테이션으로는@Scope, @Lazy 및 사용자 지정 한정자 어노테이션이 있습니다.
컴포넌트 초기화를 위한 역할 외에도 @Autowired 또는 @Inject로 표시된 주입 지점에 @Lazy어노테이션을 배치할 수도 있습니다. 이러한 맥락에서 이는 지연 해결 프록시의 주입으로 이어집니다. 그러나 이러한 프록시 접근 방식은 다소 제한적입니다. 특히 선택적 종속성과 결합된 정교한 지연 상호 작용의 경우, 대신 ObjectProvider<MyTargetBean>을 사용하는 것이 좋습니다 |
앞서 설명한 대로 자동 와이어링된 필드 및 메서드가 지원되며, @Bean 메서드의 자동 와이어링이 추가로 지원됩니다. 다음 예제에서는 이를 수행하는 방법을 보여줍니다:
@Component
public class FactoryMethodComponent {
private static int i;
@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
// use of a custom qualifier and autowiring of method parameters
@Bean
protected TestBean protectedInstance(
@Qualifier("public") TestBean spouse,
@Value("#{privateInstance.age}") String country) {
TestBean tb = new TestBean("protectedInstance", 1);
tb.setSpouse(spouse);
tb.setCountry(country);
return tb;
}
@Bean
private TestBean privateInstance() {
return new TestBean("privateInstance", i++);
}
@Bean
@RequestScope
public TestBean requestScopedInstance() {
return new TestBean("requestScopedInstance", 3);
}
}
이 예에서는 String 메서드 매개변수 country를 privateInstance라는 다른 빈의 연령속성 값에 자동 연결합니다. Spring 표현식 언어 요소는 #{ <표현식> } 표기법을 통해 속성 값을 정의합니다. Value주석의 경우, 표현식 해석기는 표현식 텍스트를 해석할 때 빈 이름을 찾도록 미리 구성되어 있습니다.
Spring 프레임워크 4.3부터는InjectionPoint (또는 보다 구체적인 하위 클래스인 DependencyDescriptor) 유형의 팩토리 메서드 매개변수를 선언하여 현재 빈의 생성을 트리거하는 요청 주입 지점에 액세스할 수도 있습니다. 이는 빈 인스턴스의 실제 생성에만 적용되며 기존 인스턴스의 주입에는 적용되지 않는다는 점에 유의하세요. 따라서 이 기능은 프로토타입 범위의 빈에 가장 적합합니다. 다른 범위의 경우, 팩토리 메서드는 주어진 범위에서 새 빈 인스턴스 생성을 트리거한 주입 지점(예: 지연 싱글톤 빈 생성을 트리거한 종속성)만 볼 수 있습니다. 이러한 시나리오에서는 제공된 주입 지점 메타데이터를 의미적으로 주의해서 사용할 수 있습니다. 다음 예제는 InjectionPoint를 사용하는 방법을 보여줍니다:
@Component
public class FactoryMethodComponent {
@Bean @Scope("prototype")
public TestBean prototypeInstance(InjectionPoint injectionPoint) {
return new TestBean("prototypeInstance for " + injectionPoint.getMember());
}
}
일반 Spring 컴포넌트의 @Bean 메서드는 Spring @Configuration 클래스 내부의 메서드와 다르게 처리됩니다. 차이점은 메서드 및 필드 호출을 가로채기 위해 @Component클래스가 CGLIB로 향상되지 않았다는 것입니다. CGLIB 프록시는 @Configuration 클래스의 @Bean 메서드 내에서 메서드 또는 필드를 호출하면 협업 객체에 대한 빈 메타데이터 참조를 생성하는 수단입니다. 이러한 메서드는 일반적인 Java 시맨틱으로 호출되는 것이 아니라 @Bean 메서드에 대한 프로그래밍 호출을 통해 다른 빈을 참조하는 경우에도 Spring 빈의 일반적인 수명 주기 관리 및 프록시를 제공하기 위해 컨테이너를 통과합니다. 반대로 일반 @Component클래스 내에서 @Bean 메서드의 메서드 또는 필드를 호출하면 특별한 CGLIB 처리 또는 기타 제약 조건이 적용되지 않고 표준 Java 시맨틱이 적용됩니다.
포함된 구성 클래스를 인스턴스로 생성하지 않고도 호출할 수 있도록 @Bean 메서드를 정적으로 선언할 수 있습니다. 이는 포스트프로세서 빈(예: BeanFactoryPostProcessor또는 BeanPostProcessor 유형)을 정의할 때 특히 의미가 있는데, 이러한 빈은 컨테이너 수명 주기 초기에 초기화되므로 해당 시점에서 구성의 다른 부분을 트리거하지 않도록 해야 하기 때문입니다.
정적 @Bean 메서드에 대한 호출은 기술적 제한으로 인해 컨테이너가 절대로 가로채지 않으며, 심지어@Configuration 클래스 내에서도(이 섹션의 앞부분에서 설명한 대로) 가로채지 않습니다: CGLIB 서브클래싱은 정적이 아닌 메서드만 재정의할 수 있습니다. 결과적으로 다른 @Bean 메서드에 대한 직접 호출은 표준 Java 시맨틱을 가지므로 팩토리 메서드 자체에서 바로 독립적인 인스턴스가 반환됩니다.
Bean 메서드의 Java 언어 가시성은 Spring 컨테이너의 결과 빈 정의에 즉각적인 영향을 미치지 않습니다. 비@Configuration 클래스는 물론 정적 메서드에 대해서도 어디서나 자유롭게 팩토리 메서드를 선언할 수 있습니다. 그러나 @Configuration 클래스의 일반 @Bean 메서드는 재정의가 가능해야 합니다. 즉, 비공개 또는 최종 메서드로 선언해서는 안 됩니다.
지정된 컴포넌트 또는 구성 클래스의 베이스 클래스와 컴포넌트 또는 구성 클래스가 구현한 인터페이스에 선언된 Java 8 기본 메서드에서도@Bean 메서드가 검색됩니다. 이를 통해 복잡한 구성 배열을 유연하게 구성할 수 있으며, Spring 4.2부터는 Java 8 기본 메서드를 통해 다중 상속도 가능합니다.
마지막으로, 단일 클래스는 런타임에 사용 가능한 종속성에 따라 사용할 여러 팩토리 메서드의 배열로 동일한 빈에 대해 여러 개의 @Bean 메서드를 보유할 수 있습니다. 이는 다른 구성 시나리오에서 "가장 탐욕스러운" 생성자 또는 팩토리 메서드를 선택하는 것과 동일한 알고리즘입니다: 컨테이너가 여러 @Autowired 생성자 중에서 선택하는 방식과 유사하게, 만족스러운 종속성 수가 가장 많은 변형이 생성 시 선택됩니다.
|
Naming Autodetected Components
스캔 프로세스의 일부로 컴포넌트가 자동 감지되면 해당 스캐너에 알려진 BeanNameGenerator 전략에 의해 해당 빈 이름이 생성됩니다.
기본적으로 AnnotationBeanNameGenerator가 사용됩니다. Spring스테레오타입 어노테이션의 경우, 어노테이션의 값 속성을 통해 이름을 제공하면 해당 이름이 해당 빈 정의의 이름으로 사용됩니다. 이 규칙은 Spring 스테레오타입 어노테이션 대신 다음 JSR-250 및 JSR-330 어노테이션을 사용하는 경우에도 적용됩니다: jakarta.annotation.ManagedBean, @javax.annotation.ManagedBean,@jakarta.inject.Named 및 @javax.inject.Named.
Spring Framework 6.1부터 빈 이름을 지정하는 데 사용되는 어노테이션 속성의 이름은 더 이상 값일 필요가 없습니다. 사용자 정의 스테레오타입 어노테이션은 이름과 같은 다른 이름으로 어노테이션을 선언하고 해당 어노테이션에 @AliasFor(annotation = Component.class, attribute = "value")로 어노테이션을 추가할 수 있습니다. 구체적인 예는 ControllerAdvice#name() 의 소스 코드 선언을 참조하세요.
Spring 프레임워크 6.1부터 규칙 기반 스테레오타입 이름에 대한 지원은 더 이상 사용되지 않으며 향후 버전의 프레임워크에서 제거될 예정입니다. 따라서 사용자 정의 스테레오타입 어노테이션은 @AliasFor를 사용하여 @Component에서 값 속성에 대한 명시적 별칭을 선언해야 합니다. 구체적인 예는 Repository#value() 및ControllerAdvice#name( )의 소스 코드 선언을 참조하세요.
|
이러한 어노테이션에서 명시적인 빈 이름을 도출할 수 없거나 기타 감지된 구성 요소(예: 사용자 지정 필터로 검색된 이름)의 경우 기본 빈 이름 생성기는 대문자가 아닌 정규화되지 않은 클래스 이름을 반환합니다. 예를 들어, 다음 컴포넌트 클래스가 감지된 경우 이름은 myMovieLister 및movieFinderImpl이 됩니다.
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
기본 빈 이름 지정 전략에 의존하지 않으려면 사용자 지정 빈 이름 지정 전략을 제공할 수 있습니다. 먼저BeanNameGenerator인터페이스를 구현하고 기본 no-arg 생성자를 포함해야 합니다. 그런 다음 스캐너를 구성할 때 다음 예제 주석과 빈 정의에 표시된 것처럼 정규화된 클래스 이름을 제공하세요.
동일한 정규화되지 않은 클래스 이름을 가진 자동 감지된 여러 구성 요소(즉, 이름은 동일하지만 다른 패키지에 있는 클래스)로 인해 이름 지정 충돌이 발생하는 경우, 생성된 빈 이름에 대해 정규화된 클래스 이름을 기본값으로 하는 BeanNameGenerator를 구성해야 할 수 있습니다. Spring Framework 5.2.3부터,org.springframework.context.annotation 패키지에 있는FullyQualifiedAnnotationBeanNameGenerator를 이러한 용도로 사용할 수 있습니다 |
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
일반적으로 다른 컴포넌트가 명시적으로 참조할 수 있는 경우 어노테이션으로 이름을 지정하는 것이 좋습니다. 반면에 컨테이너가 배선을 담당할 때마다 자동 생성된 이름이 적절합니다.
Providing a Scope for Autodetected Components
일반적으로 Spring에서 관리되는 컴포넌트와 마찬가지로, 자동 감지된 컴포넌트의 기본적이고 가장 일반적인 범위는 싱글톤입니다. 그러나 @Scope 어노테이션으로 지정할 수 있는 다른 범위가 필요한 경우도 있습니다. 다음 예시와 같이 어노테이션 내에 범위의 이름을 지정할 수 있습니다:
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
범위 어노테이션은 구체적인 빈 클래스(어노테이션된 컴포넌트의 경우) 또는 팩토리 메서드( @Bean 메서드의 경우)에 대해서만 내재화됩니다. XML 빈 정의와 달리, 빈 정의 상속이라는 개념이 없으며 클래스 수준에서의 상속 계층 구조는 메타데이터 목적과 관련이 없습니다 |
Spring 컨텍스트에서 "요청" 또는 "세션"과 같은 웹 특정 범위에 대한 자세한 내용은 요청, 세션, 애플리케이션 및 웹소켓 스코프를 참조하세요. 이러한 범위에 대해 미리 빌드된 어노테이션과 마찬가지로, Spring의 메타 어노테이션 접근 방식을 사용하여 자체 범위 어노테이션을 작성할 수도 있습니다(예: @Scope("프로토타입")로 메타 어노테이션된 사용자 정의 어노테이션, 사용자 정의 범위 프록시 모드 선언 가능).
어노테이션 기반 접근 방식에 의존하지 않고 범위 확인을 위한 사용자 정의 전략을 제공하려면ScopeMetadataResolver인터페이스를 구현할 수 있습니다. 기본 no-arg 생성자를 포함해야 합니다. 그런 다음 스캐너를 구성할 때 어노테이션과 빈 정의의 다음 예제에서 볼 수 있듯이 정규화된 클래스 이름을 제공할 수 있습니다 |
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver"/>
</beans>
특정 싱글톤이 아닌 범위를 사용하는 경우 범위가 지정된 객체에 대한 프록시를 생성해야 할 수도 있습니다. 그 이유는 종속성으로 범위 지정된 빈에 설명되어 있습니다. 이를 위해 구성 요소 스캔 요소에서 scoped-proxy 속성을 사용할 수 있습니다. 가능한 세 가지 값은 no, interfaces, targetClass입니다. 예를 들어, 다음 구성은 표준 JDK 동적 프록시를 생성합니다:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
<beans>
<context:component-scan base-package="org.example" scoped-proxy="interfaces"/>
</beans>
Providing Qualifier Metadata with Annotations
한정자를 사용하여 어노테이션 기반 자동 배선 미세 조정하기에서 @Qualifier 어노테이션에 대해 설명합니다. 해당 섹션의 예에서는 @Qualifier 어노테이션 및 사용자 지정 한정자 어노테이션을 사용하여 자동 와이어 후보를 해결할 때 세분화된 제어 기능을 제공하는 방법을 보여줍니다. 이러한 예제는 XML 빈 정의를 기반으로 했기 때문에 한정자 메타데이터는 XML에서 빈 요소의 한정자 또는 메타자식 요소를 사용하여 후보 빈 정의에 제공되었습니다. 구성 요소의 자동 감지를 위해 클래스 경로 검색에 의존하는 경우 한정자 메타데이터에 후보 클래스에 대한 유형 수준 주석을 제공할 수 있습니다. 다음 세 가지 예는 이 기법을 보여줍니다:
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Genre("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
@Component
@Offline
public class CachingMovieCatalog implements MovieCatalog {
// ...
}
대부분의 어노테이션 기반 대안과 마찬가지로 어노테이션 메타데이터는 클래스 정의 자체에 바인딩되지만, XML을 사용하면 메타데이터가 클래스가 아닌 인스턴스별로 제공되므로 동일한 유형의 여러 빈이 한정자 메타데이터에 변형을 제공할 수 있다는 점을 염두에 두세요 |
Using JSR 330 Standard Annotations
Spring은 JSR-330 표준 어노테이션(의존성 주입)을 지원합니다. 이러한 어노테이션은 Spring 어노테이션과 동일한 방식으로 스캔됩니다. 이를 사용하려면 클래스 경로에 관련 jars가 있어야 합니다.
Maven을 사용하는 경우 jakarta.inject 아티팩트는 표준 Maven 리포지토리(https://repo.maven.apache.org/maven2/jakarta/inject/jakarta.inject-api/2.0.0/)에서 사용할 수 있습니다. 파일 pom.xml에 다음 종속성을 추가할 수 있습니다:
|
Dependency Injection with @Inject and @Named
자동 연결 대신 다음과 같이 @jakarta.inject.Inject를 사용할 수 있습니다:
import jakarta.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
// ...
}
}
자동 연결과 마찬가지로 필드 수준, 메서드 수준 및 생성자-인수 수준에서 @Inject를 사용할 수 있습니다. 또한, 주입 지점을Provider로 선언하여 더 짧은 범위의 빈에 대한 온디맨드 액세스를 허용하거나 Provider.get() 호출을 통해 다른 빈에 대한 지연 액세스를 허용할 수 있습니다. 다음 예제는 앞의 예제를 변형한 것입니다:
import jakarta.inject.Inject;
import jakarta.inject.Provider;
public class SimpleMovieLister {
private Provider<MovieFinder> movieFinder;
@Inject
public void setMovieFinder(Provider<MovieFinder> movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.get().findMovies(...);
// ...
}
}
주입해야 하는 종속성에 한정된 이름을 사용하려면 다음 예제에서와 같이 @Named 어노테이션을 사용해야 합니다:
import jakarta.inject.Inject;
import jakarta.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
자동화된 @Autowired와 마찬가지로 @Inject는 java.util.Optional 또는@Nullable과 함께 사용할 수도 있습니다. 여기서는 @Inject에 필수 속성이 없기 때문에 더욱 유용하게 사용할 수 있습니다. 다음 예제는 @Inject와@Nullable을 사용하는 방법을 보여줍니다:
public class SimpleMovieLister {
@Inject
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
// ...
}
}
public class SimpleMovieLister {
@Inject
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
// ...
}
}
@Named and @ManagedBean: Standard Equivalents to the @Component Annotation
다음 예에서 보는 바와 같이 @Component 대신 @jakarta.inject.Named 또는 jakarta.annotation.ManagedBean을 사용할 수 있습니다:
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
컴포넌트의 이름을 지정하지 않고 @Component를 사용하는 것이 매우 일반적입니다. 다음 예에서 볼 수 있듯이@Named도 비슷한 방식으로 사용할 수 있습니다:
import jakarta.inject.Inject;
import jakarta.inject.Named;
@Named
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
다음 예제에서 볼 수 있듯이 @Named 또는 @ManagedBean을 사용하는 경우 Spring 어노테이션을 사용할 때와 똑같은 방식으로 컴포넌트 검색을 사용할 수 있습니다:
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
// ...
}
컴포넌트와는 달리, JSR-330 @Named 및 JSR-250 @ManagedBean어노테이션은 컴포짓할 수 없습니다. 사용자 정의 컴포넌트 어노테이션을 작성하려면 Spring의 스테레오타입 모델을 사용해야 합니다 |
Limitations of JSR-330 Standard Annotations
표준 주석으로 작업할 때는 다음 표에 나와 있는 것처럼 몇 가지 중요한 기능을 사용할 수 없다는 것을 알아야 합니다:
@Autowired | @Inject | 주입에는 '필수' 속성이 없습니다. 대신 Java 8의 Optional과 함께 사용할 수 있습니다. |
컴포넌트 | @Named / @ManagedBean | JSR-330은 컴포저블 모델을 제공하지 않으며, 명명된 컴포넌트를 식별하는 방법만 제공합니다. |
범위("싱글톤") | 싱글톤 | JSR-330의 기본 범위는 Spring의 프로토타입과 유사합니다. 그러나 Spring의 일반적인 기본값과 일관성을 유지하기 위해 Spring 컨테이너에 선언된 JSR-330 빈은 기본적으로 싱글톤입니다. 싱글톤이 아닌 다른 범위를 사용하려면 Spring의 @Scope 어노테이션을 사용해야 합니다. jakarta.in ject는 jakarta.inject.Scope 어노테이션도 제공하지만 이것은 사용자 정의 어노테이션을 만드는 데만 사용하도록 되어 있습니다. |
한정자 | @Qualifier / @Named | jakarta.inject.Qualifier는 사용자 정의 한정자를 작성하기 위한 메타 어노테이션일 뿐입니다. 구체적인 문자열 한정자(예: 값을 가진 Spring의 @Qualifier )는 jakarta.inject.Named를 통해 연결할 수 있습니다. |
@Value | - | 동등한 값 없음 |
@Lazy | - | 등가물 없음 |
ObjectFactory | Provider | jakarta.inject.Provider는 Spring의 ObjectFactory에 대한 직접적인 대안으로, get() 메서드 이름이 더 짧을 뿐입니다. 또한 Spring의 @Autowired와 함께 사용하거나 주석이 없는 생성자 및 설정자 메서드와 함께 사용할 수 있습니다. |
Java 기반 컨테이너 구성
이 섹션에서는 Java 코드에서 어노테이션을 사용하여 Spring 컨테이너를 구성하는 방법을 다룹니다.
섹션 요약
기본 개념 bean과 @Configuration
Spring의 Java 구성 지원의 핵심 아티팩트는@Configuration 어노테이션 클래스와 @Bean 어노테이션 메서드입니다.
Bean 어노테이션은 메서드가 Spring IoC 컨테이너에서 관리할 새 객체를 인스턴스화, 구성 및 초기화한다는 것을 나타내는 데 사용됩니다. Spring의 <beans/> XML 구성에 익숙한 분들을 위해 @Bean 어노테이션은 <bean/> 요소와 동일한 역할을 합니다. 모든 Spring@Component에 @Bean 어노테이션 메서드를 사용할 수 있습니다. 그러나 @Configuration 빈과 함께 가장 자주 사용됩니다.
구성으로 클래스에 주석을 달면 해당 클래스의 주요 목적이 빈 정의의 소스임을 나타냅니다. 또한 @Configuration 클래스를 사용하면 동일한 클래스에서 다른 @Bean 메서드를 호출하여 빈 간 종속성을 정의할 수 있습니다. 가능한 가장 간단한 @Configuration 클래스는 다음과 같습니다:
@Configuration
public class AppConfig {
@Bean
public MyServiceImpl myService() {
return new MyServiceImpl();
}
}
앞의 AppConfig 클래스는 다음과 같은 Spring <beans/> XML과 동일합니다:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>
일반적인 시나리오에서 @Bean 메서드는 @Configuration 클래스 내에서 선언되어 전체 구성 클래스 처리가 적용되고 따라서 교차 메서드 참조가 컨테이너의 수명 주기 관리로 리디렉션되도록 합니다. 이렇게 하면 동일한 @Bean 메서드가 일반 Java 메서드 호출을 통해 실수로 호출되는 것을 방지하여 추적하기 어려울 수 있는 미묘한 버그를 줄이는 데 도움이 됩니다.
구성으로 주석을 달지 않은 클래스 내에서 @Bean 메서드가 선언되거나 @Configuration(proxyBeanMethods=false) 이 선언되면 "라이트" 모드에서 처리되는 것으로 간주됩니다. 이러한 시나리오에서@Bean 메서드는 특별한 런타임 처리 없이(즉, CGLIB 서브클래스를 생성하지 않고) 사실상 범용 팩토리 메서드 메커니즘입니다. 이러한 메서드에 대한 사용자 정의 Java 호출은 컨테이너에 의해 가로채지 않으므로 주어진 빈에 대한 기존 싱글톤(또는 범위 지정) 인스턴스를 재사용하는 대신 매번 새 인스턴스를 생성하여 일반 메서드 호출처럼 동작합니다.
결과적으로 런타임 프록시가 없는 클래스의 @Bean 메서드는 빈 간 종속성을 전혀 선언하지 않습니다. 대신, 해당 메서드는 포함된 컴포넌트의 필드와 선택적으로 팩토리 메서드가 자동화된 공동 작업자를 수신하기 위해 선언할 수 있는 인자에 대해 작동해야 합니다. 따라서 이러한 @Bean 메서드는 다른 @Bean 메서드를 호출할 필요가 없으며, 모든 호출은 팩토리 메서드 인수를 통해 대신 표현할 수 있습니다. 이 경우 긍정적인 측면은 런타임에 CGLIB 서브클래싱을 적용할 필요가 없으므로 오버헤드와 설치 공간이 줄어든다는 것입니다.
다음 섹션에서는 @Bean 및 @Configuration 어노테이션에 대해 자세히 설명합니다. 그러나 먼저 Java 기반 구성을 사용하여 Spring 컨테이너를 생성하는 다양한 방법에 대해 설명합니다.
Instantiating the Spring Container by Using AnnotationConfigApplicationContext
다음 섹션에서는 Spring 3.0에 도입된 Spring의 AnnotationConfigApplicationContext에 대해 설명합니다. 이 다용도 ApplicationContext 구현은@Configuration 클래스뿐만 아니라 일반 @Component 클래스와 JSR-330 메타데이터로 주석이 달린 클래스도 입력으로 받을 수 있습니다.
구성 클래스가 입력으로 제공되면 @Configuration 클래스 자체가 빈 정의로 등록되고 클래스 내에서 선언된 모든 @Bean 메서드도 빈 정의로 등록됩니다.
컴포넌트 및 JSR-330 클래스가 제공되면 빈 정의로 등록되며, 필요한 경우 해당 클래스 내에서 @Autowired 또는 @Inject와 같은 DI 메타데이터가 사용되는 것으로 가정합니다.
Simple Construction
ClassPathXmlApplicationContext를 인스턴스화할 때 Spring XML 파일을 입력으로 사용하는 것과 매우 유사한 방식으로, AnnotationConfigApplicationContext를 인스턴스화할 때 @Configuration 클래스를 입력으로 사용할 수 있습니다. 이렇게 하면 다음 예제에서 볼 수 있듯이 Spring 컨테이너를 완전히 XML 없이 사용할 수 있습니다:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
앞서 언급했듯이 AnnotationConfigApplicationContext는 @Configuration 클래스로만 작동하는 것으로 제한되지 않습니다. 다음 예제에서 볼 수 있듯이 모든 @Component 또는 JSR-330 어노테이션 클래스를 생성자 입력으로 제공할 수 있습니다:
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
앞의 예제에서는 MyServiceImpl, Dependency1 및 Dependency2가 @Autowired와 같은 Spring 종속성 주입 어노테이션을 사용한다고 가정합니다.
Building the Container Programmatically by Using register(Class<?>…)
No-arg 생성자를 사용하여 AnnotationConfigApplicationContext를 인스턴스화한 다음 register( ) 메서드를 사용하여 구성할 수 있습니다. 이 접근 방식은 프로그래밍 방식으로 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…)
컴포넌트 스캔을 활성화하려면 다음과 같이 @Configuration 클래스에 주석을 달면 됩니다:
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
// ...
}
이 어노테이션은 컴포넌트 스캔을 활성화합니다. |
Spring에 익숙한 사용자라면 다음 예제와 같이 Spring의 컨텍스트에 해당하는 XML 선언인 네임스페이스에 익숙할 것입니다:
|
앞의 예제에서는 com.acme 패키지를 스캔하여@Component 어노테이션이 있는 클래스를 찾고, 해당 클래스는 컨테이너 내에 Spring 빈 정의로 등록됩니다. 다음 예제에서 볼 수 있듯이 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);
}
구성 클래스는 @Component로 메타 주석을달았으므로 컴포넌트 스캔의 후보가 된다는 점을 기억하세요. 앞의 예제에서 AppConfig가 com.acme 패키지(또는 그 아래 패키지) 내에 선언되어 있다고 가정하면 scan() 호출 중에 이 클래스가 선택됩니다. Refresh() 호출 시 모든 @Bean메서드가 처리되고 컨테이너 내에 빈 정의로 등록됩니다 |
Support for Web Applications with AnnotationConfigWebApplicationContext
AnnotationConfigApplicationContext의 WebApplicationContext 변형은 AnnotationConfigWebApplicationContext와 함께 사용할 수 있습니다. 이 구현은 Spring ContextLoaderListener 서블릿 리스너, Spring MVCDispatcherServlet 등을 구성할 때 사용할 수 있습니다. 다음 web.xml 스니펫은 일반적인 Spring MVC 웹 애플리케이션을 구성합니다( 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>
프로그래매틱 사용 사례의 경우, AnnotationConfigWebApplicationContext의 대안으로 GenericWebApplicationContext를 사용할 수 있습니다. 자세한 내용은GenericWebApplicationContext자바독을 참조하세요 |
Using the @Bean Annotation
@Bean은 메서드 수준 어노테이션이며 XML <bean/> 요소의 직접적인 아날로그입니다. 이 어노테이션은 다음과 같은 <bean/>에서 제공하는 일부 어트리뷰트를 지원합니다:
구성 어노테이션 또는 컴포넌트 어노테이션 클래스에서 @Bean 어노테이션을 사용할 수 있습니다.
Declaring a Bean
빈을 선언하려면 @Bean 어노테이션을 사용하여 메서드에 주석을 달 수 있습니다. 이 메서드를 사용하여 메서드의 반환 값으로 지정된 유형의 ApplicationContext 내에 빈 정의를 등록할 수 있습니다. 기본적으로 빈 이름은 메서드 이름과 동일합니다. 다음 예제는 @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
기본 메서드를 사용하여 빈을 정의할 수도 있습니다. 이렇게 하면 기본 메서드에 빈 정의가 있는 인터페이스를 구현하여 빈 구성을 구성할 수 있습니다.
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)이 알려집니다. 비래시 싱글톤 빈은 선언 순서에 따라 인스턴스화되므로 다른 구성 요소가 선언되지 않은 유형(예: @Autowired TransferServiceImpl은 transferService 빈이 인스턴스화된 경우에만 확인)으로 매칭을 시도하는 시기에 따라 다른 유형 일치 결과가 표시될 수 있습니다.
선언된 서비스 인터페이스로 유형을 일관되게 참조하는 경우@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 our TransferService requires an AccountRepository, we can materialize that dependency with a method parameter, as the following example shows:
- Java
- Kotlin
@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.
Receiving Lifecycle Callbacks
Bean 어노테이션으로 정의된 모든 클래스는 일반 수명 주기 콜백을 지원하며 JSR-250의 @PostConstruct 및 @PreDestroy 어노테이션을 사용할 수 있습니다. 자세한 내용은JSR-250 어노테이션을 참조하세요.
일반 Spring 라이프사이클 콜백도 완벽하게 지원됩니다. 빈이 InitializingBean, DisposableBean 또는 Lifecycle을 구현하는 경우 컨테이너에서 해당 메서드가 호출됩니다.
표준 *Aware 인터페이스 세트(예: BeanFactoryAware,BeanNameAware,MessageSourceAware,ApplicationContextAware 등)도 완벽하게 지원됩니다.
다음 예제에서 볼 수 있듯이 @Bean 어노테이션은 Spring XML의 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();
}
}
기본적으로 공개 닫기 또는 종료메서드가 있는 Java 구성으로 정의된 빈은 자동으로 파기 콜백으로 등록됩니다. 공개닫기 또는 종료 메서드가 있고 컨테이너가 종료될 때 호출되지 않도록 하려면 @Bean(destroyMethod = "") 을 빈 정의에 추가하여 기본 (추론) 모드를 비활성화할 수 있습니다.
라이프사이클이 애플리케이션 외부에서 관리되므로 JNDI로 획득한 리소스에 대해 기본적으로 이 작업을 수행할 수 있습니다. 특히 데이터소스에 대해서는 항상 이 작업을 수행해야 하는데, 이는 Jakarta EE 애플리케이션 서버에서 문제가 되는 것으로 알려져 있습니다.
다음 예제는데이터 소스에 대한 자동 소멸 콜백을 방지하는 방법을 보여줍니다:
또한 @Bean 메서드에서는 일반적으로 Spring의 JndiTemplate 또는 JndiLocatorDelegate 헬퍼를 사용하거나 JndiObjectFactoryBean 변형이 아닌 직접 JNDIInitialContext 사용을 통해 프로그래밍 방식의 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 어노테이션이 포함되어 있어 빈의 범위를 지정할 수 있습니다.
Scope 어노테이션 사용
Bean 어노테이션으로 정의된 빈이 특정 범위를 갖도록 지정할 수 있습니다.Bean Scopes 섹션에 지정된 표준 범위를 사용할 수 있습니다.
기본 범위는 싱글톤이지만 다음 예제에서 볼 수 있듯이 @Scope 어노테이션을 사용하여 이를 재정의할 수 있습니다:
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}
범위 및 범위 지정 프록시
Spring은 범위 지정프록시를 통해 범위 지정 종속성으로 작업하는 편리한 방법을 제공합니다. XML 구성을 사용할 때 이러한 프록시를 생성하는 가장 쉬운 방법은 <aop:scoped-proxy/> 요소입니다. Java에서 @Scope 어노테이션으로 빈을 구성하면 proxyMode 속성을 통해 동일한 지원을 제공합니다. 기본값은 일반적으로 구성 요소 스캔 명령어 수준에서 다른 기본값을 구성하지 않는 한 범위 지정 프록시를 생성하지 않아야 함을 나타내는 ScopedProxyMode.DEFAULT입니다.ScopedProxyMode.TARGET_CLASS, ScopedProxyMode.INTERFACES 또는 ScopedProxyMode.NO를 지정할 수 있습니다.
XML 참조 문서(범위 지정프록시 참조)의 범위 지정 프록시 예제를 Java를 사용하여 @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 메서드의 이름을 결과 빈의 이름으로 사용합니다. 그러나 다음 예제에서 볼 수 있듯이 이름 속성을 사용하여 이 기능을 재정의할 수 있습니다:
@Configuration
public class AppConfig {
@Bean("myThing")
public Thing thing() {
return new Thing();
}
}
Bean Aliasing
Bean 이름 지정하기에서 설명한 대로 단일 빈에 여러 개의 이름을 지정하는 것이 바람직할 때가 있습니다. 이를 위해 @Bean 어노테이션의 이름 속성은 문자열 배열을 허용합니다. 다음 예에서는 빈에 여러 개의 별칭을 설정하는 방법을 보여 줍니다:
@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();
}
}
Using the @Configuration annotation
구성은 객체가 빈 정의의 소스임을 나타내는 클래스 수준 어노테이션입니다. 구성 클래스는 @Bean 어노테이션 메서드를 통해 빈을 선언합니다. 또한 @Configuration 클래스에서 @Bean 메서드 호출을 사용하여 빈 간 종속성을 정의할 수 있습니다. 기본 개념을 참조하십시오: 기본 개념: @Bean 및 @Configuration에서 일반적인 소개를 참조하세요.
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
앞서 언급했듯이 조회 메서드 주입은 드물게 사용해야 하는 고급 기능입니다. 싱글톤 범위의 빈이 프로토타입 범위의 빈에 종속성을 갖는 경우에 유용합니다. 이러한 유형의 구성에 Java를 사용하면 이 패턴을 자연스럽게 구현할 수 있습니다. 다음 예제는 조회 메서드 주입을 사용하는 방법을 보여줍니다:
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 구성을 사용하면 새 (프로토타입) 명령 객체를 조회하는 방식으로 추상 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
다음 예제에서는 @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 패키지로 리패키징되어스프링 코어 JAR에 직접 포함되므로 클래스 경로에 CGLIB를 추가할 필요가 없습니다.
|
CGLIB는 시작 시점에 기능을 동적으로 추가하기 때문에 몇 가지 제한 사항이 있습니다. 특히 구성 클래스는 최종적이지 않아야 합니다. 그러나 @Autowired 또는 기본 주입을 위한 기본이 아닌 단일 생성자 선언을 사용하는 것을 포함하여 모든 생성자는 구성 클래스에서 허용됩니다.
CGLIB로 인한 제한을 피하고 싶다면 @Configuration이 아닌 클래스(예: 일반 @Component 클래스 대신)에서 @Bean메서드를 선언하거나 구성 클래스에@Configuration(proxyBeanMethods = false)을 주석으로 추가하는 것을 고려하세요. 그러면 @Bean 메서드 간의 교차 메서드 호출은 가로채지 않으므로 생성자 또는 메서드 수준에서 종속성 주입에만 의존해야 합니다.
|
Composing Java-based Configurations
Spring의 Java 기반 구성 기능을 사용하면 어노테이션을 작성하여 구성의 복잡성을 줄일 수 있습니다.
Using the @Import Annotation
Spring XML 파일 내에서 <import/> 요소가 구성을 모듈화하는 데 사용되는 것처럼, 다음 예제에서 볼 수 있듯이 @Import 어노테이션을 사용하면 다른 구성 클래스에서 @Bean 정의를 로드할 수 있습니다:
@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();
}
}
이제 컨텍스트를 인스턴스화할 때 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);
}
이 접근 방식은 컨테이너를 구성하는 동안 잠재적으로 많은 수의@Configuration 클래스를 기억할 필요 없이 하나의 클래스만 처리하면 되므로 컨테이너 인스턴스화를 간소화합니다.
Spring 프레임워크 4.2부터 @Import는 AnnotationConfigApplicationContext.register 메서드와 유사하게 일반 구성 요소 클래스에 대한 참조도 지원합니다. 이는 모든 구성 요소를 명시적으로 정의하는 진입점으로 몇 개의 구성 클래스를 사용하여 구성 요소 검색을 피하려는 경우에 특히 유용합니다 |
가져온 @Bean 정의에 종속성 주입하기
앞의 예제는 작동하지만 단순합니다. 대부분의 실제 시나리오에서 빈은 구성 클래스 간에 서로 종속성을 갖습니다. XML을 사용하는 경우, 컴파일러가 관여하지 않고 컨테이너 초기화 중에ref="someBean" 을 선언하고 Spring이 이를 처리하도록 신뢰할 수 있으므로 문제가 되지 않습니다. @Configuration 클래스를 사용하는 경우, Java 컴파일러는 구성 모델에 제약을 가하는데, 다른 빈에 대한 참조가 유효한 Java 구문이어야 한다는 점에서 제약이 있습니다.
다행히도 이 문제를 해결하는 방법은 간단합니다.이미 논의했듯이 @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");
}
동일한 결과를 얻을 수 있는 다른 방법이 있습니다. 구성 클래스는 궁극적으로 컨테이너의 또 다른 빈에 불과하다는 점을 기억하세요: 즉, 다른 빈과 동일하게@Autowired 및 @Value 주입과 기타 기능을 활용할 수 있습니다.
이러한 방식으로 주입하는 종속성은 가장 단순한 종류의 종속성만 사용해야 합니다. 컨텍스트 초기화 중에 @Configuration클래스가 매우 일찍 처리되므로 이러한 방식으로 종속성을 강제로 주입하면 예기치 않은 조기 초기화가 발생할 수 있습니다. 가능하면 앞의 예제에서와 같이 매개변수 기반 주입을 사용하세요.
동일한 구성 클래스의 @PostConstruct 메서드 내에서 로컬로 정의된 빈에 대한 액세스를 피하세요. 정적이 아닌 @Bean 메서드는 의미상 완전히 초기화된 구성 클래스 인스턴스를 호출해야 하므로 순환 참조가 발생하게 됩니다. 순환 참조가 허용되지 않는 경우(예: Spring Boot 2.6 이상)에는 BeanCurrentlyInCreationException이 트리거될 수 있습니다.
또한 @Bean을 통한 BeanPostProcessor 및 BeanFactoryPostProcessor 정의에 특히 주의하세요. 이러한 정의는 일반적으로 정적 @Bean 메서드로 선언해야 하며, 포함 구성 클래스의 인스턴스화를 트리거하지 않아야 합니다. 그렇지 않으면 @Autowired 및 @Value가 구성 클래스 자체에서 작동하지 않을 수 있는데, 이는AutowiredAnnotationBeanPostProcessor보다 먼저 빈 인스턴스로 생성할 수 있기 때문입니다.
|
다음 예제는 하나의 빈을 다른 빈에 자동 와이어링하는 방법을 보여줍니다:
@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");
}
구성 클래스에서 생성자 주입은 Spring 프레임워크 4.3부터 지원됩니다. 또한 대상 빈이 하나의 생성자만 정의하는 경우 @Autowired를 지정할 필요가 없습니다 |
앞의 시나리오에서 @Autowired를 사용하면 잘 작동하고 원하는 모듈성을 제공하지만, 자동화된 빈 정의가 선언된 위치를 정확히 파악하는 것은 여전히 다소 모호합니다. 예를 들어 ServiceConfig를 살펴보는 개발자는 @Autowired AccountRepository 빈이 어디에 선언되어 있는지 정확히 어떻게 알 수 있을까요? 코드에 명시되어 있지 않으므로 괜찮을 수도 있습니다.Eclipse용 Spring 도구는 모든 것이 어떻게 연결되어 있는지 보여주는 그래프를 렌더링할 수 있는 도구를 제공하므로 이 정도면 충분할 수 있다는 점을 기억하세요. 또한 Java IDE는 AccountRepository 유형의 모든 선언과 사용을 쉽게 찾을 수 있으며 해당 유형을 반환하는 @Bean 메서드의 위치를 빠르게 표시할 수 있습니다.
이러한 모호함이 허용되지 않고 IDE 내에서 하나의 @Configuration 클래스에서 다른 클래스로 직접 탐색하려는 경우에는 구성 클래스 자체를 자동 배선하는 것을 고려하세요. 다음 예제는 그 방법을 보여줍니다:
@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());
}
}
앞의 상황에서는 계정 리포지토리가 정의된 곳이 완전히 명시적이었습니다. 그러나 이제 서비스 구성은 리포지토리 구성에 긴밀하게 결합되어 있습니다. 이것이 트레이드오프입니다. 인터페이스 기반 또는 추상 클래스 기반 @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");
}
이제 ServiceConfig는 구체적인DefaultRepositoryConfig에 대해 느슨하게 결합되어 있으며, 기본 제공 IDE 도구는 여전히 유용합니다: RepositoryConfig 구현의 유형 계층구조를 쉽게 얻을 수 있습니다. 이렇게 하면 @Configuration 클래스와 그 종속성을 탐색하는 것이 인터페이스 기반 코드를 탐색하는 일반적인 프로세스와 다르지 않게 됩니다.
특정 빈의 시작 생성 순서에 영향을 미치고 싶다면, 일부 빈을 @Lazy (시작 시 대신 첫 번째 액세스 시 생성)로 선언하거나 특정 다른 빈을 @DependsOn (특정 다른 빈이 현재 빈보다 먼저 생성되도록 함으로써 후자의 직접적인 종속성이 의미하는 것 이상으로)으로 선언하는 것을 고려해 보세요 |
Conditionally Include @Configuration Classes or @Bean Methods
임의의 시스템 상태에 따라 전체 @Configuration 클래스 또는 개별 @Bean 메서드를 조건부로 활성화 또는 비활성화하는 것이 유용할 때가 많습니다. 이에 대한 한 가지 일반적인 예는 Spring 환경에서 특정 프로필이 활성화된 경우에만 @Profile 어노테이션을 사용하여 빈을 활성화하는 것입니다(자세한 내용은 Bean 정의 프로필참조).
프로필 어노테이션은 실제로 @Conditional이라는 훨씬 더 유연한 어노테이션을 사용하여 구현됩니다. 조건 어노테이션은 @Bean이 등록되기 전에 참조해야 하는 특정org.springframework.context.annotation.Condition 구현을 나타냅니다.
Condition 인터페이스의 구현은 참 또는 거짓을 반환하는 matches(...)메서드를 제공합니다. 예를 들어, 다음 목록은 @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;
}
자세한 내용은 @Conditional자바독을 참조하세요.
Combining Java and XML Configuration
Spring의 @Configuration 클래스 지원은 Spring XML을 100% 완벽하게 대체하는 것을 목표로 하지 않습니다. Spring XML 네임스페이스와 같은 일부 기능은 컨테이너를 구성하는 이상적인 방법으로 남아 있습니다. XML이 편리하거나 필요한 경우, 예를 들어ClassPathXmlApplicationContext를 사용하여 "XML 중심" 방식으로 컨테이너를 인스턴스화하거나AnnotationConfigApplicationContext와 @ImportResource 주석을 사용하여 필요에 따라 XML을 가져오는 "Java 중심" 방식으로 인스턴스화하는 두 가지 선택이 있습니다.
XML 중심의 @Configuration 클래스 사용
XML에서 Spring 컨테이너를 부트스트랩하고@Configuration 클래스를 임시 방식으로 포함하는 것이 바람직할 수 있습니다. 예를 들어, Spring XML을 사용하는 대규모 기존 코드베이스에서는 필요에 따라 @Configuration 클래스를 생성하고 기존 XML 파일에서 이를 포함하는 것이 더 쉽습니다. 이 섹션의 뒷부분에서는 이러한 종류의 "XML 중심" 상황에서 @Configuration 클래스를 사용하는 옵션에 대해 설명합니다.
구성 클래스는 궁극적으로 컨테이너의 빈 정의라는 것을 기억하세요. 이 시리즈 예제에서는 AppConfig라는 @Configuration 클래스를 생성하고 이를 system-test-config.xml 내에 <bean/> 정의로 포함합니다.<context:annotation-config/>가 켜져 있기 때문에 컨테이너는@Configuration 어노테이션을 인식하고 AppConfig에선언된 @Bean 메서드를 올바르게 처리합니다.
다음 예제는 Java의 일반적인 구성 클래스를 보여줍니다:
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository(dataSource);
}
@Bean
public TransferService transferService() {
return new TransferService(accountRepository());
}
}
다음 예제는 샘플 system-test-config.xml 파일의 일부를 보여줍니다:
<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>
다음 예제는 가능한 jdbc.properties 파일을 보여줍니다:
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);
// ...
}
System-test-config.xml 파일에서 AppConfig <bean/>은 id요소를 선언하지 않습니다. 그렇게 해도 되지만, 다른 빈이 이를 참조하지 않고 컨테이너에서 이름으로 명시적으로 가져올 가능성이 낮기 때문에 불필요합니다. 마찬가지로 DataSource 빈은 유형별로만 자동 연결되므로 명시적인 빈 ID가반드시 필요한 것은 아닙니다 |
구성은 @Component로 메타 주석을 달기 때문에 @Configuration 주석이 달린 클래스는 자동으로 구성 요소 검색의 후보가 됩니다. 이전 예제에서 설명한 것과 동일한 시나리오를 사용하여 구성 요소 스캔을 활용하도록 system-test-config.xml을 재정의할 수 있습니다. 이 경우 <context:annotation-config/>가 동일한 기능을 사용하므로 <context :component-scan/>을 명시적으로 선언할 필요가 없습니다.
다음 예제는 수정된 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>
컨피규레이션 클래스 중심의 @ImportResource를 사용한 XML 사용
구성 클래스가 컨테이너 구성을 위한 기본 메커니즘인 애플리케이션에서는 여전히 최소한의 XML을 사용해야 할 가능성이 높습니다. 이러한 시나리오에서는 @ImportResource를 사용하여 필요한 만큼의 XML만 정의할 수 있습니다. 이렇게 하면 컨테이너 구성에 대한 "Java 중심" 접근 방식을 달성하고 XML을 최소한으로 유지할 수 있습니다. 다음 예제(구성 클래스, 빈을 정의하는 XML 파일, 속성 파일, 메인 클래스 포함)는 @ImportResource 어노테이션을 사용하여 필요에 따라 XML을 사용하는 "Java 중심" 구성을 달성하는 방법을 보여줍니다:
@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);
// ...
}
Environment Abstraction
프로필은 지정된 프로필이 활성화된 경우에만 컨테이너에 등록되는 명명된 논리적 빈 정의 그룹입니다. 빈은 XML로 정의하든 어노테이션으로 정의하든 프로필에 할당될 수 있습니다. 프로필과 관련된 환경 개체의 역할은 현재 활성화된 프로필(있는 경우)과 기본적으로 활성화되어야 하는 프로필(있는 경우)을 결정하는 것입니다.
속성은 거의 모든 애플리케이션에서 중요한 역할을 하며 속성 파일, JVM 시스템 속성, 시스템 환경 변수, JNDI, 서블릿 컨텍스트 매개변수, 애드혹 속성 개체, 맵 개체 등 다양한 소스에서 비롯될 수 있습니다. 프로퍼티와 관련된 환경 개체의 역할은 사용자에게 프로퍼티 소스를 구성하고 이로부터 프로퍼티를 확인할 수 있는 편리한 서비스 인터페이스를 제공하는 것입니다.
Bean Definition Profiles
빈 정의 프로필은 코어 컨테이너에서 서로 다른 환경에서 서로 다른 빈을 등록할 수 있는 메커니즘을 제공합니다. "환경"이라는 단어는 사용자마다 다른 의미를 가질 수 있으며, 이 기능은 다음과 같은 많은 사용 사례에 도움이 될 수 있습니다:
- 개발 시에는 인메모리 데이터 소스를 대상으로 작업하고, QA 또는 프로덕션 시에는 JNDI에서 동일한 데이터 소스를 조회하는 경우.
- 애플리케이션을 성능 환경에 배포할 때만 모니터링 인프라를 등록하는 경우.
- 고객 A와 고객 B 배포를 위해 사용자 정의된 빈 구현을 등록하는 경우.
데이터 소스가 필요한 실제 애플리케이션의 첫 번째 사용 사례를 생각해 보세요. 테스트 환경에서는 구성이 다음과 비슷할 수 있습니다:
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("my-schema.sql")
.addScript("my-test-data.sql")
.build();
}
이제 애플리케이션의 데이터 소스가 프로덕션 애플리케이션 서버의 JNDI 디렉터리에 등록되어 있다고 가정하고 이 애플리케이션을 QA 또는 프로덕션 환경에 어떻게 배포할 수 있는지 생각해 보세요. 이제 데이터소스 빈은 다음 목록과 같습니다:
@Bean(destroyMethod = "")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
문제는 현재 환경에 따라 이 두 가지 변형을 사용하는 방법을 전환하는 것입니다. 시간이 지남에 따라 Spring 사용자들은 이를 위해 여러 가지 방법을 고안해냈는데, 일반적으로 환경 변수 값에 따라 올바른 구성 파일 경로로 확인되는 ${placeholder} 토큰이 포함된 시스템 환경 변수와 XML <import/> 문을 조합하는 방법을 사용했습니다. 빈 정의 프로파일은 이 문제에 대한 해결책을 제공하는 핵심 컨테이너 기능입니다.
앞의 환경별 빈 정의 예시에 나타난 사용 사례를 일반화하면 특정 컨텍스트에서는 특정 빈 정의를 등록해야 하지만 다른 컨텍스트에서는 등록하지 않아도 되는 경우가 발생합니다. 상황 A에서는 특정 프로필의 빈 정의를 등록하고 상황 B에서는 다른 프로필을 등록하고 싶다고 말할 수 있습니다. 이러한 요구를 반영하도록 구성을 업데이트하는 것부터 시작합니다.
프로필 사용
프로필주석을 사용하면 하나 이상의 지정된 프로필이 활성화되어 있을 때 컴포넌트를 등록할 수 있음을 나타낼 수 있습니다. 앞의 예시를 사용하여 다음과 같이 데이터 소스 구성을 다시 작성할 수 있습니다:
@Configuration
@Profile("development")
public class StandaloneDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
@Configuration
@Profile("production")
public class JndiDataConfig {
@Bean(destroyMethod = "")
public DataSource dataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
bean(destroyMethod = "") 은 기본 파괴 메서드 추론을 비활성화합니다. |
앞서 언급했듯이, @Bean 메서드를 사용하면 일반적으로 Spring의 JndiTemplate/JndiLocatorDelegate 헬퍼를 사용하거나 앞서 표시된 JndiObjectFactoryBean변형을 사용하지 않고 반환 유형을 FactoryBean 유형으로 선언하는 직접 JNDI InitialContext 사용을 통해 프로그래밍 방식의 JNDI 조회를 선택하게 됩니다 |
프로필 문자열에는 간단한 프로필 이름(예: production) 또는 프로필 표현식이 포함될 수 있습니다. 프로필 표현식을 사용하면 보다 복잡한 프로필 로직을 표현할 수 있습니다(예: production & us-east). 프로필 표현식에서는 다음 연산자가 지원됩니다:
- !: 프로필의 논리적 NOT
- &: 프로필의 논리적 AND
- |: 프로필의 논리적 OR
괄호를 사용하지 않고는 & 및 | 연산자를 혼합할 수 없습니다. 예를 들어,production & us-east | eu-central은 올바른 표현이 아닙니다.프로덕션 & (us-east | eu-central)로 표현해야 합니다 |
사용자 지정 구성된 주석을 만들 목적으로 @Profile을 메타 주석으로 사용할 수 있습니다. 다음 예에서는@Profile("production")의 드롭인 대체로 사용할 수 있는 사용자 지정@Production 어노테이션을 정의합니다:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Profile("production")
public @interface Production {
}
구성 클래스가 @Profile로 표시된 경우 지정된 프로파일 중 하나 이상이 활성화되어 있지 않으면 해당 클래스와 연결된 모든 @Bean 메서드 및@Import 어노테이션이 무시됩니다. 컴포넌트 또는 @Configuration 클래스가 @Profile({"p1", "p2"})로 표시된 경우 해당 클래스는 프로파일 'p1' 또는 'p2'가 활성화되지 않는 한 등록되거나 처리되지 않습니다. 지정된 프로필 앞에 NOT 연산자(!)가 붙으면 해당 프로필이 활성화되지 않은 경우에만 주석이 달린 요소가 등록됩니다. 예를 들어 @Profile({"p1", "!p2"})이 주어지면 프로필 'p1'이 활성화되어 있거나 프로필 'p2'가 활성화되어 있지 않은 경우에만 등록이 이루어집니다 |
다음 예제와 같이 메서드 수준에서@Profile을 선언하여 구성 클래스의 특정 빈을 하나만 포함할 수도 있습니다(예: 특정 빈의 대체 변형의 경우):
@Configuration
public class AppConfig {
@Bean("dataSource")
@Profile("development")
public DataSource standaloneDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
@Bean("dataSource")
@Profile("production")
public DataSource jndiDataSource() throws Exception {
Context ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}
}
스탠드얼론DataSource 메서드는 개발 프로필에서만 사용할 수 있습니다. | |
JndiDataSource 메서드는 프로덕션 프로필에서만 사용할 수 있습니다. |
프로필이 @Bean 메서드에 있는 경우 특별한 시나리오가 적용될 수 있습니다: 동일한 Java 메서드 이름의 @Bean 메서드가 오버로드된 경우(생성자 오버로드와 유사), 모든 오버로드된 메서드에 @Profile 조건이 일관되게 선언되어야 합니다. 조건이 일관되지 않으면 오버로드된 메서드 중 첫 번째 선언의 조건만 중요합니다. 따라서 특정 인수 서명이 있는 오버로드된 메서드를 다른 메서드보다 선택하는 데 @Profile을 사용할 수 없습니다. 동일한 빈에 대한 모든 팩토리 메서드 간의 해석은 생성 시점에 Spring의 생성자 해석 알고리즘을 따릅니다.
프로필 조건이 다른 대체 빈을 정의하려면 앞의 예에서와 같이 @Bean 이름 속성을 사용하여 동일한 빈 이름을 가리키는 고유한 Java 메서드 이름을 사용합니다. 인수 서명이 모두 동일한 경우(예: 모든 변형에 no-arg 팩토리 메서드가 있는 경우), 애초에 유효한 Java 클래스에서 이러한 배열을 표현하는 유일한 방법입니다(특정 이름과 인수 서명의 메서드는 하나만 있을 수 있으므로).
|
XML 빈 정의 프로파일
XML에 대응하는 것은 <beans> 요소의 프로파일 어트리뷰트입니다. 앞의 샘플 구성은 다음과 같이 두 개의 XML 파일로 다시 작성할 수 있습니다:
<beans profile="development"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="...">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
다음 예제에서 볼 수 있듯이 동일한 파일 내에서 <beans/> 요소를 분할하고 중첩하는 것을 피할 수도 있습니다:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!-- other bean definitions -->
<beans profile="development">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>
Spring-bean.xsd는 이러한 요소를 파일에서 마지막 요소로만 허용하도록 제한했습니다. 이렇게 하면 XML 파일이 복잡해지지 않고 유연성을 제공하는 데 도움이 됩니다.
XML 대응 버전은 앞서 설명한 프로파일 표현식을 지원하지 않습니다. 그러나 ! 연산자를 사용하여 프로필을 무효화할 수 있습니다. 다음 예에서 보는 것처럼 프로필을 중첩하여 논리적인 "and"를 적용할 수도 있습니다:
앞의 예에서는 프로덕션 프로필과us-east 프로필이 모두 활성화된 경우 데이터 소스 빈이 노출됩니다.
|
프로필 활성화
이제 구성을 업데이트했으므로 어떤 프로필이 활성화되어 있는지 Spring에 알려주어야 합니다. 샘플 애플리케이션을 지금 바로 시작하면 컨테이너가 데이터소스라는 Spring 빈을 찾을 수 없기 때문에 NoSuchBeanDefinitionException이 발생하는 것을 볼 수 있습니다.
프로파일 활성화는 여러 가지 방법으로 수행할 수 있지만 가장 간단한 방법은ApplicationContext를 통해 사용할 수 있는 환경 API에 대해 프로그래밍 방식으로 수행하는 것입니다. 다음 예제는 그 방법을 보여줍니다:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();
또한 시스템 환경 변수, JVM 시스템 속성, web.xml의 서블릿 컨텍스트 매개변수 또는 JNDI의 항목으로 지정할 수 있는spring.profiles.active 속성을 통해 선언적으로 프로파일을 활성화할 수도 있습니다( PropertySource 추상화 참조). 통합 테스트에서 활성 프로필은 스프링 테스트모듈의 @ActiveProfiles 어노테이션을 사용하여 선언할 수 있습니다( 환경 프로필을 사용한 컨텍스트 구성참조).
프로필은 '양자택일'이 아닌 '선택 사항'이라는 점에 유의하세요. 한 번에 여러 프로필을 활성화할 수 있습니다. 프로그래밍 방식으로 String... 변수를 허용하는setActiveProfiles() 메서드에 여러 프로필 이름을 제공할 수 있습니다. 다음 예제는 여러 프로필을 활성화합니다:
ctx.getEnvironment().setActiveProfiles("profile1", "profile2");
선언적으로 spring.profiles.active는 다음 예제와 같이 쉼표로 구분된 프로필 이름 목록을 허용할 수 있습니다:
-Dspring.profiles.active="profile1,profile2"
기본 프로필
기본 프로필은 활성화된 프로필이 없는 경우 활성화되는 프로필을 나타냅니다. 다음 예를 살펴보세요:
@Configuration
@Profile("default")
public class DefaultDataConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.build();
}
}
활성화된 프로필이 없는 경우 데이터소스가 만들어집니다. 이것은 하나 이상의 빈에 대한 기본 정의를 제공하는 방법으로 볼 수 있습니다. 프로필이 활성화된 경우 기본 프로필은 적용되지 않습니다.
기본 프로필의 이름은 기본값입니다. 기본 프로필의 이름은 환경의 setDefaultProfiles() 를 사용하거나 선언적으로 spring.profiles.default 속성을 사용하여 변경할 수 있습니다.
PropertySource Abstraction
Spring의 환경 추상화는 구성 가능한 속성 소스 계층 구조에 대한 검색 작업을 제공합니다. 다음 목록을 고려해 보세요:
ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);
앞의 스니펫에서는 Spring에 내 프로퍼티 속성이 현재 환경에 대해 정의되어 있는지 여부를 묻는 높은 수준의 방법을 볼 수 있습니다. 이 질문에 답하기 위해 Environment 객체는 PropertySource객체 집합에 대한 검색을 수행합니다. PropertySource는 키-값 쌍의 모든 소스에 대한 간단한 추상화이며, Spring의 StandardEnvironment는JVM 시스템 속성 집합(System.getProperties())과 시스템 환경 변수 집합(System.getenv())을 나타내는 두 개의 PropertySource 객체로 구성됩니다.
이러한 기본 속성 소스는 독립형 애플리케이션에서 사용할 수 있도록 StandardEnvironment에 제공됩니다. 표준 서블릿 환경은서블릿 구성, 서블릿 컨텍스트 매개변수, JNDI를 사용할 수 있는 경우 JndiPropertySource를포함한 추가 기본 속성 소스로 채워집니다 |
구체적으로, 표준 환경을 사용하는 경우 런타임에 내 속성 시스템 속성 또는 내 속성 환경 변수가 있는 경우 env.containsProperty("my-property")를 호출하면 true가 반환됩니다.
수행되는 검색은 계층적 검색입니다. 기본적으로 시스템 프로퍼티가 환경 변수보다 우선합니다. 따라서 env.getProperty("my-property")를 호출하는 동안 두 곳에 모두 my-property 속성이 설정되어 있으면 시스템 속성 값이 "승리"하고 반환됩니다. 속성 값은 병합되지 않고 앞의 항목에 의해 완전히 재정의된다는 점에 유의하세요.
일반적인 표준 서블릿 환경의 경우, 전체 계층 구조는 다음과 같으며 우선순위가 가장 높은 항목이 맨 위에 있습니다:
|
가장 중요한 점은 전체 메커니즘을 구성할 수 있다는 것입니다. 이 검색에 통합하고 싶은 사용자 정의 속성 소스가 있을 수도 있습니다. 그렇게 하려면 자신만의 프로퍼티 소스를 구현하고 인스턴스화하여 현재 환경에 대한 프로퍼티 소스 집합에 추가하면 됩니다. 다음 예는 그 방법을 보여줍니다:
ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());
앞의 코드에서는 검색에서 가장 높은 우선순위를 가진 MyPropertySource가 추가되었습니다. 여기에 내 프로 퍼티 속성이 포함되어 있으면 다른 프로퍼티 소스의 모든 내 프로퍼티 속성에 우선하여 해당 속성이 감지되어 반환됩니다.MutablePropertySourcesAPI는 속성 소스 집합을 정밀하게 조작할 수 있는 여러 메서드를 제공합니다.
Using @PropertySource
프로퍼티소스어노테이션은 Spring의 환경에 프로퍼티소스를추가하기 위한 편리하고 선언적인 메커니즘을 제공합니다.
App.properties라는 파일에 testbean.name=myTestBean 키-값 쌍이 포함되어 있다고 가정할 때, 다음 @Configuration 클래스는 @PropertySource를 사용하여 testBean.getName() 을 호출하면 myTestBean을 반환합니다:
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
다음 예제에서 볼 수 있듯이 @PropertySource 리소스 위치에 있는 모든 ${...} 자리 표시자는 환경에 대해 이미 등록된 속성 소스 집합에 대해 확인됩니다:
@Configuration
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
My.placeholder가 이미 등록된 속성 소스 중 하나(예: 시스템 속성 또는 환경 변수)에 있다고 가정하면 플레이스홀더는 해당 값으로 확인됩니다. 그렇지 않은 경우 기본값/경로가 기본값으로 사용됩니다. 기본값을 지정하지 않고 프로퍼티를 확인할 수 없는 경우IllegalArgumentException이 발생합니다.
프로퍼티소스는 반복 가능한 어노테이션으로 사용할 수 있습니다. 속성 재정의가 있는 사용자 정의 구성 어노테이션을 만들기 위한 메타 어노테이션으로 @PropertySource를사용할 수도 있습니다 |
Placeholder Resolution in Statements
이전에는 요소의 자리 표시자 값은 JVM 시스템 속성이나 환경 변수에 대해서만 확인할 수 있었습니다. 이제 더 이상 그렇지 않습니다. 환경 추상화가 컨테이너 전체에 통합되어 있기 때문에 이를 통해 플레이스홀더의 해석을 쉽게 라우팅할 수 있습니다. 즉, 원하는 방식으로 확인 프로세스를 구성할 수 있습니다. 시스템 속성 및 환경 변수를 통한 검색 우선순위를 변경하거나 완전히 제거할 수 있습니다. 필요에 따라 자체 속성 소스를 추가할 수도 있습니다.
구체적으로 다음 문장은 고객속성이 정의된 위치에 관계없이 환경 내에서 사용할 수 있는 한 작동합니다:
<beans>
<import resource="com/bank/service/${customer}-config.xml"/>
</beans>
LoadTimeWeaver 등록하기
LoadTimeWeaver는 Spring에서 클래스가 Java 가상 머신(JVM)에 로드될 때 동적으로 변환하는 데 사용됩니다.
로드 시간 위빙을 활성화하려면 다음 예시와 같이@Configuration 클래스 중 하나에 @EnableLoadTimeWeaving을 추가하면 됩니다:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig {
}
또는 XML 구성의 경우 context:load-time-weaver 요소를 사용할 수 있습니다:
<beans>
<context:load-time-weaver/>
</beans>
애플리케이션 컨텍스트에 대해 구성되면 해당 애플리케이션 컨텍스트내의 모든 빈이 LoadTimeWeaverAware를 구현하여 로드 시간 위버 인스턴스에 대한 참조를 받을 수 있습니다. 이는 특히 JPA 클래스 변환을 위해 로드 타임 위버가 필요할 수 있는Spring의 JPA 지원과 함께 사용할 때 유용합니다. 자세한 내용은LocalContainerEntityManagerFactoryBean자바독을 참조하세요. AspectJ 로드 타임 위빙에 대한 자세한 내용은 Spring 프레임워크에서 AspectJ를 사용한 로드 타임 위빙을 참조하세요.
Additional Capabilities of the ApplicationContext
이 장의 소개에서 설명한 것처럼, org.springframework.beans.factory패키지는 프로그래밍 방식을 포함하여 빈을 관리하고 조작하기 위한 기본 기능을 제공합니다. Org.springframework.context 패키지는 애플리케이션 프레임워크 지향적인 스타일로 추가 기능을 제공하기 위해 다른 인터페이스를 확장할 뿐만 아니라 BeanFactory 인터페이스를 확장하는ApplicationContext인터페이스를 추가합니다. 많은 사람들이 ApplicationContext를 프로그래밍 방식으로 생성하지 않고 완전히 선언적인 방식으로 사용하는 대신 ContextLoader와 같은 지원 클래스에 의존하여 Jakarta EE 웹 애플리케이션의 일반적인 시작 프로세스의 일부로ApplicationContext를 자동으로 인스턴스화합니다.
보다 프레임워크 지향적인 스타일로 BeanFactory 기능을 향상시키기 위해 컨텍스트 패키지는 다음과 같은 기능도 제공합니다:
- MessageSource 인터페이스를 통해 i18n 스타일의 메시지에 대한 액세스.
- ResourceLoader 인터페이스를 통해 URL 및 파일과 같은 리소스에 대한 액세스.
- 이벤트 게시, 즉 ApplicationListener 인터페이스를 구현하는 빈에 대한 이벤트 게시( ApplicationEventPublisher 인터페이스 사용).
- 여러 (계층적) 컨텍스트의 로드, 각 컨텍스트가 애플리케이션의 웹 레이어와 같은 특정 레이어에 집중될 수 있도록HierarchicalBeanFactory 인터페이스를 통해.
Internationalization using MessageSource
ApplicationContext 인터페이스는 MessageSource라는 인터페이스를 확장하므로 국제화("i18n") 기능을 제공합니다. Spring은 또한 메시지를 계층적으로 확인할 수 있는HierarchicalMessageSource 인터페이스를 제공합니다. 이러한 인터페이스는 함께 Spring이 메시지 확인에 영향을 미치는 기반을 제공합니다. 이러한 인터페이스에 정의된 메서드는 다음과 같습니다:
- String getMessage(String code, Object[] args, String default, Locale loc): MessageSource에서 메시지를 검색하는 데 사용되는 기본 메서드입니다. 지정된 로케일에 대한 메시지를 찾을 수 없으면 기본 메시지가 사용됩니다. 전달된 모든 인수는 표준 라이브러리에서 제공하는 MessageFormat 함수를 사용하여 대체 값이 됩니다.
- String getMessage(String code, Object[] args, Locale loc): 본질적으로 이전 메서드와 동일하지만 한 가지 차이점이 있습니다: 기본 메시지를 지정할 수 없습니다. 메시지를 찾을 수 없으면 NoSuchMessageException이 발생합니다.
- String getMessage(MessageSourceResolvable 해결 가능, Locale 로캘 로캘): 앞의 메서드에서 사용된 모든 프로퍼티는 이 메서드와 함께 사용할 수 있는MessageSourceResolvable 클래스로 래핑됩니다.
ApplicationContext가 로드되면 컨텍스트에 정의된 MessageSource빈을 자동으로 검색합니다. 빈의 이름은 메시지소스여야 합니다. 이러한 빈이 발견되면 앞의 메서드에 대한 모든 호출이 메시지 소스로 위임됩니다. 메시지 소스를 찾을 수 없으면 ApplicationContext는 같은 이름의 빈을 포함하는 부모를 찾으려고 시도합니다. 찾으면 해당 빈을 메시지 소스로 사용합니다.ApplicationContext가 메시지 소스를 찾을 수 없는 경우, 위에 정의된 메서드에 대한 호출을 수락할 수 있도록 빈DelegatingMessageSource가 인스턴스화됩니다.
Spring은 세 가지 MessageSource 구현, ResourceBundleMessageSource, ReloadableResourceBundleMessageSource및 StaticMessageSource를 제공합니다. 이들 모두는 중첩된 메시징을 수행하기 위해 HierarchicalMessageSource를 구현합니다. StaticMessageSource는 거의 사용되지 않지만 소스에 메시지를 추가하는 프로그래밍 방식을 제공합니다. 다음 예제는 ResourceBundleMessageSource를 보여줍니다:
<beans>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>format</value>
<value>exceptions</value>
<value>windows</value>
</list>
</property>
</bean>
</beans>
이 예에서는 클래스 경로에 형식, 예외 및 창이라는세 가지 리소스 번들이 정의되어 있다고 가정합니다. 메시지 확인 요청은 ResourceBundle 객체를 통해 메시지를 확인하는 JDK 표준 방식으로 처리됩니다. 이 예제에서는 위의 리소스 번들 파일 중 두 개의 내용이 다음과 같다고 가정합니다:
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
다음 예제는 MessageSource 기능을 실행하는 프로그램을 보여줍니다. 모든 ApplicationContext 구현도 MessageSource구현이므로 MessageSource 인터페이스로 캐스팅할 수 있다는 점을 기억하세요.
public static void main(String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("message", null, "Default", Locale.ENGLISH);
System.out.println(message);
}
위 프로그램의 결과 출력은 다음과 같습니다:
악어 바위!
요약하자면, MessageSource는 클래스 경로의 루트에 존재하는 beans.xml이라는 파일에 정의되어 있습니다. 메시지소스 빈 정의는 베이스네임 속성을 통해 여러 리소스 번들을 참조합니다. 목록에서 기본 이름 속성으로 전달되는 세 개의 파일은 클래스 경로의 루트에 파일로 존재하며 각각 format.properties, exceptions.properties 및windows.properties라고 합니다.
다음 예는 메시지 조회에 전달된 인수를 보여줍니다. 이러한 인수는 문자열 객체로 변환되어 조회 메시지의 플레이스홀더에 삽입됩니다.
<beans>
<!-- this MessageSource is being used in a web application -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- lets inject the above MessageSource into this POJO -->
<bean id="example" class="com.something.Example">
<property name="messages" ref="messageSource"/>
</bean>
</beans>
public class Example {
private MessageSource messages;
public void setMessages(MessageSource messages) {
this.messages = messages;
}
public void execute() {
String message = this.messages.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.ENGLISH);
System.out.println(message);
}
}
Execute() 메서드 호출의 결과 출력은 다음과 같습니다:
UserDao 인수는 필수입니다.
국제화("i18n")와 관련하여 Spring의 다양한 MessageSource구현은 표준 JDKResourceBundle과 동일한 로케일 확인 및 폴백 규칙을 따릅니다. 간단히 말해서, 앞서 정의한 메시지 소스 예제를 계속 이어서 영국(en-GB) 로케일에 대해 메시지를 확인하려는 경우 각각 format_en_GB.properties, exceptions_en_GB.properties 및windows_en_GB.properties라는 파일을 생성합니다.
일반적으로 로캘 해상도는 애플리케이션의 주변 환경에 의해 관리됩니다. 다음 예에서는 (영국) 메시지가 확인되는 로캘을 수동으로 지정합니다:
# 예외_en_GB.properties argument.required=Ebagum lad에서 ''{0}'' 인수는 필수, 즉 필수입니다.
public static void main(final String[] args) {
MessageSource resources = new ClassPathXmlApplicationContext("beans.xml");
String message = resources.getMessage("argument.required",
new Object [] {"userDao"}, "Required", Locale.UK);
System.out.println(message);
}
위 프로그램을 실행하면 다음과 같은 결과가 출력됩니다:
에바굼 라드, 'userDao' 인수는 필수입니다.
MessageSourceAware 인터페이스를 사용하여 정의된 모든MessageSource에 대한 참조를 얻을 수도 있습니다. MessageSourceAware 인터페이스를 구현하는ApplicationContext에 정의된 모든 빈은 빈이 생성 및 구성될 때 애플리케이션 컨텍스트의 MessageSource와 함께 주입됩니다.
Spring의 MessageSource는 Java의 ResourceBundle을 기반으로 하기 때문에 기본 이름이 같은 번들을 병합하지 않고 처음 발견된 번들만 사용합니다. 기본 이름이 같은 후속 메시지 번들은 무시됩니다 |
ResourceBundleMessageSource의 대안으로 Spring은ReloadableResourceBundleMessageSource 클래스를 제공합니다. 이 변형은 동일한 번들 파일 형식을 지원하지만 표준 JDK 기반ResourceBundleMessageSource 구현보다 더 유연합니다. 특히, 클래스 경로뿐만 아니라 모든 Spring 리소스 위치에서 파일을 읽을 수 있으며 번들 속성 파일의 핫 리로딩을 지원하고 그 사이에 효율적으로 캐싱합니다. 자세한 내용은 ReloadableResourceBundleMessageSource자바독을 참조하세요 |
Standard and Custom Events
ApplicationContext의 이벤트 처리는 ApplicationEvent클래스와 ApplicationListener 인터페이스를 통해 제공됩니다.ApplicationListener 인터페이스를 구현하는 빈이 컨텍스트에 배포되면ApplicationEvent가 ApplicationContext에 게시될 때마다 해당 빈이 알림을 받습니다. 기본적으로 이것은 표준 Observer 디자인 패턴입니다.
Spring 4.2부터는 이벤트 인프라가 크게 개선되어 어노테이션 기반 모델뿐만 아니라 임의의 이벤트(즉, ApplicationEvent에서 반드시 확장되지 않는 객체)를 게시할 수 있는 기능도 제공합니다. 이러한 객체가 게시되면 이벤트에 래핑됩니다 |
다음 표는 Spring이 제공하는 표준 이벤트에 대해 설명합니다:
ContextRefreshedEvent | 애플리케이션 컨텍스트가 초기화되거나 새로 고쳐질 때(예: ConfigurableApplicationContext 인터페이스의 refresh() 메서드를 사용하여) 게시됩니다. 여기서 "초기화"는 모든 빈이 로드되고, 포스트프로세서 빈이 감지 및 활성화되고, 싱글톤이 사전 인스턴스화되고, 애플리케이션 컨텍스트 객체를 사용할 준비가 되었음을 의미합니다. 컨텍스트가 닫히지 않은 한, 선택한 ApplicationContext가 실제로 이러한 "핫" 새로 고침을 지원하는 경우 새로 고침을 여러 번 트리거할 수 있습니다. 예를 들어, XmlWebApplicationContext는 핫 새로 고침을 지원하지만 GenericApplicationContext는 지원하지 않습니다. |
ContextStartedEvent | 구성 가능한 애플리케이션 컨텍스트 인터페이스의 start() 메서드를 사용하여 애플리케이션 컨텍스트가 시작될 때 게시됩니다. 여기서 "시작"은 모든 라이프사이클빈이 명시적인 시작 신호를 수신한다는 의미입니다. 일반적으로 이 신호는 명시적 중지 후 빈을 다시 시작하는 데 사용되지만, 자동 시작을 위해 구성되지 않은 구성 요소(예: 초기화 시 아직 시작되지 않은 구성 요소)를 시작하는 데도 사용될 수 있습니다. |
ContextStoppedEvent | 구성 가능한 애플리케이션 컨 텍스트 인터페이스의 stop() 메서드를 사용하여 애플리케이션 컨텍스트가 중지될 때 게시됩니다. 여기서 "중지"는 모든 라이프사이클빈이 명시적 중지 신호를 수신한다는 의미입니다. 중지된 컨텍스트는 start() 호출을 통해 다시 시작할 수 있습니다. |
ContextClosedEvent | 구성 가능한 애플리케이션 컨 텍스트 인터페이스의 close() 메서드를 사용하거나 JVM 종료 훅을 통해 애플리케이션 컨텍스트가 종료될 때 게시됩니다. 여기서 "닫힘"은 모든 싱글톤 빈이 소멸됨을 의미합니다. 컨텍스트가 닫히면 수명이 다한 것이므로 새로 고치거나 다시 시작할 수 없습니다. |
요청 처리 이벤트 | 모든 빈에 HTTP 요청이 처리되었음을 알리는 웹 전용 이벤트입니다. 이 이벤트는 요청이 완료된 후에 게시됩니다. 이 이벤트는 Spring의 DispatcherServlet을 사용하는 웹 애플리케이션에만 적용됩니다. |
서블릿 요청 처리 이벤트 | 서블릿 관련 컨텍스트 정보를 추가하는 RequestHandledEvent의 서브클래스입니다. |
사용자 정의 이벤트를 직접 생성하고 게시할 수도 있습니다. 다음 예제는 Spring의 ApplicationEvent 기본 클래스를 확장하는 간단한 클래스를 보여줍니다:
public class BlockedListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlockedListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
사용자 정의 애플리케이션 이벤트를 게시하려면애플리케이션 이벤트 게시자에서 publishEvent() 메서드를 호출합니다. 일반적으로 이 작업은ApplicationEventPublisherAware를 구현하는 클래스를 생성하고 이를 Spring 빈으로 등록하여 수행합니다. 다음 예제는 이러한 클래스를 보여줍니다:
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blockedList;
private ApplicationEventPublisher publisher;
public void setBlockedList(List<String> blockedList) {
this.blockedList = blockedList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blockedList.contains(address)) {
publisher.publishEvent(new BlockedListEvent(this, address, content));
return;
}
// send email...
}
}
구성 시 Spring 컨테이너는 EmailService가ApplicationEventPublisherAware를 구현한다는 것을 감지하고 자동으로setApplicationEventPublisher()를 호출합니다. 실제로 전달되는 매개 변수는 Spring 컨테이너 자체입니다. 애플리케이션 컨텍스트와 상호작용하는 것은애플리케이션EventPublisher 인터페이스를 통해 이루어집니다.
사용자 정의 ApplicationEvent를 수신하려면ApplicationListener를 구현하는 클래스를 생성하고 Spring 빈으로 등록할 수 있습니다. 다음 예제는 이러한 클래스를 보여줍니다:
public class BlockedListNotifier implements ApplicationListener<BlockedListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
ApplicationListener는 일반적으로 사용자 지정 이벤트의 유형( 앞의 예에서는BlockedListEvent) 으로 매개변수화되어 있습니다. 즉, onApplicationEvent() 메서드는 유형 안전을 유지할 수 있으므로 다운캐스팅이 필요하지 않습니다. 원하는 만큼 이벤트 리스너를 등록할 수 있지만 기본적으로 이벤트 리스너는 이벤트를 동기적으로 수신한다는 점에 유의하세요. 즉, 모든 리스너가 이벤트 처리를 완료할 때까지 publishEvent() 메서드가 차단됩니다. 이 동기식 단일 스레드 접근 방식의 한 가지 장점은 리스너가 이벤트를 수신할 때 트랜잭션 컨텍스트가 사용 가능한 경우 게시자의 트랜잭션 컨텍스트 내에서 작동한다는 것입니다. 이벤트 게시를 위한 다른 전략(예: 기본적으로 비동기 이벤트 처리)이 필요한 경우, 사용자 정의 " applicationEventMulticaster " 빈 정의에 적용할 수 있는 구성 옵션은 Spring의 ApplicationEventMulticaster 인터페이스 및 SimpleApplicationEventMulticaster 구현에 대한 자바독을 참조하세요. 이러한 경우 이벤트 처리를 위해 ThreadLocals 및 로깅 컨텍스트는 전파되지 않습니다. Observability 문제에 대한 자세한 내용은 @EventListener Observability 섹션을참조하세요.
다음 예제는 위의 각 클래스를 등록하고 구성하는 데 사용된 빈 정의를 보여줍니다:
<bean id="emailService" class="example.EmailService">
<property name="blockedList">
<list>
<value>known.spammer@example.org</value>
<value>known.hacker@example.org</value>
<value>john.doe@example.org</value>
</list>
</property>
</bean>
<bean id="blockedListNotifier" class="example.BlockedListNotifier">
<property name="notificationAddress" value="blockedlist@example.org"/>
</bean>
<!-- optional: a custom ApplicationEventMulticaster definition -->
<bean id="applicationEventMulticaster" class="org.springframework.context.event.SimpleApplicationEventMulticaster">
<property name="taskExecutor" ref="..."/>
<property name="errorHandler" ref="..."/>
</bean>
이 모든 것을 종합하면, emailService 빈의 sendEmail() 메서드가 호출될 때 차단해야 하는 이메일 메시지가 있는 경우차단된 목록 이벤트 유형의 사용자 지정 이벤트가 게시됩니다. 차단된 목록 알림 빈은애플리케이션 리스너로 등록되고 차단된 목록 이벤트를 수신하며, 이 시점에서 적절한 당사자에게 알릴 수 있습니다.
Spring의 이벤트 메커니즘은 동일한 애플리케이션 컨텍스트 내에서 Spring 빈 간의 간단한 통신을 위해 설계되었습니다. 그러나 보다 정교한 엔터프라이즈 통합이 필요한 경우 별도로 유지 관리되는Spring 통합 프로젝트에서 잘 알려진 Spring 프로그래밍 모델을 기반으로 하는 경량,패턴 지향, 이벤트 중심 아키텍처를 구축할 수 있도록 완벽한 지원을 제공합니다 |
어노테이션 기반 이벤트 리스너
이벤트 리스너 어노테이션을 사용하여 관리되는 빈의 모든 메서드에 이벤트 리스너를 등록할 수 있습니다. BlockedListNotifier는 다음과 같이 재작성할 수 있습니다:
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
메서드 서명은 다시 한 번 수신 대기하는 이벤트 유형을 선언하지만 이번에는 특정 리스너 인터페이스를 구현하지 않고 유연한 이름으로 선언합니다. 실제 이벤트 유형이 구현 계층 구조에서 제네릭 매개변수를 해결하는 한 이벤트 유형은 제네릭을 통해 좁힐 수도 있습니다.
메서드가 여러 이벤트를 수신해야 하거나 매개 변수가 전혀 없이 정의하려는 경우 이벤트 유형을 어노테이션 자체에 지정할 수도 있습니다. 다음 예제는 그 방법을 보여줍니다:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
특정 이벤트에 대한 메서드를 실제로 호출하려면 일치해야 하는 SpEL 표현식을 정의하는 어노테이션의 조건 속성을 사용하여 추가적인 런타임 필터링을 추가할 수도 있습니다.
다음 예는 이벤트의콘텐츠 속성이 my-event와 같은 경우에만 호출되도록 알림을 재작성하는 방법을 보여줍니다:
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
각 SpEL 표현식은 전용 컨텍스트에 대해 평가됩니다. 다음 표에는 조건부 이벤트 처리에 사용할 수 있도록 컨텍스트에서 사용할 수 있는 항목이 나열되어 있습니다:
이벤트 | 루트 객체 | 실제 ApplicationEvent. | #root.event 또는 이벤트 |
인자 배열 | 루트 객체 | 메서드를 호출하는 데 사용되는 인자(객체 배열)입니다. | 첫 번째 인자에 액세스하려면#root.args 또는 args; args[0] 등을 사용합니다. |
인수 이름 | 평가 컨텍스트 | 메서드 인수의 이름입니다. 어떤 이유로 이름을 사용할 수 없는 경우(예: 컴파일된 바이트 코드에 디버그 정보가 없기 때문) #a<#arg> 구문을 사용하여 개별 인수를 사용할 수도 있으며, 여기서 <#arg>는 인수 인덱스(0부터 시작)를 나타냅니다. | #blEvent 또는 #a0 ( #p0 또는 #p<#arg> 매개변수 표기법을 별칭으로 사용할 수도 있습니다) |
메서드 서명이 실제로 게시된 임의의 객체를 참조하는 경우에도 #root.event를 사용하면 기본 이벤트에 액세스할 수 있습니다.
다른 이벤트를 처리한 결과로 이벤트를 게시해야 하는 경우 다음 예시와 같이 메서드 서명을 변경하여 게시해야 하는 이벤트를 반환할 수 있습니다:
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
이 기능은비동기 리스너에는 지원되지 않습니다 |
HandleBlockedListEvent() 메서드는 처리하는 모든블록된 리스트 이벤트 에 대해 새로운 ListUpdateEvent를 게시합니다. 여러 이벤트를 게시해야 하는 경우 컬렉션이나 이벤트 배열을 대신 반환할 수 있습니다.
비동기 리스너
특정 리스너가 이벤트를 비동기적으로 처리하도록 하려면일반 @Async 지원을 재사용할 수 있습니다. 다음 예제는 그 방법을 보여줍니다:
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}
비동기 이벤트를 사용할 때는 다음 제한 사항에 유의하세요:
- 비동기 이벤트 리스너가 예외를 던지면 호출자에게 전파되지 않습니다. 자세한 내용은 비동기 예외핸들러를참조하세요.
- 비동기 이벤트 리스너 메서드는 값을 반환하여 후속 이벤트를 게시할 수 없습니다. 처리 결과로 다른 이벤트를 게시해야 하는 경우ApplicationEventPublisher를주입하여 수동으로 이벤트를 게시하세요.
- 이벤트 처리를 위해 기본적으로 ThreadLocals 및 로깅 컨텍스트는 전파되지 않습니다. 가관측성 문제에 대한 자세한 내용은 @EventListener가관측성 섹션을참조하세요.
리스너 순서 지정
하나의 리스너가 다른 리스너보다 먼저 호출되어야 하는 경우, 다음 예시처럼 메서드 선언에 @Order어노테이션을 추가할 수 있습니다:
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
제네릭 이벤트
제네릭을 사용하여 이벤트의 구조를 추가로 정의할 수도 있습니다. 여기서 T는 실제 생성된 엔티티의 유형인EntityCreatedEvent<T>를 사용하는 것을 고려해 보세요. 예를 들어, 다음과 같은 리스너 정의를 생성하여사람에 대한 EntityCreatedEvent만 수신할 수 있습니다:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
유형 삭제 때문에 이것은 발생하는 이벤트가 이벤트 리스너가 필터링하는 일반 매개변수를 확인하는 경우에만 작동합니다(즉,PersonCreatedEvent 클래스가 EntityCreatedEvent<Person> { ... }을 확장하는 것과 같은 경우).
특정 상황에서는 모든 이벤트가 동일한 구조를 따르는 경우(앞의 예제에서 이벤트의 경우처럼) 이 작업이 상당히 지루해질 수 있습니다. 이러한 경우 ResolvableTypeProvider를 구현하여 런타임 환경이 제공하는 것 이상으로 프레임워크를 안내할 수 있습니다. 다음 이벤트는 그 방법을 보여줍니다:
public class EntityCreatedEvent<T> extends ApplicationEvent implements ResolvableTypeProvider {
public EntityCreatedEvent(T entity) {
super(entity);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
이는 ApplicationEvent뿐만 아니라 이벤트로 전송하는 임의의 객체에 대해서도 작동합니다 |
마지막으로, 기존의 ApplicationListener 구현과 마찬가지로 실제 멀티캐스팅은 런타임에 컨텍스트 전체에 걸친 ApplicationEventMulticaster를 통해 이루어집니다. 기본적으로 이것은 호출자 스레드에 동기식 이벤트 게시가 있는SimpleApplicationEventMulticaster입니다. 이는 모든 이벤트를 비동기적으로 처리하거나 리스너 예외를 처리하는 등 "applicationEventMulticaster" 빈 정의를 통해 대체/사용자 정의할 수 있습니다:
@Bean
ApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
multicaster.setTaskExecutor(...);
multicaster.setErrorHandler(...);
return multicaster;
}
Convenient Access to Low-level Resources
애플리케이션 컨텍스트를 최적으로 사용하고 이해하려면 리소스에 설명된 대로 Spring의 리소스 추상화에 익숙해져야 합니다.
애플리케이션 컨텍스트는 Resource 객체를 로드하는 데 사용할 수 있는 ResourceLoader입니다. 리소스는 본질적으로 JDK java.net.URL 클래스의 더 풍부한 기능 버전입니다. 실제로 리소스의 구현은 적절한 경우 java.net.URL의 인스턴스를 래핑합니다. Resource는 클래스 경로, 파일 시스템 위치, 표준 URL로 설명할 수 있는 모든 위치 및 기타 변형을 포함하여 거의 모든 위치에서 투명한 방식으로 저수준 리소스를 가져올 수 있습니다. 리소스 위치 문자열이 특별한 접두사가 없는 단순한 경로인 경우, 해당 리소스의 출처는 실제 애플리케이션 컨텍스트 유형에 따라 구체적이고 적절합니다.
애플리케이션 컨텍스트에 배포된 빈이 초기화 시 자동으로 호출되도록 특수 콜백 인터페이스인 ResourceLoaderAware를 구현하도록 구성하여 애플리케이션 컨텍스트 자체를 ResourceLoader로 전달할 수 있습니다. 또한 정적 리소스에 액세스하는 데 사용되는 Resource 유형의 속성을 노출할 수 있으며, 다른 속성처럼 주입될 수 있습니다. 이러한 리소스속성을 단순한 문자열 경로로 지정하고 빈이 배포될 때 해당 텍스트 문자열을 실제 리소스 객체로 자동 변환할 수 있습니다.
ApplicationContext 생성자에 제공된 위치 경로는 실제로 리소스 문자열이며, 간단한 형식이지만 특정 컨텍스트 구현에 따라 적절하게 처리됩니다. 예를 들어 ClassPathXmlApplicationContext는 단순한 위치 경로를 클래스 경로 위치로 취급합니다. 또한 특수 접두사와 함께 위치 경로(리소스 문자열)를 사용하여 실제 컨텍스트 유형에 관계없이 클래스 경로 또는 URL에서 정의를 강제로 로드할 수도 있습니다.
Application Startup Tracking
ApplicationContext는 Spring 애플리케이션의 라이프사이클을 관리하고 컴포넌트를 중심으로 풍부한 프로그래밍 모델을 제공합니다. 따라서 복잡한 애플리케이션은 구성 요소 그래프와 시작 단계도 똑같이 복잡할 수 있습니다.
특정 메트릭으로 애플리케이션 시작 단계를 추적하면 시작 단계에서 시간이 어디에 소비되고 있는지 이해하는 데 도움이 될 수 있지만, 컨텍스트 수명 주기 전체를 더 잘 이해하는 방법으로도 사용할 수 있습니다.
추상 애플리케이션 컨텍스트 (및 그 서브클래스)는 다양한 시작 단계에 대한 StartupStep 데이터를 수집하는ApplicationStartup을 통해 계측됩니다:
- 애플리케이션 컨텍스트 수명 주기(기본 패키지 스캔, 구성 클래스 관리)
- 빈 라이프사이클(인스턴스화, 스마트 초기화, 사후 처리)
- 애플리케이션 이벤트 처리
다음은 AnnotationConfigApplicationContext의 계측 예시입니다:
// create a startup step and start recording
StartupStep scanPackages = getApplicationStartup().start("spring.context.base-packages.scan");
// add tagging information to the current step
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// perform the actual phase we're instrumenting
this.scanner.scan(basePackages);
// end the current step
scanPackages.end();
애플리케이션 컨텍스트는 이미 여러 단계로 계측되어 있습니다. 일단 기록되면 이러한 시작 단계는 특정 도구를 사용하여 수집, 표시 및 분석할 수 있습니다. 기존 시작 단계의 전체 목록은전용 부록 섹션에서 확인할 수 있습니다.
기본 ApplicationStartup 구현은 오버헤드를 최소화하기 위해 무작업 변형입니다. 즉, 애플리케이션 시작 중에 기본적으로 메트릭이 수집되지 않습니다. Spring 프레임워크에는 Java Flight Recorder를 사용하여 시작 단계를 추적하기 위한 구현인FlightRecorderApplicationStartup이 함께 제공됩니다. 이 변형을 사용하려면 인스턴스가 생성되는 즉시 해당 인스턴스를 ApplicationContext로 구성해야 합니다.
개발자가 자체AbstractApplicationContext 서브클래스를 제공하거나 보다 정확한 데이터를 수집하려는 경우 ApplicationStartup 인프라를 사용할 수도 있습니다.
ApplicationStartup은 애플리케이션 시작 시와 코어 컨테이너에만 사용되어야 하며, 결코 Java 프로파일러나 Micrometer와 같은 메트릭 라이브러리를 대체할 수 없습니다 |
사용자 정의 StartupStep 수집을 시작하려면, 구성 요소는 애플리케이션 컨텍스트에서 직접 ApplicationStartup인스턴스를 가져오거나, 구성 요소가 ApplicationStartupAware를 구현하도록 하거나, 임의의 주입 지점에서 ApplicationStartup 유형을 요청할 수 있습니다.
개발자는 사용자 정의 시작 단계를 생성할 때 "spring.*" 네임스페이스를 사용해서는 안 됩니다. 이 네임스페이스는 Spring 내부 용도로 예약되어 있으며 변경될 수 있습니다 |
Convenient ApplicationContext Instantiation for Web Applications
예를 들어컨텍스트 로더를 사용하여 선언적으로 ApplicationContext 인스턴스를 생성할 수 있습니다. 물론 애플리케이션 컨텍스트 구현 중 하나를 사용하여 프로그래밍 방식으로 애플리케이션 컨텍스트 인스턴스를 생성할 수도 있습니다.
다음 예제에서 볼 수 있듯이 ContextLoaderListener를 사용하여 ApplicationContext를 등록할 수 있습니다:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
리스너는 contextConfigLocation 매개변수를 검사합니다. 매개변수가 존재하지 않으면 리스너는 /WEB-INF/applicationContext.xml을 기본값으로 사용합니다. 매개변수가 존재하면 수신기는 미리 정의된 구분 기호(쉼표, 세미콜론, 공백)를 사용하여 문자열을 구분하고 해당 값을 애플리케이션 컨텍스트가 검색되는 위치로 사용합니다. Ant 스타일 경로 패턴도 지원됩니다. 예를 들어 /WEB-INF/*Context.xml(이름이Context.xml로 끝나고 WEB-INF 디렉터리에 있는 모든 파일) 및 /WEB-INF/**/*Context.xml( WEB-INF의 하위 디렉터리에 있는 모든 파일)이 있습니다.
Deploying a Spring ApplicationContext as a Jakarta EE RAR File
Spring ApplicationContext를 RAR 파일로 배포하여 컨텍스트와 필요한 모든 빈 클래스 및 라이브러리 JAR을 Jakarta EE RAR 배포 유닛에 캡슐화할 수 있습니다. 이는 독립형 ApplicationContext (Jakarta EE 환경에서만 호스팅됨)를 부트스트랩하여 Jakarta EE 서버 시설에 액세스할 수 있도록 하는 것과 같습니다. RAR 배포는 헤드리스 WAR 파일을 배포하는 시나리오에 대한 보다 자연스러운 대안으로, 사실상 HTTP 진입점이 없는 WAR 파일로, Jakarta EE 환경에서 SpringApplicationContext를 부트스트랩하는 데만 사용됩니다.
RAR 배포는 HTTP 엔트리 포인트가 필요하지 않고 메시지 엔드포인트와 예약된 작업으로만 구성된 애플리케이션 컨텍스트에 이상적입니다. 이러한 컨텍스트의 빈은 Spring의 표준 트랜잭션 관리와 JNDI 및 JMX 지원 기능을 통해 JTA 트랜잭션 관리자 및 JNDI 바인딩 JDBCDataSource 인스턴스, JMS ConnectionFactory 인스턴스와 같은 애플리케이션 서버 리소스를 사용할 수 있으며 플랫폼의 JMX 서버에 등록할 수도 있습니다. 또한 애플리케이션 컴포넌트는 Spring의 TaskExecutor 추상화를 통해 애플리케이션 서버의 JCA WorkManager와 상호 작용할 수 있습니다.
RAR 배포와 관련된 구성 세부 사항은SpringContextResourceAdapter클래스의 javadoc을 참조하세요.
Spring ApplicationContext를 Jakarta EE RAR 파일로 간단하게 배포할 수 있습니다:
- 모든 애플리케이션 클래스를 RAR 파일(파일 확장자가 다른 표준 JAR 파일)로 패키징합니다.
- 필요한 모든 라이브러리 JAR을 RAR 아카이브의 루트에 추가합니다.
- META-INF/ra.xml 배포 설명자( SpringContextResourceAdapter의 javadoc에 표시된 대로)와 해당 Spring XML 빈 정의 파일(일반적으로META-INF/applicationContext.xml)을 추가합니다.
- 결과 RAR 파일을 애플리케이션 서버의 배포 디렉터리에 드롭합니다.
이러한 RAR 배포 단위는 일반적으로 독립적입니다. 구성 요소를 외부에 노출하지 않으며, 심지어 같은 애플리케이션의 다른 모듈에도 노출하지 않습니다. RAR 기반 ApplicationContext와의 상호 작용은 일반적으로 다른 모듈과 공유하는 JMS 대상을 통해 이루어집니다. 예를 들어, RAR 기반 ApplicationContext는 일부 작업을 예약하거나 파일 시스템의 새 파일에 반응할 수도 있습니다(예: 파일 시스템 등). 외부에서 동기식 액세스를 허용해야 하는 경우, 예를 들어 동일한 컴퓨터의 다른 애플리케이션 모듈에서 사용할 수 있는 RMI 엔드포인트를 내보낼 수 있습니다 |
The BeanFactory API
BeanFactory API는 Spring의 IoC 기능을 위한 기본 기반을 제공합니다. 특정 컨트랙트는 주로 Spring의 다른 부분 및 관련 타사 프레임워크와의 통합에 사용되며, DefaultListableBeanFactory 구현은 상위 수준 GenericApplicationContext 컨테이너 내의 핵심 위임자입니다.
BeanFactory 및 관련 인터페이스(예: BeanFactoryAware, InitializingBean,DisposableBean)는 다른 프레임워크 구성 요소의 중요한 통합 지점입니다. 어노테이션이나 반영이 필요하지 않기 때문에 컨테이너와 해당 구성 요소 간에 매우 효율적인 상호 작용이 가능합니다. 애플리케이션 수준 빈은 동일한 콜백 인터페이스를 사용할 수 있지만, 일반적으로 어노테이션이나 프로그래밍 구성을 통해 선언적 종속성 주입을 선호합니다.
핵심 BeanFactory API 수준과 그 DefaultListableBeanFactory구현은 구성 형식이나 사용할 구성 요소 어노테이션에 대한 가정을 하지 않습니다. 이러한 모든 플레이버는 확장(예: XmlBeanDefinitionReader 및 AutowiredAnnotationBeanPostProcessor)을 통해 제공되며 핵심 메타데이터 표현으로서 공유 BeanDefinition 객체에서 작동합니다. 이것이 바로 Spring의 컨테이너를 유연하고 확장 가능하게 만드는 본질이죠.
BeanFactory or ApplicationContext?
이 섹션에서는 빈팩토리와애플리케이션컨텍스트 컨테이너 레벨 간의 차이점과 부트스트랩에 미치는 영향에 대해 설명합니다.
특별한 이유가 없는 한 애플리케이션 컨텍스트를 사용해야 하며, 사용자 정의 부트스트랩을 위한 일반적인 구현으로GenericApplicationContext와 그 서브클래스 AnnotationConfigApplicationContext를사용해야 합니다. 이들은 구성 파일 로드, 클래스 경로 스캔 트리거, 프로그래밍 방식으로 빈 정의 및 주석이 달린 클래스 등록, (5.0부터) 함수형 빈 정의 등록 등 모든 일반적인 목적을 위해 Spring의 핵심 컨테이너로 들어가는 주요 진입점입니다.
애플리케이션 컨텍스트는 BeanFactory의 모든 기능을 포함하므로, 빈 처리에 대한 완전한 제어가 필요한 시나리오를 제외하고는 일반적으로 일반 BeanFactory보다 애플리케이션 컨텍스트가 권장됩니다. 애플리케이션 컨텍스트 (예:GenericApplicationContext 구현) 내에서 여러 종류의 빈은 규칙(즉, 빈 이름 또는 빈 유형, 특히 포스트프로세서)에 따라 감지되는 반면, 일반 DefaultListableBeanFactory는 특별한 빈에 대해 무관심합니다.
어노테이션 처리 및 AOP 프록시와 같은 많은 확장 컨테이너 기능의 경우 BeanPostProcessor 확장 포인트가 필수적입니다. 일반 DefaultListableBeanFactory만 사용하는 경우 이러한 포스트 프로세서는 기본적으로 감지 및 활성화되지 않습니다. 이 상황은 실제로 빈 구성에 아무런 문제가 없기 때문에 혼란스러울 수 있습니다. 오히려 이러한 시나리오에서는 추가 설정을 통해 컨테이너를 완전히 부트스트랩해야 합니다.
다음 표에는 BeanFactory 및ApplicationContext 인터페이스와 구현에서 제공하는 기능이 나열되어 있습니다.
빈 인스턴스화/배선 | 예 | 예 |
통합 수명 주기 관리 | 아니요 | 예 |
자동 BeanPostProcessor 등록 | 아니요 | 예 |
자동 빈팩토리포스트프로세서 등록 | 아니요 | 예 |
편리한 MessageSource 액세스(국제화를 위한) | 아니요 | 예 |
내장된 ApplicationEvent 게시 메커니즘 | 아니요 | 예 |
빈 포스트프로세서를 명시적으로 DefaultListableBeanFactory에 등록하려면, 다음 예제에서 볼 수 있듯이 프로그래밍 방식으로 addBeanPostProcessor를 호출해야 합니다:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
// populate the factory with bean definitions
// now register any needed BeanPostProcessor instances
factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
factory.addBeanPostProcessor(new MyBeanPostProcessor());
// now start using the factory
BeanFactoryPostProcessor를 일반 DefaultListableBeanFactory에 적용하려면 다음 예제에서와 같이 해당 postProcessBeanFactory 메서드를 호출해야 합니다:
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
// bring in some property values from a Properties file
PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("jdbc.properties"));
// now actually do the replacement
cfg.postProcessBeanFactory(factory);
두 경우 모두 명시적인 등록 단계가 불편하기 때문에, 특히 일반적인 엔터프라이즈 설정에서 확장된 컨테이너 기능을 위해 BeanFactoryPostProcessor 및 BeanPostProcessor 인스턴스에 의존하는 경우 Spring 지원 애플리케이션에서 다양한 ApplicationContext 변형이 일반DefaultListableBeanFactory보다 선호되는 이유입니다.
AnnotationConfigApplicationContext에는 모든 일반적인 어노테이션 포스트프로세서가 등록되어 있으며 @EnableTransactionManagement와 같은 구성 어노테이션을 통해 커버 아래에 추가 프로세서를 가져올 수 있습니다. Spring의 어노테이션 기반 구성 모델의 추상화 수준에서 빈 포스트프로세서의 개념은 단순한 내부 컨테이너 세부 사항으로 바뀝니다.
|
'언어 > Spring Docs 번역' 카테고리의 다른 글
Core Technologies / Validation, Data Binding, and Type Conversion (0) | 2024.07.07 |
---|---|
Core Technologies / Resources (0) | 2024.07.07 |
Core Technologies / The IoC Container(1) (0) | 2024.07.07 |
Core Technologies (0) | 2024.07.07 |
Spring Framework Overview (0) | 2024.07.07 |