深入理解 Spring finishBeanFactoryInitialization (二)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 深入理解 Spring finishBeanFactoryInitialization (二)

todo 总结这个类


在这个方法中,主要做了两件事:两件大事!!!


第一件大事:


在实例化Bean前,第一次调用后置处理器, 这件大事绝对是有历史意义的!!!为啥呢?大家想想,bean还没有创建呢!就已经可以插手bean的创建过程了,不是很刺激吗?接着看回调了什么后置处理器呢? Spring会循环所有的处理器检查当前被遍历的处理器是否是InstantiationAwareBeanPostProcessor类型的,如果是的话呢,就执行这个后置处理器的postProcessBeforeInstantiation(beanClass, beanName);方法

这个postProcessBeforeInstantiation()是允许有返回值的,大家可以想想,这一点是不是有点可怕? 事实也是这样,后置处理器的目的是为了增强对象,而我们却可以在这里返回一个任何东西,狸猫换台子替换掉原始的,还没有被创建的对象,还有一点,就是一旦我们在这里真的是没有返回null,那后续Spring就没有义务在去为我们创建本来应该创建的对象了,代码通过if-else的选择分支会使得当前的对象不再经历其他后置处理器的增强,最终执行它父类的postProcessorAfterInitialization()


补充一点,我们通过@EnableAspectjAutoProxy添加到Spring上下文中的AnnotationAwareAspectjAutoProxyCreator对象其实就是这个类型InstantiationAwareBeanPostProcessor,也就是说在这里这个接口的相关方法会被回调,下面看看他的实现类AbstractAutoProxyCreator对这个before()方法的重写实现,源码如下:


主要逻辑就是找出需要产生代理增强的bean(切面类),和普通的bean, 需要增强的bean放在advisedBeans里面,因为需要增强的bean是需要动态植入其他逻辑的,所以不放在一起

判断当前bean是否是基础类型的,比如: Advice PointCut Advisor AopInfrastructureBean 或者是 切面Aspectj 都算是基础类型,标注这些信息的类,是不会被增强的,标记false


注意啊,上面说的都是作用都是进行了一下标记


//todo 跟进来
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
  Object cacheKey = getCacheKey(beanClass, beanName);
  if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
    if (this.advisedBeans.containsKey(cacheKey)) {
      return null;
    }
    // todo 亮点就是在这里,  如果是我们切面类来到这里,条件是满足的
    // todo advicedBeans    见名知意: 通知beans
    // todo Spring用它标识,  被放在这个方法中的类,全部都不会被增强
    // todo 满足什么条件时,通过检查呢? 就是检查是否标记有 @Aspectj  @Before ... 等注解
    // todo 说的再直接一点, 就是返回了null, 表示当前的切面仍然需要按照正常的流程创建出来,但是这里进行标记了
    if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return null;
    }
  }
  // Create proxy here if we have a custom TargetSource. todo 如果我们有一个自定义的TargetSource,在这里创建代理
  // Suppresses unnecessary default instantiation of the target bean: // todo 抑制不必要的目标bean的默认实例化:
  // The TargetSource will handle target instances in a custom fashion. todo TargetSource将以自定义方式处理目标实例。
  TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
  if (targetSource != null) {
    if (StringUtils.hasLength(beanName)) {
      this.targetSourcedBeans.add(beanName);
    }
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
    Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
  }
  return null;
}


经过上面的标记,在哪里产生的代理对象呢?其实是在AbstractAutowireCapeableBeanFactory中的initializeBean()方法中实现的postProcessAfterInitialization()实现的,在本文的末尾展开讨论


第二件大事: 实例化对象, 继续跟进


protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
  if (logger.isDebugEnabled()) {
    logger.debug("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.
  // todo 做各种各样的属性值的赋值, 比如这种 通过Spring的Bean传递给Spring框架的值  ==> bd.setPropertyValue("aaa")
  Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
  if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    mbdToUse = new RootBeanDefinition(mbd);
    mbdToUse.setBeanClass(resolvedClass);
  }
  // Prepare method overrides.
  // todo 处理 lookup-method 和 replace-method 配置,Spring 将这两个配置统称为 override method
  try {
    mbdToUse.prepareMethodOverrides();
  }
  catch (BeanDefinitionValidationException ex) {
    throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
        beanName, "Validation of method overrides failed", ex);
  }
  try {
        // todo 在实例化之前完成一次解析操作,这也是
    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 {
    //todo 调用 doCreateBean 创建bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    return beanInstance;
  }
}


我们继续跟进doCreateBean(beanName, mbdToUse, args);方法, 同样是本类AbstarctAutowireCapableBeanFactory的方法,源码如下: 这个方法也是无与伦比的重要,那这个方法中做了什么事情呢?如下

  • 创建一个 BeanWrapper,用来存放bean+其他属性
  • 创建bean的实例,封装进上面的BeanWrapper中
  • 分两次调用处理处理器
  • 设置属性,填充属性
  • 经过AOP处理,将原生对象转换成Proxy
  • 返回BeanWrapper


因为这个方法简直太重要了,上面列举的每一点都值得我们仔细分析,我们每一条的分析都写在下面代码的下面


protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    // Instantiate the bean.
    // todo BeanWrapper 用来包装bean
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
      // todo 一开始 factoryBeanInstanceCache 这个map中是没有值的, 所以进入下面的if
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // todo !!!!!!!!这里获取出来的对象是原生对象!!!!!!!!!!!!
    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.isDebugEnabled()) {
        logger.debug("Eagerly caching bean '" + beanName +
            "' to allow for resolving potential circular references");
      }
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    // Initialize the bean instance.
    Object exposedObject = bean; // todo 到目前为止还是原生对象
      //todo  用来填充属性
      //设置属性,非常重要
      populateBean(beanName, mbd, instanceWrapper);
      // todo 经过AOP处理,原生对象转换成了代理对象,跟进去
      //执行后置处理器,aop就是在这里完成的处理
      exposedObject = initializeBean(beanName, exposedObject, mbd);
    return exposedObject;
  }


实例化对象


我把源码贴在了下面,下面方法的目的就是选出一个策略来实例化一个对象, 那有什么策略呢? 这就看程序员是怎么配置的了, 程序员可以配置工厂方法,指定构造方法,或者是程序员没有做出任何干涉,让Spring按自己的方式去实例化


protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    /**
     * todo 检测一个类的访问权限, Spring默认是 允许访问非public类型的方法
     */
    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
          "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }
    /**
     *  创建一个bean的快捷方式
     */
    boolean resolved = false;
    boolean autowireNecessary = false; // todo 是否是必须自动装配
    if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
        // todo
        if (mbd.resolvedConstructorOrFactoryMethod != null) {
          resolved = true;
          //如果已经解析了构造方法的参数,则必须要通过一个带参构造方法来实例
          autowireNecessary = mbd.constructorArgumentsResolved;
        }
      }
    }
    if (resolved) {
      if (autowireNecessary) {// todo 如果是需要自动注入的,就使用构造方法自动注入
        // 通过构造方法自动装配的方式构造 bean 对象
        return autowireConstructor(beanName, mbd, null, null);
      }
      else {
        //通过默认的无参构造方法进行
        //todo 通过默认的无参构造方法
        return instantiateBean(beanName, mbd);
      }
    }
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
        mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
      // todo 使用特定的构造方法完成自动装配
      return autowireConstructor(beanName, mbd, ctors, args);
    }
    // No special handling: simply use no-arg constructor.
    //todo 使用默认的无参构造方法进行初始化
    return instantiateBean(beanName, mbd);
  }


我们主要关注上面代码的


determineConstructorsFromBeanPostProcessors(beanClass, beanName)这个方法的目的就是推测实例化需要的构造方法, 为什么需要先推测构造方法呢? 因为Spring实例化对象时,需要使用特定的构造方法才能反射出对象,这时如果程序员指定了带参数的构造方法,spring就会使用这个构造方法实例化对象,如果程序员提供了一个不带任何参数的默认构造方法,Spring会忽略它,按自己的逻辑使用默认的无参构造


所以上面的if-else分支目的很明确,先是尝试获取全部的构造方法,然后看看有没有解析出来构造方法, 解析出来的话,就使用第一种逻辑,按照 特殊的构造方法模式进行处理,有解析出来,就使用默认的构造方法


我们进一步跟进这个determineConstructorsFromBeanPostProcessors(beanClass, beanName)方法,可以发现方法里面又是一波后置处理器的回调工作,这次选出的后置处理器的类型是SmartInstantiationAwareBeanPostProcessor,见名知意,这种处理器可以感知到心仪的构造方法,它的主要实现逻辑就是,查看这个将被实例化的对象中有没有添加了@Lookup注解的方法,有的话为这种方法生成代理,循环遍历所有的构造方法,看看这些构造方法上存在不存在@Value或者@Autowired注解,因为这些注解中存在required=true,只要存在这种注解,Spring就将他当成候选的构造方法,但是如果存在多个的话,Spring也不知道到底用哪一个,但是在这里Spring会将所有符合条件的都选出来,但是一般情况下,都可以正确的选出合适的构造


选择出合适构造方法之后,就根据不同的构造方法,选择使用不同的方式去实例化对象, 都有什么方式呢? 两种方式


方式1:

这是比较复杂的方式,此时Spring需要在这个方法内存比较好几个候选的构造方法,计算它们的差异值,最终值最小的构造函数就是将要用来实例化对象的构造函数,当然很可能是选不出合适的构造函数的,于是Spring没有立即抛出异常,而是将异常添加进bean工厂的suppressedExceptions这个set集合中


如果成功的选择出来一个构造函数,就会使用jdk原生的反射机制,实例化一个对象


autowireConstructor(beanName, mbd, ctors, args);


方式2:

直接使用JDK原生的反射机制,实例化一个对象


instantiateBean(beanName, mbd);


小结:


代码看到这里,方才说的有才华的那个类AbstactAutowiredCapatableBeanFactory中的doCreateBean()方法的instanceWrapper = createBeanInstance(beanName, mbd, args); 也就看完了, 到这里也就知道了,Spring会先把所有满足条件的bean全部实例化存放起来,这里的对象是百分百原生java对象,不掺水不含糖

相关文章
|
5月前
|
Java Spring
Spring中refresh分析之finishBeanFactoryInitialization方法详解
Spring中refresh分析之finishBeanFactoryInitialization方法详解
37 0
|
缓存 Java Spring
Spring IOC源码:finishBeanFactoryInitialization详解
Spring IOC源码:finishBeanFactoryInitialization详解
50 0
|
XML 缓存 Java
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean
Spring源码解析之八finishBeanFactoryInitialization方法即初始化单例bean 七千字长文深刻解读,Spirng中是如何初始化单例bean的,和面试中最常问的Spring是如何解决循环依赖?
|
缓存 Java Spring
Spring 源码学习 15:finishBeanFactoryInitialization(重点)
可以说前面的都是准备工作,而接下来开始的才是重点,在这一步会完成 BeanFactory 的初始化,同时实例化单例 Bean。 具体怎么操作的,那就一起阅读源码吧! 不过在阅读源码之前,还是需要了解一些知识的。 1. 什么是 FactoryBean ? 2. FactoryBean 是如何使用的 ? 3. Bean 是如何初始化的? 4. 常说的循环依赖是怎么解决的?
140 0
|
缓存 Java 中间件
Spring IoC源码学习:finishBeanFactoryInitialization 详解
在介绍了obtainFreshBeanFactory、invokeBeanFactoryPostProcessors、registerBeanPostProcessors 三个重要方法后,我们终于来到了最后一个重要方法:finishBeanFactoryInitialization。finishBeanFactoryInitialization是这四个方法中最复杂也是最重要的,是整个 Spring IoC 核心中的核心。
125 0
|
XML 缓存 Java
深入理解 Spring finishBeanFactoryInitialization (三)
深入理解 Spring finishBeanFactoryInitialization (三)
144 0
|
存储 Java 程序员
深入理解 Spring finishBeanFactoryInitialization (一)
深入理解 Spring finishBeanFactoryInitialization (一)
154 0
|
Java Spring 缓存
Spring Bean生命周期-finishBeanFactoryInitialization(九)
这个方法应该是ApplicationContext刷新的时候,最重要的方法了,因为所有的bean,如果不是lazy-init的都会在这一步进行实例化,并且做一些处理。
|
8天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
下一篇
无影云桌面