【SpringBoot】自动配置原理学习总结

1. SpringBoot主要自动配置项

以一个SpringBoot的HelloWorld Web项目为例它主要为我们配好了以下这些东西:

1.1 自动配好Tomcat

配置tomecat分为了两步:

  1. 引入Tomcat依赖,这一步SpringBoot的依赖管理实现的,引入了spring-boot-starter-web依赖,就自动引入了spring-boot-starter-tomcat依赖。
    在这里插入图片描述
    在这里插入图片描述

  2. 配置Tomcat

1.2 自动配好SpringMVC
  1. 引入SpringMVC全套组件
  2. 自动配好SpringMVC常用组件(功能)
1.3 自动配好Web常见功能,如:字符编码问题SpringBoot

帮我们配置好了所有web开发的常见场景, 通过以下程序查看容器里面自动配置的组件。
在这里插入图片描述

1.4 配置好了默认的包结构:

以前整合Spring、SpringMVC,需要指定Controller都在哪个包下,包扫描等等各种规则:
在这里插入图片描述
但是现在使用SpringBoot我们不需要配置任何的包扫描。
在官方文档中对默认的包扫描规则是做了一个解释:

在这里插入图片描述
主程序所在包(myapplication)及其下面的所有子包里面的组件都会被默认扫描进来,无需以前的包扫描配置。
注:如果想将除主程序所在包及其下面的所有子包里面的组件扫描出来就需要自己配置一下,在SpringBootApplication中有一个属性叫做scanBasePackages。指定一下需要扫描的包(com)即可。
在这里插入图片描述
或者直接使用使用@ComponentScan 指定扫描路径,ComponentScan 其实就是
SpringBootApplication的一个子注解。
在这里插入图片描述
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(“com”)
这三个注解共同实现了SpringBootApplication的功能=

1.5各种配置基本都配置好了默认值,无需再次配置,也可以自己修改

以文件上传为例:
在这里插入图片描述
在这里插入图片描述
默认配置最终都是映射到某个类上,如:MultipartProperties
在这里插入图片描述配置文件的值最终会绑定每个类上,这个类会在容器中创建对象

1.6按需加载所有自动配置项:

有非常多的starter,引入了哪些场景这个场景的自动配置才会开启。
SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面。

2. 关于自动配置的一些注解

2.1 组件添加:

代码中预先准备了两个准备添加的组件:
分别是User与Pet:
在这里插入图片描述

在这里插入图片描述
在SpringBoot之前使用原生的Spring与SpringBoot做一个对比,首先创建一个Spring的配置文件:beans.xml
在这里插入图片描述
在这里插入图片描述

之后使用bean标签进行创建对应的属性
在这里插入图片描述

SpringBoot中新的创建方式:
首先创建一个类,之后加注@Configuration注释,告诉SpringBoot这是一个配置类,等同于Spring中的配置文件。
在配置文件中使用bean标签给容器中添加组件,在配置类中不能写标签了,就写一个方法构造出来,之后加上@Bean注解。 如果不想将方法名作为组件名称,可以在Bean中标注上。
在这里插入图片描述

通过这种方法将容器中所有的组件打印出来:
在这里插入图片描述
可以看到组件已经注册成功 。
在这里插入图片描述

注: 容器中注册的组件默认是单实例的,此处可以验证一下:
在这里插入图片描述
在这里插入图片描述

注 :@configuration标注的这个类本身也是一个组件

在这里插入图片描述

在这里插入图片描述
在SpringBoot2.0以后Configuration注解中多了一个属性,且默认为true
在这里插入图片描述
意思为是不是代理Bean的方法。
在这里插入图片描述
在这里插入图片描述

此处验证了外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象。因为被@configuration注释的这个对象它不是一个普通的对象,而是被SpringCGLIB增强了的代理对象。
在这里插入图片描述
如果@Configuration(proxyBeanMethods = true),即Full模式,则代理对象调用方法。SpringBoot总会检查这个组件是否在容器中有。如果有就直接调用,没有才会新创,也就是说他会保持组件单实例。

如果调为false,即Lite:
在这里插入图片描述
可以看到此时得到的对象就不是代理对象,多次调用个方法得到的也不再相等。
这种设计主要是为了组件依赖场景:
如果现在给User类中加一个属性,Pet
在这里插入图片描述
在这里插入图片描述
判断用户调用的宠物是否就是容器中的宠物
在这里插入图片描述
Full模式下:用户调用的宠物就是容器中的宠物。
在这里插入图片描述
Lite模式下,用户调用的宠物不是容器中的宠物。
在这里插入图片描述

Lite模式下SpringBoot不会来查检查方法返回的东西在容器中有没有,这样SpringBoot启动速度就快一点 。
总结:
配置类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式

2.2 通过Import组件创建组件

@Import({User.class, DBHelper.class})
给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名。

下面的类中我们加上了@Import注释,同时又自己创建了一个user01这个组件。
在这里插入图片描述
以及使用.xml文件方式注册的组件,共有3个User类型的组件
在这里插入图片描述

主程序中运行的测试代码:
在这里插入图片描述
结果:
在这里插入图片描述
共有3个User类型的组件,1个DBHelper类型的组件,其中名称为全类名的组件是Import注解创建的。

2.3 按照条件装配诸如组件

Conditional指定的条件,则进行组件注入
条件装配:当满足Conditional指定的条件,则进行组件注入:
在这里插入图片描述
例如:
ConditionalOnBean,当容器中有某个组件的时候,才会做某些行为。
例如:
当容器中有Tom这个组件的时候才会给容器中注册user01这个组件
在这里插入图片描述
当写在整个类的上面时:只有类中有一个组件名叫tom,类中的行为才会被实现(注册user01、tom组件),下图这种情况中tom、user01组件都不会被注册。
在这里插入图片描述
ConditionalOnMissBean,当容器中没有有某个组件的时候
ConditionalOnClass,当容器中有某个类的时候
ConditionalOnClass,当容器中没有某个类的时候
ConditionalOnResources,当容器中有某个资源的时候

2.4 原生配置文件引入

@ImportResource
很多组件还是以spring的配置文件的方式导入的导入,如果想要挨个迁移成注解的方式会非常麻烦。
以下是一个例子,在beans.xml中注册了两个组件,hehe和haha
在这里插入图片描述
输出容器中是否有hehe和haha
在这里插入图片描述
在这里插入图片描述

可以看到是没有注册成功的。
但是如果此时也不想一点一点钱一成@bean注解的这种写法,就需要给一个配置类上协商@ ImportResource,导入资源:类路径下的beans.xml
在这里插入图片描述
此时,再次测试可以看到邮件注册成功:
在这里插入图片描述

2.5 配置绑定
2.5.1 背景

开发过程中我们习惯于将一些容易变化的东西(数据库的账号,密码等)写到配置文件中,为了方便之后又将配制文件中的内容一一解析到JavaBean中(数据库连接池)
也就是说需要将配置文件中的数据解析到JavaBean中。
如果使用Java原生代码还是比较麻烦的:

public class getProperties {
     public static void main(String[] args) throws FileNotFoundException, IOException {
         Properties pps = new Properties();
         pps.load(new FileInputStream("a.properties"));
         Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
         while(enum1.hasMoreElements()) {
             String strKey = (String) enum1.nextElement();
             String strValue = pps.getProperty(strKey);
             System.out.println(strKey + "=" + strValue);
             //封装到JavaBean.......
         }
     }
 }
2.5.2 SpringBoot对配置的优化@Component与@ConfigurationProperties

举例:创建一个Car类
在这里插入图片描述
将和汽车有关的属性写入到配置文件中,
在这里插入图片描述
之后给Car上面写上注解@ConfigurationProperties(配之文件中的前缀)
在这里插入图片描述
在这里插入图片描述
此时还未生效,需要加上@Component将组件加入容器中,使得对应的功能生效。
在这里插入图片描述
测试:
利用Spring的自动注入,将容器中的Car拿过来,返回这个Car。
在这里插入图片描述

在这里插入图片描述

2.5.3 SpringBoot对配置的优化@EnableConfigurationProperties @ConfigurationProperties(主要使用这种方式)

@EnableConfigurationProperties (需要开启属性绑定功能的类)有两个功能:
1.开启这个类的属性绑定功能
2.将这个类作为组件自动注册到容器中
使用场景:有可能是引用的第三方包中的类,没法给类中加上@Component注解。

3. SpringBoot自动配置原理入门

@SpringBootApplication相当于下面的3个注解

3.1@SpringBootConfiguration:

进入SpringBootConfiguration源码可以看到:
在这里插入图片描述

@SpringBootConfiguration就相当于是@Configuration。代表当前是一个配置类

3.2 @ComponentScan(“com.atguigu.boot”):

主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来, 想要改变扫描路径:@SpringBootApplication(scanBasePackages=“com.atguigu”)或者ComponentScan(“com.atguigu.boot”)

3.3 @EnableAutoConfiguration:

主要由以下两个注解实现功能
在这里插入图片描述

3.3.1 @AutoConfigurationPackage:

在这里插入图片描述
在这里插入图片描述

自动配置包,指定了默认的包规则

@Import(AutoConfigurationPackages.Registrar.class)  //给容器中导入Registrar
public @interface AutoConfigurationPackage {}	

//利用Registrar给容器中导入一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。

//这是Registrar的实现:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImports(metadata));
		}

	}

//其中new PackageImports(metadata).getPackageNames()就是得到MainApplication所在的包名,将包名封装到数组中,之后注册进去(也就是将主类所在的包中的组件批量注册),这就解释了为什么默认的包路径是MainApplication所在的包
3.3.2 @Import(AutoConfigurationImportSelector.class)
  1. @Import注解的源码中的selectImports,确定到底要给容器中导入哪些组件,其返回的String[]是由getAutoConfigurationEntry(annotationMetadata)决定的。
    在这里插入图片描述

  2. 调用List configurations = getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要导入到容器中的配置类
    在这里插入图片描述
    List configurations:
    在这里插入图片描述
    利用Spring的工厂加载器 Map<String, List> loadSpringFactories(@Nullable ClassLoader classLoader);得到所有的组件
    在这里插入图片描述在这里插入图片描述

  3. 从META-INF/spring.factories位置来加载一个资源文件。
    默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
    在这里插入图片描述
    有些位置的META-INF中是没有spring.factories的
    在这里插入图片描述
    主要是扫描spring-boot-autoconfigure-2.3.4.RELEASE.jar中的META-INF/spring.factories,SpringBoot兼容的全场景的自动配置都在此处列举,共127个。
    在这里插入图片描述
    虽然我们127个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
    符合条件装配规则(@Conditional)才会导入。
    spring-boot-autoconfigure这个包中有我们需要的全场景配置:
    在这里插入图片描述

以AOP为例:
需要判断:文件中是否存在spring.aop这个配置,且spring.aop.auto的值是true,最后一个matchIFMissiing会认为就算没有将这个值配置为true,也会默认是true。
在这里插入图片描述
对于AspectJAutoProxyingConfiguration这个类,可以看到上面的 @ConditionalOnClass(Advice.class)注释 ,说明只有当Advice.class存在时,这个类中的一些配置才会生效。
在这里插入图片描述
对于ClassProxyingConfiguration这个类,可以看到
@ConditionalOnMissingClass(“org.aspectj.weaver.Advice”) 意思是如果没有org.aspectj.weaver.Advice这个类,下面的配置才会生效
@ConditionalOnProperty(prefix = “spring.aop”, name = “proxy-target-class”, havingValue = “true”,matchIfMissing = true)意思是必须有spring.aop这个配置项,且spring.aop.name属性必须是proxy-target-class,havingValue 这个配置项必须是true,如果不是true也会默认是true。
上面的条件这个类都是满足的,所以这个类中的配置是生效的。
但是这个类只是简单的AOP功能,也就是说这个AOP必须有接口有实现类才可以创建代理对象。
在这里插入图片描述

以cache为例:
在这里插入图片描述
@ConditionalOnClass(CacheManager.class)
首先确认容器中有没有CacheManager.class存在,CacheManager.class是在spring-context这个包中的类,此包是整个spring环境中的核心包,所以肯定是存在的
@ConditionalOnBean(CacheAspectSupport.class)
判断容器中有没有CacheAspectSupport.class这种类型的组件(上面的例子中都是以名称判断的,也可以通过其他关键点判断)
通过这行代码可以判断出容器中是没有CacheAspectSupport.class这种类型的组件的存在的
在这里插入图片描述
最终输出结果为0,所以不满足@ConditionalOnBean(CacheAspectSupport.class),所以这个类中的所有配置都没有生效,后面的条件已经不需要去看了。
@ConditionalOnMissingBean(value = CacheManager.class, name = “cacheResolver”)
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class,
HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })

以web中的servlet中DispatcherServletAutoConfiguration为例:
在这里插入图片描述
@AutoConfigureOrder设置配置生效顺序。介绍
@ConditionalOnWebApplication(type = Type.SERVLET):判断当前是否是一个原生的serrvlet应用,因为springboot2现在支持两种web模式开发,一种是响应式编程,一种是原生的servlet。
@ConditionalOnClass(DispatcherServlet.class):判断是否导入DispatcherServlet.class这个类
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)在ServletWebServerFactoryAutoConfiguration这个类配置完后再配置当前类。
在这里插入图片描述
DispatcherServletAutoConfiguration配置完后开始配置DispatcherServletConfiguration
在这里插入图片描述
第一个配置设置开启模式Lite,Lite模式下SpringBoot不会来查检查方法返回的东西在容器中有没有,这样SpringBoot启动速度就快一点 。
第二个在默认是生效的可以先不管
第三个确认是否有ServletRegistrartion这个类存在
第四个作用有两个,第一是开启括号中类与配置文件的配置绑定功能,会和配置文件中spring.mvc开始的所有属性一一对应进行绑定
在这里插入图片描述
第二个功能是将WebMvcProperties放入容器
在这里插入图片描述
可以看到是有一个的
在这里插入图片描述
接下来进入这个类:
配置好了DispatcherServlet
在这里插入图片描述
给容器中注册组件,在方法中自己new了一个DispatcherServlet,一步步设置好后return。
说明之前在Spring SpringMVC中需要做的那些配置,现在相当于底层已经new好了,帮用户配置好了。
这个类:配置好了文件上传组件
在这里插入图片描述
第一个注解为:当容器中有这个类型的组件
第二个为:当容器中没有这个名字的组件。
当用户自己在文件中配置了文件上传解析器,但是没有按照规定起名,此时就可以在文件中找到用户自己配置的解析器,再将解析器返回出去,完成了规范化操作。(因为这个方法的名字叫做multipartResolver,注册的组件会以方法名称作为组件名称)

以HttpEncodingAutoConfiguration(用于防止网页乱码)为例
在这里插入图片描述1.@ConditionalOnWebApplication判断是不是一个原生的Servlet应用
2.@ConditionalOnClass判断是否有某个类
3.判断是否有某个属性
进入类中注册组件@ConditionalOnMissingBean此注解判断用户是否已经配置过了,如果用户配置过则以用户的优先。
在这里插入图片描述
也就是说:SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先

4. 总结

  1. SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
  2. 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。从xxxxProperties里面拿,xxxProperties和配置文件进行了绑定
  3. 生效的配置类就会给容器中装配很多组件
  4. 只要容器中有这些组件,相当于这些功能就有了
  5. 定制化配置
    • 用户直接自己@Bean替换底层的组件
    • 用户去看这个组件是获取的配置文件什么值就去修改。

例如:
HttpEncodingAutoConfiguration这个例子中的属性就可以直接配置
在这里插入图片描述

查询配置项可以通过官方文档
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html#common-application-properties
也可以通过查询底层代码,例如查找关于cache的配置:
进入cache的自动配置代码。
在这里插入图片描述

找到EnableConfigurationProperties中写的类,进入,可以看到他的配置值是以spring.cache开头的各种属性值
在这里插入图片描述
在这里插入图片描述

导入所有的自动配置类xxxxxAutoConfiguration ----> 按需加载组件 ----> 组件xxxxProperties里面拿值 ----> xxxxProperties绑定的就是application.properties

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我顶得了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值