Весенний обзор (3) | Жизненный цикл фасоли

Spring

bean-lifecycle

Продолжая обзор Spring, сегодня я взглянул на жизненный цикл Spring Bean.

1. Типичный жизненный цикл Spring

В традиционном Java-приложении жизненный цикл bean-компонента очень прост: используйте ключевое слово Java new для создания экземпляра bean-компонента, после чего можно использовать bean-компонент. Как только bean-компонент больше не используется, не так просто быть автоматически собранным мусором с помощью Java.

Напротив, Spring управляет жизненным циклом Bean-компонентов гораздо сложнее. Очень важно правильно понимать жизненный цикл Bean-компонентов, поскольку Spring управляет Bean-компонентами очень масштабируемо. Процесс построения Bean показан ниже.

Spring Bean 生命周期

Вышеприведенная картинка из книги "Весна в действии (четвертое издание)". На картинке описывается жизненный цикл классического Spring Bean. Объяснение в книге следующее:

1. Spring создает экземпляры bean-компонентов; 2. Spring вводит значения и ссылки на bean-компоненты в свойства, соответствующие bean-компоненту; 3. Если компонент реализует интерфейс BeanNameAware, Spring передает идентификатор компонента в метод setBean-Name(); 4. Если компонент реализует интерфейс BeanFactoryAware, Spring вызовет Используйте метод setBeanFactory() для передачи экземпляра контейнера BeanFactory; 5. Если компонент реализует интерфейс ApplicationContextAware, Spring вызовет Используйте метод setApplicationContext(), чтобы установить контекст приложения, в котором находится компонент. передается ссылка; 6. Если bean-компоненты реализуют интерфейс BeanPostProcessor, Spring вызовет их Метод post-ProcessBeforeInitialization(); 7. Если bean-компоненты реализуют интерфейс InitializingBean, Spring вызовет их после методаPropertiesSet(). Точно так же, если компонент использует init- method объявляет метод инициализации, который также будет вызываться; 8. Если bean-компоненты реализуют интерфейс BeanPostProcessor, Spring вызовет их Метод post-ProcessAfterInitialization(); 9. На этом этапе bean-компоненты готовы к использованию приложением, и они всегда будут Нахождение в контексте приложения до тех пор, пока контекст приложения не будет уничтожен; 10. Если компонент реализует интерфейс DisposableBean, Spring вызовет его метод интерфейса destroy(). Аналогично, если bean-компонент объявлен с помощью метода destroy Чтобы уничтожить метод, этот метод также будет вызван.

2. Проверьте цикл Spring Bean

После написания кода для проверки приведенного выше утверждения сначала создайте класс Person, который и является Bean, который мы хотим проверить.Для удобства тестирования он реализует BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean. код показывает, как показано ниже:

package com.nasus.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;

/**
 * Project Name:review_spring <br/>
 * Package Name:PACKAGE_NAME <br/>
 * Date:2019/9/1 16:29 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
@Scope("ProtoType")
public class Person implements BeanNameAware, BeanFactoryAware,
        ApplicationContextAware, InitializingBean, DisposableBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(Person.class);

    private String name;

    public Person(){
        System.out.println("1、开始实例化 person ");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("2、设置 name 属性");
    }

    @Override
    public void setBeanName(String beanId) {
        System.out.println("3、Person 实现了 BeanNameAware 接口,Spring 将 Person 的 "
                + "ID=" + beanId + "传递给 setBeanName 方法");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("4、Person 实现了 BeanFactoryAware 接口,Spring 调"
                + "用 setBeanFactory()方法,将 BeanFactory 容器实例传入");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("5、Person 实现了 ApplicationContextAware 接口,Spring 调"
                + "用 setApplicationContext()方法,将 person 所在的应用上下文的"
                + "引用传入进来");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("8、Person 实现了 InitializingBean 接口,Spring 调用它的"
                + "afterPropertiesSet()方法。类似地,如果 person 使用 init-"
                + "method 声明了初始化方法,该方法也会被调用");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("13、Person 实现了 DisposableBean 接口,Spring 调用它的"
                + "destroy() 接口方法。同样,如果 person 使用 destroy-method 声明"
                + "了销毁方法,该方法也会被调用");
    }

    /**
     * xml 中声明的 init-method 方法
     */
    public void initMethod(){
        System.out.println("9、xml 中声明的 init-method 方法");
    }

    /**
     * xml 中声明的 destroy-method 方法
     */
    public void destroyMethod(){
        System.out.println("14、xml 中声明的 destroy-method 方法");
        System.out.println("end---------------destroy-----------------");
    }

    // 自定义初始化方法
    @PostConstruct
    public void springPostConstruct(){
        System.out.println("7、@PostConstruct 调用自定义的初始化方法");
    }

    // 自定义销毁方法
    @PreDestroy
    public void springPreDestory(){
        System.out.println("12、@PreDestory 调用自定义销毁方法");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize 方法");
    }
}

Кроме того, создается класс MyBeanPostProcessor, наследуемый от BeanPostProcessor. Этот класс заботится только о том, что делать до и после инициализации Person. Например, перед инициализацией загрузите другие компоненты. код показывает, как показано ниже:

package com.nasus.lifecycle;

import com.nasus.bean.Person;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * Project Name:review_spring <br/>
 * Package Name:PACKAGE_NAME <br/>
 * Date:2019/9/1 16:25 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class MyBeanPostProcessor implements BeanPostProcessor {

    // 容器加载的时候会加载一些其他的 bean,会调用初始化前和初始化后方法
    // 这次只关注 Person 的生命周期
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Person){
            System.out.println("6、初始化 Person 之前执行的方法");
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Person){
            System.out.println("10、初始化 Person 完成之后执行的方法");
        }
        return bean;
    }

}

Создайте новый файл bean_lifecycle.xml в папке ресурсов для внедрения связанных компонентов, код выглядит следующим образом:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描bean -->
    <context:component-scan base-package="com.nasus"/>

    <!-- 实现了用户自定义初始化和销毁方法 -->
    <bean id="person" class="com.nasus.bean.Person" init-method="initMethod" destroy-method="destroyMethod">
        <!-- 注入bean 属性名称 -->
        <property name="name" value="nasus" />
    </bean>

    <!--引入自定义的BeanPostProcessor-->
    <bean class="com.nasus.lifecycle.MyBeanPostProcessor"/>

</beans>

Протестируйте класс, получите bean-компонент person и используйте его, код выглядит следующим образом:

import com.nasus.bean.Person;
import java.awt.print.Book;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Project Name:review_spring <br/>
 * Package Name:PACKAGE_NAME <br/>
 * Date:2019/9/1 16:38 <br/>
 *
 * @author <a href="turodog@foxmail.com">chenzy</a><br/>
 */
public class lifeCycleTest {

    @Test
    public void testLifeCycle(){
        // 为面试而准备的Bean生命周期加载过程
        ApplicationContext context = new ClassPathXmlApplicationContext("bean_lifecycle.xml");
        Person person = (Person)context.getBean("person");
        // 使用属性
        System.out.println("11、实例化完成使用属性:Person name = " + person.getName());
        // 关闭容器
        ((ClassPathXmlApplicationContext) context).close();
    }

}

Наконец, метод lifeCycleTest закрывает контейнер, и вывод журнала консоли выглядит следующим образом:

1、开始实例化 person 
2、设置 name 属性
3、Person 实现了 BeanNameAware 接口,Spring 将 Person 的 ID=person传递给 setBeanName 方法
4、Person 实现了 BeanFactoryAware 接口,Spring 调用 setBeanFactory()方法,将 BeanFactory 容器实例传入
5、Person 实现了 ApplicationContextAware 接口,Spring 调用 setApplicationContext()方法,将 person 所在的应用上下文的引用传入进来
6、初始化 Person 之前执行的方法
7、@PostConstruct 调用自定义的初始化方法
8、Person 实现了 InitializingBean 接口,Spring 调用它的afterPropertiesSet()方法。类似地,如果 person 使用 init-method 声明了初始化方法,该方法也会被调用
9、xml 中声明的 init-method 方法
10、初始化 Person 完成之后执行的方法
11、实例化完成使用属性:Person name = nasus
12、@PreDestory 调用自定义销毁方法
13、Person 实现了 DisposableBean 接口,Spring 调用它的destroy() 接口方法。同样,如果 person 使用 destroy-method 声明了销毁方法,该方法也会被调用
14、xml 中声明的 destroy-method 方法
end---------------destroy-----------------

Из приведенных выше логов видно, что когда человек по умолчанию находится в режиме singleton, жизненный цикл бина совпадает с жизненным циклом контейнера, контейнер инициализируется, и бин тоже инициализируется. Контейнер уничтожается, и бин тоже уничтожается. Что, если компонент не является синглтоном?

3. После того, как создание экземпляра Bean завершено, сделайте что-нибудь перед уничтожением Иногда нам нужно что-то сделать после того, как значение свойства bean-компонента установлено и до того, как bean-компонент будет уничтожен, например, проверить, правильно ли установлено свойство в bean-компоненте. Spring Framework предоставляет множество методов, которые позволяют выполнять инициализацию и методы предварительного уничтожения в жизненном цикле Spring Bean. Я протестировал эти методы выше. Приведенный выше код реализует множество методов. Он повторяется. Во время разработки вы можете выбрать один из следующих, например:

  • методы init-method и destroy-method, указанные в файле конфигурации
  • Реализовать интерфейсы InitializingBean и DisposableBean.
  • Используйте аннотации @PostConstruct и @PreDestroy (рекомендуется для трещин в стене)

4. Жизненный цикл компонента в многоэкземплярном режиме Человек в приведенном выше тесте по умолчанию является синглтоном.Теперь мы меняем человека в режим protoType, модифицируем bean_lifecycle.xml следующим образом и оставляем остальные классы без изменений:

<!-- 实现了用户自定义初始化和销毁方法 -->
<bean id="person" scope="prototype" class="com.nasus.bean.Person" init-method="initMethod" destroy-method="destroyMethod">
     <!-- 注入bean 属性名称 -->
     <property name="name" value="nasus" />
</bean>

Вывод журнала на данный момент выглядит следующим образом:

1、开始实例化 person 
2、设置 name 属性
3、Person 实现了 BeanNameAware 接口,Spring 将 Person 的 ID=person传递给 setBeanName 方法
4、Person 实现了 BeanFactoryAware 接口,Spring 调用 setBeanFactory()方法,将 BeanFactory 容器实例传入
5、Person 实现了 ApplicationContextAware 接口,Spring 调用 setApplicationContext()方法,将 person 所在的应用上下文的引用传入进来
6、初始化 Person 之前执行的方法
7、@PostConstruct 调用自定义的初始化方法
8、Person 实现了 InitializingBean 接口,Spring 调用它的afterPropertiesSet()方法。类似地,如果 person 使用 init-method 声明了初始化方法,该方法也会被调用
9、xml 中声明的 init-method 方法
10、初始化 Person 完成之后执行的方法
11、实例化完成使用属性:Person name = nasus

В этот момент контейнер закрывается, а объект-человек не уничтожается. Причина в том, что в режиме единственного экземпляра жизненный цикл бина управляется контейнером, рождается контейнер, рождается бин, контейнер мертв, бин мертв. В многоэкземплярном режиме Spring не может так много управлять Жизненным циклом bean-компонентов управляет клиент, то есть программист или JVM.

5. Время загрузки бина в многоэкземплярном режиме

Прежде всего, давайте поговорим об одном экземпляре, в режиме одиночного экземпляра,Компонент создается в тот момент, когда контейнер загружает его., доказательство выглядит следующим образом: я включаю режим отладки, нажимаю точку останова в строке 20, и журнал выглядит следующим образом, указывая на то, что экземпляр bean-компонента был создан при инициализации контейнера в строке 19.

单实例 bean 的加载时机

Кроме того, в многоэкземплярном режиме, в этом режиме бин инициализируется, когда бин нужно использовать.Доказательство следующее.После выполнения той же строки 19, в мультиэкземплярном режиме консоль пуста , указывая на то, что бин в это время не загружен.

多实例模式下 bean 加载时机

Когда отладка достигает строки 23,Бин загружается только тогда, когда Бин нужен, проверено следующим образом. В процессе разработки мы называем этот тип загрузки отложенной загрузкой, и его цель — уменьшить накладные расходы программы и загружать ее только тогда, когда это необходимо, а не загружать все сразу.

多实例模式下 bean 加载时机

6. Как bean-компонент с одним экземпляром реализует ленивую загрузку

Просто добавьте атрибут lazy-init в значение true в xml. Таким образом, его метод загрузки становится ленивой загрузкой.

<!-- 实现了用户自定义初始化和销毁方法 -->
<bean id="person" lazy-init="true" class="com.nasus.bean.Person" init-method="initMethod" destroy-method="destroyMethod">
     <!-- 注入bean 属性名称 -->
     <property name="name" value="nasus" />
</bean>

Если вы хотите применить ленивую инициализацию ко всем одноэлементным bean-компонентам по умолчанию, вы можете установить для свойства default-lazy-init значение true для корневых bean-компонентов следующим образом:

<beans default-lazy-init="true" …>

Рекомендуемое чтение: *1, *java | Что такое динамический прокси

*2, *Весенний обзор (1) | МОК

*3, *Весенний обзор (2) | AOP

*4, *SpringBoot | Принцип запуска

5, SpringBoot | Принцип автоматической настройки

*6, *Обзор Spring MVC | Принцип работы и детали конфигурации

一个优秀的废人