在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此以外,bean 就与应用程序中的其余对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将经过配置元数据来描述。java
Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢? 例如对于Web应用来讲,Web容器对于每一个用户请求都建立一个单独的Sevlet线程来处理请求,引入Spring框架以后,每一个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每一个 JVM 内只有一个实例。web
在大多数状况下。单例 bean 是很理想的方案。不过,有时候你可能会发现你所使用的类是易变的,它们会保持一些状态,所以重用是不安全的。在这种状况下,将 class 声明为单例的就不是那么明智了。由于对象会被污染,稍后重用的时候会出现意想不到的问题。因此 Spring 定义了多种做用域的bean。spring
建立一个bean定义,其实质是用该bean定义对应的类来建立真正实例的“配方”。把bean定义当作一个配方颇有意义,它与class很相似,只根据一张“处方”就能够建立多个实例。不只能够控制注入到对象中的各类依赖和配置值,还能够控制该对象的做用域。这样能够灵活选择所建对象的做用域,而没必要在Java Class级定义做用域。Spring Framework支持五种做用域,分别阐述以下表。安全
五种做用域中,request、session 和 global session 三种做用域仅在基于web的应用中使用(没必要关心你所采用的是什么web应用框架),只能用在基于 web 的 Spring ApplicationContext 环境。session
当一个 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的做用范围。)的方式app
@Service @Scope("singleton") public class ServiceImpl{ }
当一个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
注解的方式实现就不作演示了。maven
request只适用于Web程序,每一次 HTTP 请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效,当请求结束后,该对象的生命周期即告结束。 在 XML 中将 bean 定义成 request ,能够这样配置:ide
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
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"/>
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应用中最最重要的部分了。因此来看看Spring容器在初始化一个bean的时候会作那些事情,顺序是怎样的,在容器关闭的时候,又会作哪些事情。
spring版本:4.2.3.RELEASE 鉴于Spring源码是用gradle构建的,我也决定舍弃我大maven,尝试下洪菊推荐过的gradle。运行beanLifeCycle模块下的junit test便可在控制台看到以下输出,能够清楚了解Spring容器在建立,初始化和销毁Bean的时候依次作了那些事情。
Spring容器初始化 ===================================== 调用GiraffeService无参构造函数 GiraffeService中利用set方法设置属性值 调用setBeanName:: Bean Name defined in context=giraffeService 调用setBeanClassLoader,ClassLoader Name = sun.misc.Launcher$AppClassLoader 调用setBeanFactory,setBeanFactory:: giraffe bean singleton=true 调用setEnvironment 调用setResourceLoader:: Resource File Name=spring-beans.xml 调用setApplicationEventPublisher 调用setApplicationContext:: Bean Definition Names=[giraffeService, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#0, com.giraffe.spring.service.GiraffeServicePostProcessor#0] 执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=giraffeService 调用PostConstruct注解标注的方法 执行InitializingBean接口的afterPropertiesSet方法 执行配置的init-method 执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=giraffeService Spring容器初始化完毕 ===================================== 从容器中获取Bean giraffe Name=李光洙 ===================================== 调用preDestroy注解标注的方法 执行DisposableBean接口的destroy方法 执行配置的destroy-method Spring容器关闭
先来看看,Spring在Bean从建立到销毁的生命周期中可能作得事情。
有时咱们须要在Bean属性值set好以后和Bean销毁以前作一些事情,好比检查Bean中某个属性是否被正常的设置好值了。Spring框架提供了多种方法让咱们能够在Spring Bean的生命周期中执行initialization和pre-destroy方法。
1.实现InitializingBean和DisposableBean接口
这两个接口都只包含一个方法。经过实现InitializingBean接口的afterPropertiesSet()方法能够在Bean属性值设置好以后作一些操做,实现DisposableBean接口的destroy()方法能够在销毁Bean以前作一些操做。
例子以下:
public class GiraffeService implements InitializingBean,DisposableBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("执行InitializingBean接口的afterPropertiesSet方法"); } @Override public void destroy() throws Exception { System.out.println("执行DisposableBean接口的destroy方法"); } }
这种方法比较简单,可是不建议使用。由于这样会将Bean的实现和Spring框架耦合在一块儿。
2.在bean的配置文件中指定init-method和destroy-method方法
Spring容许咱们建立本身的 init 方法和 destroy 方法,只要在 Bean 的配置文件中指定 init-method 和 destroy-method 的值就能够在 Bean 初始化时和销毁以前执行一些操做。
例子以下:
public class GiraffeService { //经过<bean>的destroy-method属性指定的销毁方法 public void destroyMethod() throws Exception { System.out.println("执行配置的destroy-method"); } //经过<bean>的init-method属性指定的初始化方法 public void initMethod() throws Exception { System.out.println("执行配置的init-method"); } }
配置文件中的配置:
<bean name="giraffeService" class="com.giraffe.spring.service.GiraffeService" init-method="initMethod" destroy-method="destroyMethod"> </bean>
须要注意的是自定义的init-method和post-method方法能够抛异常可是不能有参数。
这种方式比较推荐,由于能够本身建立方法,无需将Bean的实现直接依赖于spring的框架。
3.使用@PostConstruct和@PreDestroy注解
除了xml配置的方式,Spring 也支持用 @PostConstruct
和 @PreDestroy
注解来指定 init
和 destroy
方法。这两个注解均在javax.annotation
包中。为了注解能够生效,须要在配置文件中定义org.springframework.context.annotation.CommonAnnotationBeanPostProcessor或context:annotation-config
例子以下:
public class GiraffeService { @PostConstruct public void initPostConstruct(){ System.out.println("执行PostConstruct注解标注的方法"); } @PreDestroy public void preDestroy(){ System.out.println("执行preDestroy注解标注的方法"); } }
配置文件:
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
有些时候咱们须要在 Bean 的初始化中使用 Spring 框架自身的一些对象来执行一些操做,好比获取 ServletContext 的一些参数,获取 ApplicaitionContext 中的 BeanDefinition 的名字,获取 Bean 在容器中的名字等等。为了让 Bean 能够获取到框架自身的一些对象,Spring 提供了一组名为*Aware的接口。
这些接口均继承于org.springframework.beans.factory.Aware
标记接口,并提供一个将由 Bean 实现的set*方法,Spring经过基于setter的依赖注入方式使相应的对象能够被Bean使用。 网上说,这些接口是利用观察者模式实现的,相似于servlet listeners,目前还不明白,不过这也不在本文的讨论范围内。 介绍一些重要的Aware接口:
public class GiraffeService implements ApplicationContextAware, ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{ @Override public void setBeanClassLoader(ClassLoader classLoader) { System.out.println("执行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName()); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("执行setBeanFactory,setBeanFactory:: giraffe bean singleton=" + beanFactory.isSingleton("giraffeService")); } @Override public void setBeanName(String s) { System.out.println("执行setBeanName:: Bean Name defined in context=" + s); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("执行setApplicationContext:: Bean Definition Names=" + Arrays.toString(applicationContext.getBeanDefinitionNames())); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { System.out.println("执行setApplicationEventPublisher"); } @Override public void setEnvironment(Environment environment) { System.out.println("执行setEnvironment"); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { Resource resource = resourceLoader.getResource("classpath:spring-beans.xml"); System.out.println("执行setResourceLoader:: Resource File Name=" + resource.getFilename()); } @Override public void setImportMetadata(AnnotationMetadata annotationMetadata) { System.out.println("执行setImportMetadata"); } }
上面的*Aware接口是针对某个实现这些接口的Bean定制初始化的过程, Spring一样能够针对容器中的全部Bean,或者某些Bean定制初始化过程,只需提供一个实现BeanPostProcessor接口的类便可。 该接口中包含两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法会在容器中的Bean初始化以前执行, postProcessAfterInitialization方法在容器中的Bean初始化以后执行。
例子以下:
public class CustomerBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("执行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("执行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName); return bean; } }
要将BeanPostProcessor的Bean像其余Bean同样定义在配置文件中
<bean class="com.giraffe.spring.service.CustomerBeanPostProcessor"/>
因此。。。结合第一节控制台输出的内容,Spring Bean的生命周期是这样纸的:
用图表示一下(图来源:http://www.jianshu.com/p/d00539babca5):
与之比较相似的中文版本:
其实不少时候咱们并不会真的去实现上面说描述的那些接口,那么下面咱们就除去那些接口,针对bean的单例和非单例来描述下bean的生命周期:
当scope=”singleton”,即默认状况下,会在启动容器时(即实例化容器时)时实例化。但咱们能够指定Bean节点的lazy-init=”true”来延迟初始化bean,这时候,只有在第一次获取bean时才会初始化bean,即第一次请求该bean时才初始化。以下配置:
<bean id="ServiceImpl" class="cn.csdn.service.ServiceImpl" lazy-init="true"/>
若是想对全部的默认单例bean都应用延迟初始化,能够在根节点beans设置default-lazy-init属性为true,以下所示:
<beans default-lazy-init="true" …>
默认状况下,Spring 在读取 xml 文件的时候,就会建立对象。在建立对象的时候先调用构造器,而后调用 init-method 属性值中所指定的方法。对象在被销毁的时候,会调用 destroy-method 属性值中所指定的方法(例如调用Container.destroy()方法的时候)。写一个测试类,代码以下:
public class LifeBean { private String name; public LifeBean(){ System.out.println("LifeBean()构造函数"); } public String getName() { return name; } public void setName(String name) { System.out.println("setName()"); this.name = name; } public void init(){ System.out.println("this is init of lifeBean"); } public void destory(){ System.out.println("this is destory of lifeBean " + this); } }
life.xml配置以下:
<bean id="life_singleton" class="com.bean.LifeBean" scope="singleton" init-method="init" destroy-method="destory" lazy-init="true"/>
测试代码:
public class LifeTest { @Test public void test() { AbstractApplicationContext container = new ClassPathXmlApplicationContext("life.xml"); LifeBean life1 = (LifeBean)container.getBean("life"); System.out.println(life1); container.close(); } }
运行结果:
LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@573f2bb1 …… this is destory of lifeBean com.bean.LifeBean@573f2bb1
当scope=”prototype”
时,容器也会延迟初始化 bean,Spring 读取xml 文件的时候,并不会马上建立对象,而是在第一次请求该 bean 时才初始化(如调用getBean方法时)。在第一次请求每个 prototype 的bean 时,Spring容器都会调用其构造器建立这个对象,而后调用init-method
属性值中所指定的方法。对象销毁的时候,Spring 容器不会帮咱们调用任何方法,由于是非单例,这个类型的对象有不少个,Spring容器一旦把这个对象交给你以后,就再也不管理这个对象了。
为了测试prototype bean的生命周期life.xml配置以下:
<bean id="life_prototype" class="com.bean.LifeBean" scope="prototype" init-method="init" destroy-method="destory"/>
测试程序:
public class LifeTest { @Test public void test() { AbstractApplicationContext container = new ClassPathXmlApplicationContext("life.xml"); LifeBean life1 = (LifeBean)container.getBean("life_singleton"); System.out.println(life1); LifeBean life3 = (LifeBean)container.getBean("life_prototype"); System.out.println(life3); container.close(); } }
运行结果:
LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@573f2bb1 LifeBean()构造函数 this is init of lifeBean com.bean.LifeBean@5ae9a829 …… this is destory of lifeBean com.bean.LifeBean@573f2bb1
能够发现,对于做用域为 prototype 的 bean ,其destroy
方法并无被调用。若是 bean 的 scope 设为prototype时,当容器关闭时,destroy
方法不会被调用。对于 prototype 做用域的 bean,有一点很是重要,那就是 Spring不能对一个 prototype bean 的整个生命周期负责:容器在初始化、配置、装饰或者是装配完一个prototype实例后,将它交给客户端,随后就对该prototype实例漠不关心了。 无论何种做用域,容器都会调用全部对象的初始化生命周期回调方法。但对prototype而言,任何配置好的析构生命周期回调方法都将不会被调用。清除prototype做用域的对象并释听任何prototype bean所持有的昂贵资源,都是客户端代码的职责(让Spring容器释放被prototype做用域bean占用资源的一种可行方式是,经过使用bean的后置处理器,该处理器持有要被清除的bean的引用)。谈及prototype做用域的bean时,在某些方面你能够将Spring容器的角色看做是Java new操做的替代者,任何迟于该时间点的生命周期事宜都得交由客户端来处理。
Spring 容器能够管理 singleton 做用域下 bean 的生命周期,在此做用域下,Spring 可以精确地知道bean什么时候被建立,什么时候初始化完成,以及什么时候被销毁。而对于 prototype 做用域的bean,Spring只负责建立,当容器建立了 bean 的实例后,bean 的实例就交给了客户端的代码管理,Spring容器将再也不跟踪其生命周期,而且不会管理那些被配置成prototype做用域的bean的生命周期。 QQ讨论群组:984370849 706564342 欢迎加入讨论
想要深刻学习的同窗们能够加入QQ群讨论,有全套资源分享,经验探讨,没错,咱们等着你,分享互相的故事!