温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Spring Bean的生命周期怎么配置

发布时间:2021-12-23 11:30:04 来源:亿速云 阅读:229 作者:iii 栏目:大数据

本篇内容介绍了“Spring Bean的生命周期怎么配置”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

Bean的作用域

五种作用域中,request、session和global session三种作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

  • 当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。

  • 当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

<bean id="account" class="com.spring.master.Account" scope="prototype"/>  
 //或者
<bean id="account" class="com.spring.master.Account" singleton="false"/>
  • 当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id="loginAction" class=cn.spring.master.LoginAction" scope="request"/>

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

  • 当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id="userPreferences" class="com.spring.master.UserPreferences" scope="session"/>

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

  • 当一个bean的作用域为Global Session,表示在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

<bean id="user" class="com.spring.master.Preferences "scope="globalSession"/>

global session作用域类似于标准的HTTP Session作用域,不过仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。

Spring 生命周期

Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。

在传统的 Java 应用中,bean 的生命周期很简单,使用 Java 关键字 new 进行Bean 的实例化,然后该 Bean 就能够使用了。一旦 bean 不再被使用,则由 Java 自动进行垃圾回收。相比之下,Spring 管理 Bean 的生命周期就复杂多了,正确理解 Bean 的生命周期非常重要,因为 Spring 对 Bean 的管理可扩展性非常强,下面展示了一个 Bean 的构造过程。

Spring Bean的生命周期怎么配置

  1. Spring对bean进行实例化;

  2. Spring将值和bean的引用注入到bean对应的属性中;

  3. 如果bean实现了BeanNameAware接口,Spring将bean的ID传递给 setBean-Name()方法;

  4. 如果bean实现了BeanFactoryAware接口,Spring将调 用setBeanFactory()方法,将BeanFactory容器实例传入;

  5. 如果bean实现了ApplicationContextAware接口,Spring将调 用setApplicationContext()方法,将bean所在的应用上下文的 引用传入进来;

  6. 如果bean实现了BeanPostProcessor接口,Spring将调用它们 的post-ProcessBeforeInitialization()方法;

  7. 如果bean实现了InitializingBean接口,Spring将调用它们的 after-PropertiesSet()方法。类似地,如果bean使用init- method声明了初始化方法,该方法也会被调用;

  8. 如果bean实现了BeanPostProcessor接口,Spring将调用它们 的post-ProcessAfterInitialization()方法;

  9. 此时,bean已经准备就绪,可以被应用程序使用了,它们将一直 驻留在应用上下文中,直到该应用上下文被销毁;

  10. 如果bean实现了DisposableBean接口,Spring将调用它的 destroy()接口方法。同样,如果bean使用destroy-method声明 了销毁方法,该方法也会被调用。

验证 Spring Bean 周期

1. singleton

package com.spring.master.spring.bean.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * @author Huan Lee
 * @version 1.0
 * @date 2020-09-23 11:02
 * @describtion 业精于勤,荒于嬉;行成于思,毁于随。
 */
public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean {

    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 所在的应用上下文的"
                + "引用传入进来");
    }

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

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

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

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

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

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

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize 方法");
    }
}
package com.spring.master.spring.bean.lifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

/**
 * @author Huan Lee
 * @version 1.0
 * @date 2020-09-23 11:31
 * @describtion 后置处理器
 */
public class PersonBeanPostProcessor implements BeanPostProcessor {


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

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

后置处理器是什么?

一个类,实现了接口 BeanPostProcessor 定义的两个方法,这两个方法分别是:postProcessBeforeInitialization 和 postProcessAfterInitialization,顾名思义,就是分别在 bean 的 init-method 前后进行分别执行这两个方法。多后置处理器的有序性的 bean 在使用的过程中可以经过多个后置预处理的处理,但是,一般情况下,多个实现后置处理器接口的类是有先后顺序的,为了让 IOC 明白后置处理器之间的先后顺序,类还要实现 Ordered 接口,通过接口里的 order 属性来控制后处理器的先后顺序,默认为 0,为最高优先级。同一个容器中的后置处理器是通用的一个 context 中的后置处理器实现类不是针对某一个的 bean,这个 context 中的所有 bean 的产生过程都回去调用这个后置处理器,为了有针对性,可以通过 bean 的 id 来执行特异话的操作。 

resource 文件夹下新建一个 bean_lifecycle.xml 文件注入相关 bean ,代码如下:

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


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

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

    <!--引入自定义的BeanPostProcessor-->
    <bean class="com.spring.master.spring.bean.lifecycle.PersonBeanPostProcessor"/>

</beans>
package com.spring.master;

import com.spring.master.spring.bean.lifecycle.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

@SpringBootApplication
public class SpringMasterApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringMasterApplication.class, args);

		// 为面试而准备的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();

	}

}

输出结果:
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 = HLee
12、@PreDestory 调用自定义销毁方法
13、Person 实现了 DisposableBean 接口,Spring 调用它的destroy() 接口方法。同样,如果 person 使用 destroy-method 声明了销毁方法,该方法也会被调用
14、xml 中声明的 destroy-method 方法
end---------------destroy-----------------
finalize 方法

当 person 默认是单例模式时,bean 的生命周期与容器的生命周期一样,容器初始化,bean 也初始化。容器销毁,bean 也被销毁。

2. protoType

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">


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

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

    <!--引入自定义的BeanPostProcessor-->
    <bean class="com.spring.master.spring.bean.lifecycle.PersonBeanPostProcessor"/>

</beans>

输出:
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 = HLee
finalize 方法

此时,容器关闭,person 对象并没有销毁。原因在于,单实例模式下,bean 的生命周期由容器管理,容器生,bean 生;容器死,bean 死。而在多实例模式下,Spring 就管不了那么多了,bean 的生命周期,交由客户端也就是程序员或者 JVM 来进行管理。

备注:修改对应的xml即可

“Spring Bean的生命周期怎么配置”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI