предисловие
Содержание этой статьи взято из раздела 1.8 основной части официального документа Spring Framework 5.1.6.RELEASE, в котором кратко рассказывается, как использовать точку расширения контейнера Spring для пользовательского расширения, и примечания. Если у вас есть какие-либо вопросы, добро пожаловать на общение.
Оригинальный адрес:docs.spring.IO/весна/документы…
Тело 1.8. Точки удлинения пружинного контейнера
Как правило, разработчику приложения не нужно наследоватьApplicationContextКласс реализации. Вместо этого контейнер Spring IoC можно расширить, подключив специальные интерфейсы интеграции. Следующие главы частично описывают эти интерфейсы интеграции.
1.8.1 ИспользованиеBeanPostProcessorПользовательские бобы
BeanPostProcessorИнтерфейс определяет методы обратного вызова, которые разрешено реализовывать для предоставления вашей собственной (или переопределяющей контейнер по умолчанию) логики инициализации, логики обработки зависимостей и т. д. Если вы хотите реализовать некоторую пользовательскую логику после того, как контейнер Spring завершит инициализацию контейнера, настроит и инициализирует bean-компоненты, вы можете сделать это, вставив один или несколько пользовательскихBeanPostProcessorвыполнить.
Вы можете настроить несколькоBeanPostProcessorэкземпляр, и установивorderсвойства, чтобы контролировать этиBeanPostProcessorПорядок выполнения экземпляра. только если вашBeanPostProcessorДостигнутоOrderedинтерфейс для установки этого свойства. Если вы хотите реализовать себяBeanPostProcessor, вы также должны рассмотреть возможность реализацииOrderedинтерфейс. Подробнее см.BeanPostProcessorиOrderedjavadoc. Также см.programmatic registration of BeanPostProcessor instancesПримечания к .
BeanPostProcessorЭкземпляры воздействуют на экземпляры Бина (или объекта). То есть контейнер Spring IoC инициализирует экземпляр компонента, а затемBeanPostProcessorЭкземпляры делают свое дело.
BeanPostProcessorЭкземпляры привязаны к каждому контейнеру. Это имеет значение, только если вы работаете с иерархиями контейнеров. Если вы определяете контейнер вBeanPostProcessor, он будет выполнять только постобработку bean-компонентов в этом контейнере. Другими словами, bean-компоненты, определенные в одном контейнере, не могут быть определены в другом контейнере.BeanPostProcessorОбъекты выполняют постобработку, даже если эти контейнеры находятся в одной иерархии.Чтобы изменить определение компонента (то есть схему, определяющую компонент), необходимо использовать
BeanFactoryPostProcessor,Такие какCustomizing Configuration Metadata with aBeanFactoryPostProcessorописано.
org.springframework.beans.factory.config.BeanPostProcessorИнтерфейс состоит из двух методов обратного вызова.Когда класс зарегистрирован в контейнере в качестве постпроцессора, для каждого экземпляра компонента, созданного контейнером, постпроцессор будет вызываться в методе инициализации контейнера (например,InitializingBean.afterPropertiesSet()или любое заявлениеinitметод) перед вызовом и после любой инициализации компонента. Постпроцессор компонента обычно используется для проверки в интерфейсе обратного вызова или может использовать прокси для переноса компонента. Некоторые классы инфраструктуры SpringAOP реализованы с использованием постпроцессоров bean-компонентов для обеспечения логики с прокси-оболочкой.
ApplicationContextАвтоматически обнаруживать те, которые реализованы в метаинформации конфигурацииBeanPostProcessorКомпоненты интерфейса.ApplicationContextЭти bean-компоненты регистрируются как постпроцессоры, которые будут вызываться позже при создании bean-компонента. Постпроцессоры компонентов могут быть развернуты в контейнере так же, как и другие компоненты.
Обратите внимание, что, но в классе конфигурации через@BeanФабричный метод объявляетBeanPostProcessor, возвращаемый тип фабричного метода должен быть самим реализующим классом илиorg.springframework.beans.factory.config.BeanPostProcessorИнтерфейс, который явно указывает, что этот компонент имеет природу постпроцессора. в противном случае,ApplicationContextНевозможно автоматически определить его по типу, пока он не будет полностью создан. так какBeanPostProcessorПреждевременное создание экземпляра требуется для того, чтобы воздействовать на другие экземпляры компонента в том же контексте, поэтому эта ранняя проверка типов имеет решающее значение.
Программная регистрация
BeanPostProcessorПримерНесмотря на то что
BeanPostProcessorРекомендуемый способ регистрации — позволитьApplicationContextОбнаружены автоматически (как описано ранее), вы можете зарегистрировать их программно, черезConfigurableBeanFactoryиспользоватьaddBeanPostProcessorметод. Вся помощь, когда вам нужно обработать условную логику перед регистрацией или скопировать постпроцессоры bean-компонентов между контекстами в иерархии. Обратите внимание, однако, что программно добавленныйBeanPostProcessorэкземпляр не следуетOrderedинтерфейс. Здесь порядок оформления определяет порядок исполнения. Обратите также внимание на то, что программно зарегистрированныеBeanPostProcessorЭкземпляры всегда обрабатываются до того, как экземпляры будут зарегистрированы с помощью автоопределения, любое явное упорядочение не будет иметь силы.
BeanPostProcessorЭкземпляры и автопрокси AOPреализован в контейнере
BeanPostProcessorКлассы интерфейсов являются особыми и обрабатываются по-разному. В рамках специального начального этапа всеBeanPostProcessorЭкземпляры и bean-компоненты, на которые они напрямую ссылаются, создаются при запуске. Далее всеBeanPostProcessorЭкземпляры будут зарегистрированы упорядоченным образом и будут применены ко всем другим bean-компонентам в текущем контейнере. Поскольку автопрокси AOP основан наBeanPostProcessorосуществленный,BeanPostProcessorЭкземпляры и bean-компоненты, на которые они напрямую ссылаются, не подходят для автоматического проксирования, поэтому эти bean-компоненты не могут быть связаны с аспектами.Для такого bean-компонента вы должны увидеть сообщение информационного журнала:
Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).Если вы пройдете автоинъекцию или
@Resourceпуть в вашемBeanPostProcessorВнедрение bean-компонентов, когда Spring основан на кандидатах зависимостей сопоставления типов, Spring может получить доступ к непреднамеренным bean-компонентам, и поэтому они не подходят для автоматического проксирования или других типов постпроцессоров bean-компонентов. Например, у вас есть тег зависимости с@Resource, и имя этого поля или метода установки не соответствует напрямую объявленному имени компонента и не использует атрибут имени, Spring будет сопоставлять их для доступа к другим компонентам по типу.
Следующий пример показываетApplicationContextкак написать, зарегистрироваться и использоватьBeanPostProcessorпример.
Пример: Привет, мир!BeanPostProcessor-style
Первый пример демонстрирует базовое использование, этот пример показывает пользовательскийBeanPostProcessorреализация, которая вызываетtoStringметод, результат выводится на системную консоль.
Показанный ниже пользовательский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;
}
}
использовать нижеInstantiationTracingBeanPostProcessorэлемент фасоли
<?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>
УведомлениеInstantiationTracingBeanPostProcessorто, как он определен, у него даже нет хорошего имени, и, поскольку это bean-компонент, в него можно вводить зависимости, как и в любой другой bean-компонент. (Предыдущая конфигурация также определяет bean-компонент, созданный сценарием Groovy. Поддержка динамического языка SpringDynamic Language Supportподробно в одной главе.
Следующая программа на 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 = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
Как и в предыдущей программе, появится следующий ввод:
Bean 'messenger' created : org.springframework.scripting.groovy.GroovyMessenger@272961
org.springframework.scripting.groovy.GroovyMessenger@272961
Пример:RequiredAnnotationBeanPostProcessor
Обычный способ расширить контейнер Spring IoC — объединить интерфейсы обратного вызова или аннотации с пользовательскимиBeanPostProcessorдобиться комбинированного использования. ВеснаRequiredAnnotationBeanPostProcessorявляется таким примером,BeanPostProcessorРеализовано, чтобы гарантировать, что свойства JavaBean в bean-компонентах, помеченных определенными аннотациями, могут быть зависимы от значений, введенных во время выполнения Spring.
1.8.2 ИспользованиеBeanFactoryPostProcessorМетаданные для пользовательской конфигурации
Давайте посмотрим на следующее расширениеorg.springframework.beans.factory.config.BeanFactoryPostProcessor, Семантика этого интерфейса такая же, какBeanPostProcessorАналог, основные отличия:BeanFactoryPostProcessorУправляет метаданными конфигурации bean-компонента. То есть контейнер Spring IoC позволяетBeanFactoryPostProcessorЧтение метаданных конфигурации в дополнение к созданию экземпляра контейнера.BeanFactoryPostProcessorКомпоненты вне экземпляра перед изменением их метаданных конфигурации.
Вы можете настроить несколькоBeanFactoryPostProcessorэкземпляр, и установивorderсвойства, чтобы контролировать этиBeanFactoryPostProcessorПорядок запуска экземпляров. но толькоBeanFactorPostProcessorДостигнутоOrderedинтерфейс для установки этого свойства. Если вы реализуете свой собственныйBeanFactoryPostProcessor, вам также необходимо рассмотреть возможность реализацииOrderedинтерфейс. Подробнее см.BeanFactoryPostProcessorиOrderedджавадок.
Если вы хотите изменить экземпляр Bean, вам следует использовать
BeanPostProcessor(описано в предыдущемCustomizing Beans by Using aBeanPostProcessor), хотя технически возможно использоватьBeanFactoryPostProcessor(например, с помощьюBeanFactory.getBean()), но это приведет к преждевременному созданию экземпляра компонента, что нарушит стандартный жизненный цикл контейнера. Это может иметь негативные последствия, такие как обход обычной постобработки компонента.Кроме,
BeanFactoryPostProcessorЭкземпляры привязаны к каждому контейнеру. Имеет значение, только если вы работаете с иерархиями контейнеров. Если вы определяете контейнер вBeanFactoryPostProcessor, который работает только с определениями bean-компонентов в этом контейнере. Даже если эти контейнеры находятся в одной иерархии, определение bean-компонента одного контейнера не будет определено в другом контейнере.BeanFactoryPostProcessorЭкземпляры проходят постобработку.
Чтобы вступили в силу изменения в метаданных конфигурации, определяющих контейнер, когда постпроцессор фабрики компонентов объявлен вApplicationContext, он будет выполнен автоматически. Spring включает ряд предопределенных постпроцессоров bean factory, таких какPropertyOverrideConfigurerиPropertyPlaceholderConfigurer. Вы также можете использовать пользовательскиеBeanFactoryPostProcessor- Например, регистрация пользовательского редактора свойств.
ApplicationContextавтоматически обнаружит, что объявление реализовано внутри себяBeanFactoryPostProcessorКомпоненты интерфейса. Он будет использовать эти bean-компоненты в качестве постпроцессоров bean factory в нужное время. Вы можете объявить эти bean-компоненты постпроцессора, как и любые другие bean-компоненты.
и
BeanPostProcessorНапример, вы обычно не хотите настраиватьBeanFactoryPostProcessorинициализируется после задержки. Если никакие другие компоненты не ссылаются на BeanFactoryPostProcessor, этот постпроцессор вообще не будет создан. Поэтому лениво загруженные теги игнорируются, даже если поставитьdefault-lazy-initсвойство установлено наtrue,BeanFactoryPostProcessorТакже создается как можно раньше.
Пример: замена имени классаPropertyPlaceholderConfigurer
ты можешь использоватьPropertyPlaceholderConfigurerиз автономного с использованием стандартной JavaPropertiesформатировать файл для выражения значений свойств определения bean-компонента. Это позволяет людям развертывать приложения на основе свойств среды, таких как URL-адреса базы данных и пароли, без сложности и риска изменения основного XML-файла конфигурации или файлов-контейнеров.
См. следующий фрагмент метаданных конфигурации на основе XML, в котором объявляетсяdataSource:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<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>
В примере показана конфигурация свойств из внешнего файла свойств. Во время выполненияPropertyPlaceholderConfigurerзаменит метаданные приложения наdataSourceнекоторые свойства. Заменяемое значение указывается как$ {property-name}Заполнитель для формы, соответствующей стилям Ant, log4j и JSP EL.
Фактическое значение исходит от другого, чтобы стандартизировать JavaPropertiesФормат файла:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
следовательно,${jdbc.username}Строки заменяются на sa во время выполнения точно так же, как и для других значений-заполнителей, соответствующих соответствующему ключу в файле свойств.PropertyPlaceholderConfigurerПроверяются большинство заполнителей свойств и свойств, определяемых bean-компонентом. Кроме того, вы можете настроить префикс и суффикс заполнителей.
Представлен весной 2.5contextВ пространстве имен вы можете настроить заполнители свойств с помощью специализированных элементов конфигурации. ты сможешьlocationВ свойстве указан список одного или нескольких местоположений, разделенных запятыми, как показано в следующем примере:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
PropertyPlaceholderConfigurerне только в вашем ограниченномPropertiesНайдите свойства в файле. По умолчанию, если свойство не может быть найдено в конкретном файле свойств, оно также будет найдено в файле свойств Java.Systemсвойства проверяются. Вы можете настроить объект, установивsystemPropertiesModeСвойство настраивает это поведение, и следующие три поддерживаемых им целочисленных значения:
-
never(0): Системные свойства никогда не проверяются. -
fallback(1): Если проблема не решена в данном файле свойств, проверьте свойства системы. Это поведение по умолчанию. -
override(2): Прежде чем анализировать конкретный файл свойств, сначала проверьте системные свойства. Это позволяет системным свойствам переопределять любой другой источник свойств.
Подробнее см.PropertyPlaceholderConfigurerjavadoc.
ты можешь использовать
PropertyPlaceholderConfigurerЗамените имена классов, которые могут пригодиться, когда вам нужно выбрать конкретный класс реализации во время выполнения. Ниже показан пример того, как это сделать:<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <value>classpath:com/something/strategy.properties</value> </property> <property name="properties"> <value>custom.strategy.class=com.something.DefaultStrategy</value> </property> </bean> <bean id="serviceStrategy" class="${custom.strategy.class}"/>Если класс не может быть преобразован в допустимый класс во время выполнения, разрешение компонента завершается ошибкой при создании компонента. Это произойдет в
ApplicationContextв бобах без ленивой загрузкиpreInstantiateSingletonsсцена.
Пример:PropertyOverrideConfigurer
PropertyOverrideConfigurer, еще один постпроцессор bean factory, сPropertyPlaceholderConfigurerОчень похоже, но в отличие от последнего, для свойств компонента исходное определение может иметь значение по умолчанию или не иметь значения. Если переопределеноPropertiesЕсли файл не имеет свойства компонента, используется определение контекста по умолчанию.
Обратите внимание, что определение bean-компонента не знает о переопределении, поэтому сразу не видно, что XML-файл определения переопределяет используемую конфигурацию. Если есть несколькоPropertyOverrideConfigurerЭкземпляр определяет свойство компонента, но другие значения, поэтому из-за механизма переопределения вступает в силу последнее определенное значение.
PropertiesВсе строки конфигурации файла имеют следующий формат:
beanName.property=value
Пример формата приведен ниже:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
Образец файла конфигурации доступен для контейнера, который определяет файл с именемdataSourceБобdriverиurlАтрибуты.
Составные имена свойств также поддерживаются, если каждый компонент пути (за исключением переопределяемого окончательного реализованного свойства) не равен нулю (все они инициализируются конструктором).
В следующем примере с именемtomбобыfredатрибутbobатрибутsammyСвойству присваивается скалярное значение 123:
tom.fred.bob.sammy=123
Значения, указанные для переопределения, должны быть литералами, они не будут преобразованы в ссылки на бины. Это соглашение также применяется, когда необработанное значение в определении bean-компонента XML указывает ссылку на bean-компонент.
Представлено использование Spring 2.5contextпространство имен, которое можно переопределить с помощью специальных элементов конфигурации для настройки свойств, как показано в следующем примере:
<context:property-override location="classpath:override.properties"/>
1.8.3 ИспользованиеFactoryBeanпользовательская логика создания экземпляров
ты можешь достичьorg.springframework.beans.factory.FactoryBeanинтерфейс для создания объектов, которые сами являются фабриками.
FactoryBeanИнтерфейс подключается к реализации логики создания экземпляров контейнера Spring IoC. Если у вас есть сложный код инициализации, использование кода Java лучше, чем подробная конфигурация XML, вы можете создать свой собственныйFactoryBean, напишите сложные экземпляры в этом классе и поместите пользовательскиеFactoryBeanВставьте в контейнер.
FactoryBeanИнтерфейс предоставляет три метода:
-
Object getObject(): возвращает объект экземпляра, созданный фабрикой. Этот экземпляр может быть общим, в зависимости от того, возвращает ли мастер фабрики объект-одиночку или объект-прототип. -
boolean isSignletion(): еслиFactoryBeanВозврат одноэлементного объекта возвращаетtrue, иначеfalse -
Class getObjectType(): метод возвратаgetObject()тип объекта или возврат, если тип не был определенnull
FactoryBeanКонцепции и реализации используются во многих местах Spring Framework, а сама Spring предоставляет более 50FactoryBeanвыполнить.
когда вам нужно получить доступ к определенному контейнеруFactoryBeaninstance вместо bean-компонентов, которые он производит, при использованииApplicationContextизgetBeanметод, использование&Символическая роль фасолиidпрефикс. Например, учитываяidдля myBeanFactoryBean,перечислитьgetBean("myBean")может получитьFactoryBeanсгенерированный bean-компонент при вызовеgetBean("&myBean")возвращениеFactoryBeanсам экземпляр.