Java-第十五部分-源码解读-Bean生命周期、事务、循环依赖简略版

169 阅读10分钟

源码解读全文

Bean生命周期

  • Bean对象是由Spring创建的
  1. 使用类的构造方法创建对象
  2. 会为其内部的属性进行依赖注入(如果需要),在创建Bean的时候,通过反射生成对象,遍历每一个属性,查看这个属性是否被@Autowired修饰,如果有修饰,那么就进行赋值
  3. 进行初始化步骤
  4. 生成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使用指定构造方法
  • 如果该构造方法需要参数,且该参数是一个BeanSpring会从单例池中找当前参数,如果没有则创建
  1. 但是一个Bean通过不同的方式被注册,单例池中可能含有不同的key对应相同的类型
  2. Spring先通过类型进行查找,在通过名字决定是用哪个Bean先ByType,再ByName

单例池

  • Map 单例池来维护单例Bean,初始化大小为256 image.png
  • doGetBean中,尝试获取单例Bean image.png
  • 获取为空,会去创建 image.png
  • 创建完成后,加入单例池 image.png

初始化过程

  • 创建实例完成后,会进行初始化 image.png

初始化前

  • Spring会将方法上带有@PostConstruct加入集合
  • 进行初始化前的操作,@PostConstruct修饰的方法 image.png
  • getBeanPostProcessors实际上获取的是List<BeanPostProcessor>,维护处理器的集合,调用其初始化前的方法 image.png

初始化

  • 实现InitializingBean接口,自定义afterPropertiesSet方法
  • 初始化时,会检查Bean是否实现了该接口,进行调用 image.png

初始化后(AOP)

  • 会进行AOP,产生代理对象,本质上与被代理的类是两个对象,是父子类的关系
  • 生成的代理对象是没有被依赖注入的,经过AOP后,是没有依赖注入的环节,因此,属性为null image.png
  • AOP中,cglib使用父子类的形式实现,代理类继承目标类,其中同样包括一个目标类的对象实例,在对方法进行增强时,实际上是重写切点方法,使用目标类调用方法,在该方法前后调用切面方法 image.png
  • joinPoint连接点,可以获取target切面 image.png image.png

Aware

  • 根据继承不同的Aware会调用不同的回调方法,进行设置 image.png

事务

  • 事务生成的代理对象 image.png
  • 开启事务的过程,需要扫描方法上是否有@Transactional
  1. 利用事务管理器新建一个数据库连接,通过ThreadLocals进行连接传递
  2. 修改数据库连接的autocommit = false,让其不能自动提交
  3. jdbcTmplate获取事务建立的数据库连接,执行sql
  4. 根据前面是否有异常抛出决定commit/rollback
  • 设置autoCommit image.png

执行事务的过程

  • invokeWithinTransaction核心增强方法
  • 对真正要执行的sql进行环绕增强,如果有异常,需要completeTransactionAfterThrowing进行异常回滚,并清理事务 image.png
  • 在返回前,进行提交,而这个过程中会进行,如果有异常,则回滚 image.png

调用同样被事务注解的方法

  • 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(),那么对于以上的存储方式,jdbcTemplatetransactionManager对应的就不是一个数据源,就拿不到一个连接

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,以保证创建的是一个对象,在通过动态代理执行目标类的方法 image.png image.png

循环依赖

  • 两个对象互相需要依赖注入,在生成Bean的过程中,产生循环
  • Spring内部会通过三级缓存解决,DefaultSingletonBeanRegistry类中包含三级缓存
  1. Map<String, Object> singletonObjects单例池,第一级缓存,存储Bean对象
  2. Map<String, Object> earlySingletonObjects,第二级缓存,保存提早出现的单例对象,这些单例对象中的普通对象并没有进行完整初始化,提高性能,在循环依赖情况下保持单例
  3. Map<String, ObjectFactory<?>> singletonFactories,第三级缓存,打破循环依赖,在实例化后,保存普通对象的地址,方便获取
  4. Set<String> singletonsCurrentlyInCreation,保存正在创建过程的Bean,判定循环依赖

解决思路

  • ABeanBBean产生循环依赖注入,用map负责保存普通对象,当循环以来过程中,BBean单例池中找不到A对象,则去该map中获取普通对象,但是此时赋给BBean的是普通对象,如果A对象在初始化后进行AOP,那么A代理对象无法赋值给BBean
  • 如果将ABean生成过程中的AOP提前,可以保证注入BBean中的是A代理对象,但是不是所有Bean都需要进行提前AOP
  1. Spring需要判断Bean是否产生了循环依赖,需要提前AOP
  2. 用一个Set保存正在创建的Bean,在注入过程中,发生了循环依赖,那么就能在BBean的创建过程中,在注入ABean时,能够在Set中发现AABean,就能说明ABeanBBean发生了循环依赖
  3. 当发现了ABean发生了循环依赖,就需要为ABean进行提前AOP,保证注入BBean中的为A代理对象
  • 以上的问题
  1. 此时A代理对象中的A普通对象还不是完整的,没有经过完整的初始化,就不能放到单例池中
  2. 如果ABeanCBean也同时发生了循环依赖,就会导致再次生成了一个A代理对象,违反了单例的原则
  • 通过二级缓存earlySingletonObjects解决
  1. 当在单例池和Set中,都没有找到ABean,判断为循环以来后,先去二级缓存中找
  2. 如果没有,则表明第一次进行循环依赖的判定,在三级缓存中获取普通对象,进行提前AOP
  3. 放入二级缓存
  4. ABean处理完循环依赖的问题后,回到自身普通对象的初始化过程,不去做AOP的逻辑,而最终从二级缓存中获取A代理对象,放入单例池中,此时A代理对象的普通对象是经过完整初始化的
  • 在处理循环以来过程中,需要对A普通对象进行提前AOP,就需要提前获得A普通对象

从三级缓存中,获取A普通对象(实际上获取过程中就进行了AOP处理),打破循环

源码

  • 围绕AbstractAutowireCapableBeanFactory中的doCreateBean方法
  1. exposedObjectbean都是普通对象,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普通对象的早期引用,同时尝试从二级缓存移除,保证单例 image.png
  • 发生循环依赖,需要从三级缓存中获取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 image.png
  • 通过AOP去提前创建一个A代理对象,那么实际上,从三级缓存获取的就是A普通对象的代理对象 image.png

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 image.png image.png
  • doCreatBean中,最后会尝试去二级缓存中取代理对象,如果取得到,且没有发生改变,就返回这个代理对象,取不到证明没有发生循环依赖,那么就返回正常步骤的Bean image.png

Async和Lazy

  • @Async修饰方法,需要生成代理对象,不是基于AOP的,在处理循环依赖的过程中,不会经过AOP的过程,生成AOP的代理对象
  • 在后续的过程中,会经过Async的处理器,进行生成代理对象 image.png
  • @Lazy修饰属性,只创建了该属性的代理对象,并没有真正创建该对象本身,避免了循环依赖
  1. 单例情况,在Spring启动时,先避免了循环依赖生成了ABean,在顺利生成单例BBean,真正使用时进行替换
  2. 多例情况,在使用BBean时,才会真正创建ABean中的BBean
  • 构造方法发生循环构造,@Lazy修饰构造方法,同样在构造过程中,不会真正为其注入
  • @Lazy在一个类上加,就可以打破循环依赖