Как говорилось во введении к главе, пакет org.springframework.beans.factory
предоставляет базовую функциональность для управления и манипулирования бинами, в том числе программным способом. Пакет org.springframework.context
добавляет интерфейс ApplicationContext
, который расширяет интерфейс BeanFactory
в дополнение к расширению других интерфейсов с целью обеспечения дополнительной функциональности в более ориентированном на структуру приложения стиле. Многие используют ApplicationContext
полностью декларативно, даже не создавая его программно, а полагаясь на вспомогательные классы, такие как ContextLoader
, для автоматического создания ApplicationContext
в рамках обычного процесса запуска веб-приложения на Java EE.
Чтобы расширить функциональность BeanFactory
в более ориентированном на структуру стиле, пакет контекста также предоставляет следующую функциональность:
-
Доступ к сообщениям в стиле интернационализации (i18n) через интерфейс
MessageSource
. -
Доступ к ресурсам, таким как URL и файлы, через интерфейс
ResourceLoader
. -
Публикация событий, а именно бинов, реализующих интерфейс
ApplicationListener
, с помощью интерфейсаApplicationEventPublisher
. -
Загрузка нескольких (иерархических) контекстов, что позволяет ориентировать каждый из них на один конкретный уровень, например, уровень взаимодействия с интернетом приложения, с помощью интерфейса
HierarchicalBeanFactory
.
Интернационализация с использованием 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 resolvable, Locale locale)
: Все свойства, используемые в предыдущих методах, также обернуты в класс с именемMessageSourceResolvable
, который можно использовать с этим методом.
Когда загружается ApplicationContext
, он автоматически ищет бин MessageSource
, определенный в контексте. Бины должны иметь имя messageSource
. Если такой бин будет найден, все вызовы предыдущих методов делегируются источнику сообщений. Если источник сообщения не будет найден, интерфейс ApplicationContext
попытается найти родительский класс, содержащий бин с таким же именем. Если такой будет найден, то он использует этот бин в качестве MessageSource
. Если 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>
В примере предполагается, что в вашем classpath определены три пакета ресурсов под названиями format
, exceptions
и windows
. Любой запрос на разрешение сообщения обрабатывается стандартным для JDK способом разрешения сообщений через объекты ResourceBundle
. Для примера предположим, что содержимое двух из вышеупомянутых файлов пакета ресурсов выглядит следующим образом:
# 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);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("message", null, "Default", Locale.ENGLISH)
println(message)
}
Результат работы вышеуказанной программы выглядит следующим образом:
Alligators rock!
Подводя итог, MessageSource
определяется в файле под названием beans.xml
, который находится в корне вашего пути классов. Определение бина messageSource
ссылается на ряд ресурсных локализационных пакетов через свойство basenames
. Три файла, которые передаются в списке свойству basenames
, существуют как файлы в корне вашего пути классов и называются format.properties
, exceptions.properties
и windows.properties
, соответственно.
В следующем примере показаны аргументы, передаваемые для поиска сообщений. Эти аргументы преобразуются в объекты String
и вставляются в плейсхолдеры в поисковом сообщении.
<beans>
<!-- этот MessageSource используется в веб-приложении -->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="exceptions"/>
</bean>
<!-- давайте внедрим вышеупомянутый MessageSource в этот 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);
}
}
class Example {
lateinit var messages: MessageSource
fun execute() {
val message = messages.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.ENGLISH)
println(message)
}
}
Результат вызова метода execute()
выглядит следующим образом:
The userDao argument is required.
Что касается интернационализации ("i18n"), то различные реализации MessageSource
в Spring следуют тем же правилам разрешения локали и возврата, что и стандартный JDK ResourceBundle
. Короче говоря, продолжая рассматривать пример messageSource
, определенный ранее, если нужно разрешать сообщения по британской английской (en-GB
) локали, вам необходимо создать файлы format_en_GB.properties
, exceptions_en_GB.properties
и windows_en_GB.properties
, соответственно.
Как правило, разрешение локали управляется окружением приложения. В следующем примере локаль, по которой разрешаются (британский английский) сообщения, указывается вручную:
# 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);
}
fun main() {
val resources = ClassPathXmlApplicationContext("beans.xml")
val message = resources.getMessage("argument.required",
arrayOf("userDao"), "Required", Locale.UK)
println(message)
}
В результате выполнения вышеуказанной программы получаем следующие результаты:
Ebagum lad, the 'userDao' argument is required, I say, required.
Вы также можете использовать интерфейс MessageSourceAware
для получения ссылки на любой определенный MessageSource
. Любой бин, определенный в ApplicationContext
, который реализует интерфейс MessageSourceAware
, внедряется с помощью MessageSource
контекста приложения, когда бин создается и конфигурируется.
MessageSource
в Spring основан на ResourceBundle
из Java, он не объединяет пакеты с одинаковым основным именем, а использует только первый найденный пакет. Последующие пакеты сообщений с тем же основным именем игнорируются.ResourceBundleMessageSource
, Spring предоставляет класс ReloadableResourceBundleMessageSource
. Этот вариант поддерживает тот же формат файла пакета, но является более гибким, чем стандартная реализация ResourceBundleMessageSource
, основанная на JDK. В частности, он позволяет считывать файлы из любого места расположения ресурсов Spring (не только из пути классов) и поддерживает горячую перезагрузку файлов свойств пакета (при этом эффективно кэшируя их в промежутках). Подробности см. в javadoc по ReloadableResourceBundleMessageSource
.
Стандартные и кастомные события
Обработка событий в ApplicationContext
осуществляется с помощью класса ApplicationEvent
и интерфейса ApplicationListener
. Если в контексте развернут бин, реализующий интерфейс ApplicationListener
, то каждый раз, когда в ApplicationContext
публикуется событие ApplicationEvent
, этот бин получает уведомление. По сути, это стандартный шаблон проектирования Observer.
ApplicationEvent
). Если такой объект публикуется, то затем он оборачивается в событие.В следующей таблице описаны стандартные события, которые предоставляет Spring:
Событие | Пояснение |
---|---|
|
Публикуется при инициализации или обновлении |
|
Публикуется при запуске |
|
Публикуется, если |
|
Публикуется, если |
|
Событие, специфичное для веб-среды, сообщающее всем бинам о том, что HTTP-запрос был обработан. Это событие публикуется после выполнения запроса. Это событие применимо только к веб-приложениям, использующим |
|
Подкласс |
Вы также можете создавать и публиковать собственные специальные события. В следующем примере показан простой класс, который расширяет базовый класс ApplicationEvent
в Spring:
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)) и другие методы...
}
class BlockedListEvent(source: Any,
val address: String,
val content: String) : ApplicationEvent(source)
Чтобы опубликовать специальное событие ApplicationEvent
, вызовите метод publishEvent()
для ApplicationEventPublisher
. Обычно это делается путем создания класса, реализующего 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;
}
// отправляем электронное письмо...
}
}
class EmailService : ApplicationEventPublisherAware {
private lateinit var blockedList: List<String>
private lateinit var publisher: ApplicationEventPublisher
fun setBlockedList(blockedList: List<String>) {
this.blockedList = blockedList
}
override fun setApplicationEventPublisher(publisher: ApplicationEventPublisher) {
this.publisher = publisher
}
fun sendEmail(address: String, content: String) {
if (blockedList!!.contains(address)) {
publisher!!.publishEvent(BlockedListEvent(this, address, content))
return
}
// send email...
}
}
Во время конфигурирования контейнер Spring определяет, что EmailService
реализует ApplicationEventPublisherAware
, и автоматически вызывает setApplicationEventPublisher()
. На самом деле передаваемый параметр – это сам контейнер Spring. Вы взаимодействуете с контекстом приложения через его интерфейс ApplicationEventPublisher
.
Для получения специального 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) {
// уведомляем соответствующие стороны через notificationAddress...
}
}
class BlockedListNotifier : ApplicationListener<BlockedListEvent> {
lateinit var notificationAddress: String
override fun onApplicationEvent(event: BlockedListEvent) {
// уведомляем соответствующие стороны через notificationAddress...
}
}
Обратите внимание, что ApplicationListener
параметризован общим типом вашего специального события(BlockedListEvent
в предыдущем примере). Это означает, что метод onApplicationEvent()
может оставаться типобезопасным, что позволяет избежать необходимости выполнения приведения вниз к соответствующему типу. Вы можете зарегистрировать столько слушателей событий, сколько пожелаете, но учтите, что по умолчанию слушатели событий получают события синхронно. Это означает, что метод publishEvent()
будет блокироваться до тех пор, пока все слушатели не закончат обработку события. Одно из преимуществ этого синхронного и однопоточного подхода заключается в том, что когда слушатель получает событие, он работает внутри контекста транзакции публикатора, если контекст транзакции доступен. Если возникает необходимость в другой стратегии публикации событий, см. javadoc по интерфейсу ApplicationEventMulticaster
и реализации SimpleApplicationEventMulticaster
в Spring для вариантов конфигурации.
В следующем примере показаны определения бинов, используемые для регистрации и настройки каждого из вышеперечисленных классов:
<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>
Сводя все воедино, при вызове метода sendEmail()
бина emailService
, если существуют какие-либо сообщения электронной почты, которые должны быть заблокированы, публикуется специальное событие типа BlockedListEvent
. Бин blockedListNotifier
регистрируется как ApplicationListener
и получает событие BlockedListEvent
, после чего может уведомить соответствующие стороны.
Слушатели (получатели) событий на основе аннотаций
Вы можете зарегистрировать слушателя событий для любого метода управляемого бина, используя аннотацию @EventListener
. BlockedListNotifier
можно переписать следующим образом:
public class BlockedListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlockedListEvent(BlockedListEvent event) {
// уведомляем соответствующие стороны через notificationAddress...
}
}
class BlockedListNotifier {
lateinit var notificationAddress: String
@EventListener
fun processBlockedListEvent(event: BlockedListEvent) {
// уведомляем соответствующие стороны через notificationAddress...
}
}
В сигнатуре метода снова объявляется тип события, который он "прослушивает", но на этот раз с гибким именем и без реализации конкретного интерфейса слушателя. Тип события также модно сужать с помощью дженериков до тех пор, пока реальный тип события будет позволять использовать ваш типизированный параметр в своей иерархии реализации.
Если ваш метод должен прослушивать несколько событий или вы хотите определить его вообще без параметров, типы событий также можно указать в самой аннотации. В следующем примере показано, как это сделать:
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
// ...
}
@EventListener(ContextStartedEvent::class, ContextRefreshedEvent::class)
fun handleContextStart() {
// ...
}
Также можно добавить дополнительную фильтрацию во время выполнения, используя атрибут condition
в аннотации, определяющей выражение SpEL
, которое должно совпадать с фактическим вызовом метода для определенного события.
В следующем примере показано, каким образом можно переписать наше средство уведомления, чтобы оно вызывалось только в том случае, если атрибут content
события равен my-event
:
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlockedListEvent(BlockedListEvent blEvent) {
// уведомляем соответствующие стороны через notificationAddress...
}
@EventListener(condition = "#blEvent.content == 'my-event'")
fun processBlockedListEvent(blEvent: BlockedListEvent) {
// уведомляем соответствующие стороны через notificationAddress...
}
Каждое выражение SpEL
оценивается по выделенному контексту. В следующей таблице перечислены элементы, доступные для контекста, чтобы их можно было использовать для условной обработки событий.
Имя | Местонахождение | Описание | Пример |
---|---|---|---|
Событие | корневой объект | Фактическое событие ApplicationEvent . |
#root.event или event |
Массив аргументов | корневой объект | Аргументы (в виде массива объектов), используемые для вызова метода. | #root.args или args ; args[0] для доступа к первому аргументу и т.д. |
Имя аргумента | контекст вычислений | Имя любого из аргументов метода. Если по какой-то причине имена недоступны (например, из-за отсутствия отладочной информации в скомпилированном байт-коде), отдельные аргументы можно также использовать с помощью синтаксиса #a<#arg> , где <#arg> обозначает индекс аргумента (начиная с 0). |
#blEvent или #a0 (в качестве псевдонима можно также использовать обозначения параметров #p0 или #p<#arg> ) |
Обратите внимание, что #root.event
дает доступ к базовому событию, даже если сигнатура вашего метода на самом деле ссылается на произвольный объект, который был опубликован.
Если вам нужно опубликовать событие в результате обработки другого события, можно изменить сигнатуру метода, чтобы вернуть событие, которое необходимо опубликовать, как показано в следующем примере:
@EventListener
public ListUpdateEvent handleBlockedListEvent(BlockedListEvent event) {
// уведомляем соответствующие стороны через notificationAddress и
// затем публикуем ListUpdateEvent...
}
@EventListener
fun handleBlockedListEvent(event: BlockedListEvent): ListUpdateEvent {
// уведомляем соответствующие стороны через notificationAddress и
// затем публикуем ListUpdateEvent...
}
Метод handleBlockedListEvent()
публикует новое событие ListUpdateEvent
для каждого события BlockedListEvent
, которое он обрабатывает. Если вам нужно опубликовать несколько событий, вы можете вернуть Collection
или массив событий.
Асинхронные слушатели
Если вам нужно, чтобы определенный слушатель обрабатывал события асинхронно, вы можете повторно воспользоваться обычной аннотацией @Async
. В следующем примере показано, как это сделать:
@EventListener
@Async
public void processBlockedListEvent(BlockedListEvent event) {
// BlockedListEvent обрабатывается в отдельном потоке
}
@EventListener
@Async
fun processBlockedListEvent(event: BlockedListEvent) {
// BlockedListEvent обрабатывается в отдельном потоке
}
Помните о следующих ограничениях при использовании асинхронных событий:
-
Если асинхронный слушатель событий генерирует
Exception
, оно не передается вызывающему коду. Более подробную информацию см.AsyncUncaughtExceptionHandler
. -
Методы асинхронных слушателей событий не могут публиковать последующее событие, возвращая значение. Если вам нужно опубликовать другое событие в результате обработки, введите
ApplicationEventPublisher
, чтобы опубликовать событие вручную.
Упорядочение слушателей
Если вам нужно, чтобы один слушатель была вызван раньше другого, то можно добавить аннотацию @Order
к объявлению метода, как показано в следующем примере:
@EventListener
@Order(42)
public void processBlockedListEvent(BlockedListEvent event) {
// уведомляем соответствующие стороны через notificationAddress...
}
@EventListener
@Order(42)
fun processBlockedListEvent(event: BlockedListEvent) {
// уведомляем соответствующие стороны через notificationAddress...
}
Типизированные события
Также можно использовать джеерики для дальнейшего определения структуры вашего события. Рассмотрим возможность использования EntityCreatedEvent<T>
, где T
- тип фактической сущности, которая была создана. Например, можно создать следующее определение слушателя, чтобы получать только EntityCreatedEvent
для Person
:
@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
// ...
}
@EventListener
fun onPersonCreated(event: EntityCreatedEvent<Person>) {
// ...
}
Из-за стирания типов это работает только в том случае, если запускаемое на выполнение событие разрешает общие параметры, по которым слушатель событий осуществляет фильтрацию (то есть что-то наподобие class PersonCreatedEvent extends 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()));
}
}
class EntityCreatedEvent<T>(entity: T) : ApplicationEvent(entity), ResolvableTypeProvider {
override fun getResolvableType(): ResolvableType? {
return ResolvableType.forClassWithGenerics(javaClass, ResolvableType.forInstance(getSource()))
}
}
ApplicationEvent
, но и для любого произвольного объекта, который вы посылаете в качестве события.Удобный доступ к низкоуровневым ресурсам
Для оптимального использования и понимания контекстов приложений, следует ознакомиться с абстракцией Resource
в Spring.
Контекст приложения представляет собой ResourceLoader
, который можно использовать для загрузки объектов Resource
. Resource
– это, по сути, более функциональная версия класса java.net.URL
из JDK. На деле реализации Resource
обертывают экземпляр класса java.net.URL
, когда это необходимо. Resource
может получать низкоуровневые ресурсы практически из любого места понятным образом, включая из пути классов, местоположения файловой системы, любого места, описываемого стандартным URL, и некоторых других вариантов. Если строка расположения ресурсов представляет собой простой путь без каких-либо специальных префиксов, то место, откуда берутся эти ресурсы, является конкретным и соответствует реальному типу контекста приложения.
Вы можете настроить бин, развернутый в контексте приложения, на реализацию специального интерфейса обратного вызова, ResourceLoaderAware
, для автоматического вызова во время инициализации с передачей самого контекста приложения в качестве ResourceLoader
. Вы также можете открывать свойства типа Resource
, чтобы использовать их для доступа к статическим ресурсам. Они внедряются в него, как и любые другие свойства. Вы можете задать эти свойства Resource
в виде простых String
путей и полагаться на автоматическое преобразование этих текстовых строк в реальные объекты Resource
при развертывании бина.
Путь или пути расположения, передаваемые конструктору интерфейса ApplicationContext
, на самом деле являются строками ресурсов и в простой форме обрабатываются соответствующим образом в зависимости от конкретной реализации контекста. Например, ClassPathXmlApplicationContext
считает простой путь расположения расположение в пути классов. Вы также можете использовать пути расположения (строки ресурсов) со специальными префиксами для принудительной загрузки определений из пути классов или URL, независимо от фактического типа контекста.
Отслеживание запуска приложений
Интерфейс ApplicationContext
управляет жизненным циклом приложений в Spring и обеспечивает полнофункциональную модель программирования для компонентов. В результате сложные приложения могут иметь столь же сложные графы компонентов и фазы запуска.
Отслеживание этапов запуска приложения с помощью конкретных метрических показателей (метрик) может помочь понять, на что уходит время на этапе запуска, но оно также может быть использовано как способ лучшего понимания жизненного цикла контекста в целом.
AbstractApplicationContext
(и его подклассы) оснащен ApplicationStartup
, который собирает данные StartupStep
о различных фазах запуска:
-
жизненный цикл контекста приложения (сканирование базовых пакетов, управление конфигурационными классами)
-
жизненный цикл бинов (создание экземпляра, интеллектуальная инициализация, последующая обработка)
-
обработка событий приложения
Вот пример инструментирования в AnnotationConfigApplicationContext
:
// создаем этап запуска и начинаем запись
StartupStep scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan");
// добавляем информацию о метках к текущему этапу
scanPackages.tag("packages", () -> Arrays.toString(basePackages));
// выполняем фактическую фазу, которую инструментируем
this.scanner.scan(basePackages);
// завершаем текущий этап
scanPackages.end();
// создаем этап запуска и начинаем запись
val scanPackages = this.getApplicationStartup().start("spring.context.base-packages.scan")
// добавляем информацию о метках к текущему этапу
scanPackages.tag("packages", () -> Arrays.toString(basePackages))
// выполняем фактическую фазу, которую инструментируем
this.scanner.scan(basePackages)
// завершаем текущий этап
scanPackages.end()
Контекст приложения уже инструментирован несколькими этапами. После записи эти этапы запуска могут быть собраны, отображены и проанализированы с помощью специальных инструментов. Полный список существующих этапов запуска можно найти в специальном разделе приложения.
Реализация ApplicationStartup
по умолчанию – это фиктивный (no-op) вариант, позволяющий свести к минимуму задержки из-за обработки. Это означает, что по умолчанию при запуске приложения метрики собираться не будут. Spring Framework поставляется с реализацией для отслеживания этапов запуска с помощью Java Flight Recorder: FlightRecorderApplicationStartup
. Чтобы использовать этот вариант, необходимо настроить его экземпляр на ApplicationContext
сразу после его создания.
Разработчики также могут использовать инфраструктуру ApplicationStartup
, если они указывают свой собственный подкласс AbstractApplicationContext
, или если они хотят собрать более точные данные.
ApplicationStartup
предназначен только для использования во время запуска приложения и для основного контейнера; это ни в коем случае не замена Java-профилировщиков или библиотек метрик, таких как Micrometer.
Чтобы начать сбор специального StartupStep
, компоненты могут либо получить экземпляр ApplicationStartup
непосредственно из контекста приложения, либо принудить свой компонент реализовать ApplicationStartupAware
, либо запросить тип ApplicationStartup
в любой точке внедрения.
"spring.*"
при создании специальных этапов запуска. Это пространство имен зарезервировано для внутреннего использования Spring и может быть изменено.Удобное создание экземпляра ApplicationContext для веб-приложений
Вы можете создавать экземпляры ApplicationContext
декларативно, используя, например, ContextLoader
. Конечно, также можно создавать экземпляры ApplicationContext
программно, используя одну из реализаций ApplicationContext
.
Вы можете зарегистрировать ApplicationContext
с помощью ContextLoaderListener
, как показано в следующем примере:
<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
по умолчанию. Если параметр существует, слушатель разделяет String
с помощью предопределенных разделителей (запятая, точка с запятой и пробел) и использует значения в качестве мест поиска контекстов приложений. Также поддерживаются шаблоны путей в стиле Ant. Примерами являются /WEB-INF/*Context.xml
(для всех файлов, имена которых заканчиваются на Context.xml
и которые находятся в каталоге WEB-INF
) и /WEB-INF/**/*Context.xml
(для всех таких файлов в любом подкаталоге WEB-INF
).
Развертывание Spring ApplicationContext
как RAR-файла в Java EE
Можно развернуть ApplicationContext
из Spring как RAR-файл, заключив контекст и все его необходимые классы бинов и библиотеки JAR в единицу развертывания Java EE RAR. Это эквивалентно загрузке отдельного ApplicationContext
(только размещенного в среде Java EE), имеющего доступ к средствам серверов Java EE. Развертывание RAR является более естественной альтернативой сценарию развертывания консольного WAR-файла - по сути, WAR-файла без каких-либо точек входа HTTP, который используется только для загрузки ApplicationContext
из Spring в среде Java EE.
Развертывание RAR идеально подходит для контекстов приложений, которые не нуждаются в точках входа HTTP, а состоят только из конечных точек сообщений и запланированных заданий. Бины в таком контексте могут задействовать ресурсы сервера приложений, такие как диспетчер транзакций JTA и связанные с JNDI экземпляры DataSource
из JDBC и экземпляры ConnectionFactory
из JMS, а также могут регистрироваться на JMX-сервере платформы - все это с помощью стандартного управления транзакциями из Spring и средств поддержки из JNDI и JMX. Компоненты приложения также могут взаимодействовать с WorkManager
сервера приложений из JCA через абстракцию TaskExecutor
из Spring.
Подробные сведения о конфигурации, связанные с развертыванием RAR, см. в javadoc по классу SpringContextResourceAdapter
.
For a simple deployment of a Spring ApplicationContext as a Java EE RAR file:
-
Упакуйте все классы приложения в файл RAR (который представляет собой стандартный файл JAR с другим расширением файла).
-
Добавьте все необходимые библиотеки JAR в корень архива RAR.
-
Добавьте дескриптор развертывания
META-INF/ra.xml
(как показано в javadoc дляSpringContextResourceAdapter
) и соответствующий файл(ы) определения бинов Spring XML (обычноMETA-INF/applicationContext.xml
). -
Поместите полученный RAR-файл в каталог развертывания вашего сервера приложений.
ApplicationContext
на основе RAR обычно происходит через JMS-приемники, которые он использует совместно с другими модулями. ApplicationContext
на основе RAR может также, например, планировать некоторые задания или учитывать новые файлы в файловой системе (или тому подобное). Если ему необходимо дать синхронный доступ извне, он может (например) экспортировать конечные точки RMI, которые могут использоваться другими прикладными модулями на той же машине.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ