【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则

简介:

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

原文地址https://www.cnblogs.com/binghe001/p/13063046.html

相关文章
|
5月前
|
人工智能 Java 数据库
飞算 JavaAI:革新电商订单系统 Spring Boot 微服务开发
在电商订单系统开发中,传统方式耗时约30天,需应对复杂代码、调试与测试。飞算JavaAI作为一款AI代码生成工具,专注于简化Spring Boot微服务开发。它能根据业务需求自动生成RESTful API、数据库交互及事务管理代码,将开发时间缩短至1小时,效率提升80%。通过减少样板代码编写,提供规范且准确的代码,飞算JavaAI显著降低了开发成本,为软件开发带来革新动力。
|
5月前
|
前端开发 Java UED
从基础到进阶:Spring Boot + Thymeleaf 整合开发中的常见坑与界面优化
本文深入探讨了 **Spring Boot + Thymeleaf** 开发中常见的参数绑定问题与界面优化技巧。从基础的 Spring MVC 请求参数绑定机制出发,分析了 `MissingServletRequestParameterException` 的成因及解决方法,例如确保前后端参数名、类型一致,正确设置请求方式(GET/POST)。同时,通过实际案例展示了如何优化支付页面的视觉效果,借助简单的 CSS 样式提升用户体验。最后,提供了官方文档等学习资源,帮助开发者更高效地掌握相关技能。无论是初学者还是进阶用户,都能从中受益,轻松应对项目开发中的挑战。
208 0
|
3月前
|
Java 测试技术 数据库
说一说 SpringBoot 整合 Junit5 常用注解
我是小假 期待与你的下一次相遇 ~
|
3月前
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
371 0
|
5月前
|
人工智能 Java 定位技术
Java 开发玩转 MCP:从 Claude 自动化到 Spring AI Alibaba 生态整合
本文详细讲解了Java开发者如何基于Spring AI Alibaba框架玩转MCP(Model Context Protocol),涵盖基础概念、快速体验、服务发布与调用等内容。重点包括将Spring应用发布为MCP Server(支持stdio与SSE模式)、开发MCP Client调用服务,以及在Spring AI Alibaba的OpenManus中使用MCP增强工具能力。通过实际示例,如天气查询与百度地图路线规划,展示了MCP在AI应用中的强大作用。最后总结了MCP对AI开发的意义及其在Spring AI中的实现价值。
1426 9
|
5月前
|
人工智能 缓存 自然语言处理
保姆级Spring AI 注解式开发教程,你肯定想不到还能这么玩!
这是一份详尽的 Spring AI 注解式开发教程,涵盖从环境配置到高级功能的全流程。Spring AI 是 Spring 框架中的一个模块,支持 NLP、CV 等 AI 任务。通过注解(如自定义 `@AiPrompt`)与 AOP 切面技术,简化了 AI 服务集成,实现业务逻辑与 AI 基础设施解耦。教程包含创建项目、配置文件、流式响应处理、缓存优化及多任务并行执行等内容,助你快速构建高效、可维护的 AI 应用。
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
497 0
|
6月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——拦截自定义异常
本文介绍了在实际项目中如何拦截自定义异常。首先,通过定义异常信息枚举类 `BusinessMsgEnum`,统一管理业务异常的代码和消息。接着,创建自定义业务异常类 `BusinessErrorException`,并在其构造方法中传入枚举类以实现异常信息的封装。最后,利用 `GlobalExceptionHandler` 拦截并处理自定义异常,返回标准的 JSON 响应格式。文章还提供了示例代码和测试方法,展示了全局异常处理在 Spring Boot 项目中的应用价值。
254 0