前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring源码解析---一文讲透@Configuration

spring源码解析---一文讲透@Configuration

作者头像
一行Java
发布2022-04-06 16:32:58
4510
发布2022-04-06 16:32:58
举报

这是一篇长文,但是很自信的是我相信我讲的已经非常透彻了,想必你读完之后,一定对@Configuration有一个很透彻的理解。

@Configuration注解在实际的开发过程中,好像显的不是那么的重要,因为使用的过程中,加他,不加他,貌似都没有什么大的影响,那他存在到底有什么意义呢?我们来看一下下面的这段测试实例。

代码语言:javascript
复制
// 测试类A
public class A {
  public A(){
    System.out.println("create a");
  }
}

// 测试类B
public class B {
  public B(){
    System.out.println("create b");
  }
}

// 配置类
@Component
@Configuration
@ComponentScan("com.lupf.configuration")
public class AppConfig {

  @Bean
  public A a() {
    // 这里调用b()
    b();
    return new A();
  }

  @Bean
  public B b(){
    return new B();
  }

}

// 启动类
public class Test {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
    ac.register(AppConfig.class);
    ac.refresh();
  }
}

测试代码很简单,就是很普通的A与B两个类,然后通过@Bean的方式注入;我们在@Bean的a()方法中调用了b()方法,按方法的调用关系来看的话,这样就违背了spring单例的原则,直接调用b()方法的时候会new一个B;a()方法执行b();这一行代码的时候又会new一个B;那实际情况是不是这样呢?

  • 不加@Configuration的测试

和我们所想的是一样的,果然B被实例化了两次。很显然,他违背了单例原则;

  • 加上@Configuration的测试

wtf? 正常了。。。不过这才是应该有的样子。那加了注解,他到底经历了什么呢?

  • 我们来看一下加与不加 spring生成的AppConfig是什么样的?
代码语言:javascript
复制
// 以下代码获取AppConfig
AppConfig bean = ac.getBean(AppConfig.class);

代理对象?这样就可以解释了,这两个对象创建的过程被cglib代理了

图示代理流程

原理分析

结合上面的流程图,我们来一步步的分析;要关注的核心点就是这个代理对象如何产生的?长什么样?起什么用?

  • 伪代理对象原理分析
代码语言:javascript
复制
public class AppConfigProxy extends AppConfig{
  //bean工厂
  BeanFactory $$beanFactory;

  @Override
  public A a() {
    return super.a();
  }

  @Override
  public B b(){
    // 通过工厂去获取一个B
    B b = (B)$$beanFactory.getBean("b");
    // 如果拿到了,就直接返回
    if(null!=b){
      return b;
    }
    // 如果没有拿到,就调用父类的方法创建
    return super.b();
  }
}

代理对象重写了b()方法;这个b()方法并不是直接去new一个对象,而是先去Bean工厂里面去取一遍,如果取到之后,就直接返回,如果没有取到,那么就调用目标对象(父类方法,因为cglib是基于继承实现的)去实例化!原理是不是比较的容易理解!spring就是这么做的?对!他也是这么做的,只不过最终实现起来,并没有这么简单,因为我们是写死的,但对于spring框架来说,他并不知道你是一个什么类,做了哪些逻辑;下面一起从源码的角度去分析一下他是如何去代理的。

源码解析@Configuration扫描

  • 一,Bean的扫描判断是否是全注解类! 当spring通过Bean的后置处理器扫描类的时候,会去判断当前类是否是一个全注解的类;何为全注解?加了@Configuration注解的类就是一个全注解类;在执行Bean注册后置处理器BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法时,ConfigurationClassPostProcessor这个实现中会调用一个工具方法ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)),其中有一段代码是用来校验是否是全注解的
代码语言:javascript
复制
/**
 * 确定是不是一个全配置类  判断是否加了 @Configuration
 * @see AttributeAccessorSupport 保存到一个用于存储元数据的list中
 */
if (isFullConfigurationCandidate(metadata)) {
  // 往元素据的Map attributes中添加一个值为full的键值对
  beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
} else if (isLiteConfigurationCandidate(metadata)) {
  // 往元素据的Map attributes中添加一个值为lite的键值对
  beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
} else {
  return false;
}

//============================================================================
// isFullConfigurationCandidate工具类,很直观,就时判断是否加了@Configuration注解
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
  return metadata.isAnnotated(Configuration.class.getName());
}

//============================================================================
// isLiteConfigurationCandidate工具类
// 半注解的校验也很简单,就是看是否加了
// @Component、@ComponentScan、@Import、@ImportResource、@Bean这些注解
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
  // Do not consider an interface or an annotation...
  // 如果是一个接口直接返回false
  if (metadata.isInterface()) {
    return false;
  }

  /**
   * Any of the typical annotations found?
   * candidateIndicators包含以下四种注解
   * Component.class.getName();
   * ComponentScan.class.getName();
   * Import.class.getName();
   * ImportResource.class.getName();
   */
  for (String indicator : candidateIndicators) {
    if (metadata.isAnnotated(indicator)) {
      return true;
    }
  }

  // Finally, let's look for @Bean methods...
  try {
    // 查找是否有@Bean的方法
    return metadata.hasAnnotatedMethods(Bean.class.getName());
  } catch (Throwable ex) {
    if (logger.isDebugEnabled()) {
      logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
    }
    return false;
  }
}

其中最重要的一行判断metadata.isAnnotated(Configuration.class.getName())就确定了当前类是否是全注解。由于AppConfig是一个全注解的类,因此会在这个BeanDefinition的元数据Map中放一个值为full的键值对,作为一个标识。可是扯半天,又没有去做啥实际的操作,这有啥用?有用!!!下面的操作就会用上。以下为AppConfig扫描时的过程。

  • 二、完成代理 当后置处理器执行完BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法之后;会执行BeanFactoryPostProcessor的postProcessBeanFactory方法;这个接口的实现类ConfigurationClassPostProcessor就去完成了全注解对象的代理,代码如下:
代码语言:javascript
复制
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  int factoryId = System.identityHashCode(beanFactory);
  this.factoriesPostProcessed.add(factoryId);
  if (!this.registriesPostProcessed.contains(factoryId)) {
    processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
  }

  // 调用了该方法进行了属性增强
  enhanceConfigurationClasses(beanFactory);
  beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

//=============================================================================
// 继续走enhanceConfigurationClasses,代码如下
public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
  Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
  for (String beanName : beanFactory.getBeanDefinitionNames()) {
    BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);

    /**
     * 判断当前类是否是一个全注解的类
     * 这个时候,前面添加的值为full的键值对就用上了
     */
    if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
      configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
    }
  }
  if (configBeanDefs.isEmpty()) {
    // nothing to enhance -> return immediately
    return;
  }

  /**
   * 如果是一个全注解的类 那么下面的代码就会对全注解的类生成一个代理对象
   * 目的是为了解决一个依赖调用的问题
   */
  ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
  for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
    AbstractBeanDefinition beanDef = entry.getValue();
    // If a @Configuration class gets proxied, always proxy the target class
    beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
    try {
      // Set enhanced subclass of the user-specified bean class
      // 这里拿到目标对象(原始对象)的class
      Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);
      if (configClass != null) {
        // 这里实例化一个代理对象的class
        // 注意这里只是一个class 如果得到的是一个对象,那么将无法通过spring去管理这个对象
        // 拿到class之后,后续Bean的生命周期会将其实例化
        Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
        // 判断是否成功获取到代理对象的class
        // 如果获取成功,那么enhancedClass必定就是另外一个class对象了
        if (configClass != enhancedClass) {
          // 得到class之后 将这个class设置到对应的beanDefinition中
          // 目的是为了后面spring实例化对象
          beanDef.setBeanClass(enhancedClass);
        }
      }
    } catch (Throwable ex) {
      throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
    }
  }
}

为了篇幅,删掉了源码中部分无用代码;可以看出,方法一进来,就循环所有的BD判断当前类是不是一个全注解的类(判断条件就是前面添加的值为full的键值对),如果是,就将他放到一个容器里面;循环完没有一个全注解的类,就直接返回;如包含全注解的类,就通过Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);生成了一个代理Class,然后通过beanDef.setBeanClass(enhancedClass);替换原有的Class;这样就完成了下图所示的部分功能。 为什么这里是一个Class而不是一个Object呢?因为spring实例化对象的生命周期并不是在这里,如果这里直接创建一个Object,那这个Object是一个没有走标准生命周期的Object,所以也就不会被spring进行管理;这里生成一个Class,后续的流程会将这个Class实例化一个标准的Bean,放置到容器中。. 到此,一个被代理后的AppConfig对应的class就这么生成并返回了;但是到这里还并没有看到底裤,我们最终的目的可是要看到底裤,长驱直入!!!

代理类是如何产生的?

继续深入enhance方法。。。。

代码语言:javascript
复制
  public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {

    /**
     * 判断当前类是否已经被代理过了
     * 如果代理过了,cglib在生成代理对象的时候,会在代理类上面实现EnhancedConfiguration 方法
     */
    if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
      // 如果已经试代理对象了  就直接返回就好了
      return configClass;
    }
    /**
     * newEnhancer(configClass, classLoader) 表示实例化一个Enhancer对象
     * createClass 方法执行完 得到一个代理对象
     */
    Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
    // 返回代理对象
    return enhancedClass;
  }

发现他只是个花架子,但是已经发现核心代码了createClass(newEnhancer(configClass, classLoader));newEnhancer源码如下,这里就是整个代理对象的关键:

代码语言:javascript
复制
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
  // 实例化一个Enhancer对象
  Enhancer enhancer = new Enhancer();
  // 设置代理对象的父类  因为cglib代理是通过继承的方式实现的
  enhancer.setSuperclass(configSuperClass);
  // 标示用于增强的接口 代理对象回实现这个接口
  // 同时在代理对象生成的过程中 也会使用这个对象来判断是否已经生成过代理对象了
  enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});

  enhancer.setUseFactory(false);

  // 设置代理对象的命名规则
  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);

  /**
   * 设置一个策略
   * 当cglib生成一个代理对象的时候,会为这个对象添加一个$$beanFactory属性
   * 见代码: declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
   *
   * 该属性是用来获取bean对象的
   *
   * 这里只是添加了一个BeanFactory对象 会在后续的代码中给实例化
   *
   */
  enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));

  /**
   * 这里会添加回调的过滤器
   */
  enhancer.setCallbackFilter(CALLBACK_FILTER);
  enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
  return enhancer;
}
关键代码说明!
  • 设置代理对象实现的接口
代码语言:javascript
复制
enhancer.setInterfaces(new Class<?>[]{EnhancedConfiguration.class});

  // =================================================
  // 具体的EnhancedConfiguration接口类
  public interface EnhancedConfiguration extends BeanFactoryAware {
  }

这行代码会让最后生成的代理对象实现指定的EnhancedConfiguration接口;上面代码可以看出该接口是一个空接口,但是他的父类BeanFactoryAware有一个setBeanFactory(BeanFactory beanFactory)的接口;因此,实现EnhancedConfiguration接口的主要目的是为了后面给$$beanFactory赋值及赋值前生命周期流程中判断是否是一个代理对象所用;如果不知道$$beanFactory是个啥可以跳过,下面一段就是介绍这个$$beanFactory是咋来的

  • 设置策略
代码语言:javascript
复制
enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));

// 以下为BeanFactoryAwareGeneratorStrateg对象
private static class BeanFactoryAwareGeneratorStrategy extends DefaultGeneratorStrategy {

  @Nullable
  private final ClassLoader classLoader;

  public BeanFactoryAwareGeneratorStrategy(@Nullable ClassLoader classLoader) {
    this.classLoader = classLoader;
  }

  @Override
  protected ClassGenerator transform(ClassGenerator cg) throws Exception {
    ClassEmitterTransformer transformer = new ClassEmitterTransformer() {
      @Override
      public void end_class() {
        declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null);
        super.end_class();
      }
    };
    return new TransformingClassGenerator(cg, transformer);
  }

  @Override
  public byte[] generate(ClassGenerator cg) throws Exception {
    if (this.classLoader == null) {
      return super.generate(cg);
    }

    Thread currentThread = Thread.currentThread();
    ClassLoader threadContextClassLoader;
    try {
      threadContextClassLoader = currentThread.getContextClassLoader();
    } catch (Throwable ex) {
      // Cannot access thread context ClassLoader - falling back...
      return super.generate(cg);
    }

    boolean overrideClassLoader = !this.classLoader.equals(threadContextClassLoader);
    if (overrideClassLoader) {
      currentThread.setContextClassLoader(this.classLoader);
    }
    try {
      return super.generate(cg);
    } finally {
      if (overrideClassLoader) {
        // Reset original thread context ClassLoader.
        currentThread.setContextClassLoader(threadContextClassLoader);
      }
    }
  }
}

这里面有一段很关键的代码 declare_field(Constants.ACC_PUBLIC, BEAN_FACTORY_FIELD, Type.getType(BeanFactory.class), null); 就是为这个代理对象添加一个名叫$$beanFactoryBeanFactory属性(下图为证),对应的实现为DefaultListableBeanFactory,其目的就是用来获取Bean,是不是和最开始我们构想的伪代码有异曲同工之妙!可是那这玩儿动态加进去的,在哪里给$$beanFactory赋值的呢?您接着往后看

  • 添加回调过滤器
代码语言:javascript
复制
/**
 * 这里会添加回调的过滤器
 */
enhancer.setCallbackFilter(CALLBACK_FILTER);

// CALLBACK_FILTER是啥?
private static final ConditionalCallbackFilter CALLBACK_FILTER = new ConditionalCallbackFilter(CALLBACKS);

// CALLBACKS是个啥?
private static final Callback[] CALLBACKS = new Callback[]{
    // 在这里,执行方法的时候,就会优先去判断,对象是否已经创建
    // 创建之后就返回,没有创建就调用父接口创建
    new BeanMethodInterceptor(),

    // 在这里 会通过反射的方式 给$$beanFactory赋值
    new BeanFactoryAwareMethodInterceptor(),
    NoOp.INSTANCE
};

从上面的代码可以看出,其最终是添加了两个方法执行的拦截器;这两个方法执行的拦截器非常重要;BeanFactoryAwareMethodInterceptor主要的职责就是为$$beanFactory对象赋值;BeanMethodInterceptor的主要功能是判断Bean是否已经创建了;具体见下面的详细分析

  • BeanFactoryAwareMethodInterceptor 该MethodInterceptor的主要功能就是通过反射的方式,给上面添加的$$beanFactory赋值,具体代码如下:
代码语言:javascript
复制
 private static class BeanFactoryAwareMethodInterceptor implements MethodInterceptor, ConditionalCallback {

      @Override
      @Nullable
      public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
          // 找到$$beanFactory对应的field
          Field field = ReflectionUtils.findField(obj.getClass(), BEAN_FACTORY_FIELD);
          Assert.state(field != null, "Unable to find generated BeanFactory field");
          // 通过反射为$$beanFactory赋值
          field.set(obj, args[]);

          // Does the actual (non-CGLIB) superclass implement BeanFactoryAware?
          // If so, call its setBeanFactory() method. If not, just exit.
          if (BeanFactoryAware.class.isAssignableFrom(ClassUtils.getUserClass(obj.getClass().getSuperclass()))) {
              return proxy.invokeSuper(obj, args);
          }
          return null;
      }

      @Override
      public boolean isMatch(Method candidateMethod) {
          // 这里会去判断是否是setBeanFactory方法,如果是,才会执行上面的intercept 方法
          return isSetBeanFactory(candidateMethod);
      }

      public static boolean isSetBeanFactory(Method candidateMethod) {
          return (candidateMethod.getName().equals("setBeanFactory") &&
                  candidateMethod.getParameterCount() ==  &&
                  BeanFactory.class == candidateMethod.getParameterTypes()[] &&
                  BeanFactoryAware.class.isAssignableFrom(candidateMethod.getDeclaringClass()));
      }
  }

isSetBeanFactory(candidateMethod);这个判断需要注意一下,因为他的存在,约束了这个拦截器就只会去处理setBeanFactory这个方法的调用;具体的赋值其实已经很简单了,找到Field,然后通过反射去赋值;但是又带出了一个更重要的问题,这个方法什么时候调用的?

  • setBeanFactory是什么时候调用的? 讨论这个问题之前,我们需要往前回顾一点东西,就是ConfigurationClassPostProcessor的postProcessBeanFactory方法通过调用enhanceConfigurationClasses()去完成了代理对象的生成之后,还做了一些事情;如果不记得了,这里我在把代码再贴一遍:
代码语言:javascript
复制
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
      int factoryId = System.identityHashCode(beanFactory);
      this.factoriesPostProcessed.add(factoryId);
      if (!this.registriesPostProcessed.contains(factoryId)) {
          processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
      }

      // 调用了该方法进行了增强
      enhanceConfigurationClasses(beanFactory);

      /**
       * 这里的后处理器的作用就是给实现了EnhancedConfiguration接口的bean设置BeanFactory对象
       * 如果是全注解Bean,生成的代理Class都实现了EnhancedConfiguration接口
       */
      beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
  }

可以看到,当完成了代理class生成之后,就往Bean工厂里面添加了一个叫ImportAwareBeanPostProcessor的后置处理器,这个后置处理器就是调用setBeanFactory的关键,其中postProcessProperties方法(其他的方法暂时不重要)的具体代码如下:

代码语言:javascript
复制
 @Override
  public PropertyValues postProcessProperties(@Nullable PropertyValues pvs, Object bean, String beanName) {
      if (bean instanceof EnhancedConfiguration) {
          ((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
      }
      return pvs;
  }

代码也很简单明了,就是判断bean是不是实现了EnhancedConfiguration接口,如果实现了就调用setBeanFactory方法,这样也就和前面说的为什么要实现这个接口的原因呼应上了;现在是调用的地方找到了,那ImportAwareBeanPostProcessor这个后置处理器又是在哪里调用的呢?这里就涉及到Bean生命周期了,详细细节很难一两句说清楚,那么这里就直接给个结论,后续会对spring bean生命周期进行详细的介绍。如果让你为一个对象去调用set方法赋值,你会在什么地方去做?当然是在对象创建之后撒!spring也是一样,spring通过doCreateBean方法创建Bean的时,对象创建完会通过populateBean方法去完成属性注入,注入时有一段代码就是来调用这个后置处理器的,详情如下:

代码语言:javascript
复制
for (BeanPostProcessor bp : getBeanPostProcessors()) {
      if (bp instanceof InstantiationAwareBeanPostProcessor) {
          InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
              /**
           * 在这里通过bean的后置处理器去完成依赖注入
           * 如果使用的是@Autowried进行注入的时候,使用的是 {@link AutowiredAnnotationBeanPostProcessor} 进行属性的注入
           * @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
           *
           * 如果使用的xml配置的,那么就使用的是 CommonAnnotationBeanPostProcessor 进行属性的注入
           * @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
           */
          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;
      }
  }

当AppConfig对象实例化成功之后,去注入属性时,就会执行上面的for;而ImportAwareBeanPostProcessor就是要执行的后置处理器的其中一个,自然也就会调用setBeanFactory完成了AppConfig代理对象中$$beanFactory的填充。

  • BeanMethodInterceptor 继续回来介绍另外一个拦截器,他的主要职责就是通过$$beanFactory去判断对象是否已经生成了,由于代码比较多,这里只贴intercept的部分(无用代码已经删除)
代码语言:javascript
复制
 public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
                          MethodProxy cglibMethodProxy) throws Throwable {

      // 得到beanFactory
      ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
      // 拿到beanName
      String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);

      // Determine whether this bean is a scoped-proxy
      if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
          String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
          if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
              beanName = scopedBeanName;
          }
      }

      /**
       * 这里是用来判断当前的Bean是不是一个FactoryBean 如果是的话,那么就先创建FactoryBean
       * 然后再创建Bean
       */
      if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
              factoryContainsBean(beanFactory, beanName)) {
          Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
          if (factoryBean instanceof ScopedProxyFactoryBean) {
              // Scoped proxy factory beans are a special case and should not be further proxied
          } else {
              // It is a candidate FactoryBean - go ahead with enhancement
              return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
          }
      }

      if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
          // 如果返回的是同一个方法,说明正在实例化
          // 那么就调用目标对象的方法
          return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
      }

      // 如果不是一个   那么就去获取bean
      return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
  }
  • beanFactory获取
代码语言:javascript
复制
 ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);

 // 核心代码 通过反射获取
 Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
 Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
  • beanName获取 通过拿到Mathod上面的@Bean注解,从注解中取到name属性
  • beanFactory判断 优先判断是否是一个beanFactory,如果是,先创建beanFactory,再获取Bean
  • 核心判断,校验是否已经创建对象了
代码语言:javascript
复制
if (isCurrentlyInvokedFactoryMethod(beanMethod))

  //====================================
  private boolean isCurrentlyInvokedFactoryMethod(Method method) {
      // 获取到当前是谁在调用对应的方法
      Method currentlyInvoked = SimpleInstantiationStrategy.getCurrentlyInvokedFactoryMethod();
      // 校验method是否和currentlyInvoked为同一个方法
      return (currentlyInvoked != null && method.getName().equals(currentlyInvoked.getName()) &&
              Arrays.equals(method.getParameterTypes(), currentlyInvoked.getParameterTypes()));
  }

通过以下示例代码讲解这个判断,注意,这里很重要,也是理解这块儿东西的关键

代码语言:javascript
复制
@Bean
public A a() {
    // 这里调用b()
    b();
    return new A();
}

@Bean
public B b(){
    return new B();
}

当spring创建@Bean对象的时候,会直接调用对应的方法,如创建对象B,就会直接调用appConfig.b();上面的那个判断的逻辑就是创建这个对象的方法和调用当前的方法是不是同一个;这里创建B对象的时候,创建对象的方法是b(),调用者也是b(),所以判断返回的为true,就是执行了cglibMethodProxy.invokeSuper调用目标方法去实例化一个对象; 当执行a()方法的时候,同样会经历上面的判断,发现调用者和实例化的方法都是a();调用目标方法;但是(注意转折了哈!),执行目标方法的时候,第一行就是去调用b();此时就来到了b()方法的调用,因为拦截器拦截的原因这里也会触发这个intercept方法;但是现在就和之前不一样了,现在的调用者是谁?是a()方法,执行实例化的是谁?是b()方法!这样上面的if判断返回的就是false,就会让流程走到resolveBeanReference方法去获取Bean,如果有就直接从容器里面返回,没有就去实例化一个;就这样,就算a()方法中会调用b();结果发现容器里面已经有一个了,就直接返回了。如此骚操作,就可以保证对象只会创建一个,如果你们没有想明白,麻烦把这一段话仔细读几遍,应该就能明白了

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 一行Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 图示代理流程
  • 原理分析
  • 源码解析@Configuration扫描
  • 代理类是如何产生的?
    • 关键代码说明!
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档