第二部分:bean的加载
一 bean加载流程概览
调用XmlBeanFactory.getBean(String name)方法时,最终会进入到AbstractBeanFactory的doGetBean方法
1)转换对应beanName:
final String beanName = transformedBeanName(name);
因为传入的name可能是别名,也可能是FactoryBean,所以需要进行一系列解析
a.去除FactoryBean的修饰符
b.取指定alias所表示的最终beanName
2)尝试从缓存中加载单例
Object sharedInstance = getSingleton(beanName);
单例在Spring的同一个容器内只会被创建一次,后续再获取bean,就直接从单例缓存中获取。
尝试加载时,首先从缓存中加载,如果加载不成功则再次尝试从singletonFactories中加载。
在Spring中创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean则直接使用ObjectFactory
加载流程:
a.首先尝试从singletonObjects里面获取实例
b.再尝试从earlySingletonObjects里面获取
c.再尝试从singletonFactories里面获取beanName对应的ObjectFactory,然后调用这个ObjectFactory的getObject方法创建bean,并放到earlySingletonObjects里面,并且从singletonFactories里面remove掉这个ObjectFactory
存储bean的map说明:
a.singletonObjects:用于保存beanName和创建的bean实例之间的关系
b.singletonFactories:用于保存beanName和创建bean的工厂之间的关系
c.earlySingletonObjects:当一个单例bean被放到这里面后,当bean还在创建过程中时,就可以通过这个map获取到了,目的是用来检测循环引用
3)bean的实例化
Object bean = getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
将从缓存中得到的bean的原始状态实例化为最终的bean,其实就是用于当前bean是否是FactoryBean类型的bean。如果是,那么需要调用该bean对应的FactoryBean实例中的getObject()作为返回值。
另外,Spring在获取bean时,会尽可能保证所有bean初始化后都会调用注册的BeanPostProcessor的postProcessAfterInitialization方法进行处理。
4)对原型模式进行依赖检查
只有在单例模式下spring才会尝试解决循环依赖问题,如果是原型模式下,直接抛出异常
5)检查parentBeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
如果检测到当前加载的XML配置文件中不包含beanName所对应的配置,且parentBeanFactory不为空,则尝试去parentBeanFactory寻找
6)将储存XML配置文件的GernericBeanDefinition转换为RootBeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
因为所有Bean后续处理都是针对于RootBeanDefinition的,所以需要一个转换
7)寻找依赖
String[] dependsOn = mbd.getDependsOn();
在Spring的加载顺序中,在初始化某一个bean的时候,首先会初始化这个bean所对应的依赖
8)针对不同的scope进行bean的创建**
Object createBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
在Spring中存在着不同的scope(singleton/prototype/request等),Spring会根据不同的配置进行不同的初始化策略
a.根据设置的class属性或者根据className来解析Class
b.对 override属性进行标记及验证
c.应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作
<span style="font-family:SimSun;font-size:14px;"><span style="color:#3333FF;"><span style="color:#000000;">Object bean = resolveBeforeInstantiation(beanName, mbd);
if (bean != null) {
return bean;
}
}</span></span></span>
当经过前置处理后返回的结果如果不为空,那么会直接罗国后续的Bean的创建而直接返回结果,AOP功能就是基于这里的判断的。
在bean的实例化前会调用后处理器的applyBeanPostProcessorsBeforeInstantiation方法进行处理,可以给子类一个生成代理bean的机会
在bean实例化后会调用后处理器的applyBeanPostProcessorsAfterInitialization方法,用于尽可能保证将主持的后处理器的postProcessAfterInitialization方法应用到该bean中
d.创建bean
见下方
9)创建Bean
AbstractAutowireCapableBeanFactory.doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
a.如果是单例则需要首先清除缓存
BeanWrapper instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
b.实例化bean,将BeanDefinition转换为BeanWrapper
BeanWrapperinstanceWrapper = createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)
(使用工厂方法->使用带参构造函数->使用默认构造函数)
确认实例化bean所需的构造函数及相应参数->
使用构造函数进行bean的实例化时:首先判断 1 如果beanDefinition.getMethodOverrides()为空也就是用户没有使用replace或者lookup的配置方法,那么直接使用反射的方式,简单快捷;2 如果使用了这两个特性,因为需要将这两个配置提供的功能切入进去,所以就必须要使用动态代理的方式将包含两个特性所对应的逻辑的拦截增强器设置进去,这样才可以保证在调用方法的时候会被相应的拦截器增强,返回值为包含拦截器的代理实例.
c.MergedBeanDefinitionPostProcessors的应用,即Bean合并后的处理
d.依赖处理
单例涉及循环依赖时,通过放入缓存中的ObjectFactory来创建实例。
Spring处理循环依赖的解决办法:在B中创建依赖A时通过ObjectFactory提供的实例化方法来中断A中的属性填充,使B中持有的A仅仅是刚刚初始化并没有填充任何属性的A,而正在初始化A的步骤还是在最开始创建A的时候进行的;但是因为A与B中的A所表示的属性地址是一样的,所以在A中创建好的属性填充自然可以通过B中的A获取,这样就解决了循环依赖的问题
e.属性填充,将所有属性填充至bean实例中:populateBean(beanName, mbd, instanceWrapper)
e.1 根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中(实现根据名称自动匹配的第一步就是寻找bw中需要依赖注入的属性;同样对于根据类型自动匹配的实现来讲第一步也是寻找bw中需要依赖注入的属性,然后遍历这些属性并寻找类型匹配的bean)
e.2 应用InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法,在属性获取完毕填充前,对属性进行再e.3 次处理
将属性应用到bean中applyPropertyValues(beanName, mbd, bw, pvs);(属性填充中获取的属性是以PropertyValues形式存在的,还并没有应用到已经实例化的bean中,应用这一工作是在applyPropertyValues完成的)
f.调用初始化方法(比如init-method):
exposedObject = initializeBean(beanName, exposedObject, mbd)
f.1 激活Aware方法
f.2 处理器的应用
f.3 激活自定义的init方法
g.循环依赖检查
h.注册DisposableBean。如果配置了destroy-method,这里需要注册以便于在销毁时调用
registerDisposableBeanIfNecessary(beanName, bean, mbd);
h.完成创建并返回
补充 spring相关使用示例
5.1 FactoryBean的使用
一般情况下,Spring通过反射机制利用beand class属性指定实现类来实例化bean。在某些情况下,实例化类的过程比较复杂,这时采用编码方式配置bean会是一个简单的方案。
Spring提供了一个org.springframework.beans.factory.FactoryBean<T>的工厂类接口,用户可以通过实现该接口定制实例化bean的逻辑
Spring自身就提供了70多个FactoryBean 的实现,他们隐藏了实例化一些复杂bean的细节,给上层应用带来了便利
<span style="font-family:SimSun;font-size:12px;"><span style="font-family:SimSun;font-size:14px;">public interface FactoryBean<T> {
//返回由FactoryBean创建的bean实例
T getObject() throws Exception;
//返回FactoryBean创建的bean类型
Class<?> getObjectType();
//如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中
boolean isSingleton();
}</span></span>
示例代码
a.实际要生成的Car类
<span style="font-family:SimSun;font-size:12px;"><span style="font-family:SimSun;font-size:14px;">public class Car {
private int maxSpeed;
private String brand;
private double price;
}</span></span>
b.简化Car生成的FactoryBean类
<span style="font-family:SimSun;font-size:12px;"><span style="font-family:SimSun;font-size:14px;">public class CarFactoryBean implements FactoryBean<Car> {
private String carInfo;
@Override
public Car getObject() throws Exception {
Car car = new Car();
String[] infos = this.carInfo.split(",");
car.setBrand(infos[0]);
car.setMaxSpeed(Integer.valueOf(infos[1]));
car.setPrice(Double.valueOf(infos[2]));
return car;
}
@Override
public Class<?> getObjectType() {
return Car.class;
}
@Override
public boolean isSingleton() {
return false;
}
public String getCarInfo() {
return carInfo;
}
public void setCarInfo(String carInfo) {
this.carInfo = carInfo;
}
}</span></span>
c.Spring配置文件
<span style="font-family:SimSun;font-size:12px;"><span style="font-family:SimSun;font-size:14px;"> <bean id="car" class="com.zhangyiwen.website03.base_exercise.spring.factory_bean.CarFactoryBean">
<property name="carInfo"><value>超级跑车,400,2000000</value></property>
</bean></span></span>
d.测试代码
<span style="font-size:14px;"><span style="font-family:SimSun;font-size:14px;">Car car = (Car) bf.getBean("car");
System.out.println(car.toString());</span></span>
e.测试结果
<span style="font-family:SimSun;font-size:14px;"><span style="font-family:SimSun;font-size:14px;">Car [maxSpeed=400, brand=超级跑车, price=2000000.0]</span></span>
5.2 Aware的使用
Spring中提供一些Aware相关接口,比如BeanFactoryAware、ApplicationContextAware...实现这些Aware接口的bean在被初始化后,可以取得一些相对应的资源。
例如实现ApplicationContextAware的bean,在bean被初始后,将会被注入ApplicationContext的实例
例子:aware和event机制的结合使用
bean定义
//实现了Aware接口的bean
public class HelloBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
private String helloWord = "Hello!World";
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public void setHelloWord(String helloWord) {
this.helloWord = helloWord;
}
public String getHelloWord() {
applicationContext.publishEvent(new PropertyGettedEvent("{" + helloWord
+ "} is getted"));
return helloWord;
}
}
//自定义事件
public class PropertyGettedEvent extends ApplicationEvent {
private static final long serialVersionUID = 1L;
public PropertyGettedEvent(Object source) {
super(source);
}
}
//事件监听器
public class PropertyGettedListener implements
ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("~~~~~~~" + event.getSource().toString());
}
}
配置文件
<bean id="listener" class="com.zhangyiwen.website03.base_exercise.spring.aware.PropertyGettedListener"></bean>
<bean id="helloBean" class="com.zhangyiwen.website03.base_exercise.spring.aware.HelloBean">
<property name="helloWord">
<value>oracle</value>
</property>
</bean>
测试代码
HelloBean bean = ctx.getBean(HelloBean.class);
String helloWord = bean.getHelloWord();
System.out.println(helloWord);
输出结果
~~~~~~~{oracle} is getted
oracle
5.3 init-method和destroy-method的使用
在xml中配置 init-method和 destory-method方法,定义spring容器在初始化bean和容器销毁之前的所做的操作,基于xml的配置只是一种方式.
例子:
配置文件
<span style="font-family:SimSun;font-size:12px;"><bean id="personService" class="com.myapp.core.beanscope.PersonService" scope="singleton" init-method="init" destroy-method="cleanUp"> </bean></span>
bean
<span style="font-family:SimSun;font-size:12px;">package com.myapp.core.beanscope;
public class PersonService {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void init(){
System.out.println("init");
}
// how validate the destory method is a question
public void cleanUp(){
System.out.println("cleanUp");
}
}
</span>
测试类
<span style="font-family:SimSun;font-size:12px;">package com.myapp.core.beanscope;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainTest {
public static void main(String[] args) {
AbstractApplicationContext context =new ClassPathXmlApplicationContext("SpringBeans.xml");
PersonService person = (PersonService)context.getBean("personService");
person.setMessage("hello spring");
PersonService person_new = (PersonService)context.getBean("personService");
System.out.println(person.getMessage());
System.out.println(person_new.getMessage());
context.registerShutdownHook();
}
}
</span>
测试结果:
<span style="font-family:SimSun;font-size:12px;">init
hello spring
hello spring
cleanUp</span>
5.4 alias的使用
在对bean进行定义时,除了使用id属性来指定名称之外,为了提供多个名称,需要通过alias属性来加以指定。
而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易的对公共组件进行引用。
例子:
配置文件
<alias name="personService" alias="personService2"/>
测试代码
PersonService person = (PersonService) bf.getBean("personService2");
person.setMessage("hello spring");
PersonService person_new = (PersonService) bf.getBean("personService");
System.out.println(person.getMessage());
System.out.println(person_new.getMessage());
测试结果
init
init
hello spring
hello spring