源码解读全文
Bean生命周期
Bean对象是由Spring创建的
- 使用类的构造方法创建对象
- 会为其内部的属性进行依赖注入(如果需要),在创建
Bean的时候,通过反射生成对象,遍历每一个属性,查看这个属性是否被@Autowired修饰,如果有修饰,那么就进行赋值- 进行初始化步骤
- 生成
Bean对象,如果是单例Bean,放入单例池
获取Bean的方式
getBean通过beanName找@AutoWired和构造传参,先通过类型,在通过beanName
Spring优先找OrderService及其子类,如果找到多个,但是找不到当前orderService123的,无法确定选择哪个类,报错
@Resource先通过beanName,在通过类型确定
Spring优先找OrderService123,如果不存在则找OrderService该类,如果OrderService有子类,那么就会报错也无法确定,是选哪个类,子类和本类都是OrderService
@Resource
private OrderService orderService123;
推断构造方法
- 面对多个构造方法,如果包含无参的构造方法,则调用无参的构造方法,如果没有无参的构造方法,则会报错
Caused by: java.lang.NoSuchMethodException: com.java.service.UserService.<init>()
- 在构造方法上加
@Autowired,相当于告诉Spring使用指定构造方法 - 如果该构造方法需要参数,且该参数是一个
Bean,Spring会从单例池中找当前参数,如果没有则创建
- 但是一个
Bean通过不同的方式被注册,单例池中可能含有不同的key对应相同的类型Spring先通过类型进行查找,在通过名字决定是用哪个Bean,先ByType,再ByName
单例池
- 用
Map 单例池来维护单例Bean,初始化大小为256 doGetBean中,尝试获取单例Bean- 获取为空,会去创建
- 创建完成后,加入单例池
初始化过程
- 创建实例完成后,会进行初始化
初始化前
Spring会将方法上带有@PostConstruct加入集合- 进行初始化前的操作,
@PostConstruct修饰的方法 getBeanPostProcessors实际上获取的是List<BeanPostProcessor>,维护处理器的集合,调用其初始化前的方法
初始化
- 实现
InitializingBean接口,自定义afterPropertiesSet方法 - 初始化时,会检查
Bean是否实现了该接口,进行调用
初始化后(AOP)
- 会进行
AOP,产生代理对象,本质上与被代理的类是两个对象,是父子类的关系 - 生成的代理对象是没有被依赖注入的,经过
AOP后,是没有依赖注入的环节,因此,属性为null AOP中,cglib使用父子类的形式实现,代理类继承目标类,其中同样包括一个目标类的对象实例,在对方法进行增强时,实际上是重写切点方法,使用目标类调用方法,在该方法前后调用切面方法joinPoint连接点,可以获取target和切面
Aware
- 根据继承不同的
Aware会调用不同的回调方法,进行设置
事务
- 事务生成的代理对象
- 开启事务的过程,需要扫描方法上是否有
@Transactional
- 利用事务管理器新建一个数据库连接,通过
ThreadLocals进行连接传递- 修改数据库连接的
autocommit = false,让其不能自动提交jdbcTmplate获取事务建立的数据库连接,执行sql- 根据前面是否有异常抛出决定
commit/rollback
- 设置
autoCommit
执行事务的过程
invokeWithinTransaction核心增强方法- 对真正要执行的
sql进行环绕增强,如果有异常,需要completeTransactionAfterThrowing进行异常回滚,并清理事务 - 在返回前,进行提交,而这个过程中会进行,如果有异常,则回滚
调用同样被事务注解的方法
Propagation.NEVER,如果当前有一个事务在运行,则会抛异常,但是实际情况是不会抛
test1由目标对象调用test后,进行间接调用的,是通过代理对象中的普通对象直接调用的,并没有机会能判断test1上的注解
@Transactional
public void test() {
jdbcTemplate.execute("update bank set money = money + 100 where name = 'zhangsan';");
test1();
}
@Transactional(propagation = Propagation.NEVER)
public void test1() {
jdbcTemplate.execute("update bank set money = money + 100 where name = 'zhangsan';");
}
- 可以通过自己注入自己去实现,让代理对象去调目标方法
@Transactional
public void test() {
System.out.println(orderService);
jdbcTemplate.execute("update bank set money = money + 100 where name = 'zhangsan';");
userService.test1();
}
Configuration注解对事务的影响
- 事务底层,通过
ThreadLocals<Map<Object, Object>>来保存一个线程-多个数据源-多个连接的关系 - 而在配置中,使用的是分别调用
dataSource(),那么对于以上的存储方式,jdbcTemplate和transactionManager对应的就不是一个数据源,就拿不到一个连接
DataSource dataSource如果作为参数传入,实际上,就通过依赖注入生成,是一个对象,就不用@Configuration
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
@Bean
public PlatformTransactionManager transactionManager() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager;
}
- 加了
@Configuration,产生的AppCofig配置对象,是基于动态代理生成的代理对象
代理对象调用
super.jdbcTemplate(),在执行前,会现在容器内找是否已经有这个对象(对于单例Bean)
- 过程中,会先调用
intercept方法,走createBean的逻辑,其中会判断是否为单例Bean,以保证创建的是一个对象,在通过动态代理执行目标类的方法
循环依赖
- 两个
对象互相需要依赖注入,在生成Bean的过程中,产生循环 Spring内部会通过三级缓存解决,DefaultSingletonBeanRegistry类中包含三级缓存
Map<String, Object> singletonObjects单例池,第一级缓存,存储Bean对象Map<String, Object> earlySingletonObjects,第二级缓存,保存提早出现的单例对象,这些单例对象中的普通对象并没有进行完整初始化,提高性能,在循环依赖情况下保持单例Map<String, ObjectFactory<?>> singletonFactories,第三级缓存,打破循环依赖,在实例化后,保存普通对象的地址,方便获取Set<String> singletonsCurrentlyInCreation,保存正在创建过程的Bean,判定循环依赖
解决思路
ABean与BBean产生循环依赖注入,用map负责保存普通对象,当循环以来过程中,BBean在单例池中找不到A对象,则去该map中获取普通对象,但是此时赋给BBean的是普通对象,如果A对象在初始化后进行AOP,那么A代理对象无法赋值给BBean- 如果将
ABean生成过程中的AOP提前,可以保证注入BBean中的是A代理对象,但是不是所有Bean都需要进行提前AOP
Spring需要判断Bean是否产生了循环依赖,需要提前AOP- 用一个
Set保存正在创建的Bean,在注入过程中,发生了循环依赖,那么就能在BBean的创建过程中,在注入ABean时,能够在Set中发现AABean,就能说明ABean和BBean发生了循环依赖- 当发现了
ABean发生了循环依赖,就需要为ABean进行提前AOP,保证注入BBean中的为A代理对象
- 以上的问题
- 此时
A代理对象中的A普通对象还不是完整的,没有经过完整的初始化,就不能放到单例池中- 如果
ABean与CBean也同时发生了循环依赖,就会导致再次生成了一个A代理对象,违反了单例的原则
- 通过二级缓存
earlySingletonObjects解决
- 当在单例池和
Set中,都没有找到ABean,判断为循环以来后,先去二级缓存中找- 如果没有,则表明第一次进行循环依赖的判定,在三级缓存中获取普通对象,进行提前
AOP- 放入二级缓存
ABean处理完循环依赖的问题后,回到自身普通对象的初始化过程,不去做AOP的逻辑,而最终从二级缓存中获取A代理对象,放入单例池中,此时A代理对象的普通对象是经过完整初始化的
- 在处理循环以来过程中,需要对
A普通对象进行提前AOP,就需要提前获得A普通对象
从三级缓存中,获取
A普通对象(实际上获取过程中就进行了AOP处理),打破循环
源码
- 围绕
AbstractAutowireCapableBeanFactory中的doCreateBean方法
exposedObject和bean都是普通对象,bean记录原始对象
面对循环依赖的提前准备
- 循环依赖解决的准备,支持循环依赖就会被放入三级缓存
// earlySingletonExposure 标志位
// 是单例 && 可以自动解决循环依赖 && 是在创建过程
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");
}
//加入三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
三级缓存的处理
addSingletonFactory,通过工厂形式获得Bean普通对象的早期引用,同时尝试从二级缓存移除,保证单例- 发生循环依赖,需要从三级缓存中获取
A普通对象时,会真正调用getEarlyBeanReference,过程中会将Bean加入earlyProxyReferences,进行维护
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
- 最终会调用
wrapIfNecessary - 通过
AOP去提前创建一个A代理对象,那么实际上,从三级缓存获取的就是A普通对象的代理对象
getSingleton
- 在依赖注入过程中会被调用,且经过了循环依赖的判定
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
// 单例池没有 && 且正在创建中 相当于出现了循环依赖
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//在二级缓存中找
singletonObject = this.earlySingletonObjects.get(beanName);
//二级缓存中没有 && 可以创建提早的普通对象
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) { //double-check,防止过程中其他线程创建
// Consistent creation of early reference within full singleton lock
//单例池
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//二级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
//三级缓存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//保存在二级缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
//三级缓存的对这个bean的使命就结束了,只要处理一次就好
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
如果进行了提前AOP
populateBean中会进行依赖注入,会调用getSingleton检查是否发生循环依赖- 处理完循环依赖后,
initializeBean进行到初始化后进行AOP时,执行到AbstractAutoProxyCreator中的postProcessAfterInitialization,会对earlyProxyReferences集合进行判断,该集合内的Bean不进行AOP doCreatBean中,最后会尝试去二级缓存中取代理对象,如果取得到,且没有发生改变,就返回这个代理对象,取不到证明没有发生循环依赖,那么就返回正常步骤的Bean
Async和Lazy
@Async修饰方法,需要生成代理对象,不是基于AOP的,在处理循环依赖的过程中,不会经过AOP的过程,生成AOP的代理对象- 在后续的过程中,会经过
Async的处理器,进行生成代理对象 @Lazy修饰属性,只创建了该属性的代理对象,并没有真正创建该对象本身,避免了循环依赖
- 单例情况,在
Spring启动时,先避免了循环依赖生成了ABean,在顺利生成单例BBean,真正使用时进行替换- 多例情况,在使用
BBean时,才会真正创建ABean中的BBean
- 构造方法发生循环构造,
@Lazy修饰构造方法,同样在构造过程中,不会真正为其注入 @Lazy在一个类上加,就可以打破循环依赖