day10 - Spring 之配置类源码解析

day10 - Spring 之配置类源码解析

摘要:

本文深入解析Spring配置类源码,重点分析BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor的区别,以及checkConfigurationClassCandidate()方法的实现逻辑。前者是父接口,用于修改现有Bean定义;后者继承前者并新增方法,可直接操作Bean定义注册中心。checkConfigurationClassCandidate()方法通过多种方式获取注解元数据,判断候选类是否为配置类,并处理代理Bean方法等特

**前言:**主分享对spring源码配置类源码的理解。

流程图如下:

1、BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor区别

1)接口集成关系

BeanFactoryPostProcessor是Spring提供的后置处理器,定义如下

c 复制代码
@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

postProcessBeanFactory 允许在我们所有Bean定义,(BeanDefinition)已加载完成后、Bean实例化之前,对ConfigurableListableBeanFactory 进行定制化操作。比如:修改Bean定义的属性值、添加PropertyEditor等。

BeanDefinitionRegistryPostProcessor 它是 BeanFactoryPostProcessor 的子接口

c 复制代码
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

允许我们直接操作Bean的定义注册中心,即可以项向容器中注册新的Bean的定义,移除已有的Bean定义,或对注册表进行其他的修改。

对比维度 BeanDefinitionRegistryPostProcessor BeanFactoryPostProcessor
集成关系 继承 BeanFactoryPostProcessor 父接口
新增方法 postProcessBeanDefinitionRegistry() 无,仅有 postProcessBeanFactory()
操作对象 BeanDefinitionRegistry 可增删改Bean的定义 (Bean的新注册) ConfigurableListableBeanFactory 修改现有的Bean
调用时机 在 postProcessBeanFactory 之前执行 在所有 BeanDefinitionRegistryPostProcessor 的 方法 postProcessBeanDefinitionRegistry 完毕后执行
典型实现 ConfigurationClassPostProcessor(处理 @Configuration、@ComponentScan 等)CachingMetadataReaderFactoryPostProcessor 等 PropertySourcesPlaceholderConfigurer(处理占位符)CustomScopeConfigurer(注册自定义作用域)EventListenerMethodProcessor 等

2、checkConfigurationClassCandidate()方法讲解

全路径:org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate

c 复制代码
public static boolean checkConfigurationClassCandidate(
       BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
   
    // 1)获取类名,如果类名为空 说明bean的定义无法确定类
    // 检查工厂方法名, getFactoryMethodName() 不为null,说明这个bean是通过@Bean 方法定义的
    // 对于通过 @Bean 方法创建的 Bean,其定义本身不携带配置类注解信息(配置类注解在包含 @Bean 方法的类上),因此不应作为配置类候选处理,直接返回 
    // false。

    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
       return false;
    }
    
    // 2)获取注解元数据 AnnotationMetadata
    AnnotationMetadata metadata;
    
    // 2.1)AnnotatedBeanDefinition 类名匹配 AnnotatedBeanDefinition 内部已经持有 AnnotationMetadata ,直接复用可以提高性能。
    // 检查类名一致,确保元数据对应当前类
    if (beanDef instanceof AnnotatedBeanDefinition &&
          className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
       // Can reuse the pre-parsed metadata from the given BeanDefinition...
       metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
    }

    // 2.2)hasBeanClass() 为true表示 Bean的Class已经被加载到JVM中。
    
    else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
       // Check already loaded Class if present...
       // since we possibly can't even load the class file for this Class.
       Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
       // 如果该类实现了特定的接口比如:BeanFactoryPostProcessor、BeanPostProcessor... 直接返回false。
       // 因为这些接口实现的视线类需要在配置类处理之前就实例化,如果它们本身又是配置类,可能会导致循环依赖或过早初始化的问题。Spring明确不允许这类的Bean作为配置类
       if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
             BeanPostProcessor.class.isAssignableFrom(beanClass) ||
             AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
             EventListenerFactory.class.isAssignableFrom(beanClass)) {
          return false;
       }
       metadata = AnnotationMetadata.introspect(beanClass);
    }
    //  其他情况:通过metadataReaderFactory 读取字节码,对于既不是AnnotatedBeanDefinition又未加载类的BeanDefinition
   //  使用 metadataReaderFactory 读取 .class文件(ASM方式),获取元数据而不加载类
    else {
       try {
          MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
          metadata = metadataReader.getAnnotationMetadata();
       }
       catch (IOException ex) {
          if (logger.isDebugEnabled()) {
             logger.debug("Could not find class file for introspecting configuration annotations: " +
                   className, ex);
          }
          return false;
       }
    }

    Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());


    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
       beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }

    else if (config != null || isConfigurationCandidate(metadata)) {
       beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
       return false;
    }

    // It's a full or lite configuration candidate... Let's determine the order value, if any.
    Integer order = getOrder(metadata);
    if (order != null) {
       beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }

    return true;
}

检查 @Configuration 注解并标记FULL/LITE

c 复制代码
    if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
       beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }

    else if (config != null || isConfigurationCandidate(metadata)) {
       beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
       return false;
    }
  • FULL 模式:如果 @Configuration 存在且 proxyBeanMethods 不为 false(即 true 或未显式设置,默认为 true),则标记为 FULL。FULL 模式下,Spring 会为配置类创建 CGLIB 代理,确保 @Bean 方法之间调用遵循单例语义(每次调用返回同一个 Bean)。
  • LITE 模式:有两种情况进入 LITE:
    a.存在 @Configuration 但 proxyBeanMethods = false。
    b.不存在 @Configuration,但满足 isConfigurationCandidate(metadata) 条件。

isConfigurationCandidate(metadata) 实现检查

1)类上是否标注了 @Component 、@ComponentScan、@Import、@ImportResource中的任意一个。

2)或者类中是否存在至少一个带有@Bean注解的方法,满足任一条即视为LITE配置类(即普通的Spring组件,但包含@Bean定义)。LITE模式下不会创建CGLIB代理,@Bean方法之间的调用是普通的JAVA调用,不会拦截。

3)如果以上都不满足,返回false,表示该Bean定义不需要作为配置类处理。

补充:FULL和LITE的区别

FULL: 对应 @Configuration(proxyBeanMethods=true),Spring 会创建 CGLIB 代理,保证@Bean方法内部调用另一个@Bean方法时返回的是容器中的单例Bean,而不是新实例。通常用于集中定义Bean的配置类。

LITE:对应普通类 (可能带有@Bean方法或@Component等),不会创建代理,@Bean方法间调用是普通JAVA调用,可能会导致同一个Bean被多次实例化。通常用于简单配置或自动扫描的组件中内嵌的@Bean方法。

3、Spring框架中处理导入配置类的方法

registerBeanDefinitionForImportedConfigurationClass 方法用于为通过@Import注解导入的配置类注册Bean定义。

c 复制代码
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    // 获取配置类元数据
    AnnotationMetadata metadata = configClass.getMetadata();
    // 创建bean的定义:基于元数据创建一个带注解的通用Bean定义对象
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
    // 解析作用域,比如:singleton、prototype 等
    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());
    // 生成bean的名称
    String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
   
    // 处理常见注解 @Lazy、@DependsOn、@Role...将这些注解信息设置到bean定义中  
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
    // 创建作用域代理
    // 1.检查作用域配置中是否设置了代理模式
    // 2.如果配置了proxyMode = ScopedProxyMode.TARGET_CLASS,会创建一个作用域代理工厂Bean
    // 3.这样可以在单例Bean安全的注入原型(prototype)或其他作用域的Bean
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
      // 注册Bean定义
      this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
    configClass.setBeanName(configBeanName);

    if (logger.isTraceEnabled()) {
       logger.trace("Registered bean definition for imported class '" + configBeanName + "'");
    }
}

4、Spring处理@ComponentScan核心方法

作用是解析配置类上的@ComponentScan注解,并根据注解扫描指定的包来注册Bean定义。

c 复制代码
// 主要作用是解析@ComponentScan注解的所有属性,并配置扫描器来扫描制定的包
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    // 创建一个ClassPathBeanDefinitionScanner扫描器
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
          componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    
    // 获取用户自定义的Bean名称生成器(通过nameGenerator属性)
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    // 默认是AnnotationBeanNameGenerator
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
          BeanUtils.instantiateClass(generatorClass));
    
    // 设置作用域、代理模式
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
       scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
       Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
       scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }
    
    // 设置资源匹配模式
    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    // 处理包含 |排除过滤器
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
       for (TypeFilter typeFilter : typeFiltersFor(filter)) {
          scanner.addIncludeFilter(typeFilter);
       }
    }
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
       for (TypeFilter typeFilter : typeFiltersFor(filter)) {
          scanner.addExcludeFilter(typeFilter);
       }
    }
    
    // 设置懒加载,如果lazyInit = true ,则扫描到所有的Bean都为懒加载
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
       scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    // 确定要扫描的基础包路径
    // basePackages:直接指定包名字符串(支持占位符和多个包)
    // basePackageClasses:通过指定类来获取它们所在的包
    // 默认值:如果都没指定,就使用声明 @ComponentScan 注解的类所在的包
    Set<String> basePackages = new LinkedHashSet<>();
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
       String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
             ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
       Collections.addAll(basePackages, tokenized);
    }
    // 添加排除过滤器:添加一个排除过滤器,排除掉@ComponentScan 类本身,为了避免将配置类自己也扫描进去造成循环
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
       basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
       basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
       @Override
       protected boolean matchClassName(String className) {
          return declaringClass.equals(className);
       }
    });
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

5、Spring框架中处理配置类的逻辑

c 复制代码
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
       Predicate<String> filter) throws IOException {
    // 获取内部类
    Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
    if (!memberClasses.isEmpty()) {
       List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
       for (SourceClass memberClass : memberClasses) {
           // 排除自身
           // 检查内部类是否是配置类候选者(是否有 @Configuration、@Component、@Import 等注解)
          if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
             candidates.add(memberClass);
          }
       }

       // 排序
       OrderComparator.sort(candidates);
       
       for (SourceClass candidate : candidates) {

          if (this.importStack.contains(configClass)) {
             // 报错 说明出现了循环依赖
             this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
          }
          else {
             this.importStack.push(configClass);
             try {
                processConfigurationClass(candidate.asConfigClass(configClass), filter);
             }
             finally {
                this.importStack.pop();
             }
          }
       }
    }
}

核心作用;

1)支持在配置类中定义内部配置类

2)检测并防止循环导入

3)确保所有配置类(包括内部类)都被正确处理

比如;

c 复制代码
@Configuration
public class AppConfig {
    
    @Bean
    public DataSource dataSource() {
        return new DataSource();
    }
    
    // 内部配置类
    @Configuration
    public static class DatabaseConfig {
        @Bean
        public Connection connection() {
            return new Connection();
        }
    }
}

这个方法会发现 DatabaseConfig 这个内部类也是配置类,然后递归地处理它,将其中的 @Bean 方法也注册到 Spring 容器中。

这段代码是 Spring 框架中处理配置类所实现的接口的逻辑。

c 复制代码
// 用于递归处理配置类实现的所有接口,检查接口中是否有 @Bean 方法,并将其注册到配置类中。
private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
    for (SourceClass ifc : sourceClass.getInterfaces()) {
       Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(ifc);
       for (MethodMetadata methodMetadata : beanMethods) {
          if (!methodMetadata.isAbstract()) {
             configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
          }
       }
       processInterfaces(configClass, ifc);
    }
}

实际应用场景:

示例 1:接口中有 default 的 @Bean 方法

c 复制代码
@Configuration
public class AppConfig implements DatabaseConfig {
    // 继承自接口的 default @Bean 方法也会被识别
}

public interface DatabaseConfig {
    
    @Bean
    default DataSource dataSource() {
        return new HikariDataSource();
    }
    
    @Bean
    default TransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }
}

在这种情况下:

AppConfig 实现了 DatabaseConfig 接口

接口中的 dataSource() 和 transactionManager() 是 default 方法(非抽象)

Spring 会通过这个方法将它们识别为 Bean 定义

最终容器中会注册 DataSource 和 TransactionManager 两个 Bean

示例 2:接口继承层次

c 复制代码
@Configuration
public class AppConfig implements ServiceConfig {
}

public interface ServiceConfig extends DaoConfig {
    @Bean
    default Service service() {
        return new ServiceImpl();
    }
}

public interface DaoConfig {
    @Bean
    default Dao dao() {
        return new DaoImpl();
    }
}

通过递归处理(第 422 行),Spring 会:

处理 ServiceConfig 接口,发现 service() 方法

递归处理 DaoConfig 接口,发现 dao() 方法

最终 AppConfig 会注册 service 和 dao 两个 Bean

核心接口和作用

✅ 支持接口中的默认 Bean 方法:识别并注册接口中的 default @Bean 方法

✅ 递归处理接口层次:处理接口继承关系,不遗漏任何层级的 Bean 定义

✅ Java 8+ 特性支持:充分利用 Java 8 的 default 方法特性,提供更灵活的配置方式

这种机制让 Spring 配置更加模块化,可以通过接口组合的方式复用配置!

有不足之处欢迎指出,一起学习。喜欢我的文章希望点个在看,或者点赞,持续更新中ing... ♥️

相关推荐
普通网友2 小时前
SQL Server 2019安装详细教程(图文详解,非常靠谱)
后端·python·flask
行者-全栈开发2 小时前
JDK 17 + Spring Boot 3.5.8:企业级开发技术栈全景
java·开发语言·spring boot·系统架构·技术栈·系统架构全景分析·springboot技术栈
WHS-_-20222 小时前
mCore: Achieving Sub-millisecond Scheduling for 5G MU-MIMO Systems
java·算法·5g
Jin、yz2 小时前
黑马苍穹外卖项目收获
java
panzer_maus2 小时前
Java多线程介绍
java·开发语言
AMoon丶2 小时前
Golang--多种控制结构详解
java·linux·c语言·开发语言·后端·青少年编程·golang
indexsunny2 小时前
互联网大厂Java面试实战:微服务与Spring Boot在电商场景下的应用解析
java·spring boot·redis·docker·微服务·kubernetes·oauth2
薛定谔之死猫2 小时前
Ruby简单粗暴把图片合成PDF文档
java·pdf·ruby