@SpringBootApplication 注解到底做了什么?你真的知道吗?

本文详细解析了SpringBoot中的@SpringApplicationConfiguration注解,包括其内部注解如@EnableAutoConfiguration和ComponentScan的工作原理,以及SpringFactoriesLoader和JavaSPI在自动装配中的角色,帮助开发者理解SpringBoot的自动装配机制和约定配置思想。
摘要由CSDN通过智能技术生成

前言:

使用 Spring Boot 的都知道,我们只需要在启动类上加上一个 @SpringBootApplication 注解,就可以轻松的完成项目启动及各种 bean 的加载,也就是我们常说的自动装配,那你知道自动装配是怎么完成的吗? @SpringBootApplication 注解是自动装配不可或缺的一环,本文我们就分析一下 @SpringBootApplication 注解。

@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;
}

我们看到 @SpringBootApplication 注解上有又有7个注解,因此可以知道的是 @SpringBootApplication 注解是一个组合注解,我们分析一下这7个注解,如下:

  • @Target({ElementType.TYPE}):限制注解使用的范围,比如使用在类、接口、枚举、方法、成员变量、局部变量、方法参数、注解、包等,ElementType.TYPE 表示可以修饰类、接口、注解或枚举类型。
  • @Retention(RetentionPolicy.RUNTIME):用于指定被修饰的注解可以保留多长时间,Retention.SOURCE 表示只在源码中保留,在编译期间删除,Retention.CLASS 表示在编译期存在,在运行时不可获取,是默认值,RetentionPolicy.RUNTIME 表示在运行时有效,可以使用反射获取注解信息。
  • @Documented:表示被修饰的注解可以被 javadoc 工具提取成文档,定义注解类时使用 @Document 进行修饰,则使用该注解修饰的程序的 API 文档中将会包含该注解说明。
  • @Inherited:表示注解具有继承性,如果某个注解使用 @Inherited 进行修饰,则该类使用该注解时,其子类将自动被修饰。
  • @SpringBootConfiguration:内部结构是 @Configuration 注解,@Configuration我们就非常熟悉了,标明当前类是一个配置类,项目启动时该类会被加载到 spring 容器中。
  • @EnableAutoConfiguration:开启自动配置功能,实现自动装配的核心注解,内部包含两个注解,@AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)。
  • @ComponentScan:默认扫描 @ComponentScan 注解所在包及子包中标注了 @Component 注解的类以及衍生注解标注的类(@Repository、@Service、@Controller)。

这7个注解中,其中前面4个是 jdk 的元注解,后面3个注解是需要重点关注的:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

@SpringBootConfiguration 注解源码分析:

从源码中可以看到 @SpringBootConfiguration 就是一个特殊的 @Configuration 注解,在 Spring Boot 应用程序中,它表示该类是一个配置类,并且可以被自动加载到 IOC 容器中。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

属性分析:

  • proxyBeanMethods:表示被 @Configuration 注解修饰的类是否会被代理,true 表示会被代理且是单例模式,false 表示不会被代理且不是单例模式,默认值是 true。

@Configuration 注解源码分析:

从源码中可以看到 @Configuration 又是一个特殊的 @Component 注解,被 @Configuration 注解标注的类,能够自动注册到IOC容器并进行实例化。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

属性分析:

  • value:Bean 在 Spring IOC 容器中的 id。
  • proxyBeanMethods:表示被 @Configuration 注解修饰的类是否会被代理,true 表示会被代理且是单例模式,false 表示不会被代理且不是单例模式,默认值是 true。

@EnableAutoConfiguration 注解源码分析:

@EnableAutoConfiguration 注解开启自动配置功能,是 Spring Boot 最核心的一个特性,也是约定大于配置设计思想的主要体现,从源码中可以看到 @EnableAutoConfiguration 内部包含两个注解 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class)组成。

@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 {};
}

属性分析:

  • exclude:排除指定的自动配置类,让它们不会被自动装配到 Spring IOC 容器中。
  • proxyBeanMethods:excludeName:排除指定自动配置类名,让它们不会被自动装配到 Spring IOC 容器中。

@AutoConfigurationPackage 注解源码分析:

通过源码可以看到 @AutoConfigurationPackage 注解又使用了 @Import({Registrar.class}) 注解,其他没有任何东西,也就是发挥左右的主要是 Registrar 类。

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

@Registrar 注解源码分析:

从源码看出 Registrar 是 AutoConfigurationPackages 的内部类,Registrar 实现了 ImportBeanDefinitionRegistrar 接口 的 register 方法,register 方法有一个参数是 BeanDefinitionRegistry,BeanDefinitionRegistry 是一个接口,实际上用的是 DefaultListableBeanFactory 类,DefaultListableBeanFactory 类有一个 beanDefinitionMap 属性,beanDefinitionMap key 为 bean 名称,value 为 bean 对象本身,这里其实就是走到类 Spring IOC 容器的核心部分,也就是为什么 Spring Boot 可以自动装配的原因,其实就是利用注解然后调用到 Spring 的 IOC 相关的接口实现自动装配。

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
        Registrar() {
        }

        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
            AutoConfigurationPackages.register(registry, (new AutoConfigurationPackages.PackageImport(metadata)).getPackageName());
        }

        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new AutoConfigurationPackages.PackageImport(metadata));
        }
    }

@Import(AutoConfigurationImportSelector.class) 注解源码分析:

AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口,DeferredImportSelector 接口又实现了 ImportSelector 类selectImports 是 AutoConfigurationImportSelector 类的核心方法,selectImports 方法返回的是需要自动装配类的全限类名,也就是获取 META-INF/spring.factories 文件的全限定名,完成这些类自动装配,这里其实还用到了 Spring 的 SPI 机制,AutoConfigurationImportSelector 配合SpringFactoriesLoader 完成 META-INF/spring.factories 文件中全限定名对应的类的自动装配。

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
	....................
}

public interface DeferredImportSelector extends ImportSelector {
	....................
}

//selectImports 方法是 AutoConfigurationImportSelector 类的核心方法,
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!this.isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	} else {
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
		//获取自动装配的类
		AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}
}


protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
	if (!this.isEnabled(annotationMetadata)) {
		return EMPTY_ENTRY;
	} else {
		AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
		//获取需要自动专装配的类
		List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
		configurations = this.removeDuplicates(configurations);
		Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
		this.checkExcludedClasses(configurations, exclusions);
		configurations.removeAll(exclusions);
		configurations = this.filter(configurations, autoConfigurationMetadata);
		this.fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
	}
}


protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	//这里调用了 SpringFactoriesLoader 类的 loadFactoryNames 方法  这里起始设计到了SPI
	List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
	//这里的提示已经说明了获取的是 META-INF/spring.factories 下的类
	Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
	return configurations;
}

什么是 SPI ?

Java SPI 全称 Service Provider Interface,是一种服务发现机制,它允许在运行时动态地加载实现特定接口的类,而不需要在代码中显式地指定该类,从而实现解耦和灵活性,ava SPI 是基于 Java 类加载机制和反射机制实现的,当使用 ServiceLoader.load() 方法时会检查 META-INF/services 目录下是否有类的全限定名命名的文件,如果有则读取文件中的接口实现类全限定名,并通过 Class.forName() 方法加载对应的类,Spring 对 Java SPI 技术做了扩展。

@ComponentScan 注解源码分析:

@ComponentScan 注解主要作用就是指定扫描路径,Spring 会把 @ComponentScan 注解扫描到的类且带有指定注解(比如:@Controller、@Service、@Component、@Repository)的类注册到 Spring IOC 容器中。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
    @AliasFor("basePackages")
    String[] value() default {};

    @AliasFor("value")
    String[] basePackages() default {};

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

    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

    String resourcePattern() default "**/*.class";

    boolean useDefaultFilters() default true;

    ComponentScan.Filter[] includeFilters() default {};

    ComponentScan.Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;

        @AliasFor("classes")
        Class<?>[] value() default {};

        @AliasFor("value")
        Class<?>[] classes() default {};

        String[] pattern() default {};
    }
}

属性分析:

  • basePackages:扫描哪些包下的类。
  • value:和 basePackages 一样的作用,扫描哪些包下的类。
  • useDefaultFilters:默认使用过滤策略,一般和 includeFilters、excludeFilters 一起使用。
  • includeFilters:指定扫描哪些包下的类,和 useDefaultFilters 一起使用。
  • excludeFilters:指定不扫描哪些包下的类,和 useDefaultFilters 一起使用。
  • lazyInit:是否开启懒加载,由于是包扫描一次性的,不能单独配置哪个组件是否懒加载,因此提供该属性用于声明是否开启懒加载。

Filter 自定义过滤规则

Filter 自定义过滤规则,通过 FilterType 来设置自定义过滤规则的类型,自定义过滤规则枚举值如下:

  • FilterType.ANNOTATION:按照注解过滤。
  • FilterType.ASSIGNABLE_TYPE:按照给定的类型过滤。
  • FilterType.ASPECTJ:按照 ASPECTJ 表达式过滤。
  • FilterType.REGEX:按照正则表达式过滤。

总结:本文简单分析了 @SpringBootApplication 注解的源码,有助于我们更深入理解 Spring Boot 的自动装配及约定大于配置的理念,同时对我们开发中合理使用注解也有帮助,希望可以帮助到有需要的小伙伴。

欢迎提出建议及对错误的地方指出纠正。

  • 35
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值