SpringBoot自动配置的魔法是怎么实现的 ?

springboot自动配置

如果对SpringBoot不甚了解, 可能会对其just run的做事方式有所顾虑,
可能会对AutoConfiguration的魔法感到有一丝丝未知的恐惧?也许会认为这个框架太过于简单无法满足需要? 不过,
一旦你了解SpringBoot,这些疑问句都会变成感叹句…, SpringBoot对初学者展示了足够的友好,也同时有极大的灵活性,让我们自己通过自己的做事风格,打造属于自己springboot,追随风的脚步,一起去领略SpringBoot的风采…

本节讨论springboot自动配置原理 …


本博客基于springboot2.1.6:
在讨论之前 , 带着几个问题 .

  • 配置文件到底能写那些东西?

  • 怎么写?

    对于第一个问题… 硬核安利 . 配置文件能配置的属性参照

    自动配置原理:

    1),在springboot启动的时候,加载主配置类过程中,开启了自动配置的功能.@EnableAutoConfiguration

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

    2), 主要起作用的就是@EnableAutoConfiguration ,下面重点探讨@EnableAutoConfiguration的作用

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

    配置注解下, 利用AutoConfigurationImportSelector给容器中导入一些,自动配置的组件 . 易知,我们的答案就在此类中.

    • 直接查看selectImports()方法中的内容 . 发现getAutoConfigurationEntry()方法是这个方法的核心.

    • 跟随getAutoConfigurationEntry()方法 ,通过debug方法分析,getCandidateConfigurations()方法获取了所有的候选自动配置类 .

在这里插入图片描述

  • 点开方法 getCandidateConfigurations () . 其中核心方法是springFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class , ClassLoader) .
    在这里插入图片描述

  • 通过SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class , ClassLoader) . 扫描所有jar包类路径下的 WEB-INF/spring.factories,在把扫描到的这些文件内容一一的包装成properties对象,在从properties中获取到EnableAutoConfigurationes.class类对应的值 ,然后,在把他们添加到容器中,

    在这里插入图片描述

  • 一起来查看一下spring.factories里面配置的所有EnableAutoConfiguration的值. 以上的操作,将EnableAutoConfiguration的值加入到了容器中 .

在这里插入图片描述

上面的每一个以AutoConfiguration结尾的类,都是容器中的一个组件,都加入到容器中;用他们来做自动配置.

上面的类来做自动配置

3),每一个自动配置类在其中都具有相关的自动配置功能.

4), 以HttpEncodingAutoConfiguration (springmvc自动配置),来解释xxxAutoConfiguration类是如何进行自动配置, 和 boot中的默认值是以什么样的方式给予他默认值的?

@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpEncodingProperties.class)
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;并把HttpEncodingProperties加入到ioc容器中
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
//Spring底层@Conditional注解(如果不懂,请及时复习spring注解),根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效; 这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断应用中有没有这个类CharacterEncodingFilter,如果有 ,当前配置类生效
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) 
//判断在配置文件中年是否存在配置spring.http.encoding ,如果不存在,判断成立,默认的spring.http.encoding.enabled = true . 
public class HttpEncodingAutoConfiguration {

	private final HttpEncodingProperties properties; //注解已知,和配置文件已经映射了.

	public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
		this.properties = properties;//参数的值从容器中取出
	}

	@Bean //容器中添加组件,组件的某些值要从properties获取
	@ConditionalOnMissingBean//判断有误这个组件CharacterEncodingFilter
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

根据前面的条件进行判断,配置类是否生效 …
如果配置类生效;当前配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取
的,properties类又是和配置文件进行绑定的 …

@ConfigurationProperties(prefix = "spring.http.encoding")
//从配置文件中获取指定的值和bean的属性进行绑定
public class HttpEncodingProperties {
	public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; //默认值
	
	private Charset charset = DEFAULT_CHARSET;
	private Boolean force;

	private Boolean forceRequest;

	private Boolean forceResponse;

	private Map<Locale, Charset> mapping;

springboot中如何给予默认值 ?

以force为例 . 如果没有在配置文件中配置 ,取得的值 ,应该为:

public boolean isForce() {
   return Boolean.TRUE.equals(this.force);
}

所以,默认的force为false , 其他的可以自行查看源码 .
还有其他方式通过spring中的条件注解来判断是否注入到容器中.
@ConditionalOnMissingBean ,等等…

回到最初的问题:到底properties中能配置啥?

能配置的是xxxproperties类的属性值 .


如何精简的概括这一过程?

Spring Boot启动的时候会通过@EnableAutoConfiguration注解找到META-INF/spring.factories配置文件中的所有自动配置类,并对其进行加载,而这些自动配置类都是以AutoConfiguration结尾来命名的,它实际上就是一个JavaConfig形式的Spring容器配置类,它能通过以Properties结尾命名的类中取得在全局配置文件中配置的属性.

如:server.port,而XxxxProperties类是通过@ConfigurationProperties注解与全局配置文件中对应的属性进行绑定的。

总结 :

1)SpringBoot启动会加载大量的自动配置类
2)观察需要的功能是否有SpringBoot默认写好的自动配置类;
3)自动配置类中配置了哪些组件;(只要我们要用的组件有,我们就不需要再来配置了)
4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。可以从properties中配置相关属性值.
灵活变通:

	 xxxxAutoConfigurartion:自动配置类;给容器中添加组件
	xxxxProperties:      封装配置文件中相关属性;

整个过程

过程分析:

其实SpringApplication作为一个bootstrap类, 既可以加载JavaConfig形式的配置, 也可以加载XML形式的配置, 然后根据情况下创建相应类型的ApplicationContext实例, 为了简化理解难度,我们以JavaConfig为主线, 那么一般情况下, SpringBoot就会创建一个对应处理JavaConfig形式配置的AnnotationConfigApplicationContext实例(或者如果有Servlet等类,则创建ConfigurableWebApplicationContext)。

然后一个CommandLinePropertySource会被创建且其内容会加载到当前SpringBoot应用的Environment之中(这也就是为什么命令行上提供的参数可以优先覆盖其它配置的原因),当然, 其它的PropertySource这个时候也会随后一起加载然后并到Environment, 然后交给ApplicationContext实例后继使用(不要纠结与代码细节,虽然代码细节里是先做了包括设置env的一些事情然后再创建ApplicationContext实例)。

在ApplicationContext创建的之前和之后, SpringBoot会使用SpringFactoriesLoader这个特性,从当前classpath下所有的META-INF/spring.factories下加载如下类型的一些callback接口并在前中后等不同时机执行:

  1. org.springframework.boot.SpringApplicationRunListener
  2. org.springframework.context.ApplicationContextInitializer
  3. org.springframework.context.ApplicationListener

这些杂事我这里就不细说了, 总是上面提到的事儿做完后,ApplicationContext就正式加载SpringApplication.run方法传入进来的配置了(JavaConfig形式或者XML形式), 然后,因为我们标注了@SpringBootApplication, 容器会自动完成指定语意的一系列职能,包括@EnableAutoConfiguration要求的事情, 比如, 从SpringBoot提供的多个starter模块中加载JavaConfig配置, 然后将这些JavaConfig配置筛选上来的bena定义加入Spring容器中(即ApplicationContext中), refresh容器,然后就完事大吉了,一个SpringBoot应用启动完成。

不过,其实最后还有一个关键组件,一般用于扩展, 在容器准备好之后,SpringBoot其实还会根据类型去容器中挑选一批CommandLineRunner, 然后依次执行这些CommandLineRunner, 我们可以根据需求和场景,实现一些自己的CommandLineRunner并添加到容器来对SpringBoot应用进行某种扩展。

问题来了 ,我们怎么知道哪些自动配置类生效了?

通过启用 debug=true属性;让控制台打印自动配置报告,即可知道哪些自动配置生效
============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   CodecsAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)

   CodecsAutoConfiguration.JacksonCodecConfiguration matched:
      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
                   ............ 
Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)

                    ..........

2019/6/29

参考博客:https://blog.csdn.net/u014745069/article/details/83820511

​ https://afoo.me/posts/2015-07-09-how-spring-boot-works.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值