《Spring揭秘》——IOC梳理2(容器启动,bean生命周期)

IoC容器背后的秘密

主要分为两个阶段:容器启动阶段、Bean实例化阶段。

容器启动阶段:

容器需要依赖某些工具类(BeanDefinitionReader)对加载的Configuration MetaData( 通常也就是XML格式的配置信息)。进行解析和分析,并将分析后的信息编组为相应的BeanDefinition,最后把这些保存了bean定义必要信息的BeanDefinition,注册到相应的BeanDefinitionRegistry,这样容器启动工作就完成了。 

Bean实例化阶段:

该阶段,容器会首先检查所请求的对象之前是否已经初始化。如果没有,则会根据注册的BeanDefinition所提供的信息实例化被请求对象,并为其注入依赖。

 

Spring容器内部工作机制:

1.ResourceLoader从存储介质中加载Spring配置信息,并使用Resource表示这个配置文件的资源;

2.BeanDefinitionReader读取Resource所指向的配置文件资源,将每一个<bean>解析成一个BeanDefinition对象,并保存到BeanDefinitionRegistry中;

3.容器扫描BeanDefinitionRegistry中的BeanDefinition,识别实现BeanFactoryPostProcessor接口(Bean工厂后处理器)的Bean,调用这些Bean对BeanDefinition进行加工处理;

4.从BeanDefinitionRegistry中取出加工后的BeanDefinition,并调用InstantiationStrategy进行Bean实例化的工作;

5.容器使用BeanWrapper对Bean进行封装,完成Bean属性的设置工作;

6.利用容器中注册的Bean后置处理器(实现BeanPostProcessor接口的Bean)对已经完成属性加工的Bean进行后续加工,装配出一个准备就绪的Bean。

 

插手“容器的启动”

Spring提供了BeanFactoryPostProcessor的容器扩展机制,允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改。相当于在容器实现的第一阶段最后加入一道工序,对最终的BeanDefinition做一些额外的操作,比如修改其中bean定义的某些属性,为bean定义增加其他信息等。一般情况下我们不会直接写该接口的实现类,而是使用Spring提供的相关类:PropertyPlaceholderConfigurer 、PropertyOverrideConfigurer、CustomEditorConfigurer。

1. PropertyPlaceholderConfigurer

PropertyPlaceholderConfigurer允许我们在XML配置文件中使用占位符(PlaceHolder),并将这些占位符所代表的资源单独配置到简单的properties文件中来加载。

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations"> 
    <list>
      <value>conf/jdbc.properties</value>
      <value>conf/mail.properties</value>
    </list> 
  </property>
</bean>

 或者<context:property-placeholder location="classpath:db.properties"/>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="user" value="${jdbc.user}"></property>
  ...		    
</bean>
2. PropertyOverrideConfigurer

通过PropertyOverrideConfigurer对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。详情见P69。

配置在properties文件中的信息通常都以明文表示,PropertyOverrideConfigurer的父类PropertyResourceConfigurer 提供了一个protected类型的方法convertPropertyValue,允许子类覆盖这个方法对相应的配置项进行转换,如对加密后的字符串解密之后再覆盖到相应的bean定义中。当然,既然PropertyPlaceholderConfigurer也同样继承了PropertyResourceConfigurer,我们也可以针对PropertyPlaceholderConfigurer应用类似的功能。

(还是偏向于使用PropertyPlaceholderConfigurer,对配置文件中如用户名及密码加密了,想要解密即可使用PropertyPlaceholderConfigurer。

public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {	
	private String[] encryptPropNames ={"jdbc.user","jdbc.password"};
	
	@Override
	protected String convertProperty(String propertyName, String propertyValue) {		
		if(isEncryptProp(propertyName)){
		  String decryptValue = DESUtils.getDecryptString(propertyValue);
		  return decryptValue;
		}else{
		  return propertyValue;
		}
	}
	
	/**
	 * 判断是否是加密的属性
	 */
	private boolean isEncryptProp(String propertyName){
		for(String encryptPropName:encryptPropNames){
			if(encryptPropName.equals(propertyName)){
			  return true;
			}
		}
		return false;
	}
}

在applicationContext.xml中需要进行如下配置:

<bean class="com.echo.utils.EncryptPropertyPlaceholderConfigurer" p:location="classpath:db.properties" p:fileEncoding="utf-8"></bean> 

3.CustomEditorConfigurer

上面两个都是通过对BeanDefinition中的数据进行变更以达到某中目的,CustomEditorConfigurer只是辅助性地将后期会用到的信息注册到容器,对BeanDefinition没有做任何变动。实际应用时,主要是将XML中String字符串转化为对象类型。具体参照P72。下面的是Spring3.x中的实例:

public class Car {
	private int maxSpeed;
	private double price;
	private String brand;
	//getter和setter
}

public class Boss {
	private String name;
	private Car car;
        //getter和setter
}

public class CustomCarEditor extends PropertyEditorSupport{

	@Override
	public void setAsText(String text) {
		if(text == null || text.indexOf(",") == -1){
			throw new IllegalArgumentException("设置的字符串格式不正确");
		}
		String[] infos = text.split(",");
		Car car = new Car();
		car.setBrand(infos[0]);
		car.setMaxSpeed(Integer.parseInt(infos[1]));
		car.setPrice(Double.parseDouble(infos[2]));
		setValue(car);
	}
}

配置文件:

    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="property.Car" value="property.CustomCarEditor"></entry>            
            </map>
        </property>
    </bean> 
    
    <bean id="boss" class="property.Boss">
        <property name="name" value="John"></property>
        <property name="car" value="BWM,200,500000"></property>
    </bean>

Spring 2.0之后,比较提倡使用propertyEditorRegistrars属性来指定自定义的PropertyEditor。(P72)  

public class CarPropertyEditorRegistrar implements PropertyEditorRegistrar{

	private PropertyEditor propertyEditor;
	
	@Override
	public void registerCustomEditors(PropertyEditorRegistry registry) {
		registry.registerCustomEditor(Car.class, getPropertyEditor());
	}

	public PropertyEditor getPropertyEditor() {
		return propertyEditor;
	}

	public void setPropertyEditor(PropertyEditor propertyEditor) {
		this.propertyEditor = propertyEditor;
	}
}

配置文件:

  <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="propertyEditorRegistrars">
            <list>
                <ref bean="carPropertyEditorRegistrar" /> 
            </list>
        </property>
    </bean> 
    
    <bean id="carPropertyEditorRegistrar" class="property.CarPropertyEditorRegistrar">
        <property name="propertyEditor" ref="customCarEditor" />
    </bean>
    
    <bean id="customCarEditor" class="property.CustomCarEditor"></bean>
     
    <bean id="boss" class="property.Boss">
        <property name="name" value="John"></property>
        <property name="car" value="BWM,200,500000"></property>
    </bean>

Bean生命周期

1.bean的实例化与BeanWrapper

初始化bean实例的两种方式:通过反射或者CGLIB动态字节码生成来初始化相应的bean实例或者动态生成其子类。

  • 实例化策略的抽象接口:org.springframework.beans.factory.support.InstantiationStrategy
  • SimpleInstantiationStrategy通过反射来实例化对象实例,但不支持方法注入方式的对象实例化。 
  • CglibSubclassingInstantiationStrategy(默认)继承SimpleInstantiationStrategy的以反射方式实例化对象的功能,并且通过CGLIB的动态字节码生成功能,可动态生成某个类的子类,进而满足了方法注入所需的对象实例化需求。

注意:不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例。
BeanWrapper可以以统一的方式对对象属性进行访问,用以修改对象属性。

2.各类Aware接口

Spring容器会检查当前对象实例是否实现了一系列的以Aware命名结尾的接口定义,比如:如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法;如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。 

3. BeanPostProcessor

BeanPostProcessor前置处理将执行的方法:public Object postProcessAfterInitialization(Object bean, String beanName)
BeanPostProcessor后置处理将执行的方法:public Object postProcessBeforeInitialization(Object bean, String beanName)
两个方法都传入了对象实例的引用,可以对传入的对象实例进行任意操作。

public class MyBeanPostProcessor implements BeanPostProcessor {

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcess<After>Initialization:"+bean+","+beanName);
		return bean;
	}

	//注意:可以在这两个方法中修改返回的Bean,甚至可以返回一个新的bean
	
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("postProcess<Before>Initialization:"+bean+","+beanName);
		return bean;
	}
}

此外,要注意在XML文件中配置该Bean后置处理器。

补充:有一种特殊类型的BeanPostProcessor:InstantiationAwareBeanPostProcessor 。实例化bean对象步骤之前,容器会首先检查容器中是否注册有该接口。如果有,首先使用相应的InstantiationAwareBeanPostProcessor来构造对象实例。

4. InitializingBeaninit-method

检测当前对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。该接口在Spring容器内部广泛使用,实际业务中使用有限。

Spring还提供了另一种方式来指定自定义的对象初始化操作,那就是在XML配置的时候,使用<bean>init-method属性。 

5. DisposableBeandestroy-method

对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。

容器将检查singleton类型的bean实例,看其是否实现了DisposableBean接口。或者其对应的bean定义是否通过<bean>的destroy-method属性指定了自定义的对象销毁方法。

DisposableBean和destroy-method为对象提供了执行自定义销毁逻辑的机会 。


Bean生命周期个人总结:(可面试口述)

1.按照Bean的构造函数或工厂方法实例化Bean对象。(InstantiationAwareBeanPostProcessor较为特殊,可忽略。)

2.通过BeanWrapper设置对象属性

3.检查是否实现了Aware相关的接口。(如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法;如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。 等等...

4.如果Bean实现了BeanPostProcessor接口,则调用其前置处理方法(postProcessBeforeInitialization)。

5.若Bean实现了InitializingBean接口,调用其afterPropertiesSet()方法。或者执行<bean>init-method方法。

6.如果Bean实现了BeanPostProcessor接口,则调用其后置处理方法(postProcessAfterInitialization)。

7.对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。对于scope="singleton"的Bean,若实现了DisposableBean接口,则调用destory方法。或者执行<bean>destory-method方法。

 

Bean生命周期完整版:

1.若容器注册了InstantiationAwareBeanPostProcessor,将调用postProcessBeforeInstantiation()方法

2.按照Bean的构造函数或工厂方法实例化Bean

3.若容器注册了InstantiationAwareBeanPostProcessor,将调用postProcessAfterInstantiation()方法

4.调用InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法

5.调用Bean的属性设置方法设置属性值

6.如果Bean实现了BeanNameAware接口,则将该Bean对应的名称传给setBeanName方法

7.如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法

8.如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法

9.如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法

10.若在bean中有init-method属性,则执行这个初始化方法

11.如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用

12.对于scope="prototype"的Bean,调用者负责Bean后续生命的管理。对于scope="singleton"的Bean,Bean将放入IOC容器的缓存池中,将Bean引用返回给调用者,Spring负责后续生命的管理

13.对于scope="singleton"的Bean,若实现了DisposableBean接口,则调用destory方法

14.对于scope="singleton"的Bean,若在bean中有destory-method属性,则执行这个销毁方法

转载于:https://www.cnblogs.com/weilanzz/p/6777264.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值