Spring Boot 之 BeanDefinitionLoader 详解

入口

BeanDefinitionLoader顾名思义是用来加载bean定义的。在spring boot启动过程中的准备上下文件阶段会进行source关联资源的bean定义加载,为后续bean的实例化做准备。关键代码如下:

java 复制代码
//SpringApplication
public class SpringApplication {

    //...
    
    private void prepareContext(...) {
    
        //...
        
        f (!AotDetector.useGeneratedArtifacts()) {
            // Load the sources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            load(context, sources.toArray(new Object[0]));
        }
        
        //....
    }
    
    protected void load(ApplicationContext context, Object[] sources) {
        if (logger.isDebugEnabled()) {
           logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        if (this.beanNameGenerator != null) {
           loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
           loader.setResourceLoader(this.resourceLoader);
        }
        if (this.environment != null) {
           loader.setEnvironment(this.environment);
        }
        loader.load();
    }
    
    //...
}

构造函数

上面代码主要包含 BeanDefinitionLoader 实例的创建、设置一些属性、最后的装载。先让我看看 BeanDefinitionLoader 的构造函数。

java 复制代码
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    this.xmlReader = new XmlBeanDefinitionReader(registry);
    this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null);
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}

从构造函数中可以看出,bean定义的加载主要有四种方式,分别为:注解的方式、xml配置方式、groovy脚本方式和路径扫描方式。在构造函数中提前构建好四种加载工具,在后续真正load()的地方再根据*sources* 的类型来判断具体使用哪种 reader 来完成bean定义的加载。

Spring boot 默认通过 AnnotatedBeanDefinitionReader 方式来加载bean定义。这里需要注意下在 AnnotatedBeanDefinitionReader 构造函数中会实例化 ConditionEvaluator 条件评估器,ConditionEvaluator 内部又会构造一个**ConditionContextImpl **实例。然后会将注解配置处理器注册到bean工厂中。

java 复制代码
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

让我们通过源码的方式详细分析下 registerAnnotationConfigProcessors 做了什么操作。

java 复制代码
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
       BeanDefinitionRegistry registry, @Nullable Object source) {

    //从上下文中取出 beanFactory
    DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
    if (beanFactory != null) {
       //注意这里设置dependencyComparator、autowireCandidateResolver 只有在首次实例化AnnotatedBeanDefinitionReader
       //时才会赋值,而首次实例化是在'AnnotationConfigServletWebServerApplicationContext'上下文创建阶段。
       if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
          beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
       }
       if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
          beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
       }
    }

    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    
    //同理只在AnnotatedBeanDefinitionReader首次实例化时,才会注册以下相关后置处理器
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // Check for Jakarta Annotations support, and if present add the CommonAnnotationBeanPostProcessor.
    if ((jakartaAnnotationsPresent || jsr250Present) &&
          !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition();
       try {
          def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                AnnotationConfigUtils.class.getClassLoader()));
       }
       catch (ClassNotFoundException ex) {
          throw new IllegalStateException(
                "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
       }
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }

    if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    }

    if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
       RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
       def.setSource(source);
       beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
    }

    return beanDefs;
}

注意 :在registerPostProcessor内部实际会调用DefaultListableBeanFactory. registerPostProcessor方法进行bean定义的注册,这里不详细介绍。大概的逻辑就是判断集合是否包含要注册的BeanDefinition,无就加入,存在就根据情况抛异常或者覆盖。具体逻辑可以阅读相关代码。

load() 加载

接下来让我们看看load()加载的具体实现。

java 复制代码
void load() {
    for (Object source : this.sources) {
       load(source);
    }
}

private void load(Object source) {
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?> clazz) {
       load(clazz);
       return;
    }
    if (source instanceof Resource resource) {
       load(resource);
       return;
    }
    if (source instanceof Package pack) {
       load(pack);
       return;
    }
    if (source instanceof CharSequence sequence) {
       load(sequence);
       return;
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

load的加载会判断 source 的类型,不同的类型走不同的处理逻辑。一般 source 是 Class类型,默认走以下方法进行处理。

java 复制代码
private void load(Class<?> source) {
    if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
       // Any GroovyLoaders added in beans{} DSL can contribute beans here
       GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
       ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
    }
    if (isEligible(source)) {
       this.annotatedReader.register(source);
    }
}

可以看到,在 isEligible(source) == true 的情况,最终是通过 AnnotatedBeanDefinitionReader 来完成bean定义的注册。让我继续看看register(source )的实现。

java 复制代码
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
       @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
       @Nullable BeanDefinitionCustomizer[] customizers) {
    
    //创建AnnotatedGenericBeanDefinition实例对象,
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
    //判断是否跳过bean的注册,根据条件判断(通常是被@Conditional注解)如果要跳过,则不进行bean定义的注册
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
       return;
    }
    // 设置属性 org.springframework.context.annotation.ConfigurationClassPostProcessor.candidate = true
    abd.setAttribute(ConfigurationClassUtils.CANDIDATE_ATTRIBUTE, Boolean.TRUE);
    // 设置实例supplier
    abd.setInstanceSupplier(supplier);
    // 解析bean的作用域,并将作用域信息绑定到当前bean定义上
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    //生成bean名称
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

    //处理通用定义注解,包含@Lazy、@Primary、@DependsOn、@Role、@Description,将其对应的值设置到AnnotatedGenericBeanDefinition中
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if (qualifiers != null) {
       for (Class<? extends Annotation> qualifier : qualifiers) {
          if (Primary.class == qualifier) {
             abd.setPrimary(true);
          }
          else if (Lazy.class == qualifier) {
             abd.setLazyInit(true);
          }
          else {
             abd.addQualifier(new AutowireCandidateQualifier(qualifier));
          }
       }
    }
    if (customizers != null) {
       for (BeanDefinitionCustomizer customizer : customizers) {
          customizer.customize(abd);
       }
    }
    //构建bean定义holder对象
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    //根据bean定义的代理模式,确定是否生成一个代理的bean定义,决定后续生成bean的时候是否生成代理对象
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //最后将bean定义注册到beanFactory中
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

经过以上的流程处理,我们最终识别出 sources 关联的注解并将他们解析成 BeanDefinition 注册到 beanFactory 中,为后续bean的实例化做准备。

相关推荐
月光有害1 小时前
DDD 核心概念梳理:从领域模型到领域事件
java·ddd
玉宇夕落1 小时前
Node.js 路径模块(path)的学习
后端
snow@li1 小时前
Java:后端项目会有一个类似前端node_modules的目录吗 / jar包在本地仓库 ~/.m2/repository 中按版本共存
java·开发语言·前端
glartchen1 小时前
旧电脑配置Pixi 环境下 Whisper-CTranslate2 的若干坑与修复指南
后端
济*沧*海1 小时前
创建springboot项目时会出现注释乱码情况
java·spring boot·后端
IT_陈寒1 小时前
Vite冷启动快?我遇到了个奇怪的依赖问题
前端·人工智能·后端
geovindu2 小时前
java: Builder Pattern
java·开发语言·后端·设计模式·建造者模式·生成器模式
郝学胜_神的一滴2 小时前
系统设计 019:分片拆分与副本容灾深度解析
后端·架构
程序员cxuan2 小时前
Cursor 掀桌子了,Grok 4.5 这次要上天了?
人工智能·后端·程序员