当前位置 : 主页 > 编程语言 > java >

Spring Bean的生命周期

来源:互联网 收集:自由互联 发布时间:2023-02-04
前言 在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中

前言

在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。

 

Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢? 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。

bean的作用域

创建一个bean定义,其实质是用该bean定义对应的类来创建真正实例的“配方”。把bean定义看成一个配方很有意义,它与class很类似,只根据一张“处方”就可以创建多个实例。不仅可以控制注入到对象中的各种依赖和配置值,还可以控制该对象的作用域。这样可以灵活选择所建对象的作用域,而不必在Java Class级定义作用域。Spring Framework支持五种作用域,分别阐述如下表。

类别 说明 singleton 在Spring IoC容器中仅存在一个Bean实例,Bean以单例方式存在,默认值 prototype 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean() request 每次HTTP请求都会创建一个新的Bean,该该作用域仅适用于WebApplicationContext环境 session 同一个HTTP Session共享一个Bean,不同Session使用不同Bean,仅适用于WebApplicationContext环境 globalSession 一般用于Portlet应用环境,该作用域仅适用于WebApplicationContext环境

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

singleton

singleton唯一 bean 实例,当一个 bean 的作用域为 singleton,那么Spring IoC容器中只会存在一个共享的 bean 实例,并且所有对 bean 的请求,只要 id 与该 bean 定义相匹配,则只会返回bean的同一实例。 singleton 是单例类型(对应于单例模式),就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,但我们可以指定Bean节点的 lazy-init=”true” 来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。 每次获取到的对象都是同一个对象。注意,singleton 作用域是Spring中的缺省作用域。要在XML中将 bean 定义成 singleton ,可以这样配置:

<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" scope="singleton">

也可以通过 @Scope 注解(它可以显示指定bean的作用范围。)的方式

@Service @Scope("singleton") public class ServiceImpl{ }

prototype

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

<bean id="account" class="com.foo.DefaultAccount" scope="prototype"/> 或者 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>

通过 @Scope 注解的方式实现就不做演示了。

request

request每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效,request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 在 XML 中将 bean 定义成 request ,可以这样配置:

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

session

session每一次HTTP请求都会产生一个新的 bean,该bean仅在当前 HTTP session 内有效,session只适用于Web程序,session 作用域表示该针对每一次 HTTP 请求都会产生一个新的 bean,同时该 bean 仅在当前 HTTP session 内有效.与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的 HTTP session 中根据 userPreferences 创建的实例,将不会看到这些特定于某个 HTTP session 的状态变化。当HTTP session最终被废弃的时候,在该HTTP session作用域内的bean也会被废弃掉。

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

globalSession

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

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

Spring Bean的生命周期

Spring容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。

 

而对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期。每次客户端请求 prototype 作用域的 Bean 时,Spring 容器都会创建一个新的实例,并且不会管那些被配置成 prototype 作用域的 Bean 的生命周期。

 

了解 Spring 生命周期的意义就在于,可以利用 Bean 在其存活期间的指定时刻完成一些相关操作。这种时刻可能有很多,但一般情况下,会在 Bean 被初始化后和被销毁前执行一些相关操作。

 

在 Spring 中,Bean 的生命周期是一个很复杂的执行过程,我们可以利用 Spring 提供的方法定制 Bean 的创建过程。

 

当一个 Bean 被加载到 Spring 容器时,它就具有了生命,而 Spring 容器在保证一个 Bean 能够使用之前,会进行很多工作。Spring 容器中 Bean 的生命周期流程如图 1 所示。

Spring Bean的生命周期

Spring Bean的生命周期

Bean 生命周期的整个执行过程描述如下:

  • 1、根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。

  • 2、利用依赖注入完成 Bean 中所有属性值的配置注入。

  • 3、如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。

  • 4、如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

  • 5、如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

  • 6、如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

  • 7、如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。

  • 8、如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

  • 9、如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

  • 10、如果在 <bean> 中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用范围为 scope="prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该 Bean。

  • 11、如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

Spring 为 Bean 提供了细致全面的生命周期过程,通过实现特定的接口或 <bean> 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以随意配置 <bean> 的属性,但是建议不要过多地使用 Bean 实现接口,因为这样会导致代码和 Spring 的聚合过于紧密。

BeanPostProcessor

该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下:

public interface BeanPostProcessor { /** * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; * if {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet */ Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; /** * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean * initialization callbacks (like InitializingBean's {@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. * The returned bean instance may be a wrapper around the original. * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean * instance and the objects created by the FactoryBean (as of Spring 2.0). The * post-processor can decide whether to apply to either the FactoryBean or created * objects or both through corresponding {@code bean instanceof FactoryBean} checks. * <p>This callback will also be invoked after a short-circuiting triggered by a * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method, * in contrast to all other BeanPostProcessor callbacks. * @param bean the new bean instance * @param beanName the name of the bean * @return the bean instance to use, either the original or a wrapped one; * if {@code null}, no subsequent BeanPostProcessors will be invoked * @throws org.springframework.beans.BeansException in case of errors * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet * @see org.springframework.beans.factory.FactoryBean */ Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; } 方法 说明 postProcessBeforeInitialization 实例化、依赖注入完毕,在调用显示的初始化之前完成<br>一些定制的初始化任务 postProcessAfterInitialization 实例化、依赖注入、初始化完毕时执行

一、自定义后置处理器演示

1、自定义处理器

package com.dpb.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; /** * 自定义BeanPostProcessor实现类 * BeanPostProcessor接口的作用是: * 我们可以通过该接口中的方法在bean实例化、配置以及其他初始化方法前后添加一些我们自己的逻辑 * @author dengp * */ public class MyBeanPostProcessor implements BeanPostProcessor{ /** * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("before--实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } /** * 实例化、依赖注入、初始化完毕时执行 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("after...实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } }

注意:接口中两个方法不能返回null,如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象,因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中。

2、Pojo类

public class User { private int id; private String name; private String beanName; public User(){ System.out.println("User 被实例化"); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { System.out.println("设置:"+name); this.name = name; } public String getBeanName() { return beanName; } public void setBeanName(String beanName) { this.beanName = beanName; } /** * 自定义的初始化方法 */ public void start(){ System.out.println("User 中自定义的初始化方法"); } }

3、配置文件注册

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.dpb.pojo.User" id="user" init-method="start"> <property name="name" value="波波烤鸭" /> </bean> <!-- 注册处理器 --> <bean class="com.dpb.processor.MyBeanPostProcessor"></bean> </beans>

4、测试

@Test public void test() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); User user = ac.getBean(User.class); System.out.println(user); }

输出结果:

User 被实例化 设置:波波烤鸭 before--实例化的bean对象:com.dpb.pojo.User@65e2dbf3 user User 中自定义的初始化方法 after...实例化的bean对象:com.dpb.pojo.User@65e2dbf3 user com.dpb.pojo.User@65e2dbf3

通过输出语句我们也能看到postProcessBeforeInitialization方法的输出语句是在Bean实例化及属性注入后执行的,且在自定义的初始化方法之前执行(通过init-method指定)。而postProcessAfterInitialization方法是在自定义初始化方法执行之后执行的。

 

注意:

  • BeanFactory和ApplicationContext两个容器对待bean的后置处理器稍微有些不同。ApplicationContext容器会自动检测Spring配置文件中那些bean所对应的Java类实现了BeanPostProcessor接口,并自动把它们注册为后置处理器。在创建bean过程中调用它们,所以部署一个后置处理器跟普通的bean没有什么太大区别。
  • BeanFactory容器注册bean后置处理器时必须通过代码显示的注册,在IoC容器继承体系中的ConfigurableBeanFactory接口中定义了注册方法
/** * Add a new BeanPostProcessor that will get applied to beans created * by this factory. To be invoked during factory configuration. * <p>Note: Post-processors submitted here will be applied in the order of * registration; any ordering semantics expressed through implementing the * {@link org.springframework.core.Ordered} interface will be ignored. Note * that autodetected post-processors (e.g. as beans in an ApplicationContext) * will always be applied after programmatically registered ones. * @param beanPostProcessor the post-processor to register */ void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

测试代码如下

@Test public void test2() { //ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); // 显示添加后置处理器 bf.addBeanPostProcessor(bf.getBean(MyBeanPostProcessor.class)); User user = bf.getBean(User.class); System.out.println(user); }

二、多个后置处理器

我们可以在Spring配置文件中添加多个BeanPostProcessor(后置处理器)接口实现类,在默认情况下Spring容器会根据后置处理器的定义顺序来依次调用。

public class MyBeanPostProcessor implements BeanPostProcessor{ /** * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("A before--实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } /** * 实例化、依赖注入、初始化完毕时执行 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("A after...实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } } public class MyBeanPostProcessor2 implements BeanPostProcessor{ /** * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("B before--实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } /** * 实例化、依赖注入、初始化完毕时执行 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("B after...实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return 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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="com.dpb.pojo.User" id="user" init-method="start"> <property name="name" value="波波烤鸭" /> </bean> <!-- 注册处理器 --> <bean class="com.dpb.processor.MyBeanPostProcessor"/> <bean class="com.dpb.processor.MyBeanPostProcessor2"/> </beans>

测试结果:

User 被实例化 设置:波波烤鸭 A before--实例化的bean对象:com.dpb.pojo.User@7fac631b user B before--实例化的bean对象:com.dpb.pojo.User@7fac631b user User 中自定义的初始化方法 A after...实例化的bean对象:com.dpb.pojo.User@7fac631b user B after...实例化的bean对象:com.dpb.pojo.User@7fac631b user com.dpb.pojo.User@7fac631b

三、显示指定顺序

在Spring机制中可以指定后置处理器调用顺序,通过让BeanPostProcessor接口实现类实现Ordered接口getOrder方法,该方法返回一整数,默认值为 0,优先级最高,值越大优先级越低

public class MyBeanPostProcessor implements BeanPostProcessor,Ordered{ /** * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("A before--实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } /** * 实例化、依赖注入、初始化完毕时执行 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("A after...实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } @Override public int getOrder() { // TODO Auto-generated method stub return 10; } } public class MyBeanPostProcessor2 implements BeanPostProcessor,Ordered{ /** * 实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("B before--实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } /** * 实例化、依赖注入、初始化完毕时执行 * 注意:方法返回值不能为null * 如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 * 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("B after...实例化的bean对象:"+bean+"\t"+beanName); // 可以根据beanName不同执行不同的处理操作 return bean; } @Override public int getOrder() { // TODO Auto-generated method stub return 2; } }

测试输出结果:

User 被实例化 设置:波波烤鸭 B before--实例化的bean对象:com.dpb.pojo.User@7fac631b user A before--实例化的bean对象:com.dpb.pojo.User@7fac631b user User 中自定义的初始化方法 B after...实例化的bean对象:com.dpb.pojo.User@7fac631b user A after...实例化的bean对象:com.dpb.pojo.User@7fac631b user com.dpb.pojo.User@7fac631b

数值越大的优先级越低,所以A的输出就在后面了。

 

参考: http://c.biancheng.net/view/4261.html

https://www.cnblogs.com/dengpengbo/p/10464892.html

https://www.cnblogs.com/yoci/p/10642553.html

https://my.oschina.net/u/3905482/blog/1859227

上一篇:Spring使用AspectJ开发AOP
下一篇:没有了
网友评论