一、bean生命周期
先来回顾一下spring最基本的应用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<bean name="source" class="org.example.spring.pojo.Source">
<property name="drink" value="拿铁"/>
<property name="sugar" value="加糖"/>
<property name="size" value="超大杯"/>
</bean>
</beans>
public class Source {
private String drink;
private String sugar;
private String size;
public String getDrink() {
return drink;
}
public void setDrink(String drink) {
this.drink = drink;
}
public String getSugar() {
return sugar;
}
public void setSugar(String sugar) {
this.sugar = sugar;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
}
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
Source source = (Source) context.getBean("source");
System.out.println(source.getDrink());
System.out.println(source.getSize());
System.out.println(source.getSugar());
}
}
运行结果
上面就是spring的一个最基本的应用。我们仅定义了source
,就可以直接在测试类TestSpring中获取到source实例及其对应的方法调用。很明显spring帮我们完成了source
类的实例化及初始化。
我们知道spring的定位可以作为容器来理解,容器里面的内容就是一个个的bean
,每一个bean要想被正常使用,必须完成实例化、初始化步骤。
spring对bean的实例化内容相对较少(反射),这里主要来看初始化都经历了哪些步骤:
在完成了实例化操作后,此时对象的属性值还都是默认值,
因此初始化的第一个操作就是
给对象的属性赋值:populateBean
在文档开篇的xml文件里面,bean标签里面可以定义init-method,这个就是初始化方法。
<bean name="source" class="org.example.spring.pojo.Source" init-method="method">
在完成属性赋值后就开始执行该标签定义的方法
执行初始化方法:invokeInitMethods
此时容器里面就有了可以完整调用的bean供我们使用了
context.getBean(“source”);
看起来很简单,就是两点内容
- populateBean
- invokeInitMethods
思考一个问题,我们定义的对象除了可以定义类似sugar size这样的基本属性外,如果我们在对象中定义BeanFactory这样的属性
public class Source {
private String drink;
private String sugar;
private String size;
private BeanFactory beanFactory;
}
那beanFactory该如何赋值,我们知道对象属性的赋值可以通过set方法赋值,如果我们给beanFactory添加对应的setBeanFactory方法是否可以完成beanFactory的赋值操作?
aware该出场了,spring通过标记接口完成类似BeanFactory属性的赋值
public interface Aware {
}
看下实现类
子类比较多,找到我们看似熟悉的BeanFactoryAware
public interface BeanFactoryAware extends Aware {
/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after the population of normal bean properties
* but before an initialization callback such as
* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
* @param beanFactory owning BeanFactory (never {@code null}).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}
没错,该接口就提供了一个set方法,如果要完成BeanFactory属性的赋值,实现此接口即可,类似的接口还有很多,例如
ApplicationContextAware
、ImportAware
、ResourceLoaderAware
、EnvironmentAware
等。
所以在完成populateBean后,spring会继续对容器对象属性赋值
调用aware接口的方法:
invokeAwareMethods
至此,属性赋值的操作已全部完成。
接下来,正常情况是直接调用初始化方法,但是spring为我们提供了bean的拓展功能,也可以理解为钩子函数,在初始化方法被调用前后,会有BeanPostProcessor的方法被执行
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
before、after中间的内容就是invokeInitMethods
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
在真正执行invokeInitMethods
时会先去判断bean是否实现了InitializingBean接口,如果是,则会首先调用InitializingBean#afterPropertiesSet
。至此,spring的主要初始化工作结束。总结如下:
二、循环依赖
什么是循环依赖?出现在哪个阶段?
spring如何解决循环依赖?
通过添加缓存是方式解决循环依赖
具体细节:
未添加缓存的时候,初始化对象的过程是这样的
添加了缓存之后模拟函数调用栈如下: