2、BeanDefinition扫描源码

前面介绍过,BeanDefinition就是对Bean进行定义的,在Spring启动的过程中,肯定会经历扫描解析Class类并生成BeanDefinition的过程。下面就来介绍一下这个过程的源码,这里只介绍BeanDefinition扫描和生成的主流程,内部一些细节可能会照顾不到

流程图

在使用AnnotationConfigApplicationContext类来Spring启动时,AnnotationConfigApplicationContext的构造方法中,会执行三个方法

  1. this() 自己的无参构造方法
  2. register(componentClasses);
  3. refresh(); Spring启动的核心方法

AnnotationConfigApplicationContext()–构造方法

AnnotationConfigApplicationContext()构造方法内部很简单,只是初始化了readerscanner两个属性。

reader属性被赋值为AnnotatedBeanDefinitionReader对象。

scanner属性被赋值为ClassPathBeanDefinitionScanner对象。

public AnnotationConfigApplicationContext() {
    StartupStep createAnnotatedBeanDefReader = getApplicationStartup().start("spring.context.annotated-bean-reader.create");
    // 初始化BeanDefinitionReader,这个类主要用于解析注解或声明式的Bean,会解析@Component@DependsOn@Lzay@Scope@FallBack等注解
    this.reader = new AnnotatedBeanDefinitionReader(this);
    createAnnotatedBeanDefReader.end();
    // 初始化ClassPathBeanDefinitionScanner扫描器
    // 主要用于在Java类路径中扫描组件类(如带有@Controller、@Service、@Repository、@Component等注解的类)
    // 并将其转换为Bean定义,供Spring容器管理,从而实现自动装配和简化配置。
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}

这里说明一下:在创建ClassPathBeanDefinitionScanner的时候,最终会调用到一个传入useDefaultFilters参数的构造方法中。这里的useDefaultFilters传入的一般是true,这样在初始化扫描器时,Spring会向includeFilters添加new AnnotationTypeFilter(Component.class)过滤器。这样在扫描时,Spring就会处理带有@Component注解的类

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
        Environment environment, @Nullable ResourceLoader resourceLoader) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;

    if (useDefaultFilters) {
        // 使用默认的过滤器
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}

启动时扫描Class并生成BeanDefinition

AnnotationConfigApplicationContext的有参构造方法内,会执行AbstractApplicationContext#refresh()方法来启动Spring,在这个方法中会调用一系列的方法来初始化BeanFactory、扫描Class生成BeanDefinition、生成Bean、执行aware回调等。其中,在invokeBeanFactoryPostProcessors(beanFactory);中就会执行扫描Class并生成BeanDefinition。这个方法最终会调用到ConfigurationClassParser#doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)方法,具体的调用链可以在这个方法内打断点查看

ConfigurationClassParser#doProcessConfigurationClass()

这个方法主要就是解析处理配置类,主要是处理配置类上的注解和配置类中添加了@Bean注解的方法。其中最主要的就是解析@ComponentScan@ComponentScans注解。当存在多个@ComponentScan注解时,会循环依次处理,这里就会调用ComponentScanAnnotationParser#parse(AnnotationAttributes componentScan, String declaringClass)方法来进行扫描

protected final @Nullable SourceClass doProcessConfigurationClass(
			ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
			throws IOException {
    //...其他代码
    
    // Search for locally declared @ComponentScan annotations first.
    // 处理@ComponentScan注解
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class,
            MergedAnnotation::isDirectlyPresent);

    // Fall back to searching for @ComponentScan meta-annotations (which indirectly
    // includes locally declared composed annotations).
    if (componentScans.isEmpty()) {
        componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(),
                ComponentScan.class, ComponentScans.class, MergedAnnotation::isMetaPresent);
    }

    if (!componentScans.isEmpty()) {
        List<Condition> registerBeanConditions = collectRegisterBeanConditions(configClass);
        if (!registerBeanConditions.isEmpty()) {
            throw new ApplicationContextException(
                    "Component scan for configuration class [%s] could not be used with conditions in REGISTER_BEAN phase: %s"
                            .formatted(configClass.getMetadata().getClassName(), registerBeanConditions));
        }
        for (AnnotationAttributes componentScan : componentScans) {
            // The config class is annotated with @ComponentScan -> perform the scan immediately
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    // 这里开始处理配置类上有@ComponentScan和@ComponentScans注解中配置的包路径
                    /**
                     * {@link ComponentScanAnnotationParser#parse(AnnotationAttributes componentScan, String declaringClass)}
                     */
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            // Check the set of scanned definitions for any further config classes and parse recursively if needed
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                    parse(bdCand.getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    //...其他代码
}

ComponentScanAnnotationParser#parse()

这个方法会返回一个BeanDefinitionHolder的集合。在这个方法中主要是创建了一个scanner扫描器,并对scanner进行设置,包括:

  • BeanNameGenerator:beanName生成的类,默认会使用AnnotationBeanNameGenerator
  • ScopedProxyMode:scopeProxy模式(非本次重点)
  • IncludeFilters:@ComponentScan注解上配置的IncludeFilters
  • ExcludeFilters:@ComponentScan注解上配置的ExcludeFilters
  • 设置BeanDefinitionDefaultlazyInit的值:这里会读取@ComponentScan上配置的lazyInit属性,当这个属性为true时,会默认的将该@ComponentScan所配置的扫描包下的所有bean的lzayInit属性设置为true,但如果类上有单独设置@Lazy属性的,以类上的为准
  • 获取注解配置的basePackages属性的值,放入basePackages集合
  • 获取注解配置的basePackageClasses属性的值,放入basePackages集合
  • 如果basePackages集合还为空,则取配置类的包路径放入basePackages集合
  • 再添加一个ExcludeFilter,用于排除传入的配置类,因为配置类在Spring启动时已经是BeanDefinition了
  • 执行scanner.doScan方法进行扫描,返回BeanDefinitionHolder集合
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
    // 创建一个扫描器,这里传入使用默认的过滤器为true,则会默认初始化一个解析@Component注解的includeFilter放到includeFilters中
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
            componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    /**
      * 得到beanName生成的类,默认使用{@link AnnotationBeanNameGenerator}
      */
    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    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"));
    // 设置扫描器中包含需要处理那个注解的filter,这里是配置类上的@ComponentScan中主动配置的
    for (AnnotationAttributes includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
        List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
                this.resourceLoader, this.registry);
        for (TypeFilter typeFilter : typeFilters) {
            scanner.addIncludeFilter(typeFilter);
        }
    }
    // 设置扫描器中要排除那个注解的filter,这里是配置类上的@ComponentScan中主动配置的
    for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
        List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
            this.resourceLoader, this.registry);
        for (TypeFilter typeFilter : typeFilters) {
            scanner.addExcludeFilter(typeFilter);
        }
    }
    // 懒加载初始化标识
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }
    // 基础的扫描路径
    Set<String> basePackages = new LinkedHashSet<>();
    // 配置的包路径数组,可能有多个basePackages,每个都要处理
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        // 处理单个basePackages中的配置,一个字符串中,可以使用英文;分割
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
        // 如果basePackages为空,则使用declaringClass的包名作为basePackage
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        // 添加一个默认排除器,排除传入的当前的配置类
        // 因为在Spring启动时,这个配置类已经是Bean了,就不用重复扫描生成BeanDefinition了
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    // 执行扫描
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

注意:这个方法内会新建一个scanner,而传入的useDefaultFilters取自componentScan.getBoolean(“useDefaultFilters”),默认为true,因此这个scanner中也就包括了对Component注解的处理

ClassPathBeanDefinitionScanner#doScan()

这个方法主要工作有以下几点:

  1. 进行扫描,生成候选的BeanDefinition的集合
  2. 处理集合中的每一个候选BeanDefinition,
    1. 先获取BeanName
    2. 然后根据BeanDefinitionDefault给每一个候选BeanDefinition设置默认属性
    3. 最后解析处理类上Lazy,Primary,Fallback,DependsOn,Role,Description注解的值
    4. 最后判断是否能成为BeanDefinitionHolder,能得话生成BeanDefinitionHolder,放入集合

因为这个方法中是传入的basePackages的数组,因此方法内会循环处理每一个basePackage。在获取候选BeanDefinition的时候,调用的是父类ClassPathScanningCandidateComponentProviderfindCandidateComponents(basePackage),而这个方法中会调用到ClassPathScanningCandidateComponentProviderscanCandidateComponents(String basePackage)方法来进行扫描

以下是ClassPathScanningCandidateComponentProvider#scanCandidateComponents()的源码注释

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 循环处理 basePackages
    for (String basePackage : basePackages) {
        // 获取basePackages下所有的BeanDefinition集合,这就包括扫描和生成BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            // 循环处理生成的BeanDefinition集合

            //处理BeanDefinition的Scope元数据
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            // 获取BeanName
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);

            if (candidate instanceof AbstractBeanDefinition abstractBeanDefinition) {
                // 这里处理beanDefinition默认属性
                postProcessBeanDefinition(abstractBeanDefinition, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition annotatedBeanDefinition) {
                // 这里处理Lazy,Primary,Fallback,DependsOn,Role,Description注解的值
                AnnotationConfigUtils.processCommonDefinitionAnnotations(annotatedBeanDefinition);
            }
            if (checkCandidate(beanName, candidate)) {
                // 检查候选人,看能否成为BeanDefinitionHolder
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 将BeanDefinitionHolder存入registry,也就是beanFactory中的beanDefinitionMap中
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

ClassPathScanningCandidateComponentProvider#scanCandidateComponents(basePackage)

这个方法中就会真正的使用ASM来扫描解析Class文件,并生成BeandDefinition了

  1. 将basePackage添加前缀classpath*:和后缀**/*.class,生成packageSearchPath方便扫描。比如包中非class类型的就不用扫描了
  2. 使用Spring的ResourcePatternResolver来读取packageSearchPath的所有Class文件的Resource资源
  3. 循环处理每一个资源,获取到每个类的MetadataReader其实也就可以理解为每个类。MetadataReader包括了该Class文件对应类的所有信息
  4. 判断这个类是否能生成BeanDefinition。判断条件为
    1. 只要匹配了excludeFilters中的filter,返回false,不生成
    2. 只要匹配了includeFilters中的一个,返回true,生成 (其实还需要判断类上的@Condition注解,这里暂时先不做说明)
    3. excludeFilters和includeFilters都没有匹配的,返回false,不生成
  5. 当判断这个类可以生成BeanDefinition时,会创建ScannedGenericBeanDefinition对象
  6. 判断当前的ScannedGenericBeanDefinition能否加入候选BeanDefinition集合
    1. 判断是否是独立的类,是才可以加入
    2. 同时 (判断不是接口和抽象类 || 是抽象类,但存在一个@Lookup注解的方法) 才可以加入集合

以下是ClassPathScanningCandidateComponentProvider#scanCandidateComponents()的源码注释

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 给basePackage添加前缀和后缀,方便扫描。比如包中非class类型的就不用扫描成BeanDefinition了
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 通过ResourcePatternResolver读取需要扫描的类
        // 这里实际并没有加载类到JVM
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        for (Resource resource : resources) {
            // 获取到文件名,也就是类名
            String filename = resource.getFilename();
            // 判断类名是不是包含了cglib代理类的特殊类名,如果是就跳过不处理
            if (filename != null && filename.contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
                // Ignore CGLIB-generated classes in the classpath
                continue;
            }
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            try {
                // 通过ASM获取类的元数据
                // 元数据包括resource和annotationMetadata
                // annotationMetadata中包含了有包名的类名;方法名、方法参数类型;所有的注解信息等
                MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                if (isCandidateComponent(metadataReader)) {
                    // 判断是否存在需要排除的注解,如果是,则不会进这里
                    // 判断是否能匹配includeFilter,当没有匹配到时,也不会进这里

                    // 根据metadataReader来创建BeanDefinition
                    // 在创建时,保存了beanClassName、metadata的resource、annotationMetadata
                    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                    // 设置BeanDefinition的source资源
                    sbd.setSource(resource);

                    if (isCandidateComponent(sbd)) {
                        // 判断是否有资格存入BeanDefinition的集合中,能作为BeanDefinition的条件
                        // 1、是一个独立的类,不是内部类
                        // 2、不是接口也不是抽象类 || 是抽象类,但存在@Loop修饰的方法
                        if (debugEnabled) {
                            logger.debug("Identified candidate component class: " + resource);
                        }
                        // 将BeanDefinition加入集合
                        candidates.add(sbd);
                    }
                    else {
                        if (debugEnabled) {
                            logger.debug("Ignored because not a concrete top-level class: " + resource);
                        }
                    }
                }
                else {
                    if (traceEnabled) {
                        logger.trace("Ignored because not matching any filter: " + resource);
                    }
                }
            }
            catch (FileNotFoundException ex) {
                if (traceEnabled) {
                    logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());
                }
            }
            catch (ClassFormatException ex) {
                if (shouldIgnoreClassFormatException) {
                    if (debugEnabled) {
                        logger.debug("Ignored incompatible class format in " + resource + ": " + ex.getMessage());
                    }
                }
                else {
                    throw new BeanDefinitionStoreException("Incompatible class format in " + resource +
                            ": set system property 'spring.classformat.ignore' to 'true' " +
                            "if you mean to ignore such files during classpath scanning", ex);
                }
            }
            catch (Throwable ex) {
                throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);
            }
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
    }
    return candidates;
}
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    // 循环判断在excludeFilters中的注解,如果类上存在,就返回false,跳过生成BeanDefinition
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    // 循环判断在includeFilters中的注解,如果类上存在,就返回true,生成BeanDefinition
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值