自动配置原理
依赖管理
- 父项目做依赖管理
- 依赖管理
1 |
|
他的父项目几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
1 | <parent> |
- 开发导入starter场景启动器
1、见到很多 spring-boot-starter-* : *就某种场景
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters
4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
5、所有场景启动器最底层的依赖
1 | <dependency> |
无需关注版本号,自动版本仲裁
引入依赖默认都可以不写版本
引入非版本仲裁的jar,要写版本号。
可以修改默认版本号
1、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置
1 | <properties> |
自动配置
自动配好Tomcat
- 引入Tomcat依赖。
- 配置Tomcat
1 | <dependency> |
自动配好SpringMVC
- 引入SpringMVC全套组件
- 自动配好SpringMVC常用组件(功能)
自动配好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.19</version> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.19</version> <scope>compile</scope> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-
- 默认的包结构
- - 主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来
- ```
com
+- example
+- myapplication
+- MyApplication.java
|
+- customer
| +- Customer.java
| +- CustomerController.java
| +- CustomerService.java
| +- CustomerRepository.java
|
+- order
+- Order.java
+- OrderController.java
+- OrderService.java
+- OrderRepository.java- 无需以前的包扫描配置
- 想要改变扫描路径,@SpringBootApplication(scanBasePackages=“com.yumo”)
- 无需以前的包扫描配置
- 或者@ComponentScan 指定扫描路径
1 |
|
各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
按需加载所有自动配置项
- 非常多的starter
- 引入了哪些场景这个场景的自动配置才会开启
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
……
容器功能
组件添加
@Configuration
基本使用
Full模式与Lite模式
- 示例
- 最佳实战
- 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
- 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
1 | #############################Configuration使用示例###################################################### |
@Bean、@Component、@Controller、@Service、@Repository
@ComponentScan、@Import
1 | * 4、 |
@Import 高级用法: https://www.bilibili.com/video/BV1gW411W7wy?p=8
@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
1 | =====================测试条件装配========================== |
原生配置文件引入
@ImportResource
1 | ======================beans.xml========================= |
1 |
|
配置绑定
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
1 | public class getProperties { |
@EnableConfigurationProperties + @ConfigurationProperties
1 | /** |
1 | /** |
@Component + @ConfigurationProperties
1 | /** |
自动配置原理
引导加载自动配置类
1 |
|
1、@SpringBootConfiguration
**@Configuration
**。代表当前是一个配置类
1 |
|
2、@ComponentScan
指定扫描哪些,Spring注解;
1 |
|
3、@EnableAutoConfiguration
1 |
|
1、@AutoConfigurationPackage
自动配置包,指定了默认的包规则
1 |
|
Registrar.registerBeanDefinitions()
AutoConfigurationPackages类的Registrar静态内部类
利用Registrar给容器中导入一系列组件
1 | /** |
该方法调用的**register
**方法中传递了参数 new PackageImports(metadata).getPackageNames().toArray(new String[0])
其中new PackageImports(metadata).getPackageNames()
得到该注解标识的类所在的包。
然后将其转换为数组作为形参。
将指定的一个包(MainApplication 所在包)下的所有组件导入进来。
register()
1 | private static final String BEAN = AutoConfigurationPackages.class.getName(); |
该方法以编程方式注册自动配置包名称。后来的调用会将给定的包名(packageNames)添加到已经被注册的包名(AutoConfigurationPackages类在IOC容器registry中注册的组件beanDefinition)。
首先通过if语句判断当前IOC容器registry中是否包含有已被注册的包名所对应的组件:
- 若为ture,则说明当前的注册方法是将参数中给定的包名packageNames添加到已被注册的包名中
- 否则,调用registerBeanDefinition方法,将给定的包名packageNames创建为组件,并作为参数传入
registerBeanDefinition()
1 | /** Map of bean definition objects, keyed by bean name. */ |
验证
调用validate方法验证参数beanName和beanDeficition是否为空
更新
从当前DefaultListableBeanFactory类的IOC容器beanDefinitionMap中获取与参数beanName对应的组件
若该组件存在,则判断是否允许组件重写
- 若不允许,则抛出BeanDefinitionOverrideException异常
- 若运行,则进行日志的记录
最后将beanName所对应的新组件beanDefinition放入IOC容器中。至此完成bean重写
若不存在,则调用hasBeanCreationStarted方法判断IOC容器的组件创建解析进程是否已经开始
- 若已开始,则同步进行IOC容器各项属性的更新
- 否则,仍然强制进行更新
至此,完成向容器中组件的注册。
2、@Import(AutoConfigurationImportSelector.class)
AutoConfigurationImportSelector类
selectImports()
利用getAutoConfigurationEntry(annotationMetadata);
给容器中批量导入一些组件
getAutoConfigurationEntry
获取注解属性
调用getArrtibute方法获取参数annotationMetadata属性
可以看到参数annotationMetadata中包含了所有注解的类型及其所标识的类,
由arrtibutes可知该注解需要排除的组件类型(exclude)及名称(excludeName)
获取配置类组件
调用List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes)
获取到所有需要导入到容器中的配置类
利用工厂加载 Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader);
得到所有的组件
获取资源
从META-INF/spring.factories位置来加载一个文件
默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件
spring-boot-autoconfigure-2.6.7.jar包里面也有META-INF/spring.factories
一共**133
**行
1 | # Auto Configure |
该文件里面写死了spring-boot一启动就要给容器中加载的所有配置类
从而得到所有的Configuration自动配置类组件
按需开启自动配置项
虽然133个场景的所有自动配置启动的时候默认全部加载。xxxxAutoConfiguration
按照条件装配规则(**@Conditional**),最终会按需配置。
DispatcherServletAutoConfiguration
在此以DispatcherServletAutoConfiguration为例:
1 |
|
- `@ConditionalOnWebApplication(type = Type.SERVLET) 当该应用是一个web应用时
- `@ConditionalOnClass(DispatcherServlet.class) 当DispatcherSevlet类存在时
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
要在ServletWebServerFactoryAutoConfiguration
类配置之后
如果不满足其中任何一个条件,则该配置类不会进行自动配置。
DispatcherServletConfiguration
当全部满足后,开始进行自动配置:
静态内部类**DispatcherServletConfiguration
**:
@Configuration(proxyBeanMethods = false)
表示该类是配置类,且不使用代理模式@Conditional(DefaultDispatcherServletCondition.class)
表示条件是DefaultDispatcherServletCondition类存在@ConditionalOnClass(ServletRegistration.class)
表示条件是ServletRegistration类存在@EnableConfigurationProperties(WebMvcProperties.class)
表示WebMvcProperties类开启配置绑定
dispatcherServlet
1 | public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; |
dispatcherServlet方法上的注解**@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
说明会自动将返回值注册为名字为dispatcherServlet
**的组件
在该方法中new了一个DispatcherServlet类对象,在设置完一堆东西后返回这个对象,由此可以看出DispatcherServlet类已经由SpringBoot在底层进行了自动创建。
multipartResolver
1 |
|
@ConditionalOnBean(MultipartResolver.class)
容器中有MultipartResolver文件上传解析器类型组件@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
容器中没有名为 multipartResolver 的组件
给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
防止用户创建了MultipartResolver类的组件,但名字不符合规范。
因为@Bean的缘故,在返回该对象时会将其注册为一个组件,在其过程中对同类型而不同名的组件进行了重命名(Override)。
修改默认配置
SpringBoot默认会在底层配好所有的组件。但是如果用户自己配置了以用户的优先
下面以HttpEncodingAutoConfiguration类为例:
HttpEncodingAutoConfiguration
1 |
|
@EnableConfigurationProperties(ServerProperties.class)
开启配置绑定,与ServerProperties类进行绑定
@ConditionalOnClass(CharacterEncodingFilter.class)
条件为CharacterEncodingFilter字符编码类存在
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
条件为 属性前缀为server.servlet.encoding的值enabled为ture时,默认为true
HttpEncodingAutoConfiguration
1 | private final Encoding properties; |
构造器方法以ServerProperties类对象为参数,调用properties.getServlet().getEncoding()
赋给this.properties,从而获取到ServerProperties类中关于encoding的属性值
characterEncodingFilter
1 |
|
- 创建OrderedCharacterEncodingFilter类对象
- 获取this.properties的charset属性值,并将其设置为该filter对象的encoding
- 强制设置请求和响应的编码
- 返回该对象并注册为组件
**@ConditionalOnMissingBean
**表示条件为没有创建这个组件时:
- 若用户没有进行CharacterEncodingFilter组件的注册,则进行该方法注册为新的组件
- 否则不进行该方法
加了**@ConditionalOnMissingBean
**注解的均为spring boot底层默认配置,但如果用户自定义了这些组件,则这些默认配置失效。
此为定制化配置。
自动配置流程总结
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
每个自动配置类按照条件装配规则进行生效,默认都会绑定配置文件指定的值。xxxxProperties里面拿。xxxProperties和配置文件进行了绑定
生效的配置类就会给容器中装配很多组件
只要容器中有这些组件,相当于这些功能就有了
定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 —-> application.properties