吾日五省我身 2020-06-08
Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则。如果Spring内置的过滤规则不能够满足我们的需求,那么我们就可以通过自定义TypeFilter来实现我们自己的过滤规则。
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
在使用@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类型的参数,含义分别如下所示。
接下来,使用@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注解驱动开发不再迷茫。