@Component修饰的类注册到IOC容器中的流程解析

一、引入ComponentScanBeanDefinitionParser

当我们使用注解@Component@Controller@Service等向Spring中注册Bean时,并不是在我们需要注册的类上加上@Component等注解就可以了,还需要在XML配置文件中开启组件扫描:
<context:component-scan base-package="com.yk"></context:component-scan>
开启组件扫描是必不可少的一步,只有开启了组件扫描,Spring才能帮我们把被@Component等注解修饰的类注册到IOC容器中,所以分析被@Component修饰的类注册到IOC容器中的流程还要从开启组件扫描这个配置入手
<context:component-scan></context:component-scan>是自定义命名空间下的结点,所以在Spring解析XML配置文件解析到这行配置的时候,会调用parseCustomElement方法去解析,首先它会找到context命名空间所对应的命名空间处理器ContextNamespaceHandler,接着会找到component-scan所对应的BeanDefinition的解析器ComponentScanBeanDefinitionParser所以这里可以猜测到,将被@Component等注解修饰的类转换为BeanDefinition并注册到容器中的工作是由**ComponentScanBeanDefinitionParser**完成的
在这里插入图片描述

二、注册流程分析

BeanDefinitionParser中只有一个parse方法,这个方法就是具体执行解析工作的方法,所以接下来就是分析ComponentScanBeanDefinitionParser中的parse方法。

parse方法执行流程:

  1. 获取到我们在配置文件中指定的要扫描的包,并对其进行处理;
  2. 创建一个BeanDefinition的扫描器ClassPathBeanDefinitionScanner,BeanDefinition的获取和注册都由这个扫描器完成;
  3. 发送注册事件;
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 获取要扫描的包
    String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
    basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
    // 可以配置多个,所以这里要进行分割
    String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

    // 创建一个扫描器
    ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
    
    // 让扫描器去扫描我们指定的包,得到所有符合规则的BeanDefinition,并注册到IOC容器中
    Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
    
    // 发送注册事件
    registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
    return null;
}

创建扫描器

在parse方法中,通过这行代码得到一个扫描器,所以扫描器的创建过程要看configureScanner方法。

ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);

主要流程:

  1. 判断是否使用默认的过滤器
  2. 创建扫描器
  3. 解析resource-pattern属性
  4. 解析name-generator属性
  5. 解析scope-resolver和scoped-proxy属性
  6. 解析子结点和
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
    // 判断是否使用默认的过滤器,对应<context:component-scan>的use-default-filters属性
    boolean useDefaultFilters = true;
    if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
        useDefaultFilters = Boolean.parseBoolean(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
    }
	// 创建扫描器,这里创建出一个初始的扫描器
    ClassPathBeanDefinitionScanner scanner = createScanner(parserContext.getReaderContext(), useDefaultFilters);
    
    scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
    scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());

    // 解析resource-pattern属性并设置到扫描器中
    if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
        scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
    }

    try {
        // 解析name-generator属性并设置到扫描器中
        parseBeanNameGenerator(element, scanner);
    }

    try {
        // 解析scope-resolver和scoped-proxy属性并设置到扫描器中
        parseScope(element, scanner);
    }

    // 解析子结点<include-filter></include-filter>和<exclude-filter></exclude-filter>并设置到扫描器中
    parseTypeFilters(element, scanner, parserContext);
    return scanner;
}

下面是真正创建扫描器的代码,对应主要流程中的第二步

protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
    return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters,
            readerContext.getEnvironment(), readerContext.getResourceLoader());
}

创建扫描器的过程中,非常关键的是是否使用默认的过滤器:根据Spring的注释来看,默认过滤器包含对@Component@Controller@Service@Repository的过滤
在这里插入图片描述

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);
}

下面是注册默认过滤器的代码,可以看到这里注册了Component的过滤器,但是这里貌似并未注册**@Controller****@Service****@Repository**,至于为什么,后面再说。

protected void registerDefaultFilters() {
    // 添加@Component注解的过滤器
    this.includeFilters.add(new AnnotationTypeFilter(Component.class));
    ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
    try {
        // 添加@ManagedBean注解的过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
    }
    
    try {
        // 添加@Named注解的过滤器
        this.includeFilters.add(new AnnotationTypeFilter(
                ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
    }
}

下面看下注册自定义过滤器,对应主要流程中的第六步:

protected void parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext) {
    // ......
    // 获取到<context:component-scan>的子结点:<include-filter>或者<exclude-filter>
    NodeList nodeList = element.getChildNodes();
    // 遍历每一个子结点
    for (int i = 0; i < nodeList.getLength(); i++) {
        Node node = nodeList.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
            // 获取结点名称
            String localName = parserContext.getDelegate().getLocalName(node);
            try {
                // 结点为<includ-filter>
                if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
                    TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                    scanner.addIncludeFilter(typeFilter);
                }
                // 结点为<exclude-filter> 
                else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
                    TypeFilter typeFilter = createTypeFilter((Element) node, classLoader, parserContext);
                    scanner.addExcludeFilter(typeFilter);
                }
            }
        }
    }
}

通过上面的代码可以看出include-filter和exclude-filter的创建过程是一样的,只是创建完成后一个加入到includeFilters集合中,一个加入到excludeFilters集合中。
下面是创建自定义Filter的过程:根据不同的filterType实例化不同类型的TypeFilter

protected TypeFilter createTypeFilter(Element element, @Nullable ClassLoader classLoader,
        ParserContext parserContext) throws ClassNotFoundException {

    String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
    String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
    expression = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(expression);
    if ("annotation".equals(filterType)) {
        return new AnnotationTypeFilter((Class<Annotation>) ClassUtils.forName(expression, classLoader));
    }
    else if ("assignable".equals(filterType)) {
        return new AssignableTypeFilter(ClassUtils.forName(expression, classLoader));
    }
    else if ("aspectj".equals(filterType)) {
        return new AspectJTypeFilter(expression, classLoader);
    }
    else if ("regex".equals(filterType)) {
        return new RegexPatternTypeFilter(Pattern.compile(expression));
    }
    else if ("custom".equals(filterType)) {
        Class<?> filterClass = ClassUtils.forName(expression, classLoader);
        return (TypeFilter) BeanUtils.instantiateClass(filterClass);
    }
}

到这里,扫描器已经创建完成,下一步就是进行扫描操作,主要就是让根据basePackage和指定的TypeFilter过滤得到指定的.class文件并解析得到BeanDefinition。

扫描器扫描得到BeanDefinition

下面是进行扫描器执行扫描操作得到BeanDefinition的代码

Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);

主要流程:遍历我们在配置中指定的basePackage数组,对每一个basePackage执行以下操作:

  1. 找到当前包下所有符合要求的.class文件,并解析得到它们所对应的BeanDefinition;
  2. 对解析得到的每一个BeanDefinition做处理,且根据条件考虑是否注册到容器中
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 遍历我们指定的basePackages
    for (String basePackage : basePackages) {
        // 找到当前包下所有符合要求的.class文件,并解析得到它们所对应的BeanDefinition
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            // 创建的是ScannedGenericBeanDefinition
            // public class ScannedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition
            // public class GenericBeanDefinition extends AbstractBeanDefinition
            // 所以下面两个处理都会走
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            // 这里主要判断当前beanName是否已经存在对应的BeanDefinition
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

这里主要看下第一步的详细解析,即:找到当前包下所有符合要求的.class文件,并解析得到它们所对应的BeanDefinition。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 首先将当前路径下的所有的.class文件分别转化为一个Resource类型的对象,得到一个Resource类型的数组
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        // 遍历每一个Resource对象
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 判断给定的Resource对象是否是符合我们要求的对象,即:
                    // 当前Resource对象不满足任何一个exclude-filter,至少满足include-filter的其中一个。
                    if (isCandidateComponent(metadataReader)) {
                        // 满足条件就创建BeanDefinition类型的对象,并把当前Resource的相关属性传递给BeanDefinition
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setSource(resource);
                        // 这里又进行了一次判断,主要是判断当前Resource是否不是接口和非静态内部类以及抽象类,因为最终Spring
                        // 最终要根据我们注册都容器中的BeanDefinition进行实例化Bean,而接口和抽象类是直接实例化的
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                    }
                }
            }
        }
    }
    return candidates;
}

上面的方法中,最主要的是判断是否是符合我们要求的Resource

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    // 遍历所有的exclude-filter,如果匹配任何一个,则说明当前Resource不是我们想要的
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    // 遍历所有的include-filter,如果匹配任何一个,则说明当前Resource是我们想要的
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            // 这里主要是对加了@Conditional注解的类进行再次判断
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

tf.match中有一个matchSelf方法,这个方法就是用来处理@Component@Controller等注解的。

protected boolean matchSelf(MetadataReader metadataReader) {
    AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
    return metadata.hasAnnotation(this.annotationType.getName()) ||
            (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

默认情况下,this.annotationType.getName()为Component。

通过MetadataReader可以获取到实体类上的注解信息,因为上面注册include-filter的时候注册了只注册了@Component,所以,如果类上面加的是Component注解,那么第一个判断就为true,直接返回true,而对于Controller这些注解,需要走第二步判断,considerMetaAnnotations是是否考虑元注解,这个属性为true,所以**最主要的就是metadata.hasMetaAnnotation这个判断,**默认情况下,这个方法用了判断是否用于@Component元注解,元注解指的是加在其它注解上的注解,可以看到@Controller@Service上面都有元注解@Component,所以校验会通过,经过后续步骤会被解析成BeanDefinition注册到IOC容器中。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
	@AliasFor(annotation = Component.class)
	String value() default "";
}

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
	@AliasFor(annotation = Component.class)
	String value() default "";
}

参考链接:
https://www.cnblogs.com/aigongsi/archive/2012/04/24/2467183.html
https://javazhiyin.blog.csdn.net/article/details/107587683

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值