SpringBoot的配置原理 java进阶之了解SpringBoot的配置原理
梦里一明月 人气:0一、Spring Boot的特点
首先我们要知道 Spring Boot 在底层已经为我们添加好了很多依赖。比如我们常用的Tomcat,Spring,SpringMVC这些,甚至连mysql数据库的依赖也为我们添加好了
不过 SpringBoot 2.5.0 使用的mysql依赖版本是8.0.25的,如果还在使用 mysql 5 版本的小伙伴们就需要在项目的 pom.xml 文件中再次指定自己所用的依赖版本号。(因为 maven 在引入依赖时采取就近原则,你如果指定了依赖版本号的话,它会加载离它近的,而不会去加载远的)
例如,我要修改 mysql 依赖的版本为5.1.43
//在当前项目里面重写配置 <properties> <mysql.version>5.1.43</mysql.version> </properties>
二、了解容器的功能
添加组件
@Configuration、@Bean注解
首先,这个注解是写在类上面的,告诉 spring boot 这是一个配置类,等同于 以前的配置文件
配置类里面使用@Bean
标注在方法上给容器注册组件,默认情况下是单例的。以方法名就是组件的 id 。返回类型就是组件类型。返回的值,就是组件在容器中的实例
为什么他会是单例的呢? 原因是在@Configuration
注解的源码中,还定义了一个属性 proxyBeanMethods ,默认值是 true。
当然我们也可以修改他的值为false,这样他就会创建多个对象了。
举个例子:
我现在在配置类里面定义了一个组件,他会返回一个 User 对象,当proxyBeanMethods = true
时,无论调用多少次 user01()
方法,在容器中都只会存在一个实例对象,但我现在把它改为 false
,来测试一下他到底是不是能创建多个实例了。
在主方法中进行测试:
User user=config.user01(); User user1=config.user01(); System.out.println(user==user1);
最后输出的结果是
false
这就说明现在创建了两个对象了,在容器中user
和user1
并不是指向同一块内存地址
那我们什么时候可以把它改成 false 来使用呢? 这就要设计到两种编写Spring Boot的方式了
- 一种是FULL模式 全模式(单例)
- 另一种是Lite模式 轻量级模式(非单例)
如果有组件依赖必须使用Full模式(默认)。其他默认是否Lite模式
Import注解
加入 IOC 容器的方式有很多种,上面的@Bean
是一种,这里提到的@Import
也是用来注册组件的,@Import
注解可以用于导入第三方包 (当然@Bean
也可以)
它是写在类上面的,
它所创建的组件 id 默认是类的全限定名称
具体用法参考:b站Spring注解驱动教程
Conditional注解
条件装配:满足Conditional指定的条件,则进行组件注入
@Conditional
注解下面还有许多的子注解
因为它的子注解实在太多了,下面我们具体实现一个例子来说明一下它的功能
先在 User 类中再加入一个 Pet 属性
现在我希望容器在没有 Pet 的情况下,我也不要 User 对象
要实现这个需求,可以这样做,在 User 组件前面添加@ConditionalOnBean
注解,并指定条件为 Pet 组件的 id 来进行限制
执行测试方法
运行结果:
容器中是否有tomcat:false
容器中是否有user:false
这样就对组件 User 的注册加以限制了
也可以把@ConditionalOnBean(name="tom")
注解加在配置类上面,当容器中有 tom 组件时,这个类中的其他组件才会生效,否则不生效
三、原配置文件的引入
如果你原有的项目还是使用 beans.xml 等配置文件的方式来注册组件的话,SpringBoot 是肯定无法自动配置的,那怎么样才能让SpringBoot用我这个配置文件去注册组件呢?
@ImportResource注解
使用@ImportResource
注解可以引入以前那种 xml 配置文件的方式写的组件
使用方法:直接加在现在的配置类上面,例如:
@ImportResource("classpath:beans.xml")
配置绑定
配置绑定是什么意思呢?其实就是使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用
具体做法:使用@ConfigurationProperties
注解
这个注解是加在你需要从 properties 属性配置文件中要导入的属性的类上面。
比如:我现在写了一个 Car 类,然后在 properties 文件中写好了它的属性
( 注意:properties 文件中的所有属性均要小写,驼峰命名法也不行,可以用 - 或 _ 来代替 )
我们想要将配置文件中定义好的属性绑定到实例对象上的话,就可以在 Car 这个类上面加上@ConfigurationProperties(prefix = "mycar")
,prefix
代表前缀的意思。
单加上这一个注解还不行,因为此时容器中还没有这个对象,可以采用两种方式来把 Car 这个对象加入容器中:
1.使用@Component注解
2.使用@EnableConfigurationProperties注解
@Component+@ConfigurationProperties
在@ConfigurationProperties
注解上方加上@Component
注解,即可将 Car 加入到容器中
测试方法:
运行,访问 “/car” 查看结果
@EnableConfigurationProperties+@ConfigurationProperties
使用@EnableConfigurationProperties
注解则需要在配置类上面添加,而不是 Car 上面
这个注解的作用就是
1.开启Car的属性配置功能
2.把这个Car这个组件自动注册到容器中
再次运行程序,可以得到相同的结果
四、自动配置原理
说完上面这些,我们来看一下,Spring Boot是如何实现自动装配的
引导加载自动配置类
我们先点进@SpringBootApplication
的源码中去,发现它其实是3个注解的合成注解:
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
@SpringBootConfiguration
其中,@SpringBootConfiguration
的源码底层使用了@Configuration
注解,说明它其实也是一个配置类,只不过它相当于是一个主配置。
@ComponentScan
根据我们之前的学习,这个注解其实就是一个组件扫描器的作用,是Spring的注解
@EnableAutoConfiguration(核心)
最关键最核心的注解就是这个,@EnableAutoConfiguration
@AutoConfigurationPackage
我们点进它的源码里面去,发现它底层调用了一个叫@AutoConfigurationPackage
的注解,翻译过来就是自动配置包,它指定了默认的包规则
再继续查看它的底层源码,发现它导入了一个叫Register
的组件
点进去,给它打上一个断点,来看一下到底是如何运作的
代码现在在这里停住了,让我们来看一下这一行代码里面,发现它能够找到我们所在的包名,由此我们也就能知道为什么 Spring Boot 能够将指定的一个包下的所有组件导入进来了,
@Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration
注解下还有一个注解,它是干什么的呢?让我们看一下源码就知道了
在AutoConfigurationImportSelector
类下面我们找到了一个方法getAutoConfigurationEntry(annotationMetadata)
,它是用来给容器中批量导入一些组件的
给他打一个断点,看看到底加入了些什么组件进容器里
说明这 131 个对象都是要加入到容器中的,并且都存储在了一个List集合当中 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes)
为什么会是131个呢? 其实是Spring Boot 里面写死了,一启动就要给容器中加载的所有配置类
打开spring-boot-autoconfigure-2.5.0.RELEASE.jar下META-INF/spring.factories
位置的文件,让我们来看一下源码是怎么写的
这里写了一大堆 xxxxAutoConfiguration 的配置类,从26行开始,到156行结束,刚好是 131 个。
虽然我们 131 个场景的所有自动配置启动的时候默认全部加载,但并不是都会生效的,比如 AOP 的部分功能就需要你导入 aspectj 相关的包才能生效。它是按照条件装配规则(@Conditional
),最终会按需配置。
如图, aspectj 是爆红的
除了AOP之外,还有一些类也是没有生效的(比如CacheAutoConfiguration
),这里就不再赘述了,感兴趣的同学可以去看源码了解一下。
五、修改默认配置(约定大于配置)
SpringBoot 默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
以 SpringMVC 中的文件上传解析器为例,他在容器中的默认名字是multipartResolver
,但是我们写代码的时候可能不知道底层源码里面给他的默认名字是这个,我们给他起了另外一个名字,这个时候 SpringBoot 就会去容器中找到你所配置的那个组件,并且返回那个组件,也就是下面这段代码:
这是为了防止有些用户配置的文件上传解析器不符合规范。
六、总结
- SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
- 每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
- 生效的配置类就会给容器中装配很多组件
- 只要容器中有这些组件,相当于这些功能就有了
- 定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
整个的流程:xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> application.properties
所以,当我们需要修改组件的配置的时候,只需要在application.properties里面进行配置即可
加载全部内容