SpringBoot的自动装配原理
在使用 springboot的时候,我们体会到了它的舒服之处,容易上手,开箱即用。撇去了繁杂的xml配置,使用注解和 yml的方式统一配置和管理。使我们的开发变得非常的优雅。
用了这么久的springboot 天天在那看@SpringBootApplication 那么 springboot的 自动装配原理是什么呢?
接下来认认真真来看一次 springboot 装配原理。
自动配置原理
@SpringBootApplication public class DemoTestApplication { public static void main(String[] args) { SpringApplication.run(DemoTestApplication.class, args); } }
点开 @SpringBootApplication 可以看到
@SpringBootConfiguration @EnableAutoConfiguration @ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class} ), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class} )} ) public @interface SpringBootApplication {
删去一些不重要的注解,其中最为重要的两个注解为
@SpringBootConfiguration 和 @EnableAutoConfiguration
@SpringBootConfiguration
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration {
经由分析,可以简单看到 @SpringBootConfiguration 里边值得我们注意的一个 注解是 @Configuration,这表明这是一个 spring的配置类,我们将它交给 IOC容器来管理。
@EnableAutoConfiguration
见名知意,开启自动自动配置。看来这就是我们要找的主要类了,那我们来看看它到底做了什么
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage // 自动配置包 @Import({AutoConfigurationImportSelector.class}) //自动配置导入选择器 public @interface EnableAutoConfiguration {
我们可以看到,这个类最最重要的一条配置就是 @Import 导入了一个 自动配置导入选择器,那么这个类到底干嘛的呢?
AutoConfigurationImportSelector.class
// 我们来看看它的 selectImports 放 public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry //这个方法就是我们要着重分析的方法了,getAutoConfigurationEntry 获取自动配置条目 = this.getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } } protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); /* 所有的配置都存放在configurations中, 而这些配置都从getCandidateConfiguration中获取, 这个方法是用来获取候选的配置。 */ List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); ... } }
点进 getCandidateConfigurations(annotationMetadata, attributes)方法,我们知道了这个方法可以用来获取所有候选的配置,那么这些候选的配置又是从哪来的呢?
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
可以看到源码中有一个 loadFactoryNames 中传入了一个 getSpringFactoriesLoaderFactoryClass,通过这个方法我看到了熟悉的 EnableAutoConfiguratioin 注解,标注了这个类的包不就是 @SpringBootApplication
吗?
所以我们可以得出结论:它兜兜转转绕了这么多地方,就是为了将启动类所需的所有资源导入。
然后看到 springboot 给我们的提示 Assert.notEmpty("No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."
);
这个断言的意思是,configurations必须非空,否则就打印一段话,No auto configuration classes found in META-INF/spring.factories
,我们把这个逻辑反过来想想。如果这个集合不为空,是不是就代表找到了这个spring.factories并且会去加载这个文件中的内容呢?
我们点进去这个 文件会发现里边包含了很多自动配置属性
大致浏览一下spring.factories 会发现,大部分的文件名都是 xxxAutoConfiguration结尾
那么 loadFactoryNames 这个方法干嘛的?经过几次跳转我们发现代码中最重要的一行代码
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
它将我们传入的文件内容包封装成了 Properties 对象
也就是 这个 EnableAutoConfiguration值,然后把他们添加到容器中。
(图截不下...一直到最后)
然后每一个自动配置类会进行自动配置功能
以 HttpAutoConfiguration 为例
我们点进去HttpEncodingAutoConfiguration 看看,然后分析一下他是怎么从 yml里获取我们的配置的。
@Configuration// 表明当前是一个配置类 @EnableConfigurationProperties({ServerProperties.class})// 启用指定类的@ConfigurationProperties功能 //这里是启动 ServerProperties 的@ConfigurationProperties @ConditionalOnWebApplication // 如果当前是 Web 项目则生效 @ConditionalOnClass({CharacterEncodingFilter.class}) // 项目组存在 CharacterEncodingFilter 这个类则生效 @ConditionalOnProperty( prefix = "server.servlet.encoding", value = {"enabled"}, matchIfMissing = true )// 默认这个字段 server.servlet.encoding = true public class HttpEncodingAutoConfiguration { private final Encoding properties; //当类中 只有一个 有参构造器的时候,这个时候的参数 会从 IOC容器里去找 //所以这里,我们就将与 yml 对应的 Properties 文件与 AutoConfigutation这个类 绑定了 public HttpEncodingAutoConfiguration(ServerProperties properties) { this.properties = properties.getServlet().getEncoding(); } @Bean @ConditionalOnMissingBean//如果当前类不存在,就执行这个方法。 public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); //从 properties/yml获取值 filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE)); return filter; } }
关于 ServerProperties
@ConfigurationProperties( prefix = "server", ignoreUnknownFields = true ) //看到这里我们非常的熟悉,我们不也常用这个 @ConfigurationProperties 来写自己的配置类吗 public class ServerProperties {
小总结:
springboot 启动会加载大量的配置类,如果我们需要的功能有默认写好的配置类,那么我们直接用就行了,还可以看看这些自动配置类装载了哪些组件啊,对应的 Properties 文件里有哪些可以配置的属性啊。
如果没有的话,那我们可就要自己来配置了,相信看完本文后大家也都知道,对应的基本套路了。
XxxxAutoConfig
XxxxProperties
然后在自己项目类路径下 也就是 src/resources/META-INF/spring.factories(自己创建的) 里注册配置一下就行了
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
xxx.xxx.xxx包.XxxxAutoConfig,\
转载:https://www.yuque.com/zzuli-tree/my6a8r/lkk3a0