参考:
先说好不能骂我 : Spring源码学习--Aware相关接口
Water : Spring 如何解决 Bean 的循环依赖(循环引用)
概念
什么是 IOC ?
IoC(Inversion of Control,控制反转)就是把原来代码里需要实现的对象创建、依赖,反转给容器来帮忙实现。我们需要创建一个容器,同时需要一种描述来让容器知道要创建的对象与对象的关系。这个描述最具体的表现就是我们所看到的配置文件。
什么是 DI ?
DI(Dependency Injection,依赖注入)就是指对象被动接受依赖类而不自己主动去找,换句话说,就是指对象不是从容器中查找它依赖的类,而是在容器实例化对象时主动将它依赖的类注入给它。
BeanFactory 和 ApplicationContext
BeanFactory | ApplicationContext |
最基本的IOC容器,提供管理Bean的基本功能 | 高级容器,继承 extends BeanFactory,添加了一些高级功能。 |
仅支持 Singleton 和 Prototype bean 范围 | 支持所有类型的 bean 范围,例如 Singleton、Prototype、Request、Session 等。 |
不支持注释。仅支持 xml | 支持 Bean 自动装配中基于注释的配置 |
不提供 messaging(i18n 或国际化)功能 | ApplicationContext 接口扩展了 MessageSource 接口,因此它提供 messaging(i18n 或国际化)功能。 |
不支持事件发布功能。 | ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。 |
当调用 getBean() 方法时,BeanFactory 将创建一个 bean 对象, 从而使其成为延迟初始化。 |
ApplicationContext 仅在启动时加载所有 bean 并创建对象,从而使其进行热切初始化。 |
需要较少的内存。对于基本功能已经足够并且内存消耗很关键的独立应用程序, 我们可以使用BeanFactory。 |
提供所有基本功能和高级功能,包括一些面向企业应用程序的功能,因此需要更多内存。 |
Bean 的几种注入容器的方式
1、@Configuration + @Bean
@Configuration用来声明一个配置类,然后在方法上使用 @Bean 注解,会将方法返回的对象加入到Spring容器中。
@Configuration @ConditionalOnClass(value = {RestTemplate.class, HttpClient.class}) public class RestTemplateConfiguration { @Value("${restclient.maxTotalConnect:0}") private int maxTotalConnect; //连接池的最大连接数默认为0 @Value("${restclient.maxConnectPerRoute:200}") private int maxConnectPerRoute; //单个主机的最大连接数 @Value("${restclient.connectTimeout:2000}") private int connectTimeout; //连接超时默认2s @Value("${restclient.readTimeout:30000}") private int readTimeout; //读取超时默认30s @Value("${restclient.connectionRequestTimeout:5000}") private int connectionRequestTimeout; //客户端和服务器建立连接的timeout @Value("${restclient.socketTimeout:5000}") private int socketTimeout; //初始化httpClient,并加入spring的Bean工厂,由spring统一管理 @Bean("httpClient") @ConditionalOnMissingBean(name = "httpClient") public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(this.socketTimeout) .setConnectTimeout(this.connectTimeout) .setConnectionRequestTimeout(this.connectionRequestTimeout) .build(); CloseableHttpClient client = HttpClientBuilder.create().setMaxConnTotal(this.maxTotalConnect) .setMaxConnPerRoute(this.maxConnectPerRoute).setDefaultRequestConfig(requestConfig).build(); return client; }
2、通过包扫描特定注解的方式
@ComponentScan 放置在我们的配置类上,然后可以指定一个路径,进行扫描带有特定注解的bean,然后加至容器中。
特定注解包括@Controller、@Service、@Repository、@Component
@Service public class Person { //... } @ComponentScan(basePackages = "com.dabin.test.*") public class Demo1 { public static void main(String[] args) { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Demo1.class); Person bean = applicationContext.getBean(Person.class); System.out.println(bean); } }
3、implements FactoryBean 接口
我们的Bean工厂由于也是对象的一种,所以我们要把它加上 @Component 注解注册进Spring容器中
@Component public class FactoryBean_test implements FactoryBean { @Override public Object getObject() throws Exception { return new User(); } @Override public Class<?> getObjectType() { return User.class; } }
它实际上会注册进两个Bean对象,一个是我们的工厂对象,另一是我们生产出来的对象。
//获取生产出来的对象 User user1 = applicationContext.getBean("user",User.class); //获取工厂对象 FactoryBean_test factoryBean_test = applicationContext.getBean("&user",FactoryBean_test.class);
Bean 的生命周期
一、Bean 核心生命周期:init 和 destory
虽然spring提供了大量Bean级生命周期接口和容器级生命空间接口来扩展bean的生命周期,但bean的核心生命周期只有四个阶段:
bean实例化,依赖注入,自定义init方法,自定义destroy方法
- bean 是单例才会调用 destroy,是多例 prototype 不会。多例的 bean 创建成功后就交给用户管理了,不由 spring 管理
init 和 destroy 有下面三种配置方法:
1.通过在 @Bean 注解 或在 bean 的 xml 配置 init/destroy 方法
@Bean 注解里可以定义 init 方法 和 destroy 方法。
如下面这个数据源的,可以在 initMethod = "init", destroyMethod = "close" 两个方法里进行 druid 的创建和销毁
@Configuration public class DataSourceConfiguration { /** * The constant TEST_DRUID_PREFIX. */ private static final String TEST_DRUID_PREFIX = "test.datasource"; @Bean(name = "testDataSource", initMethod = "init", destroyMethod = "close") @ConditionalOnMissingBean(name = "testDataSource") @ConfigurationProperties(UCC_DRUID_PREFIX) public CoreDataSourceBean testDataSource( @Qualifier("testDruidDataSourceProperties") DruidDataSourceProperties druidDataSourceProperties) { DruidDataSource druidDataSource = new DruidDataSourceBuilder().properties(druidDataSourceProperties).build(); return new CoreDataSourceBean(druidDataSource); }
xml 配置 bean 属性
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <bean id="hw" class="beans.HelloWorld" init-method="init" destroy-method="destroy"/> </beans>
2.在方法上加 @PostConstruct 和 @PreDestroy
这是 java 的注解,可以先忽略
@PostConstruct public void createStudentDBConnection() throws ClassNotFoundException, SQLException { // load driver Class.forName(driver); // get a connection con = DriverManager.getConnection(url, userName, password); // execute query stmt = con.createStatement(); } public void closeConnection() throws SQLException { con.close(); } @PreDestroy public void destroy() throws SQLException { closeConnection(); }
3.通过实现接口 InitializingBean 和 DisposableBean
并重写 afterPropertiesSet() 和 destroy() 方法
// Java program to create a bean // in the spring framework package beans; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; // HelloWorld class which implements the // interfaces public class HelloWorld implements InitializingBean,DisposableBean { @Override // It is the init() method of our bean and it gets invoked on bean instantiation public void afterPropertiesSet() throws Exception { System.out.println("Bean HelloWorld has been instantiated and I'm the init() method"); } @Override // This method is invoked just after the container is closed public void destroy() throws Exception { System.out.println("Container has been closed and I'm the destroy() method"); } }
二、容器级生命周期接口:BeanPostProcessor
它允许自定义修改 Spring Bean Factory 创建的新 bean 实例。如果我们想要实现一些自定义逻辑,例如在 Spring 容器完成实例化、配置和初始化 bean 后检查标记接口或用代理包装 bean,我们可以插入 BeanPostProcessor 实现。
BeanPostProcessor 接口包含两个回调方法如下:
- postProcessBeforeInitialization() 方法
- postProcessAfterInitialization() 方法
BeanPostProcessor 也是作为bean注册到spring容器中的,Spring会保证BeanPostProcessor在业务Bean之前初始化完成。
// Java Program to Illustrate CustomProcessor Class package com.geeks.beans; // Importing required classes import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; // Class // Implementing BeanPostProcessor interface public class CustomProcessor implements BeanPostProcessor { // Method 1 public Object postProcessBeforeInitialization( Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization() is called for EmployeeImpl"); return bean; } // Method 2 public Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization() is called for EmployeeImpl"); return bean; } }
可以配置多个 BeanPostProcessor,并且只有我们配置的 BeanFactoryPostProcessor 同时实现了 Ordered 接口的话,还可以控制这些 BeanPostProcessor 执行的顺序。
【每个 bean 】初始化前后,就会【依次串行】调用这些 BeanPostPocessor
AbstractAutowireCapableBeanFactory 类,doCreateBean-----initializeBean----applyBeanPostProcessorsBeforeInitialization
@Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessBeforeInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
三、Bean 级的其它补充
Spring源码中接口以Aware结尾的接口(XXXAware),在Spring中表示对XXX可以感知,通俗点解释就是:如果在某个类里面想要使用spring的一些东西,就可以通过实现XXXAware接口告诉Spring,Spring看到后就会给你送过来,而接收的方式是通过实现接口唯一方法set-XXX。
比如:有一个类想要使用当前的 ApplicationContext,那么我们只需要让它实现ApplicationContextAware接口,然后实现接口中的唯一方法
void setApplicationContext(ApplicationContext applicationContext)
就可以了,spring会自动调用()这个方法将 applicationContext传给我们,我们只需要接受就可以了,并可以用接收到的内容做一些业务逻辑。
BeanNameAware
public interface BeanNameAware extends Aware { void setBeanName(String var1); }
BeanClassLoaderAware
public interface BeanClassLoaderAware extends Aware { void setBeanClassLoader(ClassLoader var1); }
如果一个 Bean 实现了 Aware 接口,那么就会 调用它实现的 Aware 接口的相关方法
AbstractAutowireCapableBeanFactory 类,doCreateBean-----initializeBean----invokeAwareMethods
private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware)bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = this.getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware)bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware)bean).setBeanFactory(this); } } }
注意 init方法 和 destroy方法,都是调用 InitializingBean 和 DisposableBean 的接口方法 在定义的 init 和 destroy 前面
如果Bean是多例(原型模式 prototype)的话,在 BeanPostProcessor#after 之后,容器将Bean返回给用户,剩下的生命周期由用户控制。而单例会一直在Map缓存中。
代码如下:每个 bean 初始化前后,分别、依次串行调用 BeanPostProcessor 的 BeforeInitialization 和 AfterInitialization
@SuppressWarnings("deprecation") protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { 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, ex.getMessage(), ex); } if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
Bean的作用域
- 单例对象:scope="singleton",默认值,一个应用只有一个对象的实例。它的作用范围就是整个引用。
- 生命周期:
- 对象出生:当应用加载,创建容器时,对象就被创建了。
- 对象活着:只要容器在,对象一直活着。
- 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
- 生命周期:
- 多例对象(原型):scope="prototype":每次访问对象时,都会重新创建对象实例。
- 生命周期:
- 对象出生:当使用对象时,创建新的对象实例。
- 对象活着:只要对象在使用中,就一直活着。
- 对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
- 生命周期:
- request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global session:WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么globalSession 相当于session.
依赖注入
Spring的自动装配有三种模式:byType(根据类型),byName(根据名称)、constructor(根据构造函数)。
@Autowired 和 @Resource 的区别
主要体现在以下 5 点:
- 来源不同:@Autowired 是 Spring 定义的注解,而 @Resource 是 Java 定义的注解
- 依赖查找的顺序不同:@Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找。@Resource 则相反,先根据名称(找不到)再根据类型。
- 支持的参数不同:@Autowired 只支持设置一个 required 的参数,而 @Resource 支持 name type 等7个参数
- 依赖注入的用法不同:@Autowired 支持属性注入、构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入
@Qualifier意思是合格者,一般跟 @Autowired 配合使用,需要指定一个 bean 的名称,如果 @Autowired 找到多个同类型的 bean,通过 @Qualifier 指定的bean名称就能找到需要装配的bean。
属性注入、构造方法注入、setter 注入
属性注入
@Service public class UserExtService { // 属性注入 @Autowired private UserService userService; //...... }
构造方法注入
@Service public class UserExtService { // 构造方法注入 private UserService userService; @Autowired public UserExtService(UserService userService) { this.userService = userService; } //...... }
setter 注入
@Service public class UserExtService { // Setter 注入 private UserService userService; @Autowired public void setUserService(UserService userService) { this.userService = userService; } //..... }
@Autowired 原理(MergedBeanDefinitionPostProcessor)
Spring 中通过 AutowiredAnnotationBeanPostProcessor来解析注入注解为目标注入值。
- 该类继承自 InstantiationAwareBeanPostProcessorAdapter:该类继承了 BeanPostProcessor,主要方法是 postProcessPropertyValues。用来进行依赖注入。在 doCreateBean 的 populateBean 方法中,会依次串行调用 instance of 这个类的所有 BeanPostProcessor。
- 该类实现了 MergedBeanDefinitionPostProcessor: 该接口继承了 BeanPostProcessor,主要方法是 postProcessMergedBeanDefinition 。用来解析和设置 @Autowired 相关的依赖的元数据。在 doCreateBean 实例化后会依次串行调用 instance of 这个接口的所有 BeanPostProcessor。
PriorityOrdered, BeanFactoryAware等接口,重写的方法将在 IOC 创建每个bean实例后被调用。
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器 BeanWrapper instanceWrapper = null; // 单例模型,则从未完成的 FactoryBean 缓存中删除 if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName); } // 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // .......
synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { // 对 @Autowired 等依赖注解的元信息进行查找和解析。依次串行调用实现了 MergedBeanDefinationPostProcessor 接口的 BeanPostProcessor applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } //... /* * 开始初始化 bean 实例对象 */ Object exposedObject = bean; try { // 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性 // 递归进行依赖注入。依次串行调用继承了 InstantitionAwareBeanPostProcessor 的所有 BeanPostProcessor populateBean(beanName, mbd, instanceWrapper); // 注入 Aware 相关的对象 // 调用 后置处理器 BeanPostProcessor 里面的postProcessBeforeInitialization方法 // 调用 initialzingBean,调用实现的 afterPropertiesSet() // 调用 init-mothod,调用相应的init方法 // 调用 后置处理器 BeanPostProcessor 里面的调用实现的postProcessAfterInitialization方法 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); } } //..... return exposedObject; } }
- 该类实现了 MergedBeanDefinitionPostProcessor: 该接口继承了 BeanPostProcessor,主要方法是 postProcessMergedBeanDefinition 。用来解析和设置 @Autowired 相关的依赖的元数据。在 doCreateBean 实例化后会依次串行调用 instance of 这个接口的所有 BeanPostProcessor。的 postProcessMergedBeanDedination 方法。
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) { Iterator var4 = this.getBeanPostProcessors().iterator(); while(var4.hasNext()) { BeanPostProcessor bp = (BeanPostProcessor)var4.next(); if (bp instanceof MergedBeanDefinitionPostProcessor) { MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor)bp; bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName); } } }
- 该类继承自 InstantiationAwareBeanPostProcessorAdapter:该类继承了 BeanPostProcessor,主要方法是 postProcessPropertyValues。用来进行依赖注入。在 doCreateBean 的 populateBean 方法中,会依次串行调用 instance of 这个类的所有 BeanPostProcessor。的 postProcessAfterInstantiation 方法。
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 { if (!mbd.isSynthetic() && this.hasInstantiationAwareBeanPostProcessors()) { Iterator var4 = this.getBeanPostProcessors().iterator(); while(var4.hasNext()) { BeanPostProcessor bp = (BeanPostProcessor)var4.next(); if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } //...... }
三级缓存解决循环依赖
循环依赖的发生
@Component public class A { @Autowired private B b; }
@Component public class B { @Autowired private A a; }
A 和 B 要完成 实例化、依赖注入、初始化 三个步骤后放入一级缓存 singletonObjects
原始对象 A 实例化完成,依赖注入需要注入 B 时,发现 B 不存在,就要实例化 B
B 实例化完成后,要接着进行依赖注入,需要注入 A,这时 A 还没完成依赖注入,在一级缓存里找不到
这样,A 和 B 虽然都进行了实例化,但是都完成不了依赖注入,没法成功创建加入一级缓存
三级缓存介绍
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 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 HashMap(16); //......
缓存名称 | 源码名称 | 存放内容 | 作用 | 添加或移除的时机 |
一级缓存 | singletonObjects | bean名称-bean对象(依赖注入完成,初始化也完成的bean) | 存储单例的bean |
添加:完成实例化、依赖注入、初始化,生成完整的 bean 后。同时移除二级或三级缓存 |
二级缓存 | earlySingletonObjects | bean名称-bean对象(只是实例化了的 bean,还未完成依赖注入和初始化) | 解决循环依赖时,无法完成依赖注入,也就无法成功创建bean的问题 | 添加:在三级缓存中获取到时 ObjectFactory,得到的 代理对象 添加到二级缓存,并清理三级缓存。 |
三级缓存 | singletonFactories | bean名称-ObjectFactory<T>(代理工厂接口,有 T getBean(beanName) 方法可以创建对应代理对象) | 使得循环依赖时,依赖双方,注入的都是代理后的对象 | 添加:通过 createBeanInstance 实例化Bean之后,调用populateBean 进行依赖注入之前。 |
实际上 一级 和 二级 缓存就能解决一般的 循环依赖问题
A 和 B 实例化后就放到 二级缓存 earlySingletonObjects 中。
这样原始对象 A 需要注入 B 时,发现 B 不存在,就实例化 B ,并放到 二级缓存 earlySingletonObjects 中。
接着 B 还要进行依赖注入,需要注入 A,这时 A 虽然再一级缓存中找不到,但是可以在二级缓存中找到。这样 B 就可以完成依赖注入,创建完成放入一级缓存。
接着再完成 A 的依赖注入,注入成功创建的 B ,A 也完成了创建。
但是 如果一个对象被增强了,即 是个代理对象, 这个时候就需要一个三级缓存
对象被增强后,那么需要四个步骤:实例化、依赖注入、初始化、生成代理对象
二级缓存时:
A实例化完成后放到二级缓存,开始依赖注入,发现B不存在
B开始实例化,注入二级缓存中刚实例化的A,B初始化完成后封装成代理对象(代理对象生成得晚)
A再将代理后的B注入,再做代理,那么代理A中的B就是代理后的B,但是代理后的B中的A是没用代理的A,这个没用代理的A 与最终 spring 容器中的 A 不相同
三级缓存时:
实例化后,将 ObjectFactory 放入三级缓存,这样需要注入时,首先获取三级缓存中的 ObjectFactory,通过它先创建一个代理对象,放入二级缓存
doCreateBean 三级缓存源码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory { protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // BeanWrapper是对Bean的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装bean的属性描述器 BeanWrapper instanceWrapper = null; // 单例模型,则从未完成的 FactoryBean 缓存中删除 if (mbd.isSingleton()) {anceWrapper = this.factoryBeanInstanceCache.remove(beanName); } // 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } // 创建实例对象(包装的) final Object bean = instanceWrapper.getWrappedInstance(); // 包装的实例对象的类型 Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // 检测是否有后置处理 // 如果有后置处理,则允许后置处理修改 BeanDefinition synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try {// 对 @Autowired 等依赖注解的元信息进行查找和解析。依次串行调用实现了 MergedBeanDefinationPostProcessor 接口的 BeanPostProcessor applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // 解决单例模式的循环依赖 // 单例模式 & 允许循环依赖 & 当前单例 bean 是否正在被创建 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } // 三级缓存:提前将创建的 bean 实例加入到ObjectFactory 中 // 这里是为了后期避免循环依赖 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } /* * 开始初始化 bean 实例对象 */ Object exposedObject = bean; try { // 对 bean 进行填充,将各个属性值注入,其中,可能存在依赖于其他 bean 的属性 // 递归进行依赖注入。依次串行调用继承了 InstantitionAwareBeanPostProcessor 的所有 BeanPostProcessor populateBean(beanName, mbd, instanceWrapper); // 注入 Aware 相关的对象 // 调用 后置处理器 BeanPostProcessor 里面的postProcessBeforeInitialization方法 // 调用 initialzingBean,调用实现的 afterPropertiesSet() // 调用 init-mothod,调用相应的init方法 // 调用 后置处理器 BeanPostProcessor 里面的调用实现的postProcessAfterInitialization方法 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) { // 获取 earlySingletonReference (先从一级获取,一级获取不到从二级,二级获取不到从三级,三级获取到ObjectFactory得到代理类放入二级,同时移除三级) Object earlySingletonReference = getSingleton(beanName, false); // 只有在存在循环依赖的情况下,earlySingletonReference 才不会为空 if (earlySingletonReference != null) { // 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强 if (exposedObject == bean) { exposedObject = earlySingletonReference; } // 依赖注入 else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> 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."); } } } } try { // 注册 bean registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; } }
DefaultSingletonBeanRegistry 中的 getSingleton 方法
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 从一级缓存获得 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) { synchronized(this.singletonObjects) { // 一级缓存获取不到,从二级缓存获取 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 二级缓存获取不到,从三级缓存获取 ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName); if (singletonFactory != null) { // 通过三级缓存的 ObjectFactory 生成代理对象 singletonObject = singletonFactory.getObject(); // 将三级缓存生成的代理对象放入二级缓存 this.earlySingletonObjects.put(beanName, singletonObject); // 从三级缓存中移除 ObjectFactory this.singletonFactories.remove(beanName); } } } } return singletonObject; }
Spring 无法解决的循环依赖
1、@Async 增强的 Bean 的循环依赖
普通的 AOP 代理 Bean 的循环依赖,Spring 都是可以通过上述的 三级缓存 解决的。例如 用户自定义的 @Aspect 。
它们二者在三级缓存上的主要区别:
- 普通的 AOP 代理都是通过 AnnotationAwareAspectJAutoProxyCreator 来生成代理类的,AnnotationAwareAspectJAutoProxyCreator 实现了 SmartInstantiationAwareBeanPostProcessor 接口。可以早期暴露代理对象。
- 而 @Async 标记的类是通过 AbstractAdvisingBeanPostProcessor 来生成代理类的,AbstractAdvisingBeanPostProcessor 没有实现 SmartInstantiationAwareBeanPostProcessor 接口。
AbstractAutoProxyCreator 和 AbstractAdvisingBeanPostProcessor 都实现了 BeanPostProcessor 接口的 postProcessAfterInitialization 方法,在对象初始化完成后会回调这个方法。AbstractAutoProxyCreator 和 AbstractAdvisingBeanPostProcessor 这个 postProcessAfterInitialization 方法都会返回一个代理对象(Proxy)。
添加到三级缓存中的代码:
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry { 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 HashMap(16); // 添加到三级缓存的方法 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); } } }
}
AbstractAutowireCapableBeanFactory 类的 doCreateBean 方法中,调用添加三级缓存方法的部分:
if (earlySingletonExposure) { //...... addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
所以缓存的 ObjectFactory 对象是由一个lamda表达式获得的,真正获取 三级缓存 ObjectFactory 对象的方法是 getEarlyBeanReference。
AbstractAutowireCapableBeanFactory 类的 getEarlyBeanReference 方法:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }
如果实现了 SmartInstantiationAwareBeanPostProcessor 接口,会调用实现的 SmartInstantiationAwareBeanPostProcessor 接口的 getEarlyBeanReference 方法获取三级缓存 ObjectFactory 对象。
而前面提到的 AnnotationAwareAspectJAutoProxyCreator 这个类就实现了 SmartInstantiationAwareBeanPostProcessor 接口,有 getEarlyBeanReference (早期暴露引用对象)方法为:
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { @Override public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); if (!this.earlyProxyReferences.contains(cacheKey)) { this.earlyProxyReferences.add(cacheKey); } return wrapIfNecessary(bean, beanName, cacheKey); } }
这个方法最后会调用 wrapIfNecessary 方法,前面也说过,这个方法是获取动态代理的方法,如果需要的话就会代理,比如事务注解又或者是自定义的AOP切面,在早期暴露的时候,就会完成动态代理。
为什么 @Async 注解遇上循环依赖,Spring 无法解决?
AService加了@Async注解,AService先创建,发现引用了BService,那么BService就会去创建,当Service创建的过程中发现引用了AService,那么就会通过AnnotationAwareAspectJAutoProxyCreator 这个类实现的 getEarlyBeanReference 方法获取AService的早期引用对象,此时这个早期引用对象可能会被代理,取决于AService是否需要被代理,但是一定不是处理@Async注解的代理,原因前面也说过。
于是 BService 创建好之后(已经是代理对象),注入给了AService,那么AService 初始化阶段完成之后继续往下处理,会调用所有的BeanPostProcessor的实现的 postProcessAfterInitialization 方法。于是就会依次回调 AnnotationAwareAspectJAutoProxyCreator 和 AsyncAnnotationBeanPostProcessor 的 postProcessAfterInitialization 方法实现。
这段回调有两个细节:
- AnnotationAwareAspectJAutoProxyCreator 先执行,AsyncAnnotationBeanPostProcessor 后执行,因为 AnnotationAwareAspectJAutoProxyCreator 在前面。
- AnnotationAwareAspectJAutoProxyCreator 处理后会继续交给 AsyncAnnotationBeanPostProcessor 处理,applyBeanPostProcessorsAfterInitialization方法就是这么实现的
最后的结果:
- AnnotationAwareAspectJAutoProxyCreator回调:会发现 AService 对象已经被早期引用了,什么都不处理,直接把对象AService给返回
- AsyncAnnotationBeanPostProcessor回调:发现AService类中加了@Async注解,那么就会对AnnotationAwareAspectJAutoProxyCreator返回的对象进行动态代理,然后返回了动态代理对象。早期暴露出去的对象,可能是AService本身或者是AService的代理对象,而且是通过AnnotationAwareAspectJAutoProxyCreator对象实现的,但是通过AsyncAnnotationBeanPostProcessor的回调,会对AService对象进行动态代理,这就导致AService早期暴露出去的对象跟最后完全创造出来的对象不是同一个,那么肯定就不对了。同一个Bean在一个Spring中怎么能存在两个不同的对象呢,于是就会抛出BeanCurrentlyInCreationException异常。
2、constructor 注入的循环依赖
A --> B --> A,且 都是通过构造函数依赖的
@Service public class A { private B b; public A(B b) { this.b=b; } } @Service public class B { private A a; public B(A a) { this.a=a; } }
- A 实例在创建时(createBeanInstance),由于是构造注入,这时会触发 B 的加载。
- B 实例在创建时(createBeanInstance),又会触发 A 的加载,此时,A 还没有添加到三级缓存中(A 实例还在调构造方法,还没创建成功),所以就会创建一个全新的 A。
这样,就会进入一个死循环。Spring 是解决不了这种情况下的循环依赖的。所以,提前进行了 check,并抛出了异常。
出现循环依赖异常之后如何解决?
可以在循环依赖注入的字段上加@Lazy注解
@Component public class AService { @Resource @Lazy private BService bService; @Async public void save() { } }
public A(@Lazy B b){ System.out.println("A的构造方法执行了..."); this.b = b ; }
@Lazy注解只对单例有用,对原型没有用。
当使用 @Lazy 注解时,Spring Boot会将Bean对象的初始化推迟到第一次使用时才进行。这样,当两个或多个Bean对象存在循环依赖时,其中一个Bean对象在初始化时可以直接引用另一个Bean对象的代理对象(未初始化的对象),而不需要等待另一个Bean对象完全初始化后再进行引用。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· DeepSeek “源神”启动!「GitHub 热点速览」
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· NetPad:一个.NET开源、跨平台的C#编辑器