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

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 的实现,里面排除的自动配置类的扫描 @SpringbootApplication详解和自定义类型过滤器_第1张图片

实现一个自定义类型过滤的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 的加载,现在启动服务,来请求一下接口。
@SpringbootApplication详解和自定义类型过滤器_第2张图片

你可能感兴趣的:(spring,JAVA,spring,boot)