Источник статьи:1t.click/bfHN
Введение
Ежедневный процесс разработки иногда требует загрузки определенных ресурсов после запуска приложения или освобождения ресурсов до закрытия приложения. Spring Framework предоставляет соответствующие функции дляSpring Bean
жизненный цикл, который может бытьBean
Процесс создания инициализирует ресурсы и уничтожает ихBean
Процесс освобождает ресурсы. Spring предоставляет множество различных способов инициализации/уничтоженияBean
, если вы используете эти методы одновременно, как Spring обрабатывает порядок между ними?
Как вы думаете, название очень знакомо, да название подражает второму брату"@Король Тишины 2" статьяПозор, есть так много поз для сращивания строк Java.
2. Анализ позы
Во-первых, давайте рассмотрим инициализацию/уничтожение SpringBean
Несколько способов, а именно:
-
init-method/destroy-method
-
InitializingBean/DisposableBean
-
@PostConstruct/@PreDestroy
-
ContextStartedEvent/ContextClosedEvent
PS: На самом деле есть еще один способ - наследовать Spring
Lifecycle
интерфейс. Однако этот метод более сложен и здесь рассматриваться не будет.
2.1, метод инициализации/метод уничтожения
Таким образом, методы инициализации/уничтожения указываются в файле конфигурации. Конфигурация XML выглядит следующим образом
<bean id="demoService" class="com.dubbo.example.provider.DemoServiceImpl" destroy-method="close" init-method="initMethod"/>
Или вы можете настроить его с помощью аннотаций:
@Configurable
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "destroy")
public HelloService hello() {
return new HelloService();
}
}
Я до сих пор помню, когда впервые начал изучать среду Spring, вот как я ее использовал.
2.2, InitializingBean/DisposableBean
Этот метод должен наследовать интерфейс Spring.InitializingBean/DisposableBean
,вInitializingBean
используется для инициализации действия, аDisposableBean
Используется для действий по очистке перед уничтожением. Он используется следующим образом:
@Service
public class HelloService implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("hello destroy...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("hello init....");
}
}
2.3. @PostConstruct/@PreDestroy
По сравнению с двумя вышеупомянутыми методами этот метод является самым простым в использовании, и вам нужно использовать только аннотации для соответствующих методов. Он используется следующим образом:
@Service
public class HelloService {
@PostConstruct
public void init() {
System.out.println("hello @PostConstruct");
}
@PreDestroy
public void PreDestroy() {
System.out.println("hello @PreDestroy");
}
}
Я наступил на яму здесь.Если вы используете версию после JDK9,
@PostConstruct/@PreDestroy
Необходимо импортировать отдельно с помощью mavenjavax.annotation-api
, иначе аннотация не вступит в силу.
2.4, ContextStartedEvent/ContextClosedEvent
Этот метод использует механизм событий Spring, который относительно редко используется в повседневной разработке бизнеса и часто интегрируется с инфраструктурой. Будет отправлен после начала весныContextStartedEvent
событие, которое будет отправлено перед закрытиемContextClosedEvent
событие. Нам нужно унаследовать веснуApplicationListener
для наблюдения за двумя вышеуказанными событиями.
@Service
public class HelloListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if(event instanceof ContextClosedEvent){
System.out.println("hello ContextClosedEvent");
}else if(event instanceof ContextStartedEvent){
System.out.println("hello ContextStartedEvent");
}
}
}
также можно использовать@EventListener
Аннотация используется следующим образом:
public class HelloListenerV2 {
@EventListener(value = {ContextClosedEvent.class, ContextStartedEvent.class})
public void receiveEvents(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
System.out.println("hello ContextClosedEvent");
} else if (event instanceof ContextStartedEvent) {
System.out.println("hello ContextStartedEvent");
}
}
}
пс: только звонить
ApplicationContext#start
будет отправленContextStartedEvent
. Если вы не хотите быть таким хлопотным, вы можете контролироватьContextRefreshedEvent
событие вместо этого. Как только контейнер Spring инициализирован, он отправляетсяContextRefreshedEvent
.
3. Комплексное использование
После рассмотрения вышеуказанных методов здесь мы всесторонне используем четыре вышеупомянутых метода, чтобы увидеть внутренний порядок обработки Spring. Перед просмотром результатов читатели и взрослые могут угадать порядок выполнения этих методов.
public class HelloService implements InitializingBean, DisposableBean {
@PostConstruct
public void init() {
System.out.println("hello @PostConstruct");
}
@PreDestroy
public void PreDestroy() {
System.out.println("hello @PreDestroy");
}
@Override
public void destroy() throws Exception {
System.out.println("bye DisposableBean...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("hello InitializingBean....");
}
public void xmlinit(){
System.out.println("hello xml-init...");
}
public void xmlDestory(){
System.out.println("bye xmlDestory...");
}
@EventListener(value = {ContextClosedEvent.class, ContextStartedEvent.class})
public void receiveEvents(ApplicationEvent event) {
if (event instanceof ContextClosedEvent) {
System.out.println("bye ContextClosedEvent");
} else if (event instanceof ContextStartedEvent) {
System.out.println("hello ContextStartedEvent");
}
}
}
Конфигурация xml выглядит следующим образом:
<context:annotation-config />
<context:component-scan base-package="com.dubbo.example.demo"/>
<bean class="com.dubbo.example.demo.HelloService" init-method="xmlinit" destroy-method="xmlDestory"/>
Способ запуска приложения следующий:
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/dubbo-provider.xml");
context.start();
context.close();
Вывод программы следующий:
Наконец, графическая иллюстрация используется для обобщения вышеприведенных результатов:
Четыре, анализ исходного кода
Не знаю, угадали ли читатели порядок выполнения этих методов, давайте проанализируем порядок внутренней обработки в Spring с точки зрения исходного кода.
4.1 Процесс инициализации
использоватьClassPathXmlApplicationContext
Запустите контейнер Spring, который вызоветrefresh
метод инициализации контейнера. Процесс инициализации создастBean
. Наконец, когда все будет готово, отправлюContextRefreshedEvent
. Когда контейнер инициализирован, вызовитеcontext.start()
просто отправьContextStartedEvent
событие.
refresh
Исходный код метода выглядит следующим образом:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//... 忽略无关代码
// 初始化所有非延迟初始化的 Bean
finishBeanFactoryInitialization(beanFactory);
// 发送 ContextRefreshedEvent
finishRefresh();
//... 忽略无关代码
}
}
Следуйте до концаfinishBeanFactoryInitialization
исходный код доAbstractAutowireCapableBeanFactory#initializeBean
, исходный код выглядит следующим образом:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 调用 BeanPostProcessor#postProcessBeforeInitialization 方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
// 初始化 Bean
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
}
BeanPostProcessor
Будет действовать как перехватчик, как только bean-компонент будет подходящим, будет выполнена некоторая обработка. здесь с@PostConstruct
аннотированныйBean
будетCommonAnnotationBeanPostProcessor
Перехват класса, который будет запущен внутри@PostConstruct
способ маркировки.
Затем выполнитеinvokeInitMethods
, Методы, как показано ниже:
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
// 省略无关代码
// 如果是 Bean 继承 InitializingBean,将会执行 afterPropertiesSet 方法
((InitializingBean) bean).afterPropertiesSet();
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 执行 XML 定义 init-method
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
еслиBean
наследоватьInitializingBean
интерфейс, который будет выполнятьafterPropertiesSet
метод, и если он указан в XMLinit-method
, также сработает.
Приведенный выше исходный код фактически вращается вокругBean
процесс создания, когда всеBean
После завершения создания позвонитеcontext#start
пошлетContextStartedEvent
. Исходный код здесь относительно прост:
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
4.2. Процесс уничтожения
перечислитьClassPathXmlApplicationContext#close
метод закроет контейнер, конкретная логика будет вdoClose
метод выполняется.
doClose
Этот метод сначала отправляетContextClosedEvent
, а затем начать уничтожатьBean
.
Душевная пытка: если мы поменяем порядок двух предыдущих, будет ли результат таким же?
doClose
Исходный код выглядит следующим образом:
protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
// 省略无关代码
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// 销毁 Bean
destroyBeans();
// 省略无关代码
}
}
destroyBeans
в конечном итоге выполнитDisposableBeanAdapter#destroy
,@PreDestroy
,DisposableBean
,destroy-method
Методы, определенные этими тремя, будут выполняться внутри.
сначала выполнитьDestructionAwareBeanPostProcessor#postProcessBeforeDestruction
, этот метод аналогичен предыдущемуBeanPostProcessor
.
@PreDestroy
Аннотации будутCommonAnnotationBeanPostProcessor
Intercept, этот класс также наследуетDestructionAwareBeanPostProcessor
.
Наконец, еслиBean
заDisposableBean
Подкласс , будет выполнятьdestroy
метод, если он определен в xmldestroy-method
метод, который также будет выполнен.
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
// 省略无关代码
// 如果 Bean 继承 DisposableBean,执行 destroy 方法
((DisposableBean) bean).destroy();
}
if (this.destroyMethod != null) {
// 执行 xml 指定的 destroy-method 方法
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToCall = determineDestroyMethod();
if (methodToCall != null) {
invokeCustomDestroyMethod(methodToCall);
}
}
}
V. Резюме
init-method/destroy-method
Этот метод требует использования файлов конфигурации XML или отдельных классов конфигурации аннотаций, что является относительно громоздким. иInitializingBean/DisposableBean
Этот метод требует отдельного наследования интерфейса Spring для реализации связанных методов.@PostConstruct/@PreDestroy
Этот метод аннотации прост в использовании, код понятен, рекомендуется использовать этот метод.
Кроме тогоContextStartedEvent/ContextClosedEvent
Этот метод больше подходит для использования в некоторых средах интеграции, таких как корректное завершение работы Dubbo 2.6.X для изменения механизма.
Шесть, весенняя рекомендация по исторической статье
1. Псевдонимы атрибутов аннотаций и переопределения программирования аннотаций Spring
2. AnnotationMetadata программирования аннотаций Spring
3, аннотация режима программирования аннотаций Spring
4. Происхождение Dubbo, рассказ о механизме расширения Spring XML Schema
Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: программа для общения, ежедневный толчок галантерейных товаров. Если вам интересен мой рекомендуемый контент, вы также можете подписаться на мой блог:studyidea.cn