【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则
写在前面
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注解驱动开发不再迷茫。
【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则的更多相关文章
- 【String注解驱动开发】你了解@PostConstruct注解和@PreDestroy注解吗?
写在前面 在之前的文章中,我们介绍了如何使用@Bean注解指定初始化和销毁的方法,小伙伴们可以参见<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!& ...
- 【Spring注解驱动开发】如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!
写在前面 在[String注解驱动开发专题]中,前面的文章我们主要讲了有关于如何向Spring容器中注册bean的知识,大家可以到[String注解驱动开发专题]中系统学习.接下来,我们继续肝Spri ...
- 【Spring注解驱动开发】如何使用@Value注解为bean的属性赋值,我们一起吊打面试官!
写在前面 在之前的文章中,我们探讨了如何向Spring的IOC容器中注册bean组件,讲解了有关bean组件的生命周期的知识.今天,我们就来一起聊聊@Value注解的用法. 项目工程源码已经提交到Gi ...
- 【Spring注解驱动开发】使用InitializingBean和DisposableBean来管理bean的生命周期,你真的了解吗?
写在前面 在<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!>一文中,我们讲述了如何使用@Bean注解来指定bean初始化和销毁的方法.具体的 ...
- 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【spring 注解驱动开发】spring组件注册
尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...
- 【spring 注解驱动开发】spring自动装配
尚学堂spring 注解驱动开发学习笔记之 - 自动装配 自动装配 1.自动装配-@Autowired&@Qualifier&@Primary 2.自动装配-@Resource& ...
- 【Spring注解驱动开发】使用@Scope注解设置组件的作用域
写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...
随机推荐
- 深入Spring之IOC之加载BeanDefinition
本文主要分析 spring 中 BeanDefinition 的加载,对于其解析我们在后面的文章中专门分析. BeanDefinition 是属于 Spring Bean 模块的,它是对 spring ...
- 类文件结构——深入理解Java虚拟机 笔记三
在之前的笔记中记录过,Java程序变成可执行文件的步骤是:源代码-->经过编译变成class文件-->经过JVM虚拟机变成可执行的二进制文件.因此,为了对JVM执行程序的过程有一个好的了解 ...
- Spring学习笔记(三):面向切面的Spring
Spring之面向切面编程 一.理解何为面向切面编程 对于这个的理解,我觉得Spring实战中的例子讲得很明白: 假设我现在是一个小区用户,每个月小区都要收电费,这时候就会来人查看电表,算出来这个月电 ...
- Java并发编程:线程池ThreadPoolExecutor
多线程的程序的确能发挥多核处理器的性能.虽然与进程相比,线程轻量化了很多,但是其创建和关闭同样需要花费时间.而且线程多了以后,也会抢占内存资源.如果不对线程加以管理的话,是一个非常大的隐患.而线程池的 ...
- 【比较】遗传算法GA和遗传编程GP有什么不同?
遗传算法GA 本质上有一个固定的长度,这意味着所产生的功能有限的复杂性 通常会产生无效状态,因此需要以非破坏性方式处理这些状态 通常依赖于运算符优先级(例如,在我们的例子中,乘法发生在减法之前),这可 ...
- 疯狂VirtualBox实战讲学录 以及 virtualbox完全学习手册 之我见
都是是很专业,并钻石得很深的内容,但,有些事情是交替进行着的深入,太专注于VIRTUAL BOX就必要要牺牲其它的东西.
- mouseover与mouseenter区别
学习笔记. mouseover:在鼠标移入元素本身或者子元素时都会触发事件,相当于有一个冒泡过程.而且在鼠标移入子元素中时,父元素会显示离开的状态:相应的,当鼠标从子元素移入父元素,子元素也会显示离开 ...
- 【MySQL】MyISAM和InnoDB存储引擎区别详解
MyISAM 主键索引(引擎的索引文件和数据文件是分离的) (图片来自:https://blog.csdn.net/u010922732/article/details/82994253) 非主键索引 ...
- 解决mysql:ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO/YES)
一.问题 有时候我们登录Mysql输入密码的时候,会出现这种情况 mysql -u root -p Enter Password > '密码' 错误:ERROR 1045 (28000): Ac ...
- python中几个双下划线用法的含义
_ _ init() _ _(self[,...]) 我们有时在类定义写__init()__方法,但是有时又没有.__init()__方法相当于其他面向对象的编程语言中的构造方法,也就是类在实例化成对 ...