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 三个注解是理解启动类的关键。
该注解本身就是@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
从字面理解该注解是用来开启自动化配置,从注解的定义来看,主要是通过 @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
中,组件装配有三中方式。
@Component
@Configuration
+ @Bean
的方式@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());
}
}
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 的实现,里面排除的自动配置类的扫描 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则。如果Spring内置的过滤规则不能够满足我们的需求,那么我们便可以通过自定义TypeFilter来实现我们自己的过滤规则。
在使用@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;
}
}
}
创建resources/META-INF/spring.factories
文件,添加如下内容。
org.springframework.context.ApplicationContextInitializer=com.haoyang.initializer.ApplicationInit
在前面的 MyTypeExcludeFilter 中排除了 HelloController 的加载,现在启动服务,来请求一下接口。