转载

Spring Bean生命周期和作用域以及实现方式

applicationContext.xml 中配置完 bean 之后, Bean 的声明周期状态有哪些。生命周期的各个阶段可以做什么。在 applicationContext.xml 配置 bean 的作用域有哪些。其中各个作用域代表的是什么。适用于什么情况。这篇文章做一个记录。

生命周期

初始化

可以直接查看图片,图片来自 Spring Bean Life Cycle

Spring Bean生命周期和作用域以及实现方式

从上图看出, Bean 初始化完成包括9个步骤。其中一些步骤包括接口的实现,其中包括 BeanNameAware 接口, BeanFactoryAware 接口。 ApplicationContextAware 接口。 BeanPostProcessor 接口, InitializingBean 接口。那么这些接口在整个生命周期阶段都起到什么作用?后面我们一一介绍。

实例化前

Bean 全部属性设置完毕后,往往需要执行一些特定的行为, Spring 提供了两种方式来实现此功能:

  • 使用 init-mothod 方法
  • 实现 initializingBean 接口

指定初始化方法

如下:

package com.model;

public classInitBean{
	public static final String NAME = "mark";
	public static final int AGE = 20;
	
	publicInitBean(){
		// TODO Auto-generated constructor stub
		System.out.println("执行构造方法");
	}
	
	public String name;
	public int age ;
	publicStringgetName(){
		return name;
	}
	publicvoidsetName(String name){
		this.name = name;
	}
	publicintgetAge(){
		return age;
	}
	publicvoidsetAge(intage){
		this.age = age;
	}
	
	publicvoidinit(){
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
}

编写加载器

package com.model;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.service.UserServiceImpl;

public classMain{

	publicstaticvoidmain(String[] args){		
		ApplicationContext context = new ClassPathXmlApplicationContext("initbean.xml");
		InitBean bean = (InitBean) context.getBean("init");
	}

}

配置 Bean

注意 init-method 参数

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <beanid="init"class="com.model.InitBean"init-method="init"/>
</beans>

执行结果

Spring Bean生命周期和作用域以及实现方式

实现 InitializingBean 接口

实现 InitializingBean 接口会实现 afterPropertiesSet 方法,这个方法会自动调用。但是这个方式是侵入性的。一般情况下,不建议使用。

实现 afterPropertiesSet 方法

package com.model;

import org.springframework.beans.factory.InitializingBean;

public classInitBeanimplementsInitializingBean{
	public static final String NAME = "mark";
	public static final int AGE = 20;
	
	publicInitBean(){
		// TODO Auto-generated constructor stub
		System.out.println("执行构造方法");
	}
	
	public String name;
	public int age ;
	publicStringgetName(){
		return name;
	}
	publicvoidsetName(String name){
		this.name = name;
	}
	publicintgetAge(){
		return age;
	}
	publicvoidsetAge(intage){
		this.age = age;
	}
	
	publicvoidinit(){
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
	@Override
	publicvoidafterPropertiesSet()throwsException{
		// TODO Auto-generated method stub
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
}

配置 xml

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!-- <bean id="init" class="com.model.InitBean" init-method="init"/> -->
      <beanid="init"class="com.model.InitBean"init-method="init"/>
</beans>

结果:

Spring Bean生命周期和作用域以及实现方式

销毁

Spring Bean生命周期和作用域以及实现方式

同样,上图中表示来 Bean 销毁时候的过程。包括 DisposableBean 接口。

使用 destroy-method 方法

package com.model;

import org.springframework.beans.factory.InitializingBean;

public classInitBeanimplementsInitializingBean{
	public static final String NAME = "mark";
	public static final int AGE = 20;
	
	publicInitBean(){
		// TODO Auto-generated constructor stub
		System.out.println("执行构造方法");
	}
	
	public String name;
	public int age ;
	publicStringgetName(){
		return name;
	}
	publicvoidsetName(String name){
		this.name = name;
	}
	publicintgetAge(){
		return age;
	}
	publicvoidsetAge(intage){
		this.age = age;
	}
	
	publicvoidinit(){
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
	@Override
	publicvoidafterPropertiesSet()throwsException{
		// TODO Auto-generated method stub
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
	
	publicvoidclose(){
		System.out.println("bean被销毁");
	}
}

配置 Bean

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!-- <bean id="init" class="com.model.InitBean" init-method="init"/> -->
      <beanid="init"class="com.model.InitBean"destroy-method="close"/>
</beans>

配置加载器

package com.model;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.service.UserServiceImpl;

public classMain{

	publicstaticvoidmain(String[] args){		
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("initbean.xml");
		context.registerShutdownHook();
		InitBean bean = (InitBean) context.getBean("init");
	}

}

结果:

Spring Bean生命周期和作用域以及实现方式

实现 DisposableBean 接口

实现 DisposableBean 接口

package com.model;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public classInitBeanimplementsInitializingBean,DisposableBean{
	public static final String NAME = "mark";
	public static final int AGE = 20;
	
	publicInitBean(){
		// TODO Auto-generated constructor stub
		System.out.println("执行构造方法");
	}
	
	public String name;
	public int age ;
	publicStringgetName(){
		return name;
	}
	publicvoidsetName(String name){
		this.name = name;
	}
	publicintgetAge(){
		return age;
	}
	publicvoidsetAge(intage){
		this.age = age;
	}
	
	publicvoidinit(){
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
	@Override
	publicvoidafterPropertiesSet()throwsException{
		// TODO Auto-generated method stub
		System.out.println("调用init方法进行成员变量的初始化");
		this.name = NAME;
		this.age = AGE;
		System.out.println("初始化完成");
	}
	
	publicvoidclose(){
		System.out.println("bean被销毁");
	}
	@Override
	publicvoiddestroy()throwsException{
		// TODO Auto-generated method stub
		System.out.println("bean被销毁完成");
	}
}

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <!-- <bean id="init" class="com.model.InitBean" init-method="init"/> -->
     <!-- <bean id="init" class="com.model.InitBean" destroy-method="close"/> -->
      <beanid="init"class="com.model.InitBean"/>
</beans>

Spring Bean生命周期和作用域以及实现方式

Spring Bean 的作用域

作用域 描述
singleton 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。
prototype 该作用域将单一 bean 的定义限制在任意数量的对象实例。
request 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。
session 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。
global-session 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。

配置示例

<bean id="..." class="..." scope="singleton">
</bean>

使用方法注入协调作用域不同的 Bean

正常情况下,如果singleton作用域依赖singleton作用域。即每次获取到的都是一样的对象。同理,prototype作用域依赖prototype作用域,每次获取到的都是新的对象。但是,如果singleton依赖prototype作用域,那么每次获取到的singleton中的prototype都是第一次创建的prototype。如何协调这种关系。保证每次获取到的都是正确的呢。

对于这种情况, Spring 提供了 lookup 方法用来解决这种问题。

首先我们定义一个原型:

package com.model;

public classMyHelper{

	publicvoiddoSomethingHelpful(){
		
	}
}

然后通过接口注入:

package com.model;

public interfaceDemoBean{

	MyHelpergetHelper();
	voidsomePeration();
}

配置一个单例:

package com.model;

/**
 * 测试类
 * @author kevin
 *
 */
public abstract classAbstractLookupDemoimplementsDemoBean{
	
	publicabstractMyHelpergetMyHelper();
	
	@Override
	publicMyHelpergetHelper(){
		// TODO Auto-generated method stub
		return getMyHelper();
	}
	
	@Override
	publicvoidsomePeration(){
		// TODO Auto-generated method stub
		
	}

}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <beanid="helper"class="com.model.MyHelper"scope="prototype"/>
      <beanid="standardLookupBean"class="com.model.StandardLookupDemo">
      		<propertyname="myHelper"ref="helper"></property>
      </bean>
      <beanid="abstractLookupBean"class="com.model.AbstractLookupDemo">
      		<lookup-methodname="getMyHelper"bean="helper"></lookup-method>
      </bean>
</beans>

加载配置文件:

package com.model;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StopWatch;

public classMain{

	publicstaticvoidmain(String[] args){
			
		AbstractApplicationContext context = new ClassPathXmlApplicationContext("lookBean.xml");
		context.registerShutdownHook();
		System.out.println("传递standardLookupBean");
		test(context, "standardLookupBean");
		System.out.println("传递AbstractLookupDemo");
		test(context, "abstractLookupBean");
	}
	
	publicstaticvoidtest(AbstractApplicationContext context,String beanName){
		DemoBean bean = (DemoBean) context.getBean(beanName);
		MyHelper helper1 = bean.getHelper();
		MyHelper helper2 = bean.getHelper();
		System.out.println("测试"+beanName);
		System.out.println("两个helper是否相同?"+(helper1==helper2));
		StopWatch stopWatch = new StopWatch();
		stopWatch.start("lookupDemo");
		for (int i = 0; i < 10000; i++) {
			MyHelper helper = bean.getHelper();
			helper.doSomethingHelpful();
		}
		stopWatch.stop();
		System.out.println("获取10000次花费了"+stopWatch.getTotalTimeMillis()+"毫秒");
	}

}

结果:

Spring Bean生命周期和作用域以及实现方式

从上面的结果图看出,以前的方式生成的对象每次都是相同的。通过lookup方式注入每次是不同的。可以解决这种问题。但是有没有更简单的方式,感觉这种方式优点麻烦。

Bean 感知 Spring 容器

实现 BeanNameAware ,自定设置 id 值。

实现 BeanFactoryAware , ApplicationContextAware 感知 Spring 容器。获取 Spring 容器。

Spring 国际化支持

配置配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beansxmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      <beanid="messageSource"class="org.springframework.context.support.ResourceBundleMessageSource">
      		<propertyname="basenames">
      			<list>
      				<value>message</value>
      			</list>
      		</property>
      </bean>
</beans>

新建中文配置文件

message_zh_CN.properties :

hello=welcome,{0}
now=now is : {0}

新建英文配置文件

message_en_US.properties :

hello=/u4F60/u597D,{0}
now=/u73B0/u5728/u7684/u65F6/u95F4/u662F : {0}

加载配置文件

package com.model;

import java.util.Date;
import java.util.Locale;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.util.StopWatch;

public classMain{

	publicstaticvoidmain(String[] args){
		ApplicationContext context = new ClassPathXmlApplicationContext("globalization.xml");
		String[] a = {"读者"};
		String hello = context.getMessage("hello",a, Locale.CHINA);
		Object[] b = {new Date()};
		String now = context.getMessage("now",b, Locale.CHINA);
		System.out.println(hello);
		System.out.println(now);
		hello = context.getMessage("hello",a, Locale.US);
		now = context.getMessage("now",b, Locale.US);
		System.out.println(hello);
		System.out.println(now);
		}

}
原文  https://blog.jiangtao.tech/2017/01/14/Spring-Bean%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%92%8C%E4%BD%9C%E7%94%A8%E5%9F%9F%E4%BB%A5%E5%8F%8A%E5%AE%9E%E7%8E%B0%E6%96%B9%E5%BC%8F/
正文到此结束
Loading...