Spring 核心类 ConfigurationClassPostProcessor 流程讲解及源码全面分析(三)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring 核心类 ConfigurationClassPostProcessor 流程讲解及源码全面分析(三)

解析父类

if (sourceClass.getMetadata().hasSuperClass()) {
    String superclass = sourceClass.getMetadata().getSuperClassName();
    if (superclass != null && !superclass.startsWith("java") &&
        !this.knownSuperclasses.containsKey(superclass)) {
        this.knownSuperclasses.put(superclass, configClass);
        // Superclass found, return its annotation metadata and recurse
        return sourceClass.getSuperClass();
    }
}

解析父类,如果被解析的配置类继承了某个类,那么配置类的父类也会被进行解析

对父类进行判别其是否需要处理:父类不是以 java 开头的、父类集合中未存在(防止重复处理)

满足以后返回到外层方法:doProcessConfigurationClass,do-while 循环

do {
  // 解析各种注解
  sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);

processConfigBeanDefinitions 后置结束工作

parser#validate

解析完的 Configuration 配置类进行校验

public void validate(ProblemReporter problemReporter) {
  // 获取 @Configuration 注解的属性值,除非配置类声明为 proxyBeanMethods=false 不适用 CGLIB 代理模式,否则的话不可能为 final 类
  Map<String, Object> attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());
  if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {
    // 如果配置类是final类型,则抛出异常
    if (this.metadata.isFinal()) {
      problemReporter.error(new FinalConfigurationProblem());
    }
    // 校验配置类中@Bean定义的方法
    for (BeanMethod beanMethod : this.beanMethods) {
      beanMethod.validate(problemReporter);
    }
  }
}

ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

在此方法调用之前,注入的 BeanDefinition 都是 Configuration 配置类中定义的内部 bean,因为配置类依赖于这些内部的实例,所以后面这部分工作就是处理配置类 BD 加载工作.

// 获取所有的 bean,包括扫描的 bean 对象,@Import 导入的 bean 对象
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 清除掉已经解析处理过的配置类
configClasses.removeAll(alreadyParsed);
// 判断读取器是否为空,如果为空的话,就创建完全填充好的 ConfigurationClass 实例的读取器
if (this.reader == null) {
  this.reader = new ConfigurationClassBeanDefinitionReader(
      registry, this.sourceExtractor, this.resourceLoader, this.environment,
      this.importBeanNameGenerator, parser.getImportRegistry());
}
// 核心方法,将完全填充好的ConfigurationClass实例转化为BeanDefinition注册入IOC容器
this.reader.loadBeanDefinitions(configClasses);
// 添加到已经处理的集合中
alreadyParsed.addAll(configClasses);
candidates.clear();

判断读取器 ConfigurationClassBeanDefinitionReader 实例是否为空,若为空的话,就创建完全填充好的 ConfigurationClass 实例的读取器

reader#loadBeanDefinitions:核心方法,将完全填充好的 ConfigurationClass 实例转化为 BeanDefinition 注入 IOC 容器,循环遍历调用 loadBeanDefinitionsForConfigurationClass 方法

private void loadBeanDefinitionsForConfigurationClass(
    ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
  if (trackedConditionEvaluator.shouldSkip(configClass)) {
    String beanName = configClass.getBeanName();
    if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
      this.registry.removeBeanDefinition(beanName);
    }
    this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
    return;
  }
  // 若 bean 通过 @Import(ImportSelector) 方式添加到容器中的,那么此时 configClass#isImported 返回的是 true
  // configClass.importedBy 属性里面存储的是 ConfigurationClass 就是将 bean 导入的类
  if (configClass.isImported()) {
    registerBeanDefinitionForImportedConfigurationClass(configClass);
  }
  // 判断当前 bean 是否含有 @Bean 注解方法,如果有,需要把这些方法产生 bean 放入到 BeanDefinitionMap 当中
  for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
  }
  // 将 @ImportResource 引入的资源注入 IOC 容器
  loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
  // 如果 bean 上存在 @Import 注解,且 import 是一个实现了 ImportBeanDefinitionRegistrar 接口,则执行 ImportBeanDefinitionRegistrar#registerBeanDefinitions 方法
  loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

registerBeanDefinitionForImportedConfigurationClass 方法

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
  AnnotationMetadata metadata = configClass.getMetadata();
  // 定义 bd 实例
  AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
  // 设置 scope 属性以及通过生成器生成 beanName、公共的属性值信息
  ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
  configBeanDef.setScope(scopeMetadata.getScopeName());
  String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
  AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
  definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
  configClass.setBeanName(configBeanName);
  if (logger.isTraceEnabled()) {
    logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
  }
}

处理被 @Import 导入的类,并且是没有加配置注解的【@Component、@Confuguration】否则会被先识别的配置类所替代,因为同样的配置类是只允许出现一个的

@Import 注解导入的类中如果不是 DeferredImportSelector 或 ImportBeanDefinitionRegistrar 这个类型的话,就会当作是一个普通的配置类进行 BeanDefinition 注入,importBy 属性值就是导入的类

loadBeanDefinitionsForBeanMethod 方法

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
  ConfigurationClass configClass = beanMethod.getConfigurationClass();
  // 获取方法元数据
  MethodMetadata metadata = beanMethod.getMetadata();
  // 获取方法名称
  String methodName = metadata.getMethodName();
  // 是否标记为当前 @Bean 需要被跳过
  if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
    configClass.skippedBeanMethods.add(methodName);
    return;
  }
  if (configClass.skippedBeanMethods.contains(methodName)) {
    return;
  }
  // 获取bean注解的属性
  AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
  Assert.state(bean != null, "No @Bean annotation attributes");
  // 获取别名
  List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
  String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
  for (String alias : names) {
    // 注册剩下的别名
    this.registry.registerAlias(beanName, alias);
  }
  // 是否存在同名 bean 定义,这实际上已经覆盖之前(例如通过XML)
  if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
    if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
      throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
          beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
          "' clashes with bean name for containing configuration class; please make those names unique!");
    }
    return;
  }
  // 封装为ConfigurationClassBeanDefinition,表示是来自配置类里的bean定义
  ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata, beanName);
  // 设置来源的类
  beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
  // 判断是否是静态的,设置 BeanClass
  if (metadata.isStatic()) {
    if (configClass.getMetadata() instanceof StandardAnnotationMetadata) {
      beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());
    }
    else {
      beanDef.setBeanClassName(configClass.getMetadata().getClassName());
    }
    beanDef.setUniqueFactoryMethodName(methodName);
  }
  else {
    // instance @Bean method
    // 设置工厂名
    beanDef.setFactoryBeanName(configClass.getBeanName());
    beanDef.setUniqueFactoryMethodName(methodName);
  }
  // 如果方法元数据是标准方法元数据的话,就设置解析的工厂方法
  if (metadata instanceof StandardMethodMetadata) {
    beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());
  }
  // 设置自定义装配模式,默认是构造器
  beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
  // 设置略过属性检查
  beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
      SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
  // 处理通用注解,注解里可能还有自动装配注解
  AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
  // 获取自动装配枚举信息
  Autowire autowire = bean.getEnum("autowire");
  if (autowire.isAutowire()) {
    //如果是自动装配,也就是BY_NAME 或者 BY_TYPE,再设置了一次自动装配模式
    beanDef.setAutowireMode(autowire.value());
  }
  // 自动装配候选,默认是true
  boolean autowireCandidate = bean.getBoolean("autowireCandidate");
  if (!autowireCandidate) {
    beanDef.setAutowireCandidate(false);
  }
  // 初始化方法 @PostConstruct、@PreDestroy 或 XML 或 InitializingBean、DisposableBean 接口
  String initMethodName = bean.getString("initMethod");
  if (StringUtils.hasText(initMethodName)) {
    beanDef.setInitMethodName(initMethodName);
  }
  // 销毁方法
  String destroyMethodName = bean.getString("destroyMethod");
  beanDef.setDestroyMethodName(destroyMethodName);
  // 处理作用域
  ScopedProxyMode proxyMode = ScopedProxyMode.NO;
  AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
  if (attributes != null) {
    beanDef.setScope(attributes.getString("value"));
    proxyMode = attributes.getEnum("proxyMode");
    if (proxyMode == ScopedProxyMode.DEFAULT) {
      proxyMode = ScopedProxyMode.NO;
    }
  }
  // 如果必要的话就替换原来 bean 定义与目标信息
  BeanDefinition beanDefToRegister = beanDef;
  // 如果作用域不是no的话就要使用代理
  if (proxyMode != ScopedProxyMode.NO) {
    BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
        new BeanDefinitionHolder(beanDef, beanName), this.registry,
        proxyMode == ScopedProxyMode.TARGET_CLASS);
    beanDefToRegister = new ConfigurationClassBeanDefinition(
        (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
  }
  if (logger.isTraceEnabled()) {
    logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
        configClass.getMetadata().getClassName(), beanName));
  }
  this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

将 @Bean 注解方法元数据取出来分析,分析有没有别名、有没有跟 xml 配置冲突,封装成一个configurationClassBeanDefinition,然后设置工厂方法名,获取 bean 注解的属性,设置初始化方法,销毁方法,是否自动装配,是否需要代理等,最后将 BD 信息加入到 BeanDefinitionMap、BeanDefinitionNames 集合中

loadBeanDefinitionsFromImportedResources 方法

private void loadBeanDefinitionsFromImportedResources(
    Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
  Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
  importedResources.forEach((resource, readerClass) -> {
    // 选择默认的读取器类型
    if (BeanDefinitionReader.class == readerClass) {
      if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
        // When clearly asking for Groovy, that's what they'll get...
        readerClass = GroovyBeanDefinitionReader.class;
      }
      else {
        // Primarily ".xml" files but for any other extension as well
        readerClass = XmlBeanDefinitionReader.class;
      }
    }
    BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
    if (reader == null) {
      try {
        // 初始化 reader 读取器实例
        reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
        // Delegate the current ResourceLoader to it if possible
        if (reader instanceof AbstractBeanDefinitionReader) {
          AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
          abdr.setResourceLoader(this.resourceLoader);
          abdr.setEnvironment(this.environment);
        }
        readerInstanceCache.put(readerClass, reader);
      }
      catch (Throwable ex) {
        throw new IllegalStateException(
            "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
      }
    }
    // 该方法是 XML 方式注入 Bean 核心的解析工作步骤
    reader.loadBeanDefinitions(resource);
  });
}

加载 ImportResources 中引入的 xml 文件,有两种加载类 GroovyBeanDefinitionReader、XmlBeanDefinitionReader,不指定默认采用的就是 XmlBeanDefinitionReader 读取器,然后用这个 reader 去加载 xml 资源,XmlBeanDefinitionReader#loadBeanDefinitions 核心方法博主在介绍 refresh 方法流程时有详细分析

ending

最后,还有一步保证措施需要执行,主要是为了检测 ConfigurationClassBeanDefinitionReader#loadBeanDefinitions 是否还有其他的配置类进行了引入

if (registry.getBeanDefinitionCount() > candidateNames.length) {
  String[] newCandidateNames = registry.getBeanDefinitionNames();
  Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
  Set<String> alreadyParsedClasses = new HashSet<>();
  for (ConfigurationClass configurationClass : alreadyParsed) {
    alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
  }
  // 如果有未解析的类,则将其添加到 candidates 中,这样 candidates 不为空,就会进入到下一次的 while 的循环中
  for (String candidateName : newCandidateNames) {
    if (!oldCandidateNames.contains(candidateName)) {
      BeanDefinition bd = registry.getBeanDefinition(candidateName);
      if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
          !alreadyParsedClasses.contains(bd.getBeanClassName())) {
        candidates.add(new BeanDefinitionHolder(bd, candidateName));
      }
    }
  }
  candidateNames = newCandidateNames;
}
  1. 判断 registry.getBeanDefinitionCount() > candidateNames.length 目的:为了知道 reader.loadBeanDefinitions(configClasses) 这一步有没有向 BeanDefinitionMap 中添加新的 BeanDefinition
  2. 实际上就是看配置类,例如:AppConfig 类「Spring ImportTests 测试内部静态类」会向 BeanDefinitionMap 中添加 bean,这里的 AppConfig 类向容器中添加的 bean,实际上在 parser#parse 方法 这一步已经全部被解析好了,只是待塞入到 bdNames/bdMaps 集合中
  3. 如果有,registry.getBeanDefinitionCount 就会大于 candidateNames.length,这样就需要再次遍历新加入的 BeanDefinition,并判断这些 bean 是否已经被解析过了,如果未解析,需要重新进行解析

至此,ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry 方法已经全部分析完成

postProcessBeanFactory 内核心方法

该方法也是在 refresh 中 invokeBeanFactoryPostProcessors 方法会进行调用的,优先调用 BDRPP 接口 postProcessBeanDefinitionRegistry 方法,然后再调用 BFPP 接口 postProcessBeanFactory 方法

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  int factoryId = System.identityHashCode(beanFactory);
  if (this.factoriesPostProcessed.contains(factoryId)) {
    throw new IllegalStateException(
        "postProcessBeanFactory already called on this post-processor against " + beanFactory);
  }
  this.factoriesPostProcessed.add(factoryId);
  if (!this.registriesPostProcessed.contains(factoryId)) {
    // BeanDefinitionRegistryPostProcessor 钩子显然不支持
    // 简单调用 processConfigurationClasses 懒加载在这一点上
    processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
  }
  // 为满足条件的 @Configuration 注解标识的类进行动态代理,生成的代理对象
  enhanceConfigurationClasses(beanFactory);
  // ImportAwareBeanPostProcessor 处理:对代理的配置类注入 BeanFactory,方便后续从该对象中获取到容器中的实例对象以及支持获取注解的元数据信息
  beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

从以上代码中可以看出,processConfigBeanDefinitions 方法在前面已经仔细分析过了,在这边主要介绍的核心方法就是 enhanceConfigurationClasses:对配置类生成代理子类,后续在获取 bean 实例时直接拦截调用,实现单例

enhanceConfigurationClasses 方法

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
  Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();
  for (String beanName : beanFactory.getBeanDefinitionNames()) {
    BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);
    Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);
    MethodMetadata methodMetadata = null;
    if (beanDef instanceof AnnotatedBeanDefinition) {
      methodMetadata = ((AnnotatedBeanDefinition) beanDef).getFactoryMethodMetadata();
    }
    if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {
      // // 配置类(full or lite)或配置类派生的 @Bean 方法,在这点上找到 bean 具体类型
      AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;
      if (!abd.hasBeanClass()) {
        try {
          abd.resolveBeanClass(this.beanClassLoader);
        }
        catch (Throwable ex) {
          throw new IllegalStateException(
              "Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
        }
      }
    }
    // 若 configurationClass 属性为 full,满足这个条件塞入到前面的集合中,后面的代码会对这个配置类进行代理增强处理
    if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {
      if (!(beanDef instanceof AbstractBeanDefinition)) {
        throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +
            beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
      }
      else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
        logger.info("Cannot enhance @Configuration bean definition '" + beanName +
            "' since its singleton instance has been created too early. The typical cause " +
            "is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +
            "return type: Consider declaring such methods as 'static'.");
      }
      configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);
    }
  }
  // 没有需要增强的配置类,立即返回
  if (configBeanDefs.isEmpty()) {
    return;
  }
  // 配置类动态代理增强的核心类
  ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
  for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
    AbstractBeanDefinition beanDef = entry.getValue();
    // 若 @Configuration 配置类被代理,就以目标类 CGLIB 进行代理增强
    beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
    // 为指定的配置类设置代理增强子类
    Class<?> configClass = beanDef.getBeanClass();
    Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
    if (configClass != enhancedClass) {
      if (logger.isTraceEnabled()) {
        logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +
            "enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));
      }
      beanDef.setBeanClass(enhancedClass);
    }
  }
}
  1. 先获取到 BeanDefinition 信息 configurationClass 属性,若当前 BeanDefinition 未找到具体的类型,这边通过类加载器进行加载找到
  2. 判断该属性值是否等于 full,若等于说明当前就是配置类,然后将其加入到待动态代理增强的集合中,该属性值如何设置的,下面再回顾一下:
// ConfigurationClassUtils#checkConfigurationClassCandidate 方法中
// proxyBeanMethods=true 就会设置为 full
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
  beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 若 bean 被 @Configuration 注解标注,且属性 proxyBeanMethods 为 false(使用代理模式),则将 bean 定义记为 lite
else if (config != null || isConfigurationCandidate(metadata)) {
  beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
  return false;
} 
  1. 遍历待动态代理增强的集合,挨个元素进行动态代理实现,生成 CGLIB 动态代理类然后再覆盖到当前的 beanClass 属性,等类初始化、实例化时再进行创建
public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
  if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
    if (logger.isDebugEnabled()) {
      logger.debug(String.format("Ignoring request to enhance %s as it has " +
          "already been enhanced. This usually indicates that more than one " +
          "ConfigurationClassPostProcessor has been registered (e.g. via " +
          "<context:annotation-config>). This is harmless, but you may " +
          "want check your configuration and remove one CCPP if possible",
          configClass.getName()));
    }
    return configClass;
  }
  // newEnhancer:创建 CGLIB 必备的 enhancer 实例设置好相关参数,createClass:注入实例
  Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
  if (logger.isTraceEnabled()) {
    logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
        configClass.getName(), enhancedClass.getName()));
  }
  return enhancedClass;
}
/**
 * 创建新的 CGLIB 实例
 */
private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
  Enhancer enhancer = new Enhancer();
  enhancer.setSuperclass(configSuperClass);
  enhancer.setInterfaces(new Class<?>[] {EnhancedConfiguration.class});
  enhancer.setUseFactory(false);
  // 名称生成策略器,
  enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  // 自定义的动态代理类生成策略
  enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
  // 回调拦截器类:BeanMethodInterceptor、BeanFactoryAwareMethodInterceptor,当调用配置类下方法时会被拦截处理逻辑
  enhancer.setCallbackFilter(CALLBACK_FILTER);
  enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
  return enhancer;
}

动态代理类创建完成,等到 Bean 创建过程时再将这些配置类进行实例化,通过代理对象来调用实际方法的操作

如何确保 @Bean 单例

介绍 BeanMethodInterceptor 拦截器类具体的逻辑前,先解释在 Spring 中是如何确保 @Bean 方法标注的实例是单例存在的,因为 @Configuration 配置类被加载时会被动态代理进行增强,生成代理类,然后 @Bean 方法标注的实例就会经过 BeanMethodInterceptor 拦截器处理,拦截器逻辑内部会调用 beanFactory#getBean 方法,走正常 bean 创建和获取过程,所以在 spring 内部就确保了单例

但不一定所有的配置类 @Configuration 都会是代理类,@Configuration(proxyBeanMethods = false) 如果属性设置为 false,说明该配置类无须被代理,也就是它的 configurationClass 属性为 lite,那么它下面的 @Bean 方法也就不会是单例存在,示例代码如下:

@Configuration(proxyBeanMethods = false)
public class DemoConfiguration {
    @Bean
    public Person person() {
        System.out.println(student());
        System.out.println(student());
        // 运行可以看出以上两个类地址不相同.
        return new Person();
    }
    @Bean
    public Student student() {
        return new Student();
    }
}

普通的 @Component 注解下配置 @Bean 方法不会被代理,也就是说它在获取 bean method 实例时不会走正常 spring bean 获取过程,也就不存在单例了.

BeanMethodInterceptor#intercept 方法

public Object intercept(Object enhancedConfigInstance, Method beanMethod, Object[] beanMethodArgs,
      MethodProxy cglibMethodProxy) throws Throwable {
  // 确定 bean 名称,不指定 name 属性默认就是用方法名作为 bean 名称
  ConfigurableBeanFactory beanFactory = getBeanFactory(enhancedConfigInstance);
  String beanName = BeanAnnotationHelper.determineBeanNameFor(beanMethod);
  // 确认当前 bean 方法是否被 @Scope 注解所标注,设置是否需要被代理标识,若当前类正在创建,对 beanName 进行替换
  if (BeanAnnotationHelper.isScopedProxy(beanMethod)) {
    String scopedBeanName = ScopedProxyCreator.getTargetBeanName(beanName);
    if (beanFactory.isCurrentlyInCreation(scopedBeanName)) {
      beanName = scopedBeanName;
    }
  }
  // To handle the case of an inter-bean method reference, we must explicitly check the container for already cached instances.
  // 首先,检查 bean 是否为 FactoryBean,若是的话,先创建好实现该接口的实现类实例,resolveBeanReference 会再调用 getObject 方法返回具体的实例对象
  if (factoryContainsBean(beanFactory, BeanFactory.FACTORY_BEAN_PREFIX + beanName) &&
      factoryContainsBean(beanFactory, beanName)) {
    // 先创建 FactoryBean 接口的实例对象,再判断是属于那种类型
    Object factoryBean = beanFactory.getBean(BeanFactory.FACTORY_BEAN_PREFIX + beanName);
    if (factoryBean instanceof ScopedProxyFactoryBean) {
      // 在前面的处理中对 @Bean 注解的方法进行了重新定义,如果 proxyMode 属性值不等于 DEFAULT 或 NO 情况下会对 BD 信息进行重新定义,class 就是 ScopedProxyFactoryBean
      // Scoped proxy factory beans are a special case and should not be further proxied
    }
    else {
      // 当前 @Bean 注入的类就是实现了 FactoryBean 接口的,在这里对其进行 CGLIB 代理增强后返回
      return enhanceFactoryBean(factoryBean, beanMethod.getReturnType(), beanFactory, beanName);
    }
  }
  if (isCurrentlyInvokedFactoryMethod(beanMethod)) {
    // 工厂调用 bean 方法为了注册 bean 实例,实际上调用父类方法创建实例
    // (i.e. via a getBean() call) -> invoke the super implementation of the method to actually create the bean instance.
    if (logger.isInfoEnabled() &&
        BeanFactoryPostProcessor.class.isAssignableFrom(beanMethod.getReturnType())) {
      logger.info(String.format("@Bean method %s.%s is non-static and returns an object " +
              "assignable to Spring's BeanFactoryPostProcessor interface. This will " +
              "result in a failure to process annotations such as @Autowired, " +
              "@Resource and @PostConstruct within the method's declaring " +
              "@Configuration class. Add the 'static' modifier to this method to avoid " +
              "these container lifecycle issues; see @Bean javadoc for complete details.",
          beanMethod.getDeclaringClass().getSimpleName(), beanMethod.getName()));
    }
    return cglibMethodProxy.invokeSuper(enhancedConfigInstance, beanMethodArgs);
  }
  // 会调用 beanFactory#getBean 方法从一级缓存中获取实例,此操作确保是单例的
  return resolveBeanReference(beanMethod, beanMethodArgs, beanFactory, beanName);
}
  1. 确认好 bean 名称,并确认当前 @Bean 修饰的方法是否被 @Scope 注解所修饰,设置标识,且进行判断当前修饰的 bean 实例是否正在创建,若当前类仍然在创建,就替换 bean 名称
  2. 检查 bean 是否为 FactoryBean 下接口的实例,包含它的子接口,如果是的话,先创建好该接口实现类的实例,主要是判断其是否为 ScopeProxyFactory 接口类型,如果是不作任何处理,该方法入口主要针对的就是 @Bean + @Scope 注解的逻辑处理,然后在最后一行代码 resolveBeanReference 方法中会调用 getObject 方法返回具体的实例对象
// 如果作用域不是no的话就要使用代理
if (proxyMode != ScopedProxyMode.NO) {
  // BeanDefinition 重新包装为 ScopedProxyFactoryBean 类型
  BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
      new BeanDefinitionHolder(beanDef, beanName), this.registry,
      proxyMode == ScopedProxyMode.TARGET_CLASS);
  beanDefToRegister = new ConfigurationClassBeanDefinition(
      (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata, beanName);
}
  1. resolveBeanReference 方法最为重要,它里面的逻辑才是保证了 @Bean 实例实现单例的正确入口,beanFactory#getBean 进入到正常的 spring 创建过程,若一级缓存已经存在,就不会再次进行创建,当然,一切的前提都是 @Scope 注解的 scopeName 属性为 singleton 的情况下.

总结

该篇文章介绍了 Spring 核心类 ConfigurationClassPostProcessor 一些重要的方法,方法相关的源码也分析完毕,其中涉及到注解的解析,其实只要搞懂了 processConfigBeanDefinitions 方法里面的功能,自动装配的实现理解基本是就可以拿下了;同时,里面有针对性对每个 @Configuration 标注的配置类作了额外处理,是否进行了动态代理增强,来确保被 @Configuration 注解修饰的类下所有 @Bean 方法是否保持单例,这一切可以根据自己当前的业务使用场景来定义.

如果觉得博文不错,关注我 vnjohn,后续会有更多实战、源码、架构干货分享!

推荐专栏:Spring、MySQL,订阅一波不再迷路

大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!


目录
相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
86 2
|
2月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
7天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
130 73
|
7天前
|
Java Spring
【Spring配置相关】启动类为Current File,如何更改
问题场景:当我们切换类的界面的时候,重新启动的按钮是灰色的,不能使用,并且只有一个Current File 项目,下面介绍两种方法来解决这个问题。
|
30天前
|
XML Java 数据格式
Spring Core核心类库的功能与应用实践分析
【12月更文挑战第1天】大家好,今天我们来聊聊Spring Core这个强大的核心类库。Spring Core作为Spring框架的基础,提供了控制反转(IOC)和依赖注入(DI)等核心功能,以及企业级功能,如JNDI和定时任务等。通过本文,我们将从概述、功能点、背景、业务点、底层原理等多个方面深入剖析Spring Core,并通过多个Java示例展示其应用实践,同时指出对应实践的优缺点。
56 14
|
29天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
50 2
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
69 9
|
3月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
689 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
3月前
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
253 2
|
9天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)