• 【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注解驱动开发不再迷茫。

  • 相关阅读:
    【JMeter_22】JMeter逻辑控制器__录制控制器<Recording Controller>
    【JMeter_21】JMeter逻辑控制器__模块控制器<Module Controller>
    【JMeter_20】JMeter逻辑控制器__事务控制器<Transaction Controller>
    【JMeter_19】JMeter逻辑控制器__简单控制器<Simple Controller>
    【JMeter_18】JMeter逻辑控制器__吞吐量控制器<Throughput Controller>
    【JMeter_17】JMeter逻辑控制器__随机顺序控制器<Random Order Controller>
    【JMeter_16】JMeter逻辑控制器__随机控制器<Random Controller>
    【JMeter_15】JMeter逻辑控制器__仅一次控制器<Once Only Controller>
    Golang错误和异常处理的正确姿势
    用beego开发服务端应用
  • 原文地址:https://www.cnblogs.com/binghe001/p/13063046.html
Copyright © 2020-2023  润新知