写在前面
Spring 的强大之处不仅仅是提供了 IoC 容器, 能够通过过滤规则指定排除和只包含哪些组件, 它还能够通过自定义 TypeFilter 来指定过滤规则. 如果 Spring 内置的过滤规则不能够满足我们的需求, 那么我们就可以通过自定义 TypeFilter 来实现我们自己的过滤规则.
项目工程源码已经提交到 GitHub: https://github.com/sunshinelyz/spring-annotation
FilterType 中常用的规则
在使用 @ComponentScan 注解实现包扫描时, 我们可以使用 @Filter 指定过滤规则, 在 @Filter 中, 通过 type 指定过滤的类型. 而 @Filter 注解的 type 属性是一个 FilterType 枚举, 如下所示.
- package org.springframework.context.annotation;
- public enum FilterType {
- ANNOTATION,
- ASSIGNABLE_TYPE,
- ASPECTJ,
- REGEX,
- CUSTOM
- }
每个枚举值的含义如下所示.
(1)ANNOTATION: 按照注解进行过滤.
例如, 使用 @ComponentScan 注解进行包扫描时, 按照注解只包含标注了 @Controller 注解的组件, 如下所示.
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
- }, useDefaultFilters = false)
(2)ASSIGNABLE_TYPE: 按照给定的类型进行过滤.
例如, 使用 @ComponentScan 注解进行包扫描时, 按照给定的类型只包含 PersonService 类 (接口) 或其子类 (实现类或子接口) 的组件, 如下所示.
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonService.class})
- }, useDefaultFilters = false)
此时, 只要是 PersonService 类型的组件, 都会被加载到容器中. 也就是说: 当 PersonService 是一个 Java 类时, Person 类及其子类都会被加载到 Spring 容器中; 当 PersonService 是一个接口时, 其子接口或实现类都会被加载到 Spring 容器中.
(3)ASPECTJ: 按照 ASPECTJ 表达式进行过滤
例如, 使用 @ComponentScan 注解进行包扫描时, 按照 ASPECTJ 表达式进行过滤, 如下所示.
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.ASPECTJ, classes = {AspectJTypeFilter.class})
- }, useDefaultFilters = false)
(4)REGEX: 按照正则表达式进行过滤
例如, 使用 @ComponentScan 注解进行包扫描时, 按照正则表达式进行过滤, 如下所示.
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.REGEX, classes = {RegexPatternTypeFilter.class})
- }, useDefaultFilters = false)
(5)CUSTOM: 按照自定义规则进行过滤.
如果实现自定义规则进行过滤时, 自定义规则的类必须是 org.springframework.core.type.filter.TypeFilter 接口的实现类.
例如, 按照自定义规则进行过滤, 首先, 我们需要创建一个 org.springframework.core.type.filter.TypeFilter 接口的实现类 MyTypeFilter, 如下所示.
- public class MyTypeFilter implements TypeFilter {
- @Override
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- return false;
- }
- }
当我们实现 TypeFilter 接口时, 需要实现 TypeFilter 接口中的 match()方法, match()方法的返回值为 boolean 类型. 当返回 true 时, 表示符合规则, 会包含在 Spring 容器中; 当返回 false 时, 表示不符合规则, 不会包含在 Spring 容器中. 另外, 在 match()方法中存在两个参数, 分别为 MetadataReader 类型的参数和 MetadataReaderFactory 类型的参数, 含义分别如下所示.
metadataReader: 读取到的当前正在扫描的类的信息.
metadataReaderFactory: 可以获取到其他任务类的信息.
接下来, 使用 @ComponentScan 注解进行如下配置.
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
- }, useDefaultFilters = false)
在 FilterType 枚举中, ANNOTATION 和 ASSIGNABLE_TYPE 是比较常用的, ASPECTJ 和 REGEX 不太常用, 如果 FilterType 枚举中的类型无法满足我们的需求时, 我们也可以通过实现 org.springframework.core.type.filter.TypeFilter 接口来自定义过滤规则, 此时, 将 @Filter 中的 type 属性设置为 FilterType.CUSTOM,classes 属性设置为自定义规则的类对应的 Class 对象.
实现自定义过滤规则
在项目的 io.mykit.spring.plugins.register.filter 包下新建 MyTypeFilter, 并实现 org.springframework.core.type.filter.TypeFilter 接口. 此时, 我们先在 MyTypeFilter 类中打印出当前正在扫描的类名, 如下所示.
- package io.mykit.spring.plugins.register.filter;
- import org.springframework.core.io.Resource;
- import org.springframework.core.type.AnnotationMetadata;
- import org.springframework.core.type.ClassMetadata;
- import org.springframework.core.type.classreading.MetadataReader;
- import org.springframework.core.type.classreading.MetadataReaderFactory;
- import org.springframework.core.type.filter.TypeFilter;
- import java.io.IOException;
- /**
- * @author binghe
- * @version 1.0.0
- * @description 自定义过滤规则
- */
- public class MyTypeFilter implements TypeFilter {
- /**
- * metadataReader: 读取到的当前正在扫描的类的信息
- * metadataReaderFactory: 可以获取到其他任务类的信息
- */
- @Override
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- // 获取当前类注解的信息
- AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
- // 获取当前正在扫描的类的信息
- ClassMetadata classMetadata = metadataReader.getClassMetadata();
- // 获取当前类的资源信息, 例如: 类的路径等信息
- Resource resource = metadataReader.getResource();
- // 获取当前正在扫描的类名
- String className = classMetadata.getClassName();
- // 打印当前正在扫描的类名
- System.out.println("----->" + className);
- return false;
- }
- }
接下来, 我们在 PersonConfig 类中配置自定义过滤规则, 如下所示.
- @Configuration
- @ComponentScans(value = {
- @ComponentScan(value = "io.mykit.spring", includeFilters = {
- @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
- }, useDefaultFilters = false)
- })
- public class PersonConfig {
- @Bean("person")
- public Person person01(){
- return new Person("binghe001", 18);
- }
- }
接下来, 我们运行 SpringBeanTest 类中的 testComponentScanByAnnotation()方法进行测试, 输出的结果信息如下所示.
- -----> io.mykit.spring.test.SpringBeanTest
- -----> io.mykit.spring.bean.Person
- -----> io.mykit.spring.plugins.register.controller.PersonController
- -----> io.mykit.spring.plugins.register.dao.PersonDao
- -----> io.mykit.spring.plugins.register.filter.MyTypeFilter
- -----> io.mykit.spring.plugins.register.service.PersonService
- org.springframework.context.annotation.internalConfigurationAnnotationProcessor
- org.springframework.context.annotation.internalAutowiredAnnotationProcessor
- org.springframework.context.annotation.internalCommonAnnotationProcessor
- org.springframework.context.event.internalEventListenerProcessor
- org.springframework.context.event.internalEventListenerFactory
- personConfig
- person
可以看到, 已经输出了当前正在扫描的类的名称, 同时, 除了 Spring 内置的 bean 名称外, 只输出了 personConfig 和 person, 没有输出使用 @Repository,@Service,@Controller 注解标注的组件名称. 这是因为当前 PersonConfig 上标注的 @ComponentScan 注解是使用自定义的规则, 而在 MyTypeFilter 自定义规则的实现类中, 直接返回了 false 值, 将所有的 bean 都排除了.
我们可以在 MyTypeFilter 类中简单的实现一个规则, 例如, 当前扫描的类名称中包含有字符串 Person, 就返回 true, 否则返回 false. 此时, MyTypeFilter 类中 match()方法的实现如下所示.
- @Override
- public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
- // 获取当前类注解的信息
- AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
- // 获取当前正在扫描的类的信息
- ClassMetadata classMetadata = metadataReader.getClassMetadata();
- // 获取当前类的资源信息, 例如: 类的路径等信息
- Resource resource = metadataReader.getResource();
- // 获取当前正在扫描的类名
- String className = classMetadata.getClassName();
- // 打印当前正在扫描的类名
- System.out.println("----->" + className);
- return className.contains("Person");
- }
此时, 在 io.mykit.spring 包下的所有类都会通过 MyTypeFilter 类的 match()方法, 来验证类名是否包含 Person, 如果包含则返回 true, 否则返回 false.
我们再次运行 SpringBeanTest 类中的 testComponentScanByAnnotation()方法进行测试, 输出的结果信息如下所示.
- -----> io.mykit.spring.test.SpringBeanTest
- -----> io.mykit.spring.bean.Person
- -----> io.mykit.spring.plugins.register.controller.PersonController
- -----> io.mykit.spring.plugins.register.dao.PersonDao
- -----> io.mykit.spring.plugins.register.filter.MyTypeFilter
- -----> io.mykit.spring.plugins.register.service.PersonService
- org.springframework.context.annotation.internalConfigurationAnnotationProcessor
- org.springframework.context.annotation.internalAutowiredAnnotationProcessor
- org.springframework.context.annotation.internalCommonAnnotationProcessor
- org.springframework.context.event.internalEventListenerProcessor
- org.springframework.context.event.internalEventListenerFactory
- personConfig
- person
- personController
- personDao
- personService
此时, 结果信息中输出了使用 @Repository,@Service,@Controller 注解标注的组件名称, 分别为: personDao,personService 和 personController.
好了, 咱们今天就聊到这儿吧! 别忘了给个在看和转发, 让更多的人看到, 一起学习一起进步!!
项目工程源码已经提交到 GitHub: https://github.com/sunshinelyz/spring-annotation
写在最后
如果觉得文章对你有点帮助, 请微信搜索并关注「 冰河技术 」微信公众号, 跟冰河学习 Spring 注解驱动开发. 公众号回复 "spring 注解" 关键字, 领取 Spring 注解驱动开发核心知识图, 让 Spring 注解驱动开发不再迷茫.
来源: https://www.cnblogs.com/binghe001/p/13063046.html