转载

面试季「 Spring Boot」自动装配

先赞后看,养成习惯 :rose: 欢迎微信关注 [Java编程之道] ,每天进步一点点,沉淀技术分享知识。

面试季「 Spring Boot」自动装配

SpringBoot 目前已经成为了Java程序员必备的技能项了,不论你是应届毕业生还是跳槽程序员,熟练掌握SpringBoot是必不可或缺的技能。最近自己打算利用SpringBoot的自动配置原理自己来实现一个类库,借此机会也为大家分享一下我用到了一些技术。临近秋招季,希望本文能秋招的同学带来一些帮助。

来吧!灵魂三问:

  • 什么是自动装配?
  • 自动装配为我们装配了什么?
  • 怎么实现自动装配?

同学你会吗?慌了吗?怀疑人生了吗?稳住!下面请跟着我来一探究竟,寻求答案吧。

SpringBoot自动配置

大家刚开始学习SpringBoot的时候就知道,我们只需要在:application.properties或application.yml,进行少量的配置边可以实现一个web项目的启动和使用,SpringBoot自动帮我们装配了很多Bean定义最后通过Bean工厂生成Bean缓存到容器中供你使用。 下面给大家提供除了一些常用的配置更多配置项目,大家可以参考官方文档。

SpringBoot配置项

下面咱们就进入正文,看看SpringBoot是怎么实现自动装配的。

SpringBoot启动类

@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
复制代码

这一个小小的注解帮我们启动了SpringBoot,其背后默认帮我们配置了很多自动配置类。其中最重要是 @SpringBootApplication 这个注解,我们点进去看一下。

@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 {
复制代码

解说一下三个注解:

  • @SpringBootConfiguration : Spring Boot的配置类,标注在某个类上,表示这是一个Spring Boot的配置类(等于xml方式下.xml文件)。
  • @EnableAutoConfiguration: 开启自动配置类,SpringBoot的精华所在。
  • @ComponentScan:包扫描,等同于xml下开启并设置包扫描路径。

@EnableAutoConfiguration

@EnableAutoConfiguration:告诉SpringBoot开启自动配置功能,这样自动配置才能生效。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
复制代码
  • @AutoConfigurationPackage:自动配置包
  • @Import: 导入自动配置的组件
    • @Import:通常用于有时没有把某个类注入到IOC容器中,但在运用的时候需要获取该类对应的bean,此时就需要用到@Import注解。加入IOC容器的方式有很多种,当然@Bean注解也可以,但是@Import注解快速导入的方式更加便捷。

@AutoConfigurationPackage

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放在项目的最高级中。

  • SpringApplication.run(App.class, args);为什么要传入主启动类?
    • 其一方面就是要根据该类去解析他所在包,并实现对同级和下级类的扫描。
面试季「 Spring Boot」自动装配

@Import

@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中等待被创建实例。如下:

面试季「 Spring Boot」自动装配

AutoConfigurationMetadata

Provides access to meta-data written by the auto-configure annotation processor.

官方注释翻译:提供对自动配置注解写入的元数据的访问能力。

面试季「 Spring Boot」自动装配

这个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
复制代码
面试季「 Spring Boot」自动装配

这些东西不是本文的重点,就不细说了。简单来讲 autoConfigurationMetadata 这个参数会在后面的filter方法使用到,而filter方法处理是你的配置类中某些xxxconditionOnClass的,这些在配置类中使用的Class的信息应该就是上述方法提供。

获取候选配置项

见文知意:getCandidateConfigurations 获取候选配置项,这个候选配置项就是spring.factories下的配置项。

List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
复制代码

下面我们来看看这个方法的细节和作用。翻译下面这句话(返回自动配置类名称)

面试季「 Spring Boot」自动装配

在loadSpringFactorise方法中边是通过循环如读取spring.factories下的配置信息并缓存到Map中。

面试季「 Spring Boot」自动装配
面试季「 Spring Boot」自动装配
  • 第一个箭头首先一次性去加载一些自动配置类,减少重复加载提高效率。
  • 第二个箭头则是去该路径下找spring.factories并读取其中设置的自动配置类的信息
  • 第三个箭头则是将读取的结果写入Map缓存中

其他方法

在读取完自动配置信息后,会经历下面几个方法,主要是对其进行去重处理。

  • 防止自己写的自动装配类和Springboot装配的类出现重复,进行去重。
configurations = removeDuplicates(configurations);
复制代码
  • 读取@SpringBootApplication中 exclude设置的属性,可以排除自动装填配类。
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
复制代码
面试季「 Spring Boot」自动装配
  • 处理自动配置类中如@ConditionalOnClass等注解,如果不满足则该配置不生效等情况。
checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);
  configurations = filter(configurations, autoConfigurationMetadata);
复制代码
面试季「 Spring Boot」自动装配

触发自动配置导入事件

面试季「 Spring Boot」自动装配

归纳总结

当我们使用 @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:

面试季「 Spring Boot」自动装配
原文  https://juejin.im/post/5f0180aee51d4534ae6974d8
正文到此结束
Loading...