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

简介: 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,订阅一波不再迷路

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


目录
相关文章
|
11月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
631 70
|
7月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
9月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
768 0
|
11月前
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
403 0
|
11月前
|
SQL 前端开发 Java
深入分析 Spring Boot 项目开发中的常见问题与解决方案
本文深入分析了Spring Boot项目开发中的常见问题与解决方案,涵盖视图路径冲突(Circular View Path)、ECharts图表数据异常及SQL唯一约束冲突等典型场景。通过实际案例剖析问题成因,并提供具体解决方法,如优化视图解析器配置、改进数据查询逻辑以及合理使用外键约束。同时复习了Spring MVC视图解析原理与数据库完整性知识,强调细节处理和数据验证的重要性,为开发者提供实用参考。
450 0
|
IDE Java 应用服务中间件
spring boot 启动流程
Spring Boot 启动流程简介: 在使用 Spring Boot 之前,启动 Java Web 应用需要配置 Web 容器(如 Tomcat),并将应用打包放入容器目录。而使用 Spring Boot,只需运行 main() 方法即可启动 Web 应用。Spring Boot 的核心启动方法是 SpringApplication.run(),它负责初始化和启动应用上下文。 主要步骤包括: 1. **应用启动计时**:使用 StopWatch 记录启动时间。 2. **打印 Banner**:显示 Spring Boot 的 LOGO。 3. **创建上下文实例**:通过反射创建
742 5
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
314 0
|
XML 前端开发 Java
Spring 最常用的 7 大类注解,史上最强整理!
随着技术的更新迭代,Java5.0开始支持注解。而作为java中的领军框架spring,自从更新了2.5版本之后也开始慢慢舍弃xml配置,更多使用注解来控制spring框架。
|
8月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
1219 0

热门文章

最新文章