BeanDefinition
BeanDefinition 用于保存 Bean 的相关信息,包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载、依赖的bean、创建该bean的工厂等,它是实例化 Bean 的原材料,Spring 就是根据 BeanDefinition 中的信息实例化 Bean。
FactoryBean
FactoryBean接口有getObject、getObjectType和isSingleton三个方法,实现了此接口的bean容器会注入getObject方法返回的对象。如果要拿FactoryBean对象,就在bean的name前加个"&"。
bean的创建过程
-
BeanDefinitionParser
通过解析加载xml配置文件、解析等方式读取BeanDefinition(BD),将它们注册到BD注册中心 - 执行
BeanFactoryPostProcessor
的postProcessBeanFactory
方法。该方法可以对BeanFactory做很多事,例如对BD注册中心里的BD进行增删改等。 - 加载BD里的
BeanPostProcessors
。 - 执行BeanPostProcessor(具体实现是
InstantiationAwareBeanPostProcessor. postProcessBeforeInstantiation()
); - 选取合适的构造方法,通过反射实例化bean。
- 执行执行BeanPostProcessor(具体实现是
InstantiationAwareBeanPostProcessor. postProcessAfterInstantiation()
); - bean初始化,对bean进行属性填充(
populateBean
方法里)。放入spring缓存。 - 如果bean实现了Aware接口(
BeanNameAware
、BeanClassLoaderAware
或BeanFactoryAware
),那么执行对应的set方法。然后执行BeanPostProcessor.postProcessBeforeInitialization()
方法 - 如果bean实现了
InitializingBean
接口,那么执行afterPropertiesSet
方法。然后执行自定义初始化方法。 - 执行
BeanPostProcessor.postProcessAfterInitialization()
方法。
其中6-7是在populateBean()方法里。8-9是在initializeBean()方法里。bean的生命周期从5开始,10之后bean可以使用,在容器销毁之后如果bean实现了DisposableBean方法,那么调用他的destroy方法。然后调用自定义销毁方法。
循环依赖及其解决方法
什么是Spring循环依赖
bean创建过程中出现A依赖B,然后B又依赖A,或者A依赖B,B依赖C,C依赖A的情况就是循环依赖。
spring无法解决原型模式bean的循环依赖,也无法解决单例bean的构造方法参数造成的循环依赖。它只能解决单例bean的setter造成的循环依赖。
解决方式
三级缓存
// 一级缓存
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
// 三级缓存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
// 二级缓存
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
解决流程
假设A依赖B,B依赖A,先创建A。
- 执行doGetBean拿A,先从缓存里找A,肯定无
- 实例化A,然后放进3级缓存
- 对A进行属性注入,发现依赖了B
- doGetBean B,同样步骤,直到创建完B,存到3级缓存,再对B进行属性注入,发现依赖了A
- 去缓存里找A,拿到了,并把A从3级缓存升到2级
- 拿到A的B继续走流程,直到创建完,就加入到1级缓存,清掉2、3级缓存里的B
- 回到A的流程,直到创建完,就加入到1级缓存,清掉2、3级缓存里的A
那为啥构造器模式下循环依赖不能解决呢?
假如A的构造方法里有B,在创建A前,A的beanName
会加入到singletonsCurrentlyInCreation
的Set里。在实例化A时,回去找B,然后去创建B。创建B时又发现依赖了A,又去找A,但是跟上面不一样的是,这时候缓存里没有A,spring判断了A还在singletonsCurrentlyInCreation
的Set里,于是抛了个异常。
而反过来,A如果是setter方法里依赖B,B是构造方法里依赖A的话,流程是没有问题的。因为在实例化B时,B去找缓存里的A是可以找到的。所以A在setter里依赖B,而B在构造方法里依赖A的情况下是可以解决的。
为啥要用三个缓存而不是两个?
如果一个对象被AOP注解了,那他在三级缓存里的ObjectFactory.getObject
操作是会进行代理,如果只用2个缓存(1级和3级),那每次执行ObjectFactory.getObject
都会被代理,显然不合理。所以在执行一次之后,就把他升级到二级缓存了。
网友评论