先赞后看,养成习惯 :rose: 欢迎微信关注 [Java编程之道]
,每天进步一点点,沉淀技术分享知识。
SpringBoot 目前已经成为了Java程序员必备的技能项了,不论你是应届毕业生还是跳槽程序员,熟练掌握SpringBoot是必不可或缺的技能。最近自己打算利用SpringBoot的自动配置原理自己来实现一个类库,借此机会也为大家分享一下我用到了一些技术。临近秋招季,希望本文能秋招的同学带来一些帮助。
来吧!灵魂三问:
同学你会吗?慌了吗?怀疑人生了吗?稳住!下面请跟着我来一探究竟,寻求答案吧。
大家刚开始学习SpringBoot的时候就知道,我们只需要在:application.properties或application.yml,进行少量的配置边可以实现一个web项目的启动和使用,SpringBoot自动帮我们装配了很多Bean定义最后通过Bean工厂生成Bean缓存到容器中供你使用。 下面给大家提供除了一些常用的配置更多配置项目,大家可以参考官方文档。
SpringBoot配置项
下面咱们就进入正文,看看SpringBoot是怎么实现自动装配的。
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } } 复制代码
这一个小小的注解帮我们启动了SpringBoot,其背后默认帮我们配置了很多自动配置类。其中最重要是 @SpringBootApplication 这个注解,我们点进去看一下。
@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 { 复制代码
解说一下三个注解:
@EnableAutoConfiguration:告诉SpringBoot开启自动配置功能,这样自动配置才能生效。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { 复制代码
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); } 复制代码
它其实是注册了一个Bean的定义,返回了当前主程序类的同级以及子级的包组件。如果你把类写道了APP类的上级时SpringBoot便不会帮你加载报出如404的问题,这也就是为什么,我们要把App放在项目的最高级中。
@Import(AutoConfigurationImportSelector.class) 复制代码
到这就到自动装配的核心了,离成功不远了,咱们直接看重点,AutoConfigurationImportSelector通过继承 DeferredImportSelector,ImportSelector 重写selectImports方法。
该方法奠定了SpringBoot自动批量装配的核心功能逻辑。
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = getAttributes(annotationMetadata); List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } 复制代码
可以看到getCandidateConfigurations()方法,它其实是去加载 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" 外部文件。这个外部文件里面,默认有很多自动配置的类,这些类的定义信息将会被SpringBoot批量的加载到Bean定义Map中等待被创建实例。如下:
Provides access to meta-data written by the auto-configure annotation processor.
官方注释翻译:提供对自动配置注解写入的元数据的访问能力。
这个Path下都有什么东西呢?我大概给大家罗列几个:
#Thu Jun 14 11:34:18 UTC 2018 org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration.ConditionalOnClass=com.datastax.driver.core.Cluster,org.springframework.data.cassandra.core.ReactiveCassandraTemplate,reactor.core.publisher.Flux org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration.ConditionalOnClass=org.apache.solr.client.solrj.SolrClient,org.springframework.data.solr.repository.SolrRepository org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration.AutoConfigureBefore=org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration.ConditionalOnClass=com.couchbase.client.java.CouchbaseBucket,com.couchbase.client.java.Cluster org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitTemplate,com.rabbitmq.client.Channel 复制代码
这些东西不是本文的重点,就不细说了。简单来讲 autoConfigurationMetadata 这个参数会在后面的filter方法使用到,而filter方法处理是你的配置类中某些xxxconditionOnClass的,这些在配置类中使用的Class的信息应该就是上述方法提供。
见文知意:getCandidateConfigurations 获取候选配置项,这个候选配置项就是spring.factories下的配置项。
List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes); 复制代码
下面我们来看看这个方法的细节和作用。翻译下面这句话(返回自动配置类名称)
在loadSpringFactorise方法中边是通过循环如读取spring.factories下的配置信息并缓存到Map中。
在读取完自动配置信息后,会经历下面几个方法,主要是对其进行去重处理。
configurations = removeDuplicates(configurations); 复制代码
Set<String> exclusions = getExclusions(annotationMetadata, attributes); 复制代码
checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); 复制代码
触发自动配置导入事件
当我们使用 @EnableAutoConfiguration
注解激活自动装配时,实质对应着很多 XXXAutoConfiguration
类在执行装配工作,这些 XXXAutoConfiguration
类是在spring-boot-autoconfigure jar中的META-INF/spring.factories文件中配置好的, @EnableAutoConfiguration
通过SpringFactoriesLoader机制创建 XXXAutoConfiguration
这些bean。 XXXAutoConfiguration
的bean会依次执行并判断是否需要创建对应的bean注入到Spring容器中。
在每个 XXXAutoConfiguration
类中,都会利用多种类型的条件注解 @ConditionOnXXX
对当前的应用环境做判断,如应用程序是否为Web应用、classpath路径上是否包含对应的类、Spring容器中是否已经包含了对应类型的bean。如果判断条件都成立, XXXAutoConfiguration
就会认为需要向Spring容器中注入这个bean,否则就忽略。
更多精彩好文尽在: Java编程之道
:gift:
欢迎各位好友前去关注!:rose: