@SpringbootApplication详解和自定义类型过滤器

2023-11-06

spring boot 启动类使用@SpringBootApplication标记,该注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
	Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

从定义中可以看出 @SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan 三个注解是理解启动类的关键。

@SpringBootConfiguration

该注解本身就是@Configuration,这里需要注意的是 @Configuration@Compoent 两者的区别

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    String value() default "";
    boolean proxyBeanMethods() default true;
}

默认情况下 @Configuration 中所有带 @Bean 注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
@Component 注解的类就是一个标准类,如果在这个类中有@Bean标注的方法,那么方法间的相互调用,其实就是普通Java类的方法的调用。
当注解设置为 @Configuration(proxyBeanMethods = false) 的时候,效果和直接使用 @Component 相当

public class Pet {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class User {
    private String username;
    private Pet pet;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Pet getPet() {
        return pet;
    }

    public void setPet(Pet pet) {
        this.pet = pet;
    }
}
@Configuration
public class AConfig {

    @Bean
    public User user() {
        User user = new User();
        user.setUsername("张三");
        //user组件依赖了Pet组件
        user.setPet(pet());
        return user;
    }

    @Bean
    public Pet pet() {
        Pet pet = new Pet();
        pet.setName("阿猫");
        return pet;
    }
}
public class Test {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(AConfig.class);
        ctx.refresh();
     
        User tom01 = ctx.getBean("user", User.class);
        User tom02 = ctx.getBean("user", User.class);
        Pet pet = ctx.getBean("pet", Pet.class);
        //true 可以证明向容器中注入的组件是单实例的
        System.out.println("组件:" + (tom01 == tom02));
        System.out.println("用户的宠物:" + (tom01.getPet() == pet));
    }
}

输出:

组件:true
用户的宠物:true

将 AConfig 中的注解修改为 @Component 的时候,输出为:

组件:true
用户的宠物:false

@EnableAutoConfiguration

从字面理解该注解是用来开启自动化配置,从注解的定义来看,主要是通过 @Import 导入了两个配置类。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	Class<?>[] exclude() default {};

	String[] excludeName() default {};

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

	String[] basePackages() default {};

	Class<?>[] basePackageClasses() default {};
}

想要理解这个注解,首先需要理解 @Import 主要做了什么工作。
在原生的 spring framework 中,组件装配有三中方式。

  1. Spring 2.5+ 提供了 @Component
  2. Spring 3.0+ 提供了 @Configuration + @Bean的方式
  3. Spring 3.1+ 提出了模块装配的概念,@EnableXXXX + @Import
    前两种方式在平常比较常用,第三种方式的用法如下。
public class Cat {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class Dog {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
// 给写法2准备的 @Configuration 写法
@Configuration
public class PetConfiguration {

    @Bean
    public Cat cat() {
        Cat cat = new Cat();
        cat.setName("阿猫");
        return cat;
    }

    @Bean
    public Dog dog() {
        Dog dog = new Dog();
        dog.setName("阿狗");
        return dog;
    }
}
// 定义 ImportSelector 的实现给 写法3 使用
public class PetImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{Cat.class.getName(), Dog.class.getName()};
    }
}
// 定义 DefinitionRegister 的实现给 写法4 使用
public class PetImportDefinitionRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ImportBeanDefinitionRegistrar.super.registerBeanDefinitions(importingClassMetadata, registry);
        registry.registerBeanDefinition("cat", new RootBeanDefinition(Cat.class));
        registry.registerBeanDefinition("dog", new RootBeanDefinition(Dog.class));
    }
}
// 定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
//写法1:@Import({Cat.class, Dog.class})
//写法2:@Import({PetConfiguration.class})或@Import(PetConfiguration.class)
//写法3:@Import({PetImportSelector })
//写法4:@Import({PetImportDefinitionRegister.class})
public @interface EnablePet {
}
// 使用 @EnablePet 注解
@SpringBootApplication
@EnablePet
public class AppStarter {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(AppStarter.class, args);
        Dog dog = ctx.getBean(Dog.class);
        Cat cat = ctx.getBean(Cat.class);
        System.out.println("dog.getName() = " + dog.getName());
        System.out.println("cat.getName() = " + cat.getName());
    }
}

@ComponentScan

spring boot 中该注解的默认配置为 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }),该注解主要是做包扫描的,默认扫描的启动类所在的包。excludeFilters 配置用来指定哪些类不用扫描。

  • TypeExcludeFilter.class 用来指定哪些类不扫描
  • AutoConfigurationExcludeFilter.class 则是用来指明 AutoConfiguration 的类不扫描。因为 spring boot 前面通过 EnableAutoConfiguration 已经扫描加载过了,下面是 AutoConfigurationExcludeFilter 的实现,里面排除的自动配置类的扫描 在这里插入图片描述

实现一个自定义类型过滤的TypeFilter

Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则。如果Spring内置的过滤规则不能够满足我们的需求,那么我们便可以通过自定义TypeFilter来实现我们自己的过滤规则。

FilterType预定义规则

在使用@ComponentScan注解实现包扫描时,我们可以使用@Filter指定过滤规则,在@Filter中,通过type来指定过滤的类型。

public enum FilterType {

	/**
	 * 按照注解进行包含或者排除,例如:@Filter(type=FilterType.ANNOTATION, classes={Controller.class})
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * 按照给定的类型进行包含或者排除,例如:@Filter(type=FilterType.ASSIGNABLE_TYPE, classes={BookService.class})
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * 按照ASPECTJ表达式进行包含或者排除,例如:@Filter(type=FilterType.ASPECTJ, classes={AspectJTypeFilter.class})
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * 按照正则表达式进行包含或者排除,例如: @Filter(type=FilterType.REGEX, classes={RegexPatternTypeFilter.class})
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** 按照自定义规则进行包含或者排除,如果实现自定义规则进行过滤时,自定义规则的类必须是
	 *  org.springframework.core.type.filter.TypeFilter接口的实现类。
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

}

自定义过滤器规则

要想按照自定义规则进行过滤,首先我们得创建org.springframework.core.type.filter.TypeFilter接口的一个实现类,如下:


import com.haoyang.EnablePet;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.io.IOException;

public class MyTypeExcludeFilter extends TypeExcludeFilter {

	/**
	 * 参数:
	 * metadataReader:读取到的当前正在扫描的类的信息
	 * metadataReaderFactory:可以获取到其他任何类的信息的(工厂)
	 */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException{
        return HelloController.class.getName().equals(metadataReader.getAnnotationMetadata().getClassName());
    }
}

match()方法的返回值为boolean类型。当返回true时,表示符合规则,会包含在Spring容器中;当返回false时,表示不符合规则,那就是一个都不匹配,自然就都不会被包含在Spring容器中。

添加到容器

package com.haoyang.initializer;

import com.haoyang.filter.MyTypeExcludeFilter;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.PriorityOrdered;

/**
 * @author xingmu
 * @date 2023-08-05
 * @description 容器初始化
 */
public class ApplicationInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(new TypeExcludeFilterPostProcessor());
    }

    private static class TypeExcludeFilterPostProcessor implements PriorityOrdered, BeanDefinitionRegistryPostProcessor {

        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            registry.registerBeanDefinition(MyTypeExcludeFilter.class.getName(), new RootBeanDefinition(MyTypeExcludeFilter.class));
        }

        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

        }

        @Override
        public int getOrder() {
            // 最高优先级
            return HIGHEST_PRECEDENCE;
        }
    }
}

编写spring.factories

创建resources/META-INF/spring.factories文件,添加如下内容。

org.springframework.context.ApplicationContextInitializer=com.haoyang.initializer.ApplicationInit

测试

在前面的 MyTypeExcludeFilter 中排除了 HelloController 的加载,现在启动服务,来请求一下接口。
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@SpringbootApplication详解和自定义类型过滤器 的相关文章

  • Java泛型类型要么扩展要么是父类

    我正在寻找一些如下所示的代码 public class Parent
  • Android - 检测电容式触摸屏上的触摸压力?

    我听说过 MotionEvent e float press e getPressure 但这只会在没有触摸时返回 0 当我的手指触摸屏幕时返回 1 是否可以找到手指在触摸电容屏上施加的压力值 或者我的预感是否正确 即这只适用于电阻屏幕 M
  • 为 Nimbus 外观设计简单的单元渲染器

    我有一个简单的单元格渲染器 它由一些组成JLabels 渲染器本身扩展JPanel 并且我正在尝试让它在 Nimbus 的外观和感觉中合理地渲染 基本上发生的事情是在lighter行 正如 Nimbus 所具有的交替行着色 我的特定单元格渲
  • 在 Spring 中使用事务时创建提交后

    由于某些原因 我使用 Spring PlatformTransactionManager 手动执行事务提交和回滚 我需要做的是设置一个钩子 以便在提交事务后发生提交后操作 通过查看 void commit TransactionStatus
  • 可以使用注解进行代码注入吗?

    我意识到这可能是一个已经被提出和回答的问题 但请耐心等待 我想知道是否可以使用注释将代码注入到类编译时 典型的示例是为对象的成员生成 getter 和 setter 这并不完全是我所需要的 但它可以说明基本思想 现在在互联网上我得到的基本答
  • Spring Boot 删除 Whitelabel 错误页面

    我正在尝试删除白色标签错误页面 所以我所做的是为 error 创建了一个控制器映射 RestController public class IndexController RequestMapping value error public
  • 我在 Android Studio 中使用哪个版本的 JDK 有关系吗?

    I know I can choose the SDK location in Android Studio s Project Structure 我有两个问题 当我们已经使用Android SDK时 为什么还需要JDK 毕竟我们不是为
  • 如何设置Java线程的CPU核心亲和力?

    我搜索了以前关于类似主题的帖子 但找不到合适的答案 因此提出这个问题 非常感谢您帮助回答 我知道在 Linux 中通过任务集命令设置进程与特定 CPU 核心的关联性 但我想设置 Java 线程与特定 cpu 核心的亲和力 以便属于同一进程的
  • Java Swing 自定义形状(2D 图形)

    我需要绘制自定义形状 现在 当用户单击面板上的几个点时 我使用多边形创建一个形状 public void mouseClicked MouseEvent e polygon addPoint e getX e getY repaint 但我
  • 有没有办法在@Service上使用@ControllerAdvice

    我有一个项目需求 但我没有任何需求 Controller or RestController但我需要为我的服务层提供一个全局异常处理程序 所以我需要配置 ControllerAdvice on Service 请告诉我是否还有其他方法可以做
  • 如何用Java捕获音频数据

    我想访问我的麦克风用 Java 录制的音频数据 我该怎么做呢 我的目标是保存录制的音频数据并同时向用户播放 如果您不需要 JMF 中的任何附加功能 我会避免使用它 因为开发已经停止 最后一个版本是 2004 年 它与 Java 6 存在兼容
  • 无法安装 JDK 9,因为“另一个 Java 安装正在进行中”

    我已经在 Windows 10 x64 上使用 JDK 9 一段时间了 但是当我去安装最新的早期版本 b174 时 我首先卸载了以前的版本 像往常一样 然后运行新的安装程序 它失败并显示一个消息框 显示 另一个 Java 安装正在进行中 您
  • c3p0 Java 数据库池、故障转移配置

    当数据库关闭时 IP 和端口会自动切换到另一个数据库服务器 我应该如何配置 Web 应用程序的 c3p0 连接池以遵循此数据库故障转移机制 目前 我使用的是 c3p0 但是在上次数据库故障转移中 池连接无法重新建立 请求失败后重新建立 有助
  • Java 8 中函数类型全等 lambda 表达式的用法

    我对 的定义和用法感到困惑 Stream collect Supplier
  • 调用本机方法时返回 java.lang.UnsatisfiedLinkError

    我正在尝试为第三方 DLL 制作 Java 包装器 我创建了自己的 DLL 充当 JNI 和第三方 DLL 之间的中间人 在java中我加载这个DLL很好但是错误java lang UnsatisfiedLinkError sixense
  • 错误:找不到符号 ArrayList

    我正在尝试创建某种列表来存储数组 表 中的值 我在这里使用数组列表 但我应该使用列表吗 但是 每次我尝试编译时 它都会引发以下错误 找不到标志 符号 ArrayList类 位置 玩家类 TablePlayer 代码如下 public cla
  • Java 堆分析因 SIGABRT 崩溃

    我正在尝试分析由 C 编写的方法分配并插入的本机内存JVM通过JNI 我安装了 valgrind version valgrind 3 13 0 并尝试使用以下选项运行 JVM valgrind tool massif massif out
  • 使用 Swift 在 iOS 和 Android 之间共享核心代码

    我想要的是 使用 Swift 在 Android 和 iOS 之间共享非 UI 代码 问题 Android 具有 NDK 支持 允许您使用 Java 本机接口 JNI 运行 C 和 C 代码 不是 Objective C 我是一名Java程
  • Apache Beam:如何在使用重复数据删除功能时解决“ParDo 需要确定性密钥编码器才能使用状态和计时器”

    我正在尝试使用 Apache Beam 的重复数据删除功能对来自 Google Cloud Pubsub 的输入消息进行重复数据删除 但是 我创建后遇到错误KV
  • 将菜单添加到空活动

    我在 Android Studio 中制作了一个 Android 应用程序 并想在其上创建一个选项菜单 我将其创建为一个空活动 现在意识到我最好创建一个空白活动来获取选项菜单 无论如何 是否可以在空活动中创建选项菜单 如果有人能给我指出一个

随机推荐

  • 正则表达式验证邮箱格式

    需满足的验证逻辑 之前必须有内容且只能是字母 大小写 数字 下划线 减号 点 和最后一个点 之间必须有内容且只能是字母 大小写 数字 点 减号 且两个点不能挨着 最后一个点 之后必须有内容且内容只能是字母 大小写 数字且长度为大于等于2个字
  • 使用postman做接口测试实战

    一 准备 1 postman工具 2 搭建被测系统服务 3 fiddler抓包工具 说明 本此实战测试是在完成前台界面开发后进行的 接口是通过fiddler抓包获取的 如果没有完成系统前台开发 以项目组输入的接口为准进行测试也是一样的原理
  • 博世BMA400传感器API (中文说明)

    前言 最近项目中要使用BMA400 sensor 为了以后查找 这里只是对API进行翻译 有些地方翻译可能出错 请指出来一起探讨 谢谢各位看官 BMA 400数据手册 https download csdn net download qq
  • 五种方法,教你如何在Mac上查看文件完整路径

    在MacOS上 Finder显示文件默认是不带路径展示的 你进入某个文件夹只会显示文件夹的名称而已 如下图 那如何获取或者显示文件的完整路径呢 在MacOS中有五种方法可以显示文件完整路径 第一种 使用 终端 Terminal获取文件完整路
  • Numpy 索引

    整数索引 一般形式 arr frist dim index second dim index nth dim index x np array 1 2 3 4 5 6 7 8 print x 2 3 y np array 11 12 13
  • 安卓图片裁剪之Android-Image-Cropper简单使用

    图片裁剪是一个相对用的比较多的功能 正好近期用到了 于是在最新的ChatGPT上询问了一番 两次询问 得到的最优推荐依然是 Android Image Cropper 经过一番研究使用 确实简单好用 直接看代码 首先 你需要引入依赖 dep
  • 吊打面试官系列之:我这样回答 “如何更高效的进行接口测试“,面试官果然跪了。

    高效的进行接口测试 1 引言 2 什么是接口测试 3 为什么要做接口测试 4 接口测试的范围 5 接口测试的重点 6 接口测试原则 6 1 基本原则 6 2 关键原则 6 2 其他原则 6 2 1 系统角度 7 接口测试常用工具 8 总结
  • ECCV2016 paper list (完整版)

    摘自ECCV2016的会议论文 Oral Session 1A Detection Recognition Retrieval Tuesday October 11 09 00 10 00 Chairs Bernt Schiele MPI
  • 牛客网SQL刷题四-电商场景(某东商城)

    SQL13 计算商城中2021年每月的GMV 数据 DROP TABLE IF EXISTS tb order overall CREATE TABLE tb order overall id INT PRIMARY KEY AUTO IN
  • ByteTrack 多目标跟踪 测试笔记

    目录 多目标跟踪2022个人汇总知识 Results on MOT challenge test set ByteTrack 简介 转自知乎 1 Motivation 2 byte原理
  • minicom 没有tx 信号

    在minicom s 的配置中 Serial port setup 选择F Hardware Flow Control No默认是yes 但是没有tx信号输出 改为no 后使用正常
  • 配置Docker本地镜像仓库

    1 需要先创建一个registry ui目录 存放地址随意你自己记得就行 mkdir registry ui 2 在registry ui中创建一个docker compose yml文件 touch docker compose yml
  • 【PTA】谷歌的招聘

    2004 年 7 月 谷歌在硅谷的 101 号公路边竖立了一块巨大的广告牌 如下图 用于招聘 内容超级简单 就是一个以 com 结尾的网址 而前面的网址是一个 10 位素数 这个素数是自然常数 e 中最早出现的 10 位连续数字 能找出这个
  • C语言函数大全-- t 开头的函数

    t 开头的函数 1 tan tanf tanl 1 1 函数说明 1 2 演示示例 1 3 运行结果 2 tanh tanhf tanhl 2 1 函数说明 2 2 演示示例 2 3 运行结果 3 tell 3 1 函数说明 3 2 演示示
  • MyBatis高级

    文章目录 一 动态sql 1 if 2 choose when otherwise 3 where 4 set 5 foreach 6 trim 7 Sql片段 二 分页 1 分页的使用步骤 1 1 导入maven依赖 2 mybatis配
  • sklearn GridSearchCV网格搜索和SVM的两个参数 C 和 gamma

    GridSearchCV 它存在的意义就是自动调参 只要把参数输进去 就能给出最优化的结果和参数 GridSearchCV用于系统地遍历多种参数组合 通过交叉验证确定最佳效果参数 引用自 公众号 写bug的程旭源 个人博客 写bug的程旭源
  • iOS tableView setAccessoryView 导致应用程序卡死问题

    iOS TableView TableViewCell setAccessoryView cell setAccessoryView cell accessoryView xxx iOS 14 使用 cell setAccessoryVie
  • iOS隐藏tableview多余的分割线以及解决cell左边短20px

    解决cell分割线左边短20px 解决cell分割线左边短20px的问题 void tableView UITableView tableView willDisplayCell UITableViewCell cell forRowAtI
  • 网络安全(黑客)自学建议&笔记

    前言 网络安全 顾名思义 无安全 不网络 现如今 安全行业飞速发展 我们呼吁专业化的 就职人员与大学生 而你 认为自己有资格当黑客吗 本文面向所有信息安全领域的初学者和从业人员 给你规划详细的网络安全学习路线 并附上优质的学习资料 让你在越
  • @SpringbootApplication详解和自定义类型过滤器

    spring boot 启动类使用 SpringBootApplication标记 该注解的定义如下 Target ElementType TYPE Retention RetentionPolicy RUNTIME Documented