写在最前
从十一到现在两周,IOC部分源码读了一半多,是力气活,也是个精细活。全部写出来心有余力不足,所以找些重点写写。
简述 bean 实例化之前
Spring中不是启动容器时就开启bean的实例化进程,它首先会对资源进行读取并对bean进行初始化。
Spring将资源的定义和资源的加载区分开,Resource
定义了统一的资源,默认实现是AbstractResource
,资源的加载由ResourceLoader
接口的不同实现类返回Resource。紧接着,解析Resource
资源,将用户定义的Bean装载成BeanDefinition
,每一个Bean对象都对应着一个BeanDefinition
。BeanDefinition
存在于IOC内部容器中的HashMap结构。再紧跟着,向IOC容器注册解析好的BeanDefinition
,这个过程是通过BeanDefinitionRegistry接口来实现的。
初始化之后,会进行真正bean的加载,因为BeanDefinition
不是想要的bean。初始化默认为懒加载,第一次调用getBean()
时进行初始化。
首先得到可使用正确的beanName,这是涉及到别名或者factoryBean 。bean的作用域有singleton,prototype 和其他,Spring先尝试从缓存中加载单例bean,否则开始创建bean的实例。创建bean的实例可以理解为将BeanDefinition
转换为BeanWrapper
,BeanWarpper提供了get、set方法。之后还有一系列处理:MergedBeanDefinitionPostProcessor 属性合并,单例模式的循环依赖处理,属性填充,初始化bean。
在初始化bean这个过程中的代码,就包含了对XXXAware接口的处理,后置处理器,以及自定义的init-method方法。
Bean生命周期
回到最开始,看一下spring bean 的生命周期:
-
实例化Bean
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。
对于ApplicationContext容器,当容器启动结束后,便实例化所有的bean。
容器通过获取BeanDefination对象中的信息进行实例化。并且这一步仅仅是简单的实例化,并未进行依赖注入。
实例化对象被包装在BeanWrapper对象中,BeanWrapper提供了设置对象属性的接口,从而避免了使用反射机制设置属性。
在实例化bean的过程中,Spring采用策略模式决定采用反射还是 CGLIB 动态字节码。Spring 默认采用 CglibSubclassingInstantiationStrategy实例化bean,他既可以以反射实例化对象,还可以通过 CGLIB 的动态字节码的方式,以方法(setXxx)的方式注入对象实例化。
-
设置对象属性(依赖注入)
实例化后的对象被封装在BeanWrapper对象中,并且此时对象仍然是一个原生的状态,并没有进行依赖注入。
紧接着,Spring根据BeanDefinition中的信息进行依赖注入。
并且通过BeanWrapper提供的设置属性的接口完成依赖注入。
-
注入Aware接口
紧接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。
private void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
-
BeanPostProcessor
当经过上述几个步骤后,bean对象已经被正确构造,但如果你想要对象被使用前再进行一次自定义的处理,就可以通过BeanPostProcessor接口实现。
该接口提供了两个函数:
-
postProcessBeforInitialzation(Object bean, String beanName)
当前正在初始化的bean对象会被传递进来,我们就可以对这个bean做任何处理。
这个函数会先于InitialzationBean执行,因此称为前置处理。
所有Aware接口的注入就是在这一步完成的。
-
postProcessAfterInitialzation(Object bean, String beanName)
当前正在初始化的bean对象会被传递进来,我们就可以对这个bean做任何处理。
这个函数会在initialzationBean完成后执行,因此成为后置处理
public interface BeanPostProcessor { @Nullable default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Nullable default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } }
-
-
InitializingBean 与 init-method
当BeanPostProcessor的前置处理完成后就会进入本阶段。
InitialzingBean接口只有一个函数:
- afterPropertiesSet()
这一阶段也可以在bean正式构造完成前增加我们自定义的逻辑,但它与前置处理不同,由于该函数并不会把当前bean传递进来,因此这一步没办法处理对象本身,只能增加一些额外的逻辑。若要使用它,我们需要让bean实现该接口,并把要增加的逻辑写在该函数中。然后Spring会在前置处理完成后检测当前bean是否实现了该接口,并执行afterPropertiesSet函数。
当然,Spring为了降低对客户代码的侵入性,给bean的配置提供了init-method属性,该属性指定了在这一阶段需要执行的函数名。Spring便会在初始化阶段执行我们设置的函数。init-method本质上仍然使用了InitializingBean的接口。
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
-
DisposableBean 和 destroy-method
和init-method一样,通过给destroy-method指定函数,就可以在bean销毁前执行指定的逻辑。
实例代码
先来一段代码,看看Spring中Bean的生命周期
public class LifeCycleBean implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, InitializingBean,DisposableBean {
private String test;
public LifeCycleBean() {
System.out.println("构造函数调用...");
}
public String getTest() {
return test;
}
public void display(){
System.out.println("方法调用...");
}
public void setTest(String test) {
System.out.println("属性注入....");
this.test = test;
}
@Override
public void setBeanName(String name) {
System.out.println("BeanNameAware 被调用...");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("BeanClassLoaderAware 被调用...");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryAware 被调用...");
}
public void initMethod(){
System.out.println("init-method 被调用...");
}
public void destroyMethod(){
System.out.println("destroy-method 被调用...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean afterPropertiesSet 被调动...");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean destroy 被调动...");
}
}
<bean id="lifeCycleBean" class="com.example.bean.LifeCycleBean"
init-method="initMethod" destroy-method="destroyMethod">
<property name="test" value="test"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
LifeCycleBean lifeCycleBean = (LifeCycleBean) context.getBean("lifeCycleBean");
lifeCycleBean.display();
context.destroy();
}
运行结果如下:
构造函数调用...
属性注入....
BeanNameAware 被调用...
BeanClassLoaderAware 被调用...
BeanFactoryAware 被调用...
InitializingBean afterPropertiesSet 被调动...
init-method 被调用...
方法调用...
DisposableBean destroy 被调动...
destroy-method 被调用...