曹工说Spring Boot源码(27)-- Spring的component-scan,光是include-filter属性的各种配置方式,就够玩半天了.md (3)

曹工说Spring Boot源码(27)-- Spring的component-scan,光是include-filter属性的各种配置方式,就够玩半天了.md

这个TypeFilter,就一个方法:

public interface TypeFilter { /** * Determine whether this filter matches for the class described by * the given metadata. * @param metadataReader the metadata reader for the target class * @param metadataReaderFactory a factory for obtaining metadata readers * for other classes (such as superclasses and interfaces) * @return whether this filter matches * @throws IOException in case of I/O failure when reading metadata */ boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException; }

方法很好理解,参数是:当前的被扫描到的那个类的元数据reader,通过这个reader,可以取到class文件中的各种信息,底层就是通过ASM方式来实现;第二个参数,可以先跳过。

返回值呢,就是:这个filter是否匹配,我们前面的includeFilters和excludeFilters数组,其元素类型都是这个,所以,这个typeFilter是只管匹配与否,不分是非,不管对错。

我们这里这个org.springframework.core.type.filter.AnnotationTypeFilter,就是根据注解来匹配,比如,我们前面这里的filter,就要求是@Componnet注解标注了的类才可以。

但是,我们的TestController,没有标注Component注解,只标注了Controller注解。对,是这样,但是因为Controller是被@Component标注了的,所以,你标注Controller,就相当于同时标注了下面这一坨:

@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface Controller {

同时,由于我们的AnnotationTypeFilter,在匹配算法上,做的比较漂亮,不止检测直接标注在类上的注解,如Controller,还会去检测:Controller上的注解(俗称:元注解,即,注解的注解)。这块实现逻辑在:

org.springframework.core.type.filter.AnnotationTypeFilter#matchSelf @Override protected boolean matchSelf(MetadataReader metadataReader) { AnnotationMetadata metadata = metadataReader.getAnnotationMetadata(); return metadata.hasAnnotation(this.annotationType.getName()) || (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName())); }

这里的considerMetaAnnotations,默认为true,此时,就会去检测@Controller上的元注解,发现标注了@Component,所以,这里的检测就为true。

所以,标注了Controller的类,就被扫描为Bean了。

includeFilters,什么时候添加了这么一个AnnotationTypeFilter

在xml场景下,是在如下位置:

org.springframework.context.annotation.ComponentScanBeanDefinitionParser#parse public BeanDefinition parse(Element element, ParserContext parserContext) { String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // Actually scan for bean definitions and register them. // 1 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }

上述代码,就是负责解析component-scan这个标签时,被调用的;代码1处,configureScanner代码如下:

protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) { XmlReaderContext readerContext = parserContext.getReaderContext(); boolean useDefaultFilters = true; if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) { useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)); } // 1. ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters); ... }

如上,代码1处,createScanner时,传入useDefaultFilters,这是个boolean值,默认为true,来自于component-scan的如下属性,即use-default-filters:

<context:component-scan use-default-filters="false" base-package="org.springframework.test"> <context:include-filter type="aspectj" expression="org.springframework.test.assignable.*"/> </context:component-scan>

跟踪进去后,最终会调用如下位置的代码:

protected void registerDefaultFilters() { /** * 默认扫描Component注解 */ this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ... }

ok,一切就水落石出了。

自定义typeFilter--扫描指定注解

说了那么多,我们完全可以禁用掉默认的typeFilter,配置自己想要的typeFilter,比如,我想要定义如下注解:

@Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyComponent { }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppdjd.html