SpringBoot - 自动配置原理

自动配置原理

我们用@SpringBootApplication来告诉系统这个是一个SpringBoot的应用,点进去会发现是有三个注解复合的

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@SpringBootConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
   
   

        点击去发现@SpringBootConfiguration上面是一个@Configuration,说明这个是一个配置文件,意思就是说我们的启动类是一个配置文件。可以参考:https://blog.csdn.net/qq_27062249/article/details/118058784

@EnableAutoConfiguration

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

包含了@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage中有一个@Import(AutoConfigurationPackages.Registrar.class)的注解

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

那么接下来我们就只要分析AutoConfigurationPackages.Registrar.class和AutoConfigurationImportSelector.class里面分别作了啥,我们就大概能请求SpringBoot的自动配置的原理了

  • AutoConfigurationPackages.Registrar.class

        AutoConfigurationPackages.Registrar实现了ImportBeanDefinitionRegistrar接口(可以参考:https://blog.csdn.net/qq_27062249/article/details/118067295),在Spring的IOC容器初始化的时候回调用ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,这中间就是把SpringBoot的启动类的包名获取到,让注册到Spring的IOC容器中,用于后面的组件扫描,所有我们只要在主启动类中用注解声明的组件都能被实例化到Spring的IOC容器中,而在主启动类所在包的其他包的组件就不能被扫描到的原因

  • AutoConfigurationImportSelector.class

       AutoConfigurationImportSelector实现了DeferredImportSelector接口DeferredImportSelector又实现了ImportSelector接口(可以参考:https://blog.csdn.net/qq_27062249/article/details/118067295),ImportSelector在Spring初始化IOC容器的时候会调用ImportSelector的selectImports方法,该方法返回的String类型的数组,其中的值为类的类全名(包名+类名)。其实这里就是加载工程中的META-INF文件夹下的spring.factories文件中的类全名。下面是AutoConfigurationImportSelector类中的getCandidateConfigurations方法,中间就有提到META-INF/spring.factories文件

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
				getBeanClassLoader());
		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;
	}

我们在打开spring-boot-autoconfigure的jar包中的META-INF/spring.factories文件,我们就会发现这里有非常多的xxxAutoConfiguration。这些自动配置类就是通过ImportSelector的selectImports方法返回给Spring的IOC容器,然后将这些xxxAutoConfiguration实例化成对应的实例(这里实例化的的时候是条件选择性的实例化的,用到@Conditional的扩展注解实现的,可以参考:https://blog.csdn.net/qq_27062249/article/details/118225625),然后在添加到IOC容器中的。(如果自定义启动器的流程也是这样)

@ComponentScan

        包扫描器,排除了TypeExcludeFilter.class和AutoConfigurationExcludeFilter.class。可以参考:https://blog.csdn.net/qq_27062249/article/details/118059454

最佳实践步骤

  1. 引入场景依赖spring-boot-starter-xxx,具体有哪些可以参考:https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
  2. 查看哪些配置生效了,哪些配置没有生效以及原因(在配置文件:application.properties中设置debug=true,然后启动工程,看log输出【Positive matches、Negative matches、Unconditional classes】)
    Positive matches:  //生效的配置
    -----------------
    
       AopAutoConfiguration matched:
          - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)
    
       AopAutoConfiguration.ClassProxyingConfiguration matched:
          - @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)
          - @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)
    
       DispatcherServletAutoConfiguration matched:
          - @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition)
          - found 'session' scope (OnWebApplicationCondition)
    .....省略很多....
    
    
    Negative matches:  //没有启用的配置
    -----------------
    
       ActiveMQAutoConfiguration:
          Did not match:
             - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
    
       AopAutoConfiguration.AspectJAutoProxyingConfiguration:
          Did not match:
             - @ConditionalOnClass did not find required class 'org.aspectj.weaver.Advice' (OnClassCondition)
    
       ArtemisAutoConfiguration:
          Did not match:
             - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
    .....省略很多....
    
    Exclusions:
    -----------
    
        None
    
    Unconditional classes: //没有启用的配置
    ----------------------
    
        org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
    
        org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration
    
        org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
    
        org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration
    
        org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration
    
  3. 是否需要修改

    1. 参照文档配置,参考:https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties

    2. 自己分析。根据对应的xxxAutoConfiguration中的xxxProperties类中有哪些属性,然后修改对应的值

    3. 自定义组件

    4. 自定义器(xxxCustomizer)

总结

  1. SpringBoot会加载所有的自动配置类(xxxAutoConfiguration),配置类按照条件进行条件注入,同时为每个自动配置类都有一个对应的xxxProperties的类,用来做配置类
  2. 生效的配置类就会给容器中装配很多个组件
  3. 只要容器中有这些组件,就相当于有这些功能了
  4. SpringBoot优先使用用户自己配置的组件,如果用户没有配置就使用系统自己注入的
  5. 定制化配置方式
    1. 用户直接自己注入对应的组件来替换SpringBoot底层组件
    2. 用户去修改SpringBoot底层组件的属性

Guess you like

Origin blog.csdn.net/qq_27062249/article/details/118228663