你不可不知道的 SpringBoot 启动配置原理!

使用Spring Boot

首先打开IDEA 创建一个Spring Boot项目

选中 SpringInitializer 然后next 过程就不累赘了。

然后打开pom文件我们发现Srping Boot 有个配置依赖

xml复制代码启动依赖
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

测试依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        

点击启动依赖后我们发现这里面各种依赖
原来启动依赖里包含了spring-boot-starter 启动依赖,mvc依赖 还有tomcat 以及json 转换包 还有validator验证的包等等

xml复制代码<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
	</dependencies>

然后在根目录下还有一个主程序,启动类

java复制代码@SpringBootApplication
public class SpringDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringDemoApplication.class, args);
    }

}

如果主启动类没有被 @SpringBootApplication 标注,启动时会报一个错误:Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.

@SpringBootApplication 启动注解

点进去后发现这是个组合注解

java复制代码
@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 {
 ....
 }

发现有@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan这么三个注解

可以说明 @SpringBootApplication注解是个组合注解
我们也可以在启动类上使用@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan 来启动Spring Boot

先来看@ComponentScan 注解

java复制代码@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)

这个注解作用是扫描指定包下的路径,指定了两个过滤条件 TypeExcludeFilter 和 AutoConfigurationExcludeFilter

这两个应该在启动的时候是过滤一些包 或者在启动的时候给容器中加一些组件

TypeExcludeFilter 类源码中 有个 match 方法 作用过滤的判断逻辑

kotlin复制代码    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        if (this.beanFactory instanceof ListableBeanFactory && this.getClass().equals(TypeExcludeFilter.class)) {
            Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory)this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
            Iterator var4 = delegates.iterator();

            while(var4.hasNext()) {
                TypeExcludeFilter delegate = (TypeExcludeFilter)var4.next();
                if (delegate.match(metadataReader, metadataReaderFactory)) {
                    return true;
                }
            }
        }

        return false;
    }

if (this.beanFactory instanceof ListableBeanFactory 这句可以看到 BeanFactory 底层Ioc容器, 去执行自定义的过滤方法 TypeExcludeFilter 的作用是做扩展的组件过滤

同理 AutoConfigurationExcludeFilter 里面也有一个 match 方法 可以看到源码中this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader) 判断了是不是配置和自动配置 ,暂且先不用关注。

java复制代码

public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
    private ClassLoader beanClassLoader;
    private volatile List<String> autoConfigurations;

    public AutoConfigurationExcludeFilter() {
    }

    public void setBeanClassLoader(ClassLoader beanClassLoader) {
        this.beanClassLoader = beanClassLoader;
    }

    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        return this.isConfiguration(metadataReader) && this.isAutoConfiguration(metadataReader);
    }

    private boolean isConfiguration(MetadataReader metadataReader) {
        return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
    }

    private boolean isAutoConfiguration(MetadataReader metadataReader) {
        return this.getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
    }

    protected List<String> getAutoConfigurations() {
        if (this.autoConfigurations == null) {
            this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader);
        }

        return this.autoConfigurations;
    }
}

然后看 @EnableAutoConfiguration 注解

java复制代码@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({EnableAutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

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

    String[] excludeName() default {};
}

可以看到 有@AutoConfigurationPackage 和
@Import({EnableAutoConfigurationImportSelector.class})

这两个注解

首先先看 @AutoConfigurationPackage 注解从名字上来看是自动配置包 ,点进去后
@Import({Registrar.class}) public @interface AutoConfigurationPackage { } 发现 给@ 注解导入一个组件 @Import({Registrar.class})

Registrar 组件

java复制代码   @Order(-2147483648)
   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));
       }
   }

debug一下可以看到 这个注解作用是导入我们根目录经下的包名,通过包名导入组件,也就是扫描各种bean 然后注册到Ioc容器中。

然后来研究一下 @Import({EnableAutoConfigurationImportSelector.class})
貌似这个也是导入组件
打开 EnableAutoConfigurationImportSelector类

java复制代码public class EnableAutoConfigurationImportSelector extends AutoConfigurationImportSelector

再打开 AutoConfigurationImportSelector 会看到如下代码

java复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
 

此类实现了 DeferredImportSelector接口 你会发现这个类继承了ImportSelector 这个类

java复制代码public interface DeferredImportSelector extends ImportSelector 

作用是DeferredImportSelector 的执行时机比 ImportSelector 更晚,导入组件后会去查找导入组件结果。

然后我们还发现有下面这段代码 selectImports 这个方法按名字来看是查找导入的组件 ,这也证明了AutoConfigurationImportSelector继承DeferredImportSelector这个接口的意义。

java复制代码    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            try {
                AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
                AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
                List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
                configurations = this.removeDuplicates(configurations);
                configurations = this.sort(configurations, autoConfigurationMetadata);
                Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
                this.checkExcludedClasses(configurations, exclusions);
                configurations.removeAll(exclusions);
                configurations = this.filter(configurations, autoConfigurationMetadata);
                this.fireAutoConfigurationImportEvents(configurations, exclusions);
                return (String[])configurations.toArray(new String[configurations.size()]);
            } catch (IOException var6) {
                throw new IllegalStateException(var6);
            }
        }
    }

可以看到下面这段代码

java复制代码List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

好像是配置了一个配置集合

点进去后发现以下代码

java复制代码SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

this.getSpringFactoriesLoaderFactoryClass()

java复制代码 protected Class<?> getSpringFactoriesLoaderFactoryClass() {
        return EnableAutoConfiguration.class;
    }

传入的Class就是 @EnableAutoConfiguration ,而这个类中
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; 开启自动配置

SpringFactoriesLoader.loadFactoryNames 扫描了类路径下

使用 classLoader 去加载了指定常量路径META-INF/spring.factories下的资源

我们会发现 多个包下有 spring.factories文件被加载

然后debug继续往下走发现

AutoConfigurationImportSelector类中 selectImports方法里 configurations = this.filter(configurations, autoConfigurationMetadata);这段代码进行过滤条件

传入的 参数 autoConfigurationMetadata 是上面代码AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); 所有的自动配置类的自动配置条件查询结果

点进去发现加载了 所有的spring-autoconfigure-metadata.properties文件
然后发现这个文件里 基本都是以org.springframework.boot.autoconfigure开头的全路径包名 ,瞬间明白了

AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
这个类是找到所有Spring Boot支持的自动配置的类

然后过滤加载的自动配置包里是否含有在类路径下导入

AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var8.next(); 自动配置导入过滤器

排除类路径下不存在的自动配置条件 最后是20个默认的自动配置 加入到Ioc容器中

总结

基本上Spring Boot的自动配置原理大致上分为三步:

  • 1 过滤器首先在启动类上过滤或者给容器添加组件
  • 2 扫描根路径下所有的bean
  • 3 添加类路径META-INF/spring.factories和 META-INF/spring-autoconfigure-metadata.properties自动配置的包和过滤自动配置是否在类路径下有包最后注册到Ioc中

猜你喜欢

转载自blog.csdn.net/m0_71777195/article/details/130721670