目录
- 面试题:
- 1、你解释下spring中的三级缓存?
-
- 2、三级缓存分别是什么?三个Map有什么异同?
-
- 3、什么是循环依赖?请你谈谈?看过spring源码吗?一般我们说的spring容器是什么?
-
- 4、如何检测是否存在循环依赖?实际开发中见过循环依赖的异常吗?
-
- 5、多例的情况下,循环依赖问题为什么无法解决?
-
- 6、为什么一定要用3级缓存?用2级可否?1级可否?
- 是否可以关闭循环依赖?
1、多个bean之间相互依赖,形成了一个闭环。
比如:A依赖于B、B依赖于C、C依赖于A
- public class T1 {
-
- class A {
- B b;
- }
-
- class B {
- C c;
- }
-
- class C {
- A a;
- }
- }
2、通常来说,如果问spring容器内部如何解决循环依赖,一定是指默认的单例Bean中,属性互相引用的场景。
- @Component
- public class A {
- @Autowired
- private B b;
- }
-
- @Component
- public class B {
- @Autowired
- private C c;
- }
-
- @Component
- public class C {
- @Autowired
- private A a;
- }
- Circular dependencies
- If you use predominantly constructor injection, it is possible to create an
- unresolvable circular dependency scenario.
-
- For example: Class A requires an instance of class B through constructor injection,
- and class B requires an instance of class A through constructor injection. If you
- configure beans for classes A and B to be injected into each other, the Spring IoC
- container detects this circular reference at runtime, and throws a
- BeanCurrentlyInCreationException.
-
- One possible solution is to edit the source code of some classes to be configured by
- setters rather than constructors. Alternatively, avoid constructor injection and use
- setter injection only. In other words, although it is not recommended, you can
- configure circular dependencies with setter injection.
-
- Unlike the typical case (with no circular dependencies), a circular dependency between
- bean A and bean B forces one of the beans to be injected into the other prior to
- being fully initialized itself (a classic chicken-and-egg scenario).
-
- ==================================
- 循环依赖项
- 如果主要使用构造函数注入,则有可能创建一个无法解决的循环依赖场景。
-
- 例如:类A通过构造函数注入需要类B的实例,而类B通过构造函数注入要求类A的实例。如果为类A
- 和类B配置bean以相互注入,那么Spring IoC容器会在运行时检测到这个循环引用,并抛出
- BeanCurrentlyInCreationException。
-
- 一种可能的解决方案是编辑一些类的源代码,由setter而不是构造函数进行配置。或者,避免构造函数
- 注入,只使用setter注入。换句话说,尽管不建议这样做,但您可以使用setter注入来配置循环依赖关系。
-
- 与典型的情况(没有循环依赖关系)不同,bean a和bean B之间的循环依赖关系迫使其中一个bean在
- 完全初始化之前注入另一个bean(典型的先有鸡后有蛋的场景)。
-
- ==================================
- 结论:AB循环依赖问题,只要A的注入方式是setter且singleton,就不会有循环依赖问题
spring容器循环依赖报错演示BeanCurrentlyInCreationException
ServiceA
- import org.springframework.stereotype.Component;
-
- @Component
- public class ServiceA {
-
- private ServiceB serviceB;
-
- public ServiceA(ServiceB serviceB) {
- this.serviceB = serviceB;
- }
-
- }
ServiceB
- import org.springframework.stereotype.Component;
-
- @Component
- public class ServiceB {
-
- private ServiceA serviceA;
-
- public ServiceB(ServiceA serviceA) {
- this.serviceA = serviceA;
- }
-
- }
Test
- public class Test {
-
- public static void main(String[] args) {
- new ServiceA(new ServiceB(new ServiceA(....)));
- }
-
- }
结论:构造器循环依赖是无法解决的,你想让构造器注入支持循环依赖,是不存在的
ServiceAA
- import org.springframework.stereotype.Component;
-
- @Component
- public class ServiceAA {
-
- private ServiceBB serviceBB;
-
- public void setServiceBB(ServiceBB serviceBB) {
- this.serviceBB = serviceBB;
- System.out.println("A里面设置B");
- }
-
- }
ServiceBB
- import org.springframework.stereotype.Component;
-
- @Component
- public class ServiceBB {
-
- private ServiceAA serviceAA;
-
- public void setServiceAA(ServiceAA serviceAA) {
- this.serviceAA = serviceAA;
- System.out.println("B里面设置A");
- }
-
- }
Test
- public class Test {
-
- public static void main(String[] args) {
- ServiceAA aa = new ServiceAA();
- ServiceBB bb = new ServiceBB();
- bb.setServiceAA(aa);
- aa.setServiceBB(bb);
- }
- }
结论:setter方式可以解决循环依赖
默认的单例(singleton)的场景是支持循环依赖的,不报错
pom
- "1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0modelVersion>
-
- <groupId>com.lwzgroupId>
- <artifactId>springboot_aopartifactId>
- <version>1.0-SNAPSHOTversion>
-
- <properties>
- <java.version>1.8java.version>
- properties>
-
- <dependencies>
- <dependency>
- <groupId>org.springframeworkgroupId>
- <artifactId>spring-contextartifactId>
- <version>5.1.6.RELEASEversion>
- dependency>
- dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-maven-pluginartifactId>
- plugin>
- plugins>
- build>
- project>
src\main\resources\applicationContext.xml
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
-
- <bean id="a" class="com.lwz.part3.A" scope="singleton">
- <property name="B" ref="b"/>
- bean>
- <bean id="b" class="com.lwz.part3.B" scope="singleton">
- <property name="a" ref="a"/>
- bean>
-
- beans>
A
- package com.lwz.part3;
-
- public class A {
-
- private B b;
-
- public A() {
- System.out.println("A...created success");
- }
-
- public B getB() {
- return b;
- }
-
- public void setB(B b) {
- this.b = b;
- }
-
- }
B
- package com.lwz.part3;
-
- public class B {
-
- private A a;
-
- public B() {
- System.out.println("B...created success");
- }
-
- public A getA() {
- return a;
- }
-
- public void setA(A a) {
- this.a = a;
- }
-
- }
TestSpringContainer
- package com.lwz.part3;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestSpringContainer {
-
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- A a = context.getBean("a", A.class);
- B b = context.getBean("b", B.class);
- }
- }
运行结果
- A...created success
- B...created success
原型(prototype)的场景是不支持循环依赖的,会报错
src\main\resources\applicationContext.xml 修改为prototype
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
-
- <bean id="a" class="com.lwz.part3.A" scope="prototype">
- <property name="B" ref="b"/>
- bean>
- <bean id="b" class="com.lwz.part3.B" scope="prototype">
- <property name="a" ref="a"/>
- bean>
-
- beans>
测试类
- package com.lwz.part3;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestSpringContainer {
-
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- A a = context.getBean("a", A.class);
- B b = context.getBean("b", B.class);
- }
- }
运行结果
- A...created success
- B...created success
- Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'a' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'b' while setting bean property 'B'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1433)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
- at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
- at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
- at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1111)
- at com.lwz.part3.TestSpringContainer.main(TestSpringContainer.java:10)
- Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'b' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'a' while setting bean property 'a'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:378)
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:110)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1433)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592)
- at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
- at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:338)
- at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
- ... 9 more
- Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?
- at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:264)
- at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
- at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:367)
- ... 17 more
报org.springframework.beans.factory.BeanCurrentlyInCreationException异常
结论:Spring内部通过3级缓存来解决循环依赖:spring-beans-5.1.6.RELEASE.jar包下org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
- package org.springframework.beans.factory.support;
- ...
- public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
-
- //一级缓存
- /** Cache of singleton objects: bean name to bean instance. */
- private final Map
singletonObjects = new ConcurrentHashMap<>(256); - //三级缓存
- /** Cache of singleton factories: bean name to ObjectFactory. */
- private final Map
> singletonFactories = new HashMap<>(16); - //二级缓存
- /** Cache of early singleton objects: bean name to bean instance. */
- private final Map
earlySingletonObjects = new HashMap<>(16);
第一级缓存:(也叫单例池)singletonObjects :存放已经经历了完整生命周期的Bean对象
第二级缓存:earlySingletonObjects ,存放早期暴漏出来的Bean对象,Bean的生命周期未结束(属性还未填充完)
第三级缓存:Map
结论:只有单例的bean会通过三级缓存提前暴漏来解决循环依赖的问题,而非单例的bean,每次从容器中获取都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放到三级缓存中。
前置知识
实例化:内存中申请一块内存空间
初始化:属性填充,完成属性的各种赋值
三个Map和四个方法:三个Map看上面,四个方法如下
- getSingleton() :获取Bean
-
- doCreateBean():创建Bean
-
- populateBean():属性填充
-
- addSingleton():添加Bean
第一层singletonObjects存放的是已经初始化好了的Bean。
第二层earlySingletonObjects存放的是实例化了,但是未初始化的Bean。
第三层singletonFactories存放的是FactoryBean。假如A类实现了FactoryBean,那么依赖注入的时候不是A类,而是A类产生的Bean。
A/B两对象在三级缓存中的迁移说明
1、A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
2、B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存,找到了A,然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A。
3、B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态),然后回来接着创建A,此时B已经创建结束,直接从一级缓存里面拿到了B,然后完成创建,并将A自己放到一级缓存里面。
src\main\resources\applicationContext.xml 修改为singleton
- "1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xmlns:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd">
-
- <bean id="a" class="com.lwz.part3.A" scope="singleton">
- <property name="B" ref="b"/>
- bean>
- <bean id="b" class="com.lwz.part3.B" scope="singleton">
- <property name="a" ref="a"/>
- bean>
-
- beans>
测试类第一行进行打断点
- package com.lwz.part3;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- public class TestSpringContainer {
-
- public static void main(String[] args) {
- ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
- A a = context.getBean("a", A.class);
- B b = context.getBean("b", B.class);
- }
- }
打断点的位置//F5
- package org.springframework.context.support;
- ...
- public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
- ...
-
- public ClassPathXmlApplicationContext(
- String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
- throws BeansException {
-
- super(parent);
- setConfigLocations(configLocations);
- if (refresh) {
- refresh(); //F5
- }
- }
- package org.springframework.context.support;
- ...
- public abstract class AbstractApplicationContext extends DefaultResourceLoader
- implements ConfigurableApplicationContext {
- ...
- @Override
- public void refresh() throws BeansException, IllegalStateException {
- synchronized (this.startupShutdownMonitor) {
- // Prepare this context for refreshing.
- prepareRefresh();
-
- // Tell the subclass to refresh the internal bean factory.
- ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
-
- // Prepare the bean factory for use in this context.
- prepareBeanFactory(beanFactory);
-
- try {
- // Allows post-processing of the bean factory in context subclasses.
- postProcessBeanFactory(beanFactory);
-
- // Invoke factory processors registered as beans in the context.
- invokeBeanFactoryPostProcessors(beanFactory);
-
- // Register bean processors that intercept bean creation.
- registerBeanPostProcessors(beanFactory);
-
- // Initialize message source for this context.
- initMessageSource();
-
- // Initialize event multicaster for this context.
- initApplicationEventMulticaster();
-
- // Initialize other special beans in specific context subclasses.
- onRefresh();
-
- // Check for listener beans and register them.
- registerListeners();
-
- // Instantiate all remaining (non-lazy-init) singletons.
- finishBeanFactoryInitialization(beanFactory); //F5
-
- // Last step: publish corresponding event.
- finishRefresh();
- }
-
- catch (BeansException ex) {
- if (logger.isWarnEnabled()) {
- logger.warn("Exception encountered during context initialization - " +
- "cancelling refresh attempt: " + ex);
- }
-
- // Destroy already created singletons to avoid dangling resources.
- destroyBeans();
-
- // Reset 'active' flag.
- cancelRefresh(ex);
-
- // Propagate exception to caller.
- throw ex;
- }
-
- finally {
- // Reset common introspection caches in Spring's core, since we
- // might not ever need metadata for singleton beans anymore...
- resetCommonCaches();
- }
- }
- }
- protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
- // Initialize conversion service for this context.
- if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
- beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
- beanFactory.setConversionService(
- beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
- }
-
- // Register a default embedded value resolver if no bean post-processor
- // (such as a PropertyPlaceholderConfigurer bean) registered any before:
- // at this point, primarily for resolution in annotation attribute values.
- if (!beanFactory.hasEmbeddedValueResolver()) {
- beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
- }
-
- // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
- String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
- for (String weaverAwareName : weaverAwareNames) {
- getBean(weaverAwareName);
- }
-
- // Stop using the temporary ClassLoader for type matching.
- beanFactory.setTempClassLoader(null);
-
- // Allow for caching all bean definition metadata, not expecting further changes.
- beanFactory.freezeConfiguration();
-
- // Instantiate all remaining (non-lazy-init) singletons.
- beanFactory.preInstantiateSingletons(); //F5 实例化所有剩余的(非惰性init)singleton。
- }
- @Override
- public void preInstantiateSingletons() throws BeansException {
- if (logger.isTraceEnabled()) {
- logger.trace("Pre-instantiating singletons in " + this);
- }
-
- // Iterate over a copy to allow for init methods which in turn register new bean definitions.
- // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
- List
beanNames = new ArrayList<>(this.beanDefinitionNames); -
- // Trigger initialization of all non-lazy singleton beans...
- for (String beanName : beanNames) {
- RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
- if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
- if (isFactoryBean(beanName)) {
- Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
- if (bean instanceof FactoryBean) {
- final FactoryBean> factory = (FactoryBean>) bean;
- boolean isEagerInit;
- if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
- isEagerInit = AccessController.doPrivileged((PrivilegedAction
) - ((SmartFactoryBean>) factory)::isEagerInit,
- getAccessControlContext());
- }
- else {
- isEagerInit = (factory instanceof SmartFactoryBean &&
- ((SmartFactoryBean>) factory).isEagerInit());
- }
- if (isEagerInit) {
- getBean(beanName);
- }
- }
- }
- else {
- getBean(beanName);//F5
- }
- }
- }
-
- // Trigger post-initialization callback for all applicable beans...
- for (String beanName : beanNames) {
- Object singletonInstance = getSingleton(beanName);
- if (singletonInstance instanceof SmartInitializingSingleton) {
- final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
- if (System.getSecurityManager() != null) {
- AccessController.doPrivileged((PrivilegedAction
- smartSingleton.afterSingletonsInstantiated();
- return null;
- }, getAccessControlContext());
- }
- else {
- smartSingleton.afterSingletonsInstantiated();
- }
- }
- }
- }
Spring容器以do开头的方法是真正写业务逻辑的方法。
- @Override
- public Object getBean(String name) throws BeansException {
- return doGetBean(name, null, null, false);
- }
-
- protected
T doGetBean(final String name, @Nullable final Class requiredType, - @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
-
- final String beanName = transformedBeanName(name);
- Object bean;
-
- // Eagerly check singleton cache for manually registered singletons.
- Object sharedInstance = getSingleton(beanName);//F5 获取Bean
- if (sharedInstance != null && args == null) {
- if (logger.isTraceEnabled()) {
- if (isSingletonCurrentlyInCreation(beanName)) {
- logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
- "' that is not fully initialized yet - a consequence of a circular reference");
- }
- else {
- logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
- }
- }
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
- }
-
- else {
- // Fail if we're already creating this bean instance:
- // We're assumably within a circular reference.
- if (isPrototypeCurrentlyInCreation(beanName)) {
- throw new BeanCurrentlyInCreationException(beanName);
- }
-
- // Check if bean definition exists in this factory.
- BeanFactory parentBeanFactory = getParentBeanFactory();
- if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
- // Not found -> check parent.
- String nameToLookup = originalBeanName(name);
- if (parentBeanFactory instanceof AbstractBeanFactory) {
- return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
- nameToLookup, requiredType, args, typeCheckOnly);
- }
- else if (args != null) {
- // Delegation to parent with explicit args.
- return (T) parentBeanFactory.getBean(nameToLookup, args);
- }
- else if (requiredType != null) {
- // No args -> delegate to standard getBean method.
- return parentBeanFactory.getBean(nameToLookup, requiredType);
- }
- else {
- return (T) parentBeanFactory.getBean(nameToLookup);
- }
- }
-
- if (!typeCheckOnly) {
- markBeanAsCreated(beanName);
- }
-
- try {
- final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
- checkMergedBeanDefinition(mbd, beanName, args);
-
- // Guarantee initialization of beans that the current bean depends on.
- String[] dependsOn = mbd.getDependsOn();
- if (dependsOn != null) {
- for (String dep : dependsOn) {
- if (isDependent(beanName, dep)) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName,
- "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
- }
- registerDependentBean(dep, beanName);
- try {
- getBean(dep);
- }
- catch (NoSuchBeanDefinitionException ex) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName,
- "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
- }
- }
- }
-
- // Create bean instance. 创建Bean实例
- if (mbd.isSingleton()) {//F5
- sharedInstance = getSingleton(beanName, () -> {
- try {
- return createBean(beanName, mbd, args);
- }
- catch (BeansException ex) {
- // Explicitly remove instance from singleton cache: It might have been put there
- // eagerly by the creation process, to allow for circular reference resolution.
- // Also remove any beans that received a temporary reference to the bean.
- destroySingleton(beanName);
- throw ex;
- }
- });
- bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
- }
-
- else if (mbd.isPrototype()) {
- // It's a prototype -> create a new instance.
- Object prototypeInstance = null;
- try {
- beforePrototypeCreation(beanName);
- prototypeInstance = createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
- }
-
- else {
- String scopeName = mbd.getScope();
- final Scope scope = this.scopes.get(scopeName);
- if (scope == null) {
- throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
- }
- try {
- Object scopedInstance = scope.get(beanName, () -> {
- beforePrototypeCreation(beanName);
- try {
- return createBean(beanName, mbd, args);
- }
- finally {
- afterPrototypeCreation(beanName);
- }
- });
- bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
- }
- catch (IllegalStateException ex) {
- throw new BeanCreationException(beanName,
- "Scope '" + scopeName + "' is not active for the current thread; consider " +
- "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
- ex);
- }
- }
- }
- catch (BeansException ex) {
- cleanupAfterBeanCreationFailure(beanName);
- throw ex;
- }
- }
-
- // Check if required type matches the type of the actual bean instance.
- if (requiredType != null && !requiredType.isInstance(bean)) {
- try {
- T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
- if (convertedBean == null) {
- throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
- }
- return convertedBean;
- }
- catch (TypeMismatchException ex) {
- if (logger.isTraceEnabled()) {
- logger.trace("Failed to convert bean '" + name + "' to required type '" +
- ClassUtils.getQualifiedName(requiredType) + "'", ex);
- }
- throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
- }
- }
- return (T) bean;
- }
getSingleton() :获取Bean、createBean()-->doCreateBean()
- @Nullable
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- //从singletonObjects获取实例,singletonObjects中的实例都是准备好的bean实例,可以直接使用
- Object singletonObject = this.singletonObjects.get(beanName);
- //isSingletonCurrentlyInCreation(beanName):判断当前单例bean是否正在创建中
- if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
- synchronized (this.singletonObjects) {
- //一级缓存没有就去二级缓存找
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null && allowEarlyReference) {
- //二级缓存没有就去三级缓存找
- ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- //三级缓存有的话,就把它移动到二级缓存,并在三级缓存中删除
- singletonObject = singletonFactory.getObject();
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- return singletonObject;
- }
-
- @Override
- protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
- throws BeanCreationException {
-
- if (logger.isTraceEnabled()) {
- logger.trace("Creating instance of bean '" + beanName + "'");
- }
- RootBeanDefinition mbdToUse = mbd;
-
- // Make sure bean class is actually resolved at this point, and
- // clone the bean definition in case of a dynamically resolved Class
- // which cannot be stored in the shared merged bean definition.
- Class> resolvedClass = resolveBeanClass(mbd, beanName);
- if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
- mbdToUse = new RootBeanDefinition(mbd);
- mbdToUse.setBeanClass(resolvedClass);
- }
-
- // Prepare method overrides.
- try {
- mbdToUse.prepareMethodOverrides();
- }
- catch (BeanDefinitionValidationException ex) {
- throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
- beanName, "Validation of method overrides failed", ex);
- }
-
- try {
- // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
- Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
- if (bean != null) {
- return bean;
- }
- }
- catch (Throwable ex) {
- throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
- "BeanPostProcessor before instantiation of bean failed", ex);
- }
-
- try {
- Object beanInstance = doCreateBean(beanName, mbdToUse, args);//F5
- if (logger.isTraceEnabled()) {
- logger.trace("Finished creating instance of bean '" + beanName + "'");
- }
- return beanInstance;
- }
- catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
- // A previously detected exception with proper bean creation context already,
- // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
- throw ex;
- }
- catch (Throwable ex) {
- throw new BeanCreationException(
- mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
- }
- }
doCreateBean()-->populateBean()属性填充
- protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
- throws BeanCreationException {
-
- // Instantiate the bean.
- BeanWrapper instanceWrapper = null;
- if (mbd.isSingleton()) {
- instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
- }
- if (instanceWrapper == null) {
- instanceWrapper = createBeanInstance(beanName, mbd, args);//F5创建bean的实例
- }
- final Object bean = instanceWrapper.getWrappedInstance();
- Class> beanType = instanceWrapper.getWrappedClass();
- if (beanType != NullBean.class) {
- mbd.resolvedTargetType = beanType;
- }
-
- // Allow post-processors to modify the merged bean definition.
- synchronized (mbd.postProcessingLock) {
- if (!mbd.postProcessed) {
- try {
- applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
- }
- catch (Throwable ex) {
- throw new BeanCreationException(mbd.getResourceDescription(), beanName,
- "Post-processing of merged bean definition failed", ex);
- }
- mbd.postProcessed = true;
- }
- }
-
- // Eagerly cache singletons to be able to resolve circular references
- // even when triggered by lifecycle interfaces like BeanFactoryAware.
- boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
- isSingletonCurrentlyInCreation(beanName));
- if (earlySingletonExposure) {
- if (logger.isTraceEnabled()) {
- logger.trace("Eagerly caching bean '" + beanName +
- "' to allow for resolving potential circular references");
- }
- //F5
- addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
- }
-
- // Initialize the bean instance.
- Object exposedObject = bean;
- try {
- populateBean(beanName, mbd, instanceWrapper);
- exposedObject = initializeBean(beanName, exposedObject, mbd);
- }
- catch (Throwable ex) {
- if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
- throw (BeanCreationException) ex;
- }
- else {
- throw new BeanCreationException(
- mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
- }
- }
-
- if (earlySingletonExposure) {
- Object earlySingletonReference = getSingleton(beanName, false);
- if (earlySingletonReference != null) {
- if (exposedObject == bean) {
- exposedObject = earlySingletonReference;
- }
- else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
- String[] dependentBeans = getDependentBeans(beanName);
- Set
actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); - for (String dependentBean : dependentBeans) {
- if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
- actualDependentBeans.add(dependentBean);
- }
- }
- if (!actualDependentBeans.isEmpty()) {
- throw new BeanCurrentlyInCreationException(beanName,
- "Bean with name '" + beanName + "' has been injected into other beans [" +
- StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
- "] in its raw version as part of a circular reference, but has eventually been " +
- "wrapped. This means that said other beans do not use the final version of the " +
- "bean. This is often the result of over-eager type matching - consider using " +
- "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
- }
- }
- }
- }
-
- // Register bean as disposable.
- try {
- registerDisposableBeanIfNecessary(beanName, bean, mbd);
- }
- catch (BeanDefinitionValidationException ex) {
- throw new BeanCreationException(
- mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
- }
-
- return exposedObject;
- }
属性填充populateBean:a中有属性b,需要再去创建b,重复上面步骤
- protected void addSingletonFactory(String beanName, ObjectFactory> singletonFactory) {
- Assert.notNull(singletonFactory, "Singleton factory must not be null");
- synchronized (this.singletonObjects) {
- if (!this.singletonObjects.containsKey(beanName)) {
- this.singletonFactories.put(beanName, singletonFactory);
- this.earlySingletonObjects.remove(beanName);
- this.registeredSingletons.add(beanName);
- }
- }
- }
-
- protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
- if (bw == null) {
- if (mbd.hasPropertyValues()) {
- throw new BeanCreationException(
- mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
- }
- else {
- // Skip property population phase for null instance.
- return;
- }
- }
-
- // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
- // state of the bean before properties are set. This can be used, for example,
- // to support styles of field injection.
- boolean continueWithPropertyPopulation = true;
-
- if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
- for (BeanPostProcessor bp : getBeanPostProcessors()) {
- if (bp instanceof InstantiationAwareBeanPostProcessor) {
- InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
- if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
- continueWithPropertyPopulation = false;
- break;
- }
- }
- }
- }
-
- if (!continueWithPropertyPopulation) {
- return;
- }
-
- PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
-
- if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
- MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
- // Add property values based on autowire by name if applicable.
- if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
- autowireByName(beanName, mbd, bw, newPvs);
- }
- // Add property values based on autowire by type if applicable.
- if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
- autowireByType(beanName, mbd, bw, newPvs);
- }
- pvs = newPvs;
- }
-
- boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
- boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
-
- PropertyDescriptor[] filteredPds = null;
- if (hasInstAwareBpps) {
- if (pvs == null) {
- pvs = mbd.getPropertyValues();
- }
- for (BeanPostProcessor bp : getBeanPostProcessors()) {
- if (bp instanceof InstantiationAwareBeanPostProcessor) {
- InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
- PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
- if (pvsToUse == null) {
- if (filteredPds == null) {
- filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
- }
- pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
- if (pvsToUse == null) {
- return;
- }
- }
- pvs = pvsToUse;
- }
- }
- }
- if (needsDepCheck) {
- if (filteredPds == null) {
- filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
- }
- checkDependencies(beanName, mbd, filteredPds, pvs);
- }
-
- if (pvs != null) {
- applyPropertyValues(beanName, mbd, bw, pvs);//F5 应用属性
- }
- }
addSingleton():添加Bean
- public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
- Assert.notNull(beanName, "Bean name must not be null");
- synchronized (this.singletonObjects) {
- Object singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- if (this.singletonsCurrentlyInDestruction) {
- throw new BeanCreationNotAllowedException(beanName,
- "Singleton bean creation not allowed while singletons of this factory are in destruction " +
- "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
- }
- beforeSingletonCreation(beanName);
- boolean newSingleton = false;
- boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
- if (recordSuppressedExceptions) {
- this.suppressedExceptions = new LinkedHashSet<>();
- }
- try {
- singletonObject = singletonFactory.getObject();
- newSingleton = true;
- }
- catch (IllegalStateException ex) {
- // Has the singleton object implicitly appeared in the meantime ->
- // if yes, proceed with it since the exception indicates that state.
- singletonObject = this.singletonObjects.get(beanName);
- if (singletonObject == null) {
- throw ex;
- }
- }
- catch (BeanCreationException ex) {
- if (recordSuppressedExceptions) {
- for (Exception suppressedException : this.suppressedExceptions) {
- ex.addRelatedCause(suppressedException);
- }
- }
- throw ex;
- }
- finally {
- if (recordSuppressedExceptions) {
- this.suppressedExceptions = null;
- }
- afterSingletonCreation(beanName);
- }
- if (newSingleton) {
- addSingleton(beanName, singletonObject); //F5
- }
- }
- return singletonObject;
- }
- }
addSingleton():添加Bean
把创建的b放入一级缓存,删除三级和二级缓存中的bean、b创建完继续创建a
- protected void addSingleton(String beanName, Object singletonObject) {
- synchronized (this.singletonObjects) {
- this.singletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- this.earlySingletonObjects.remove(beanName);
- this.registeredSingletons.add(beanName);
- }
- }
总结:
Spring创建bean主要分为两个步骤,创建原始bean对象,接着去填充对象属性和初始化。
每次创建bean之前,我们都会从缓存中查下有没有该bean,因为是单例只能有一个。
当我们创建beanA的原始对象后,并把它放到三级缓存中,接下来就该填充对象属性了,这时候发现了beanB,接着就又去创建beanB,同样的流程,创建完beanB填充属性时又发现它依赖beanA又是同样的流程。
不同的是:
这时候可以在三级缓存中查到刚放进去的原始对象baenA,所以不需要继续创建,用它注入beanB,完成beanB的创建,既然beanB创建好了,所以beanA就可以完成填充属性的步骤了,接着执行剩下的逻辑,闭环完成。
- @Nullable
- protected Object getSingleton(String beanName, boolean allowEarlyReference) {
- //从singletonObjects获取实例,singletonObjects中的实例都是准备好的bean实例,可以直接使用
- Object singletonObject = this.singletonObjects.get(beanName);
- //isSingletonCurrentlyInCreation(beanName):判断当前单例bean是否正在创建中
- if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
- synchronized (this.singletonObjects) {
- //一级缓存没有就去二级缓存找
- singletonObject = this.earlySingletonObjects.get(beanName);
- if (singletonObject == null && allowEarlyReference) {
- //二级缓存没有就去三级缓存找
- ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
- if (singletonFactory != null) {
- //三级缓存有的话,就把它移动到二级缓存,并在三级缓存中删除
- singletonObject = singletonFactory.getObject();
- this.earlySingletonObjects.put(beanName, singletonObject);
- this.singletonFactories.remove(beanName);
- }
- }
- }
- }
- return singletonObject;
- }
Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化但还没初始化的状态----->半成品。
实例化的过程又是通过构造器创建的,如果A还没创建好出来怎么可能提前曝光,所以构造器的循环依赖无法解决。
Spring为了解决单例的循环依赖问题,使用了三级缓存。
其中一级缓存为单例池(singletonObjects)
二级缓存为提前曝光对象(earlySingletonObjects)
三级缓存为提前曝光对象工厂(singletonFactories)
假设A、B循环引用,实例化A的时候就将其放入三级缓存中,接着填充属性的时候,发现依赖了B,同样的流程也是实例化后放入三级缓存,接着去填充属性时又发现自己依赖A,这时候从缓存中查找到早期暴露的A,没有AOP代理的话,直接将A的原始对象注入B,完成B的初始化后,进行属性填充和初始化,这时候B完成后,就去完成剩下的A的步骤,如果有AOP代理,就进行AOP处理获取代理后的对象A,注入B,走剩下的流程。
Spring解决循环依赖过程
1、调用doGetBean()方法,想要获取beanA,于是调用getSingleton()方法从缓存中查找beanA
2、在getSingleton()方法中,从一级缓存中查找,没有,返回null
3、doGetBean()方法中获取到的beanA为null,于是走对应的处理逻辑,调用getSingleton()的重载方法(参数为ObjectFactory的)
4、在getSingleton()方法中,先将beanA_name添加到一个集合中,用于标记该bean正在创建中。然后回调匿名内部类的creatBean方法
5、进入AbstractAutowireCapableBeanFactory#doCreateBean,先反射调用构造器创建出beanA的实例,然后判断:是否为单例、是否允许提前暴露引用(对于单例一般为true)、是否正在创建中(即是否在第四步的集合中)。判断为true则将beanA添加到[三级缓存]中
6、对beanA进行属性填充,此时检测到beanA依赖于beanB,于是开始查找beanB
7、调用doGetBean()方法,和上面beanA的过程一样,到缓存中查找beanB,没有则创建,然后给beanB填充属性
8、此时beanB依赖于beanA,调用getSingleton()获取beanA,依次从一级、二级、三级缓存中找,此时从三级缓存中获取到beanA的创建工厂,通过创建工厂获取到singletonObject,此时这个singletonObject指向的就是上面在doCreateBean()方法中实例化的beanA
9、这样beanB就获取到了beanA的依赖,于是beanB顺利完成实例化,并将beanA从三级缓存移动到二级缓存中
10、随后beanA继续它的属性填充工作,此时也获取到了beanB,beanA也随之完成了创建,回到getSingleton()方法中继续向下执行,将beanA从二级缓存移动到一级缓存中
一个程序员最重要的能力是:写出高质量的代码!!
有道无术,术尚可求也,有术无道,止于术。
无论你是年轻还是年长,所有程序员都需要记住:时刻努力学习新技术,否则就会被时代抛弃!