Spring中的包扫描

核心的后置处理器:ConfigurationClassPostProcessor
上一篇文章说道除了使用AnnotationApplicationContext对象手动添加的BeanDefinitionRegistryPostProcessor之外,首先回调的就是ConfigurationClassPostProcessor对象。

public class ConfigurationClassPostProcessor implements 
    BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    // 实现自BeanDefinitionRegistryPostProcessor接口
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
        // 解析配置类(@Import @ImportResource @ComponentScan ...)
        processConfigBeanDefinitions(registry);
    }
    // 实现自BeanFactoryPostProcessor
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 通过用cglib增强配置类,准备在运行时为bean请求提供服务。后面详细解释
    }
}

在此之前要先了解几种数据结构

ClassMetadata

public interface ClassMetadata {
    // Return the name of the underlying class.
    String getClassName();
    // 是否是一个接口
    boolean isInterface();
    // 是否存在注解
    boolean isAnnotation();
    // 是否被 abstract 标记
    boolean isAbstract();
    //  返回这个类是不是基础类,既不是接口,也不是抽象类
    boolean isConcrete();
    // 返回这个类是否被final标记
    boolean isFinal();
    // 判断一个类是否独立(是否是一个顶部类 或者 静态内部类)
    boolean isIndependent();
    // 返回类中是否存在静态内部类、非静态内部类或者本地类
    boolean hasEnclosingClass();
}

在Spring中将类划分为5种

public class ClassTypeTest {
    public class InnerClass{

    }
    public static class NestedClass{

    }
    public static void main(String[] args) {
        class LocalClass{
        }
        new Thread(() -> {
            System.out.println("anonymous class");
        }).start();
    }
}
  • ClassTypeTest => top level class 顶部类
  • NestedClass => nested class 静态内部类
  • InnerClass => inner class 非静态内部类
  • LocalClass => local class 在方法中定义的类
  • AnonymousClass=> anonymous class 匿名类

AnnotationMetadata

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {
    // 获取类上所有注解的类型名称
    Set getAnnotationTypes();
    // 获取类上指定注解的元注解,name是全类名
    Set getMetaAnnotationTypes(String annotationName);
    // 断定一个类上是否存在注解
    boolean hasAnnotation(String annotationName);
    // 断定一个类上的注解是否存在指定的元注解
    boolean hasMetaAnnotation(String metaAnnotationName);
    // 断定一个类中是否存在指定注解的方法
    boolean hasAnnotatedMethods(String annotationName);
    // 拿到类中标注了指定注解的所有方法
    Set getAnnotatedMethods(String annotationName);
}

ConfigurationClass

final class ConfigurationClass {
    /** 注解元信息 */
    private final AnnotationMetadata metadata;
    /** 类的资源标识 class文件路径 classLoader */
    private final Resource resource;
    /** bean名称 */
    private String beanName;
    /** 被引入的ConfigurationClass */
    private final Set importedBy = new LinkedHashSet<>(1);
    /** 内部的bean方法 */
    private final Set beanMethods = new LinkedHashSet<>();
    /** import导入的资源集合 */
    private final Map> importedResources =
        new LinkedHashMap<>();
    /** import导入的 BeanDefinitionRegistrar 集合 */
    private final Map importBeanDefinitionRegistrars =
        new LinkedHashMap<>();
    /** 需要跳过的beanMethod*/
    final Set skippedBeanMethods = new HashSet<>();
}

ConfigurationMethod 和 BeanMethod

abstract class ConfigurationMethod {
    /** 方法上注解的信息 */
    protected final MethodMetadata metadata;
    /** 方法所在类的配置类信息  */
    protected final ConfigurationClass configurationClass;
}

final class BeanMethod extends ConfigurationMethod {
    public BeanMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
        super(metadata, configurationClass);
    }
}
spring类数据元信息的数据结构.png

解析配置类

解析流程:

  • 获取是刷新前注入容器中的bdName(spring内部的+实例化context时传入的)
  • 为这些bd设置标记 full or lite,如果没有这个标记的话,这个bd就不会当成一个配置类来解析
    • 判断是否已经处理过了
    • 处理Component注解 -> 处理内部类
    • 处理PropertySources注解
    • 处理ComponentScans和ComponentScan注解 -> include/exclude + doScan -> 将bd放入容器
    • 处理Import注解,只是找出来
    • 处理ImportResource注解
    • 处理方法上的Bean注解
    • 处理这个配置类接口的默认方法
    • 处理这个配置类的父类
    • 处理 DeferredImportSelector 导入了的类
  • 处理 import导入的类、importSelector 导入的类、配置类的@Bean、扫描类的@Bean

上述的流程中用到了很多递归和循环,我们只需要关注主要的流程:

1、扫描出来的类什么时候放入容器中

2、Import的处理流程

3、@Bean处理(static @Bean和member @Bean)

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List configCandidates = new ArrayList<>();
    // 返回的是容器刷新前注入的bean定义对象
    String[] candidateNames = registry.getBeanDefinitionNames();
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 这个if是来检查这个bean定义中是否已经被标注了full或者lite
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            // 如果已经被标记了,就不会去再次解析它
        } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            // bean定义和beanName组合为一个holder放入list中
            // 这里放入的一般是我们手动在 context 注册的,因为这时还没有扫描包
            // demo中就只有注册的 appConfig 配置类
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    // 如果没有找到配置类就直接返回
    if (configCandidates.isEmpty()) {
        return;
    }
    // 排序
    // 获取beanName的生成器,可以是实现 BeanNameGenerator 接口去自定义beanName
    // 检查环境 省略不重要代码
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    // 存放候选的bean定义对象
    Set candidates = new LinkedHashSet<>(configCandidates);
    // 存放已经处理的配置类 ConfigurationClass
    Set alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        // 把候选的bean定义对象都传入到了 parse 方法中
        parser.parse(candidates);
        /**
    验证
    1、验证我们的 @Configuration 注解类是不是final的,如果是final就不能被cglib代理,所以在这里验证
    2、验证方法是不是静态方法,如果是静态方法那么无需验证 立即返回
    3、如果这个方法所在的类是一个 @Configuration 类,必须保证这个类的这个方法是可以被重写的,需要在cglib代理中重写
        */
        parser.validate();
        Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        // 去重
        configClasses.removeAll(alreadyParsed);
        // Read the model and create bean definitions based on its content
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                registry, this.sourceExtractor, this.resourceLoader, this.environment,
                this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 处理 configurationClasses 和 importBeanDefinitionRegistrars
        // 扫描出来的类并不在这里处理,在扫描出来的时候已经加入到了bd Map中了
        // 这里主要处理 import导入的类、importSelector 导入的类、配置类的@Bean、扫描类的@Bean
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        candidates.clear();
    }
    while (!candidates.isEmpty());
}

解析ComponentScan

processConfigBeanDefinitions
=> ConfigurationClassParser#parse
=> ConfigurationClassParser#parse(AnnotationMetadata, String)
=> ConfigurationClassParser#processConfigurationClass
=> ConfigurationClassParser#doProcessConfigurationClass

// doProcessConfigurationClass
// 重要的对象:componentScanParser
// 拿到了注解中配置的所有包名
Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() && 
        !this.conditionEvaluator
            .shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
    for (AnnotationAttributes componentScan : componentScans) {
        // 这个class被 @ComponentScan 注解了,那么立即执行扫描
        Set scannedBeanDefinitions =
                    this.componentScanParser
                    .parse(componentScan, sourceClass.getMetadata().getClassName());
        // 执行到这里,所有的 bd 都被扫描到了 scannedBeanDefinitions 中
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition()
                                          .getOriginatingBeanDefinition();
            if (bdCand == null) {
                bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                // Configuration Bean Component ComponentScan Import ImportResource
                // 出现以上注解会继续调用 processConfigurationClass 方法 262行
                // 继续判断这个类是否存在
                // @PropertySources @ComponentScans @Import @ImportResource @Bean
                // 所以每一个扫描出来的类都会进行整个流程的判断
                parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
        }
    }
}

componentScanParser对象的实例化,是在ConfigurationClassPostProcessor的postProcessBeanFactory方法中,在这个方法中实例化了一个ConfigurationClassParser对象,用于解析配置类。

ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);
// 构建了componentScanParser和conditionEvaluator
public ConfigurationClassParser(...) {
        this.componentScanParser = new ComponentScanAnnotationParser(
                environment, resourceLoader, componentScanBeanNameGenerator, registry);
        this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
    }

ConfigurationClassParser#parse

// 扫描包下面的类,这个方法只是做一些扫描之前的准备工作,比如beanName生成、需要排除的类、是否懒加载、作用域、获取基本包...
// @param componentScan  ComponentScan注解的信息,注解的属性值集合
// @param declaringClass 注解了@ComponentScan 的类的全称
public Set parse(AnnotationAttributes componentScan, final String declaringClass) {
    // 1、作用域代理模式 在@ComponentScan注解里配置 不写默认为default
    // 2、resourcePattern = **/*.class
    // 3、include和exclude
    // 4、是否是懒加载,默认是false
    // 5、获取componentScan中配置的包数组
        // 如果scan中配置的是class,则将class的包名放入扫描包的数组中
        // 如果没有配置basePacke,则会将当前class的包名作为 basePackage
    // 6、开始扫描
}

开始扫描

protected Set doScan(String... basePackages) {
    Set beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 完成了包的扫描,返回包下所有的需要spring实例化的bd 
        // 配置top.gmfcj => classpath*:top/gmfcj/**/*.class => 获取对应所有的class文件在下面的resources中
        // 使用ASM技术获取class
        Set candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 解析scope属性
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 先判断注解上有没有beanName,有设置beanName,没有就将类名小写设置为beanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 如果这个bd是 AbstractBeanDefinition 设置默认值比如 lazy init destroy
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            // 注解bean,进步获取一些注解 从bd的getMetadata()中获取 Lazy primary  DependsOn Role Description
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils
                .processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 这个if是判断扫描出来的这个 candidate 是否在bdmap中存在了
            if (checkCandidate(beanName, candidate)) {
                // 不存在则会将这个 candidate 注册到bdmap中
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                // 生成代理对象
                definitionHolder =
                    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 注册到bdmap中 注册的是代理对象还是真实的对象是根据每一个类的配置@Scope,如果没有配置代理的方法,默认是不会创建代理对象的
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

ScopedProxyMode:指定bean对象的代理类型

没有被切面织入,没有@Configuration注解,没有指定Cglib代理的类,Spring不会为这个类生成代理对象
被切面织入的类会在bean对象创建完成的最后一步生成代理对象(AbstractAutowireCapableBeanFactory#initializeBean=>applyBeanPostProcessorsAfterInitialization)
只要指定了Cglib代理,都会使用Cglib代理生成bean对象@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)

public enum ScopedProxyMode {
    // 默认值,一般都是NO,除非在一些注解中指定了默认值比如:ComponentScan
    DEFAULT,
    // 不创建作用域代理
    NO,
    // 创建一个实现目标对象类公开的所有接口方法的JDK动态代理
    INTERFACES,
    // 创建一个基于类的代理(使用CGLIB)
    TARGET_CLASS;
}

如果设置了代理,代理的逻辑

// ScopedProxyUtils#createScopedProxy
// proxyTargetClass=true:表示使用cglib false:使用jdk动态代理
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    String targetBeanName = getTargetBeanName(originalBeanName);
    // 新的bd中的class已经指向了ScopedProxyFactoryBean这个类的class
    RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
    // 设置目标bdr
    proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
    // 设置原始的bd
    proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
    // 将目标对象注册到容器中
    registry.registerBeanDefinition(targetBeanName, targetDefinition);
    // 返回代理对象的bdr
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

解析Import

在处理Import的时候要分为三种情况

  • Import一个类
  • Import一个Selector
  • Import一个Registrar

你可能感兴趣的:(Spring中的包扫描)