Spring Core Technologies - 1.15. Additional Capabilities of the ApplicationContext
- 1.15. Additional Capabilities of the ApplicationContext
- 1.15.1. Internationalization using MessageSource
- 1.15.2. Standard and Custom Events
- 1.15.3. Convenient Access to Low-level Resources
- 1.15.4. Application Startup Tracking
- 1.15.5. Convenient ApplicationContext Instantiation for Web Applications
- 1.15.6. Deploying a Spring
ApplicationContextas a Java EE RAR File
- 함께 읽기
- 목록으로 - [[/spring/document/core]]
- 이전 문서 - [[/spring/document/core/01-14-reg-loadtimeweaver]]
- 다음 문서 - [[/spring/document/core/01-16-bean-factory]]
1.15. Additional Capabilities of the ApplicationContext
As discussed in the chapter introduction, the
org.springframework.beans.factorypackage provides basic functionality for managing and manipulating beans, including in a programmatic way. Theorg.springframework.contextpackage adds theApplicationContextinterface, which extends theBeanFactoryinterface, in addition to extending other interfaces to provide additional functionality in a more application framework-oriented style. Many people use theApplicationContextin a completely declarative fashion, not even creating it programmatically, but instead relying on support classes such asContextLoaderto automatically instantiate anApplicationContextas part of the normal startup process of a Java EE web application.
앞의 1장에서 알아본 바와 같이, org.springframework.beans.factory 패키지는, bean을 관리하고 조작하기 위한 기본 기능을 제공합니다(프로그래밍 방식 포함).
org.springframework.context 패키지는 BeanFactory 인터페이스를 확장하는 ApplicationContext 인터페이스를 추가합니다.
그리고 애플리케이션 프레임워크 지향 스타일을 지원하는 추가 기능을 제공하기 위해 다른 여러 인터페이스도 확장한 것도 들어 있습니다.
ApplicationContext는 많은 사람들이 프로그래밍 방식을 사용해 생성하지 않고, 완전히 선언적인 방법을 사용해 생성합니다.
이 방법은 ContextLoader와 같은 지원 클래스에 의존해 Java EE 웹 애플리케이션의 정상적인 시작 프로세스 과정에서 ApplicationContext를 자동으로 인스턴스화합니다.
To enhance
BeanFactoryfunctionality in a more framework-oriented style, the context package also provides the following functionality:
- Access to messages in i18n-style, through the
MessageSourceinterface.- Access to resources, such as URLs and files, through the
ResourceLoaderinterface.- Event publication, namely to beans that implement the
ApplicationListenerinterface, through the use of theApplicationEventPublisherinterface.- Loading of multiple (hierarchical) contexts, letting each be focused on one particular layer, such as the web layer of an application, through the
HierarchicalBeanFactoryinterface.
context 패키지는 BeanFactory를 좀 더 프레임워크 지향적인 스타일로 향상시키기 위해 다음을 제공합니다.
MessageSource인터페이스를 통해 i18n 스타일의 메시지에 대한 엑세스를 제공합니다.ResourceLoader인터페이스를 통해 URL 이나 파일과 같은 resource에 대한 엑세스를 제공합니다.ApplicationEventPublisher인터페이스를 사용해ApplicationListener인터페이스를 구현한 bean에 대한 이벤트 발행.HierarchicalBeanFactory인터페이스를 통해 애플리케이션의 특정 레이어(web 레이어와 같은)에 집중할 수 있도록 여러 (계층구조) 컨텍스트 로드.
1.15.1. Internationalization using MessageSource
The
ApplicationContextinterface extends an interface calledMessageSourceand, therefore, provides internationalization (“i18n”) functionality. Spring also provides theHierarchicalMessageSourceinterface, which can resolve messages hierarchically. Together, these interfaces provide the foundation upon which Spring effects message resolution. The methods defined on these interfaces include:
ApplicationContext 인터페이스는 MessageSource 인터페이스를 확장하고 있으므로 국제화("i18n") 기능을 제공합니다.
또한 Spring은 계층 구조로 메시지를 찾을 수 있는 HierarchicalMessageSource 인터페이스도 제공합니다.
이러한 인터페이스들은 Spring의 메시지 resolution과 관련된 기반을 제공합니다.
이런 인터페이스들에 정의된 메소드는 다음과 같습니다.
String getMessage(String code, Object[] args, String default, Locale loc): The basic method used to retrieve a message from theMessageSource. When no message is found for the specified locale, the default message is used. Any arguments passed in become replacement values, using theMessageFormatfunctionality provided by the standard library.String getMessage(String code, Object[] args, Locale loc): Essentially the same as the previous method but with one difference: No default message can be specified. If the message cannot be found, aNoSuchMessageExceptionis thrown.String getMessage(MessageSourceResolvable resolvable, Locale locale): All properties used in the preceding methods are also wrapped in a class namedMessageSourceResolvable, which you can use with this method.
String getMessage(String code, Object[] args, String default, Locale loc)MessageSource에서 메시지를 가져오는 기본(basic) 방법입니다. 만약 지정한 locale에 해당하는 메시지를 찾지 못한다면 default 메시지가 사용됩니다. 이 메소드에 입력된 모든 인자는 표준 라이브러리에서 제공하는MessageFormat기능을 사용해 값이 replace 됩니다.
String getMessage(String code, Object[] args, Locale loc)- 본질적으로 앞의 방법과 동일하지만 이 방법은 default 메시지를 지정할 수 없다는 차이점이 있습니다. 만약 메시지를 찾을 수 없다면
NoSuchMessageException예외를 던집니다.
- 본질적으로 앞의 방법과 동일하지만 이 방법은 default 메시지를 지정할 수 없다는 차이점이 있습니다. 만약 메시지를 찾을 수 없다면
String getMessage(MessageSourceResolvable resolvable, Locale locale)- 위의 메소드들에서 사용하는 모든 입력값들은 이 메소드에 입력할 수 있는
MessageSourceResolvable클래스에 래핑되어 있습니다.
- 위의 메소드들에서 사용하는 모든 입력값들은 이 메소드에 입력할 수 있는
When an
ApplicationContextis loaded, it automatically searches for aMessageSourcebean defined in the context. The bean must have the namemessageSource. If such a bean is found, all calls to the preceding methods are delegated to the message source. If no message source is found, theApplicationContextattempts to find a parent containing a bean with the same name. If it does, it uses that bean as theMessageSource. If theApplicationContextcannot find any source for messages, an emptyDelegatingMessageSourceis instantiated in order to be able to accept calls to the methods defined above.
ApplicationContext는 로드되면, 컨텍스트에 정의된 MessageSource bean을 자동으로 검색합니다.
MessageSource bean은 messageSource라는 이름을 반드시 갖고 있어야 하며, 이런 bean이 발견되면 위에서 언급한 메소드에 대한 모든 호출이 메시지 소스에 위임됩니다.
만약 메시지 소스를 찾지 못한다면, ApplicationContext는 같은 이름의 bean을 갖고 있는 부모를 찾은 다음, 해당 bean을 MessageSource로 사용하게 됩니다.
ApplicationContext가 메시지 소스를 찾을 수 없는 상황이라면 위에서 언급한 메소드들을 호출은 할 수 있도록 빈(empty) DelegatingMessageSource가 인스턴스화됩니다.
Spring provides three
MessageSourceimplementations,ResourceBundleMessageSource,ReloadableResourceBundleMessageSourceandStaticMessageSource. All of them implementHierarchicalMessageSourcein order to do nested messaging. TheStaticMessageSourceis rarely used but provides programmatic ways to add messages to the source. The following example showsResourceBundleMessageSource:
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>
The example assumes that you have three resource bundles called
format,exceptionsandwindowsdefined in your classpath. Any request to resolve a message is handled in the JDK-standard way of resolving messages throughResourceBundleobjects. For the purposes of the example, assume the contents of two of the above resource bundle files are as follows:
이 예제는 여러분의 classpath에 format, exceptions, windows라는 3개의 리소스 꾸러미가 있다고 가정하고 있습니다.
메시지에 대한 모든 resolve 요청은 ResourceBundle 객체를 통해 JDK 표준 방법으로 처리됩니다.
예제를 더 잘 이해하기 위해 위의 두 리소스 번들 파일의 내용이 다음과 같다고 합시다.
# in format.properties
message=Alligators rock!
# in exceptions.properties
argument.required=The {0} argument is required.
The next example shows a program to run the
MessageSourcefunctionality. Remember that allApplicationContextimplementations are alsoMessageSourceimplementations and so can be cast to theMessageSourceinterface.
다음 예제는 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);
}
The resulting output from the above program is as follows:
위의 프로그램을 실행한 결과 출력은 다음과 같습니다.
Alligators rock!
To summarize, the
MessageSourceis defined in a file calledbeans.xml, which exists at the root of your classpath. ThemessageSourcebean definition refers to a number of resource bundles through itsbasenamesproperty. The three files that are passed in the list to thebasenamesproperty exist as files at the root of your classpath and are calledformat.properties,exceptions.properties, andwindows.properties, respectively.
요약해 봅시다. MessageSource는 classpath의 루트 경로에 있는 beans.xml 파일에 정의되어 있습니다.
messageSource bean definition은 basenames 속성에 지정된 여러 리소스 번들을 참조합니다.
목록에서 basenames 속성으로 전달된 세 개의 파일은 classpath 루트에 파일로 존재하며, 각각 format.properties, exceptions.properties, and windows.properties라고 부릅니다.
The next example shows arguments passed to the message lookup. These arguments are converted into
Stringobjects and inserted into placeholders in the lookup message.
다음 예는 메시지 조회에 전달된 인자를 보여줍니다.
이런 인자들은 String 타입으로 변환되고 조회 메시지의 placeholder에 입력됩니다.
<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);
}
}
The resulting output from the invocation of the
execute()method is as follows:
execute() 메소드를 호출하면 다음과 같은 결과가 출력됩니다.
The userDao argument is required.
With regard to internationalization (“i18n”), Spring’s various
MessageSourceimplementations follow the same locale resolution and fallback rules as the standard JDKResourceBundle. In short, and continuing with the examplemessageSourcedefined previously, if you want to resolve messages against the British (en-GB) locale, you would create files calledformat_en_GB.properties,exceptions_en_GB.properties, andwindows_en_GB.properties, respectively.
국제화("i18n")와 관련된 Spring의 다양한 MessageSource 구현은 표준 JDK ResourceBundle과 같은 방식의 locale resolution과 fallback 규칙을 따르고 있습니다.
즉, 이전에 정의된 예제의 messageSource를 그대로 사용해서 영국(en-GB) locale에 해당하는 메시지를 사용하려면 각각 format_en_GB.properties, exceptions_en_GB.properties, windows_en_GB.properties 라는 파일을 생성하면 됩니다.
Typically, locale resolution is managed by the surrounding environment of the application. In the following example, the locale against which (British) messages are resolved is specified manually:
locale 결정은 애플리케이션을 둘러싸고 있는 환경에서 관리하는 것이 일반적입니다. 다음 예제에서는 메시지 로케일이 수동으로 지정되고 있습니다.
# in exceptions_en_GB.properties
argument.required=Ebagum lad, the ''{0}'' argument is required, I say, required.
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);
}
The resulting output from the running of the above program is as follows:
위의 프로그램을 실행하면 다음과 같이 출력됩니다.
Ebagum lad, the 'userDao' argument is required, I say, required.
You can also use the
MessageSourceAwareinterface to acquire a reference to anyMessageSourcethat has been defined. Any bean that is defined in anApplicationContextthat implements theMessageSourceAwareinterface is injected with the application context’sMessageSourcewhen the bean is created and configured.
MessageSourceAware 인터페이스를 사용하면 정의된 모든 MessageSource의 참조를 얻을 수 있습니다.
MessageSourceAware 인터페이스의 구현체인 ApplicationContext 내에 정의된 모든 bean은, bean이 생성되고 configure 될 때 애플리케이션 컨텍스트의 MessageSource와 함께 주입됩니다.
(i) As an alternative to
ResourceBundleMessageSource, Spring provides aReloadableResourceBundleMessageSourceclass. This variant supports the same bundle file format but is more flexible than the standard JDK basedResourceBundleMessageSourceimplementation. In particular, it allows for reading files from any Spring resource location (not only from the classpath) and supports hot reloading of bundle property files (while efficiently caching them in between). See theReloadableResourceBundleMessageSourcejavadoc for details.
- (i)
- Spring은
ResourceBundleMessageSource의 대안으로ReloadableResourceBundleMessageSource클래스를 제공합니다.- 이 변형은 똑같은 번들 파일 포맷을 지원하면서도, 스탠다드 JDK 베이스의
ResourceBundleMessageSource구현보다 더 높은 유연성을 갖고 있습니다. - 특히, 이 클래스는 모든 Spring 리소스 경로(classpath 경로 외의 다른 경로도)에서 파일을 읽을 수 있으며, 번들 속성 파일의 hot reloading도 지원합니다.
- 이 변형은 똑같은 번들 파일 포맷을 지원하면서도, 스탠다드 JDK 베이스의
- 자세한 내용은
ReloadableResourceBundleMessageSource의 javadoc 문서를 참고하세요.
- Spring은
1.15.2. Standard and Custom Events
Event handling in the
ApplicationContextis provided through theApplicationEventclass and theApplicationListenerinterface. If a bean that implements theApplicationListenerinterface is deployed into the context, every time anApplicationEventgets published to theApplicationContext, that bean is notified. Essentially, this is the standard Observer design pattern.
ApplicationContext의 이벤트 핸들링은 ApplicationEvent 클래스와 ApplicationListener 인터페이스를 통해 제공됩니다.
ApplicationListener 인터페이스를 구현하는 bean이 컨텍스트에 배포되면 ApplicationEvent가 ApplicationContext에 발행될 때마다 해당 bean에 알림이 보내집니다.
기본적으로 이 방식은 일반적인 [[/pattern/observer]] 입니다.
As of Spring 4.2, the event infrastructure has been significantly improved and offers an annotation-based model as well as the ability to publish any arbitrary event (that is, an object that does not necessarily extend from
ApplicationEvent). When such an object is published, we wrap it in an event for you.
- Spring 4.2 버전부터 이벤트 인프라가 대폭 개선되었으며, 애노테이션 기반의 모델과 임의의 이벤트(
ApplicationEvent에서 확장할 필요가 없는 객체)를 발행하는 기능을 제공합니다. - 이런 종류의 객체가 발행되면, 우리는(Spring은) 이를 이벤트로 래핑합니다.
The following table describes the standard events that Spring provides:
Table 7. Built-in Events
- th
- Event
- Explanation
- td
ContextRefreshedEvent- Published when the
ApplicationContextis initialized or refreshed (for example, by using therefresh()method on theConfigurableApplicationContextinterface). Here, “initialized” means that all beans are loaded, post-processor beans are detected and activated, singletons are pre-instantiated, and theApplicationContextobject is ready for use. As long as the context has not been closed, a refresh can be triggered multiple times, provided that the chosenApplicationContextactually supports such “hot” refreshes. For example,XmlWebApplicationContextsupports hot refreshes, butGenericApplicationContextdoes not.
- td
ContextStartedEvent- Published when the
ApplicationContextis started by using thestart()method on theConfigurableApplicationContextinterface. Here, “started” means that allLifecyclebeans receive an explicit start signal. Typically, this signal is used to restart beans after an explicit stop, but it may also be used to start components that have not been configured for autostart (for example, components that have not already started on initialization).
- td
ContextStoppedEvent- Published when the
ApplicationContextis stopped by using thestop()method on theConfigurableApplicationContextinterface. Here, “stopped” means that allLifecyclebeans receive an explicit stop signal. A stopped context may be restarted through astart()call.
- td
ContextClosedEvent- Published when the
ApplicationContextis being closed by using theclose()method on theConfigurableApplicationContextinterface or via a JVM shutdown hook. Here, "closed" means that all singleton beans will be destroyed. Once the context is closed, it reaches its end of life and cannot be refreshed or restarted.
- td
RequestHandledEvent- A web-specific event telling all beans that an HTTP request has been serviced. This event is published after the request is complete. This event is only applicable to web applications that use Spring’s
DispatcherServlet.
- td
ServletRequestHandledEvent- A subclass of
RequestHandledEventthat adds Servlet-specific context information.
다음의 표는 Spring에서 제공하는 표준 이벤트들을 설명합니다.
- th
- 이벤트
- 설명
- td
ContextRefreshedEventApplicationContext가 "초기화"되거나 refresh되면 발행되는 이벤트 입니다. (예:ConfigurableApplicationContext인터페이스의refresh()메소드를 사용하는 경우)- 여기에서 "초기화"는 모든 bean의 로딩이 완료되고, post-processor bean들이 모두 감지되고 활성화도 되었고, 싱글톤들이 pre-instantiated 되어서,
ApplicationContext객체를 사용할 준비가 되었을 때를 의미합니다. - 컨텍스트가 닫히지 않은 한, 선택된
ApplicationContext가 실제로 "hot" refresh를 지원한다면, 새로 고침이 여러번 트리거 될 수 있습니다.- 예를 들어
XmlWebApplicationContext는 hot refresh를 지원하지만,GenericApplicationContext는 지원하지 않습니다.
- 예를 들어
- 여기에서 "초기화"는 모든 bean의 로딩이 완료되고, post-processor bean들이 모두 감지되고 활성화도 되었고, 싱글톤들이 pre-instantiated 되어서,
- td
ContextStartedEventConfigurableApplicationContext인터페이스의start()메소드를 사용해서ApplicationContext가 "시작"될 때 발행되는 이벤트입니다.- 여기에서 "시작"은 모든
Lifecyclebean이 명시적인 시작 신호를 받는 것을 의미합니다. - 일반적으로 이 신호는 명시적인 stop 후 bean을 restart하는데 사용되지만, autostart로 configure되지 않은 컴포넌트(예: 초기화 시 아직 시작되지 않은 컴포넌트)를 시작할 때에도 사용될 수 있습니다.
- 여기에서 "시작"은 모든
- td
ContextStoppedEventConfigurableApplicationContext인터페이스의stop()메소드를 써서ApplicationContext가 "정지"될 때 발행되는 이벤트입니다.- 여기에서 "정지"는 모든
Lifecyclebean이 명시적인 정지 신호를 받는 것을 의미합니다. - 정지된 컨텍스트는
start()를 호출해서 restart할 수 있습니다.
- 여기에서 "정지"는 모든
- td
ContextClosedEventConfigurableApplicationContext인터페이스의close()메소드를 사용하거나, JVM shutdown hook을 통해ApplicationContext가 "close"될 때 발행되는 이벤트입니다.- 여기에서 "close"는 모든 싱글톤 bean이 destroy 되는 것을 의미합니다.
- context가 일단 close 되면 생명주기의 끝에 도달하게 되어 refresh나 restart가 불가능해집니다.
- td
RequestHandledEvent- HTTP request가 서비스되었음을 모든 been에 알려주는 web 특화 이벤트입니다.
- 이 이벤트는 request가 완료되었을 때 발행됩니다.
- 이 이벤트는 Spring의
DispatcherServlet을 사용하는 웹 애플리케이션에서만 사용 가능합니다.
- td
ServletRequestHandledEvent- Servlet 관련 컨텍스트 정보를 추가하는
RequestHandledEvent의 서브 클래스입니다.
You can also create and publish your own custom events. The following example shows a simple class that extends Spring’s
ApplicationEventbase class:
커스텀 이벤트를 생성하고 발행하는 것도 가능합니다.
다음 예제는 Spring의 ApplicationEvent 클래스를 extends 하는 간단한 예제 클래스를 보여줍니다.
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...
}
To publish a custom
ApplicationEvent, call thepublishEvent()method on anApplicationEventPublisher. Typically, this is done by creating a class that implementsApplicationEventPublisherAwareand registering it as a Spring bean. The following example shows such a class:
커스텀 ApplicationEvent를 발행하려면 ApplicationEventPublisher의 publishEvent() 메소드를 호출하세요.
이 작업은 일반적으로 ApplicationEventPublisherAware의 구현 클래스를 생성하고 bean으로 등록하는 식으로 이루어집니다.
다음 예제는 이런 클래스를 보여줍니다.
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...
}
}
At configuration time, the Spring container detects that
EmailServiceimplementsApplicationEventPublisherAwareand automatically callssetApplicationEventPublisher(). In reality, the parameter passed in is the Spring container itself. You are interacting with the application context through itsApplicationEventPublisherinterface.
configuration time에, EmailService가 ApplicationEventPublisherAware를 구현하고 자동으로 setApplicationEventPublisher()를 호출하는 것을 Spring 컨테이너는 자동으로 감지합니다.
이 때, 이 메소드를 통해 전달된 파라미터는 Spring 컨테이너 자체입니다.
여러분은 ApplicationEventPublisher 인터페이스를 통해 애플리케이션 컨텍스트와 상호작용하고 있는 것입니다.
To receive the custom
ApplicationEvent, you can create a class that implementsApplicationListenerand register it as a Spring bean. The following example shows such a class:
커스텀 ApplicationEvent를 수신하려면, ApplicationListener를 구현하는 클래스를 생성하고 Spring bean으로 등록하면 됩니다.
다음 예제에서는 이러한 클래스를 보여줍니다.
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...
}
}
Notice that
ApplicationListeneris generically parameterized with the type of your custom event (BlockedListEventin the preceding example). This means that theonApplicationEvent()method can remain type-safe, avoiding any need for downcasting. You can register as many event listeners as you wish, but note that, by default, event listeners receive events synchronously. This means that thepublishEvent()method blocks until all listeners have finished processing the event. One advantage of this synchronous and single-threaded approach is that, when a listener receives an event, it operates inside the transaction context of the publisher if a transaction context is available. If another strategy for event publication becomes necessary, see the javadoc for Spring’sApplicationEventMulticasterinterface andSimpleApplicationEventMulticasterimplementation for configuration options.
ApplicationListener는 일반적으로 커스텀 이벤트의 타입으로 parameterized 되곤 합니다(위의 예제의 BlockedListEvent).
즉, onApplicationEvent() 메소드는 type-safe 하므로 downcasting이 필요하지 않습니다.
이벤트 리스너의 수는 원하는 만큼 얼마든지 등록할 수 있습니다. 다만, 이벤트 리스너는 기본적으로 이벤트를 동기식(synchronously)으로 수신합니다.
이는 모든 리스너가 이벤트 처리를 완료할 때까지는 publishEvent() 메소드가 block된다는 의미입니다.
이런 동기식의 단일 스레드 접근 방식의 한 가지 장점은, 리스너가 이벤트를 수신할 때 트랜잭션 컨텍스트를 사용할 수 있는 경우라면 이벤트 발행자의 트랜잭션 컨텍스트 내에서 작동하게 된다는 것입니다.
만약 이벤트 발행을 위한 다른 전략이 필요하다면, Spring ApplicationEventMulticaster 인터페이스의 javadoc과 configuration option에 대한 SimpleApplicationEventMulticaster의 구현을 참고하세요.
The following example shows the bean definitions used to register and configure each of the classes above:
다음 예제는 위의 각 클래스를 등록하고 configure하는데 사용되는 bean definition을 보여줍니다.
<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>
Putting it all together, when the
sendEmail()method of theemailServicebean is called, if there are any email messages that should be blocked, a custom event of typeBlockedListEventis published. TheblockedListNotifierbean is registered as anApplicationListenerand receives theBlockedListEvent, at which point it can notify appropriate parties.
지금까지의 내용을 종합해보면, emailService bean의 sendEmail() 메소드가 호출될 때, 만약 차단해야 하는 이메일 메시지가 있다면 BlockedListEvent가 발행됩니다.
ApplicationListener로 등록된 blockedListNotifier been은 BlockedListEvent를 수신해서 이를 적절한 처리자에게 알려줄 수 있습니다.
(i) Spring’s eventing mechanism is designed for simple communication between Spring beans within the same application context. However, for more sophisticated enterprise integration needs, the separately maintained Spring Integration project provides complete support for building lightweight, pattern-oriented, event-driven architectures that build upon the well-known Spring programming model.
- (i)
- Spring의 이벤트 처리 메커니즘은 동일한 애플리케이션 컨텍스트 내에서 Spring bean과 bean 사이의 간단한 커뮤니케이션을 위해 디자인되었습니다.
- 한편, 그보다 더 정교한 엔터프라이즈 통합 요구 사항을 만족시키기 위해 별도로 관리되고 있는 Spring Integration 프로젝트는 잘 알려져 있는 Spring 프로그래밍 모델을 기반으로 하는 가볍고 패턴 지향적인 이벤트 중심 아키텍처를 구축하기 위한 완벽한 지원을 제공합니다.
Annotation-based Event Listeners
You can register an event listener on any method of a managed bean by using the
@EventListenerannotation. TheBlockedListNotifiercan be rewritten as follows:
관리되고 있는 bean이라면 어느 메소드이건 @EventListener 애노테이션을 붙여서 이벤트 리스너를 등록할 수 있습니다.
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...
}
}
The method signature once again declares the event type to which it listens, but, this time, with a flexible name and without implementing a specific listener interface. The event type can also be narrowed through generics as long as the actual event type resolves your generic parameter in its implementation hierarchy.
메소드 시그니처는 수신하는 이벤트 타입으로 다시 한번 선언하기 마련이지만, 이번에는 특정한 리스너 인터페이스를 구현하지 않고 유연한 이름을 사용합니다. 실제 이벤트 타입이 구현 계층에서 제네릭 파라미터를 resolve한다면 이벤트 타입은 제네릭을 통해 범위를 좁힐 수도 있습니다.
If your method should listen to several events or if you want to define it with no parameter at all, the event types can also be specified on the annotation itself. The following example shows how to do so:
만약 여러 이벤트를 수신하게 하고 싶거나 파라미터 없이 정의하려 한다면, 이벤트 타입을 애노테이션에 지정하는 방법도 있습니다. 다음 예는 그 방법을 보여줍니다.
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
It is also possible to add additional runtime filtering by using the
conditionattribute of the annotation that defines aSpELexpression, which should match to actually invoke the method for a particular event.
특정 이벤트에 대한 메소드를 실제로 호출하기 위해 SpEL 표현식을 정의하는 애노테이션의 condition 속성을 사용해 runtime filtering을 추가하는 것도 가능합니다.
The following example shows how our notifier can be rewritten to be invoked only if the content attribute of the event is equal to
my-event:
다음 예제는 이벤트의 content 속성이 my-event와 동일한 경우에만 호출되도록 notifier를 다시 작성하는 방법을 보여줍니다.
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}
Each
SpELexpression evaluates against a dedicated context. The following table lists the items made available to the context so that you can use them for conditional event processing:
각 SpEL 표현식의 평가는 전용 컨텍스트에 대해 이루어집니다.
다음 표는 조건부 이벤트 처리에 사용할 수 있도록 컨텍스트에서 사용할 수 있는 항목을 나열합니다.
Table 8. Event SpEL available metadata
- th
- Name
- Location
- Description
- Example
- td
- Event
- root object
- The actual
ApplicationEvent. #root.eventorevent
- td
- Arguments array
- root object
- The arguments (as an object array) used to invoke the method.
#root.argsorargs;args[0]to access the first argument, etc.
- td
- Argument name
- evaluation context
- The name of any of the method arguments. If, for some reason, the names are not available (for example, because there is no debug information in the compiled byte code), individual arguments are also available using the
#a<#arg>syntax where<#arg>stands for the argument index (starting from 0). #blEventor#a0(you can also use#p0or#p<#arg>parameter notation as an alias)
- th
- 이름
- 위치
- 설명
- 예제
- td
- Event
- root object
- 실제
ApplicationEvent. #root.event또는event.
- td
- Arguments array
- root object
- 메소드를 호출하는 데 사용되는 인자들(객체 배열).
#root.args또는args. 첫번째 인자는args[0]와 같이 접근.
- td
- Argument name
- evaluation context
- 메소드 인자의 이름. 만약 이름을 사용할 수 없는 경우라면(예: 컴파일된 바이트 코드만 갖고 있는 경우) 개발 인자는
#a<#arg>신택스를 써서 사용할 수도 있습니다. 이때<#arg>는 인자 인덱스이며 인덱스는 0부터 시작합니다. #blEvent또는#a0(#p0또는#p<#arg>파라미터 표기법을 알리아스로 사용할 수도 있습니다.)
Note that
#root.eventgives you access to the underlying event, even if your method signature actually refers to an arbitrary object that was published.
메소드 시그니처가 실제로 발행된 어떤 객체를 참조하더라도 #root.event를 사용하면 베이스가 되는 기본 이벤트에 엑세스할 수 있습니다.
If you need to publish an event as the result of processing another event, you can change the method signature to return the event that should be published, as the following example shows:
만약 다른 이벤트를 처리한 결과를 다시 이벤트로 발행해야 한다면, 다음 에제와 같이 발행해야 하는 이벤트를 리턴하도록 메소드 시그니처를 수정할 수 있습니다.
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress and
// then publish a ListUpdateEvent...
}
(i) This feature is not supported for asynchronous listeners.
- (i)
- 이 기능은 비동기 리스너를 지원하지 않습니다.
The
handleBlockedListEvent()method publishes a newListUpdateEventfor everyBlockedListEventthat it handles. If you need to publish several events, you can return aCollectionor an array of events instead.
handleBlockedListEvent() 메소드는 핸들링하는 모든 BlockedListEvent에 대해 새로운 ListUpdateEvent를 발행합니다.
여러 이벤트를 발행해야 하는 경우라면 Collection이나 이벤트 배열을 리턴하는 방법도 사용할 수 있습니다.
Asynchronous Listeners
If you want a particular listener to process events asynchronously, you can reuse the regular
@Asyncsupport. The following example shows how to do so:
만약 리스너가 이벤트를 비동기로 처리하게 하고 싶다면, @Async 지원을 사용할 수 있습니다.
다음 예제에서 그 방법을 보여줍니다.
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent is processed in a separate thread
}
Be aware of the following limitations when using asynchronous events:
- If an asynchronous event listener throws an
Exception, it is not propagated to the caller. SeeAsyncUncaughtExceptionHandlerfor more details.- Asynchronous event listener methods cannot publish a subsequent event by returning a value. If you need to publish another event as the result of the processing, inject an
ApplicationEventPublisherto publish the event manually.
비동기 이벤트를 사용할 때에는 다음을 주의해야 합니다.
- 만약 비동기 이벤트 리스너가
Exception을 던지게 되면, 호출자에게 예외가 전달되지 않습니다.- 자세한 내용은
AsyncUncaughtExceptionHandler를 참고하세요.
- 자세한 내용은
- 비동기 이벤트 리스너 메소드는 값을 리턴하는 방식을 통해 서브 이벤트를 발행할 수 없습니다.
- 만약 처리 결과로 다른 이벤트를 발행하고 싶다면,
ApplicationEventPublisher를 주입해서 이벤트를 수동으로 발행하세요.
- 만약 처리 결과로 다른 이벤트를 발행하고 싶다면,
Ordering Listeners
If you need one listener to be invoked before another one, you can add the
@Orderannotation to the method declaration, as the following example shows:
만약 어떤 리스너를 다른 리스너보다 먼저 호출할 필요가 있다면, 다음 예제와 같이 @Order 애노테이션을 메소드에 붙여주면 됩니다.
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// notify appropriate parties via notificationAddress...
}
Generic Events
You can also use generics to further define the structure of your event. Consider using an
EntityCreatedEvent<T>whereTis the type of the actual entity that got created. For example, you can create the following listener definition to receive onlyEntityCreatedEventfor aPerson:
제네릭을 사용해 추가적인 이벤트 구조를 정의하는 것도 가능합니다.
EntityCreatedEvent<T>를 사용한다고 생각해 봅시다. 이때 T는 생성된 실제 엔티티의 타입입니다.
예를 들어, 다음과 같이 리스너를 정의하면 Person에 대한 EntityCreatedEvent 이벤트만 받을 수도 있습니다.
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
Due to type erasure, this works only if the event that is fired resolves the generic parameters on which the event listener filters (that is, something like
class PersonCreatedEvent extends EntityCreatedEvent<Person> { … }).
이 작업은 이벤트가 발생했을 때 이벤트 리스너 필터의 제네릭 파라미터가 일치하는 경우(예: class PersonCreatedEvent extends EntityCreatedEvent<Person> { … })에만 작동합니다.
In certain circumstances, this may become quite tedious if all events follow the same structure (as should be the case for the event in the preceding example). In such a case, you can implement
ResolvableTypeProviderto guide the framework beyond what the runtime environment provides. The following event shows how to do so:
만약 모든 이벤트가 똑같은 구조로(위의 예제와 같이) 이벤트를 처리한다면 상당히 장황한 코드가 될 수도 있습니다.
이런 경우에는 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()));
}
}
This works not only for
ApplicationEventbut any arbitrary object that you send as an event.
- 이 방법은
ApplicationEvent뿐만 아니라 이벤트로 전송하는 객체에 대해서도 작동합니다.
1.15.3. Convenient Access to Low-level Resources
For optimal usage and understanding of application contexts, you should familiarize yourself with Spring’s
Resourceabstraction, as described in Resources.
애플리케이션 컨텍스트를 잘 이해하고 잘 사용하려면, Resources 문서에 나와있는 것과 같이 Spring의 리소스 추상화를 익숙하게 다룰 수 있어야 합니다.
An application context is a
ResourceLoader, which can be used to loadResourceobjects. AResourceis essentially a more feature rich version of the JDKjava.net.URLclass. In fact, the implementations of theResourcewrap an instance ofjava.net.URL, where appropriate. AResourcecan obtain low-level resources from almost any location in a transparent fashion, including from the classpath, a filesystem location, anywhere describable with a standard URL, and some other variations. If the resource location string is a simple path without any special prefixes, where those resources come from is specific and appropriate to the actual application context type.
애플리케이션 컨텍스트는 Resource 객체를 로드하는 용도로 사용할 수 있는 ResourceLoader 이기도 합니다.
Resource는 본질적으로는 JDK의 java.net.URL 클래스에 기능을 풍부하게 추가한 버전이라 할 수 있습니다.
사실, Resource의 구현체들은 java.net.URL를 적절하게 래핑한 것들입니다.
Resource는 거의 모든 위치(classpath, 파일 시스템, URL로 표현 가능한 모든 곳, 그 외 기타 등등)에 있는 저수준 리소스를 투명한 방식으로 가져올 수 있습니다.
만약 리소스 경로 string이 특별한 prefix가 없는 단순한 경로라면, 해당 리소스의 위치는 실제 애플리케이션의 컨텍스트 타입에 적합한 경로가 됩니다.
You can configure a bean deployed into the application context to implement the special callback interface,
ResourceLoaderAware, to be automatically called back at initialization time with the application context itself passed in as theResourceLoader. You can also expose properties of typeResource, to be used to access static resources. They are injected into it like any other properties. You can specify thoseResourceproperties as simpleStringpaths and rely on automatic conversion from those text strings to actualResourceobjects when the bean is deployed.
특별한 콜백 인터페이스인 ResourceLoaderAware를 구현하도록 애플리케이션 컨텍스트에 등록된 bean을 configure할 수 있습니다.
이 인터페이스는 ResourceLoader로서 전달된 애플리케이션 컨텍스트 자체와 함께 초기화 시간에 자동으로 콜백됩니다.
정적 리소스에 엑세스하는 데 사용할 Resource 타입의 속성을 노출하는 것도 가능한데, 이런 속성들도 다른 속성과 마찬가지로 주입이 됩니다.
이런 Resource 속성들을 단순한 String 경로로 지정할 수 있습니다. 그러면 bean이 배포될 때 해당 텍스트 string이 실제 Resource 객체로 자동 변환됩니다.
The location path or paths supplied to an
ApplicationContextconstructor are actually resource strings and, in simple form, are treated appropriately according to the specific context implementation. For exampleClassPathXmlApplicationContexttreats a simple location path as a classpath location. You can also use location paths (resource strings) with special prefixes to force loading of definitions from the classpath or a URL, regardless of the actual context type.
ApplicationContext는 간단한 포맷을 가진 실제 리소스 string을 생성자로 넘겨 받으며, 이러한 값들은 컨텍스트 구현에 따라 적절하게 처리됩니다.
예를 들어 ClassPathXmlApplicationContext는 단순한 위치 경로를 classpath 경로로 취급합니다.
컨텍스트 타입을 무시하고 classpath나 URL에서 어떤 definition을 강제로 로드할 목적으로 특수한 prefix가 있는 위치 경로(리소스 문자열)를 사용하는 것도 가능합니다.
1.15.4. Application Startup Tracking
The
ApplicationContextmanages the lifecycle of Spring applications and provides a rich programming model around components. As a result, complex applications can have equally complex component graphs and startup phases.
ApplicationContext는 Spring 애플리케이션의 라이프사이클을 관리하고 컴포넌트와 관련된 풍부한 프로그래밍 모델을 제공합니다.
따라서 애플리케이션의 구조가 복잡하다면 컴포넌트 그래프도 복잡하고 시작 단계도 복잡해지게 됩니다.
Tracking the application startup steps with specific metrics can help understand where time is being spent during the startup phase, but it can also be used as a way to better understand the context lifecycle as a whole.
애플리케이션의 시작 단계를 측정해 보면 시작 단계의 어느 지점에서 오래 걸리는지를 이해하는 데에 도움이 될 텐데, 이런 방법을 사용하면 컨텍스트의 라이프사이클 전체를 더 잘 이해하는데에 도움이 될 것입니다.
The
AbstractApplicationContext(and its subclasses) is instrumented with anApplicationStartup, which collectsStartupStepdata about various startup phases:
- application context lifecycle (base packages scanning, config classes management)
- beans lifecycle (instantiation, smart initialization, post processing)
- application events processing
AbstractApplicationContext(그리고 그 서브클래스들)의 측정은 다양한 시작 단계에 대한 StartupStep 데이터를 수집하는 ApplicationStartup을 사용하게 됩니다.
다양한 시작 단계는 다음과 같습니다.
- 애플리케이션 컨텍스트 라이프사이클(base 패키지 스캐닝, config 클래스 관리)
- bean 라이프사이클(인스턴스화, 스마트 초기화, post processing)
- 애플리케이션 이벤트 프로세싱
Here is an example of instrumentation in the
AnnotationConfigApplicationContext:
다음은 AnnotationConfigApplicationContext에 대한 측정 예제입니다.
// create a startup step and start recording
StartupStep scanPackages = this.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();
The application context is already instrumented with multiple steps. Once recorded, these startup steps can be collected, displayed and analyzed with specific tools. For a complete list of existing startup steps, you can check out the dedicated appendix section.
애플리케이션 컨텍스트는 이미 여러 단계에 걸쳐 측정됩니다. 일단 기록이 되면, 특정한 도구를 사용해 이런 시작 단계들에 대해 정보를 수집하고, 표시하고, 분석할 수 있습니다. 시작 단계의 전체 목록은 dedicated appendix section에서 확인할 수 있습니다.
The default
ApplicationStartupimplementation is a no-op variant, for minimal overhead. This means no metrics will be collected during application startup by default. Spring Framework ships with an implementation for tracking startup steps with Java Flight Recorder:FlightRecorderApplicationStartup. To use this variant, you must configure an instance of it to theApplicationContextas soon as it’s been created.
ApplicationStartup의 기본 구현은 오버헤드를 최소화하기 위한 최소 구현이라 할 수 있습니다.
즉, 기본 구현으로는 애플리케이션 시작 단계에서 메트릭이 수집되지 않는다는 의미입니다.
Spring 프레임워크는 시작 단계를 추적하기 위한 구현으로 Java Flight Recorder FlightRecorderApplicationStartup를 제공합니다.
이 구현체를 사용하려면 생성되는 즉시 해당 인스턴스를 ApplicationContext에 configure해야 합니다.
Developers can also use the
ApplicationStartupinfrastructure if they’re providing their ownAbstractApplicationContextsubclass, or if they wish to collect more precise data.
만약 직접 구현한 AbstractApplicationContext의 서브클래스를 사용하거나, 더 정확한 데이터를 수집하려 한다면 ApplicationStartup 인프라를 사용할 수도 있습니다.
(!)
ApplicationStartupis meant to be only used during application startup and for the core container; this is by no means a replacement for Java profilers or metrics libraries like Micrometer.
- (!)
ApplicationStartup은 애플리케이션이 시작할 때, 그리고 core 컨테이너에만 사용됩니다.ApplicationStartup만으로는 Java 프로파일러나 Micrometer 같은 메트릭 라이브러리를 대체할 수 없습니다.
To start collecting custom
StartupStep, components can either get theApplicationStartupinstance from the application context directly, make their component implementApplicationStartupAware, or ask for theApplicationStartuptype on any injection point.
커스텀 StartupStep을 수집하려면 컴포넌트가 애플리케이션 컨텍스트에서 직접 ApplicationStartup 인스턴스를 가져오거나, 컴포넌트가 ApplicationStartupAware를 구현하게 하거나, 주입 지점에서 ApplicationStartup 타입을 요청하는 방법이 있습니다.
(i) Developers should not use the
"spring.*"namespace when creating custom startup steps. This namespace is reserved for internal Spring usage and is subject to change.
- (i)
- 개발자는 커스텀 시작 단계를 생성할 때
"spring.*"네임스페이스를 사용하면 안됩니다. - 이 네임스페이스는 Spring 전용으로 예약되어 있습니다.
- 개발자는 커스텀 시작 단계를 생성할 때
1.15.5. Convenient ApplicationContext Instantiation for Web Applications
You can create
ApplicationContextinstances declaratively by using, for example, aContextLoader. Of course, you can also createApplicationContextinstances programmatically by using one of theApplicationContextimplementations.
ApplicationContext 인스턴스를 선언적으로 만들 수도 있습니다(예를 들어 ContextLoader를 사용한다던가).
물론 프로그래밍 방식으로 ApplicationContext 구현체 중 하나를 사용해 ApplicationContext 인스턴스를 만드는 것도 가능합니다.
You can register an
ApplicationContextby using theContextLoaderListener, as the following example shows:
다음 예제와 같이 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>
The listener inspects the
contextConfigLocationparameter. If the parameter does not exist, the listener uses/WEB-INF/applicationContext.xmlas a default. When the parameter does exist, the listener separates theStringby using predefined delimiters (comma, semicolon, and whitespace) and uses the values as locations where application contexts are searched. Ant-style path patterns are supported as well. Examples are/WEB-INF/*Context.xml(for all files with names that end with Context.xml and that reside in theWEB-INFdirectory) and/WEB-INF/**/*Context.xml(for all such files in any subdirectory ofWEB-INF).
리스너는 contextConfigLocation 파라미터를 검사해서, 만약 파라미터가 존재하지 않는다면 리스너는 /WEB-INF/applicationContext.xml를 기본값으로 사용합니다.
만약 파라미터가 존재한다면 리스너는 사전에 정의된 구분자(콤마, 세미콜론, 공백)를 사용해 문자열을 구분하고, 그 값들을 애플리케이션 컨텍스트를 검색하는 위치로 사용합니다.
Ant 스타일의 경로 패턴도 지원합니다.
예를 들어 /WEB-INF/*Context.xml는 /WEB/INF 디렉토리에 있는 Context.xml로 끝나는 모든 파일입니다. 그리고 /WEB-INF/**/*Context.xml는 WEB-INF의 모든 서브 디렉토리에서 Context.xml로 끝나는 모든 파일입니다.
1.15.6. Deploying a Spring ApplicationContext as a Java EE RAR File
요즘 RAR 압축 포맷을 누가 사용한단 말인가…?!
함께 읽기
- 목록으로 - [[/spring/document/core]]
- 이전 문서 - [[/spring/document/core/01-14-reg-loadtimeweaver]]
- 다음 문서 - [[/spring/document/core/01-16-bean-factory]]