Эта статья является оригинальной статьей. Любая форма перепечатки приветствуется, но обязательно с указанием источникаЛенг Ленг https://lltx.github.io.
задний план
Spring Framework предоставляет ряд интерфейсов, которые можно использовать для настройки bean-компонентов, а не для простого внедрения методов получения/установки или конструктора. Если вы внимательно посмотрите на исходный код зрелых фреймворков, таких как Spring Cloud Netflix и Spring Cloud Alibaba, построенных на Spring Framework, вы обнаружите большое количество компонентов расширения, таких как
- Эврика Проверка здоровья
package org.springframework.cloud.netflix.eureka;
public class EurekaHealthCheckHandler implements InitializingBean {}
- Конфигурация Seata Feign
package com.alibaba.cloud.seata.feign;
public class SeataContextBeanPostProcessor implements BeanPostProcessor {}
пример кода
- DemoBean
@Slf4j
public class DemoBean implements InitializingBean {
public DemoBean() {
log.info("--> instantiate ");
}
@PostConstruct
public void postConstruct() {
log.info("--> @PostConstruct ");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("--> InitializingBean.afterPropertiesSet ");
}
public void initMethod() {
log.info("--> custom initMehotd");
}
}
- DemoBeanPostProcessor
@Configuration
public class DemoBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if ("demoBean".equals(beanName)){
log.info("--> BeanPostProcessor.postProcessBeforeInitialization ");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if ("demoBean".equals(beanName)){
log.info("--> BeanPostProcessor.postProcessAfterInitialization ");
}
return bean;
}
}
- DemoConfig
@Configuration
public class DemoConfig {
@Bean(initMethod = "initMethod")
public DemoBean demoBean() {
return new DemoBean();
}
}
запустить выходной журнал
- Вывод журнала всего процесса создания bean-компонента выглядит следующим образом, что соответствует циклу создания bean-компонента над горизонтальной линией на первом рисунке.
DemoBean : --> instantiate
DemoBeanPostProcessor: --> BeanPostProcessor.postProcessBeforeInitialization
DemoBean : --> @PostConstruct
DemoBean : --> InitializingBean.afterPropertiesSet
DemoBean : --> custom initMehotd
DemoBeanPostProcessor: --> BeanPostProcessor.postProcessAfterInitialization
Выполнение ядра источника
- AbstractAutowireCapableBeanFactory.initializeBean
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 执行BeanPostProcessor.postProcessBeforeInitialization
Object wrappedBean = wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
...
// 执行用户自定义初始化and JSR 250 定义的方法
invokeInitMethods(beanName, wrappedBean, mbd);
...
// 执行执行BeanPostProcessor.postProcessAfterInitialization
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
- Наилучшие варианты использования цикла расширения каждого компонента подробно описаны ниже.
BeanPostProcessor
BeanPostProcessor — это интерфейс, который можно настроить для реализации методов обратного вызова для реализации собственной логики создания экземпляров, логики разрешения зависимостей и т. д. Если вы хотите реализовать свою собственную бизнес-логику после того, как Spring завершит создание, настройку и инициализацию объекта, вы можете реализовать или Множественная обработка BeanPostProcessor.
- Он в основном используется в режиме адаптера, который можно упаковывать и конвертировать до и после создания bean-компонентов, реализующих тот же интерфейс.
// seata 上下文转换,将其他类型 wrap 成 SeataFeignContext
public class SeataContextBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName){
if (bean instanceof FeignContext && !(bean instanceof SeataFeignContext)) {
return new SeataFeignContext(getSeataFeignObjectWrapper(),
(FeignContext) bean);
}
return bean;
}
}
- Пользовательское расширение поиска аннотаций
net.dreamlu.mica.redisson.stream.RStreamListenerDetector 查找自定义 @RStreamListener 实现 基于 Redisson 的 pub/sub
public class RStreamListenerDetector implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> userClass = ClassUtils.getUserClass(bean);
ReflectionUtils.doWithMethods(userClass, method -> {
RStreamListener listener = AnnotationUtils.findAnnotation(method, RStreamListener.class);
.... do something
}, ReflectionUtils.USER_DECLARED_METHODS);
return bean;
}
}
PostConstruct
JavaEE5 представляет аннотацию @PostConstruct, которая воздействует на жизненный цикл сервлета для реализации пользовательских операций перед инициализацией bean-компонента.
- Только один нестатический метод может использовать эту аннотацию
- Аннотированные методы не могут иметь возвращаемых значений и параметров метода
- Аннотированные методы не должны генерировать исключения
Здесь следует отметить, что эта аннотация не определена Spring, а принадлежит аннотации, определенной спецификацией JavaEE JSR-250.При использовании Java11 вам необходимо вручную ввести соответствующий jar (поскольку Java11 удален)
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
</dependency>
Сценарии использования: в предыдущих версиях мы могли инициализировать данные после запуска с помощью методов, аннотированных @PostConstruct. Однако, поскольку связанный API был удален в старшей версии Java, мы не рекомендуем использовать эту аннотацию, вы можете вызвать обратную обработку событий через событие, связанное с Spring.
Метод, аннотированный @PostConstruct, выполняет этот метод при запуске проекта, и его также можно понимать как выполнение при запуске контейнера Spring, что может использоваться как обычная загрузка некоторых данных, таких как словарь данных и тому подобное.
InitializingBean
Метод интерфейса InitializingBean выполняется после того, как инициализация контейнера (геттер/сеттер/конструктор) завершает внедрение свойства бина.
Сценарий приложения: динамическое изменение параметров bean-компонента, введенных контейнером.
- Обычные параметры конфигурации пользователя вводятся в bean-компонент
security:
oauth2:
ignore-urls:
- '/ws/**'
@ConfigurationProperties(prefix = "security.oauth2")
public class PermitAllUrlProperties {
@Getter
@Setter
private List<String> ignoreUrls = new ArrayList<>();
}
- Мы обнаружили, что на данный момент конфигурация пользователя не завершена, и есть некоторые распространенные, не требующие обслуживания пользователем, которые можно расширить, реализуя обратный вызов интерфейса InitializingBean.
@ConfigurationProperties(prefix = "security.oauth2.ignore")
public class PermitAllUrlProperties implements InitializingBean {
@Getter
@Setter
private List<String> urls = new ArrayList<>();
@Override
public void afterPropertiesSet() {
urls.add("/common/*");
}
}
initMethod
Вышеупомянутый @PostConstruct не рекомендуется для всех, вместо этого вы можете использовать Bean(initMethod = 'initMehotd'), соответствующие ограничения указаны выше.
@Bean(initMethod = "initMethod")
public DemoBean demoBean() {
return new DemoBean();
}
public void initMethod() {
log.info("--> custom initMehotd");
}
Суммировать
- Ссылаться наdocs.spring.IO/весна/документы…
- mica : GitHub.com/lets-mica/no…
- pig: github.com/lltx/pig