java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring创建BeanDefinition之路径扫描

Spring创建BeanDefinition之路径扫描详解

作者:程序员侠客行

这篇文章主要介绍了Spring创建BeanDefinition之路径扫描方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、从示例开始

当我们创建AnnotationConfigApplicationContext对象时,Spring底层到底做了些什么?

来看下面示例。

package com.xiakexing;

import com.xiakexing.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {

	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
		UserService userService = context.getBean("userService", UserService.class);
		userService.test();
    }
}
package com.xiakexing;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "com.xiakexing")
public class AppConfig {

}
package com.xiakexing.service;

import org.springframework.stereotype.Component;

@Component
public class UserService {

    public void test() {
        System.out.println("hello spring");
    }
}

我们猜测这几行代码执行逻辑:

带着上面的猜想,我们来看看源码。本文暂且关注路径扫描的实现,随后的文章将讲解BeanDefinition的创建过程。

二、创建AnnotationConfigApplicationContext

构造方法的入参是componentClasses,即可以传入多个配置类。

this()中创建了AnnotatedBeanDefinitionReader、ClassPathBeanDefinitionScanner,将用于扫描指定路径下的类,创建BeanDefinition。

JFR 是 Java Flight Record (Java飞行记录),是JVM 内置的基于事件的JDK监控记录框架。StartupStep是Spring基于JFR对运行过程的监控,阅读源码时可忽略它。

注意,AnnotationConfigApplicationContext间接实现了BeanDefinitionRegistry接口,具备向容器中注册BeanDefinition的能力。

在创建ClassPathBeanDefinitionScanner对象时,指定了使用DefaultFilters:将扫描所有带有@Component注解的类。

总结:this()仅仅实例化了容器对象,创建了Reader、Scanner,用于解析类信息。

三、注册Configuration类

3.1 创建BeanDefinition

register(componentClasses),显然是将配置类注册到容器中。

来看AnnotatedBeanDefinitionReader#doRegisterBean的核心逻辑:

3.2 注册BeanDefinition

BeanDefinitionHolder类只是对BeanDefinition的包装,仅有三个属性:beanDefinition、beanName和aliases。

在BeanDefinitionReaderUtils#registerBeanDefinition中

最终会调用DefaultListableBeanFactory#registerBeanDefinition方法,执行这几行代码:

看到了BeanFactory的两个核心数据结构:

果然与我们当初的猜想一致。

至此,配置类已被添加到beanDefinitionMap中,可是@ComponentScan指定的包路径,在哪儿被处理了呢?

四、扫描包路径

先说结论:@ComponentScan包路径下的类,是在ClassPathBeanDefinitionScanner#scan中被处理的。

来看AnnotationConfigApplicationContext的另一个构造方法:入参就是指定包路径。

转调到ClassPathBeanDefinitionScanner#scan。

基于配置类创建AnnotationConfigApplicationContext时,是在哪儿调了scan()或doScan()呢?答案就在refresh()中。

4.1 BeanFactoryPostProcessor接口

先看类注释:

Factory hook that allows for custom modification of an application context's bean definitions, adapting the bean property values of the context's underlying bean factory.

工厂钩子,允许自定义修改应用程序上下文的bean定义,调整上下文的底层bean工厂的bean属性值。

A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances.

BeanFactoryPostProcessor可以与bean定义交互和修改,但不能与bean实例交互。

可见,该接口是BeanFactory的后置处理器,在创建Bean实例前,干涉BeanDefinition创建和更新。

仅有一个方法:

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

该接口有个重要的子接口BeanDefinitionRegistryPostProcessor,能够向容器注册更多的BeanDefinition。

Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks in.

对标准BeanFactoryPostProcessor SPI的扩展,允许在常规BeanFactoryPostProcessor检测开始之前注册进一步的bean定义。

4.2 ConfigurationClassPostProcessor类

源码中,BeanDefinitionRegistryPostProcessor接口仅有唯一实现ConfigurationClassPostProcessor。

在ConfigurationClassPostProcessor#processConfigBeanDefinitions中,

检查已注册的每一个BeanDefinition,是否是候选配置类(或组件),满足以下任意条件即可:

得到Set<BeanDefinitionHolder> candidates后,会调用ConfigurationClassParser.parse()

接下来会遍历处理每一个候选类

在ConfigurationClassParser#doProcessConfigurationClass中,解析@ComponentScan、@ComponentScans注解;

关于doScan方法的详细逻辑,我们下一篇再看。

五、逻辑闭环

要用ConfigurationClassPostProcessor来处理配置类,Spring容器中就得先有该类的实例。那么,这个类是何时注册到容器中的?

答案就在new AnnotatedBeanDefinitionReader(this)中:

为ConfigurationClassPostProcessor创建BeanDefinition并注册。

当执行AbstractApplicationContext#refresh时,其中有一步是调用容器中BeanFactoryPostProcessor接口所有实现。

此时,ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry将被执行。

流程图

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文