Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之初始加载自动配置类

Posted 李阿昀

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之初始加载自动配置类相关的知识,希望对你有一定的参考价值。

在上一篇文章中,我们已分析完了@EnableAutoConfiguration注解里面的第一个核心注解了,即@AutoConfigurationPackage,它只是指定了默认的包规则,大家还有印象吧!接下来,在这篇文章中,咱们接着来分析@EnableAutoConfiguration注解里面的第二个核心注解,即@Import({AutoConfigurationImportSelector.class})。很明显,该注解是利用ImportSelector接口来给容器中批量导入一些东西的,只要研究清楚了导入了哪些东西,那么@EnableAutoConfiguration注解就算是分析完了。

@Import(AutoConfigurationImportSelector.class)详解

我们不妨点进AutoConfigurationImportSelector类的源码里面去看一看,看一下利用它给容器中批量导入了哪些东西。进入该类的源码里面之后,往下翻,相信你很快会看到一个selectImports方法,如下图所示。

在这里插入图片描述

从该方法中我们就能清楚地看到它到底给容器中要导入了哪些东东,因为该方法返回的数组里面就规定了要导入哪些东东。而且,从该方法中,我们也能看到,所有要导入的东东都是调用getAutoConfigurationEntry方法得到的。得到之后,最终再转成String数组返回出去。因此,我们只要研究清楚getAutoConfigurationEntry方法就行了,因为所有的核心全部都落在该方法里面了。

最后,我们不妨总结一下,@EnableAutoConfiguration注解就是利用AutoConfigurationImportSelector类中的selectImports方法给容器中批量导入一些组件的,至于要导入哪些组件,研究清楚getAutoConfigurationEntry方法便知道了。

getAutoConfigurationEntry方法的分析

我们不妨点进getAutoConfigurationEntry方法里面去看一看,如下图所示。

在这里插入图片描述

然后,在getAutoConfigurationEntry方法里面打一个断点,如下图所示,以Debug模式来启动咱们的Spring Boot应用,来看看该方法是如何来获取所有自动配置的集合的。

在这里插入图片描述

注意,我们最好把以前的断点都去掉,只留这么一个断点就行。

这时,程序会来到标有断点的代码处,我们不妨按下F6快捷键(Step Over)让程序往下运行,直至运行到下面这行代码处。

在这里插入图片描述

这儿是来获取所有候选的配置的。不用废话,继续按下F6快捷键(Step Over)让程序往下运行,在程序往下运行的过程中,大家可以看到会对configurations集合进行一些常见操作,比如从configurations集合里面先移除一些重复的东东,再从它里面排除一些东东等等。在对configurations集合操作了一大堆之后,最终会将其封装并返回,所以,configurations集合就显得特别重要了。

在这里插入图片描述

此时,我们不妨查看一下configurations集合变量的值,如下图所示,发现它里面保存了23个全类名,这23个全类名指定的组件全部都是要导入到容器中的。

在这里插入图片描述

那我这么知道是要导入这23个全类名指定的组件呢?就是通过getCandidateConfigurations方法知道的,因为该方法就是来获取所有需要导入到容器中的配置类的,大家叫组件也行。为了研究得更深入一点,我们不妨在getCandidateConfigurations方法处打上一个断点,如下图所示。

在这里插入图片描述

注意,我们最好把以前的断点都去掉,只留这么一个断点就行。

OK,现在我们再以Debug模式来重新启动咱们的Spring Boot应用,来看一下getCandidateConfigurations方法是怎么来获取到所有需要导入到容器中的配置类的。

这时,程序会来到标有断点的代码处,如下图所示。

在这里插入图片描述

然后,我们不妨按下F5快捷键(Step Into)进入getCandidateConfigurations方法里面去一探究竟。

在这里插入图片描述

可以看到该方法是利用SpringFactoriesLoader(即Spring工厂加载器)来加载一些东东的,那加载的是些什么东东呢?还废什么话啊,直接点进loadFactoryNames方法里面去看一看不就得了。

在这里插入图片描述

似乎loadFactoryNames方法里面又调用了一个loadSpringFactories方法啊!我们再点进去它里面看看,走着。

在这里插入图片描述

可以看到该方法会返回一个Map<String, List<String>>类型的结果,想必大家也猜到了,SpringFactoriesLoader(即Spring工厂加载器)最终就是利用该方法来加载得到所有组件的。

可能有同学会问了,那到底是从哪加载得到所有组件的呢?其实,只要你看懂loadSpringFactories这个方法就知道了。我们不妨再在loadSpringFactories方法处打一个断点,如下图所示,再以Debug模式来重新启动咱们的Spring Boot应用。

在这里插入图片描述

此时,我们来看该方法是从哪加载得到所有组件的。按下F6快捷键(Step Over)让程序往下运行,由于初始定义的result集合为空,所以程序会来到else分支语句里面。当程序来到下面这行代码处时,大伙可以很明显地看到,这儿就是来加载一个资源文件的,而且该资源文件就是META-INF/spring.factories

在这里插入图片描述

所以,loadSpringFactories方法是从META-INF/spring.factories资源文件中来加载得到所有组件的。也就是说,该方法会默认扫描我们当前系统下所有位于META-INF目录下的spring.factories资源文件。

聪明的小伙伴很快就会想到,要不咱们去看看引入的那些jar包里面的META-INF目录下有没有要加载的spring.factories资源文件呗。咱说干就干!

想必你会发现,在咱们引入的这些jar包里面,有些jar包里面的META-INF目录下是没有要加载的spring.factories这个资源文件的,而有些就有,比如与Spring Boot相关的jar包里面就有spring.factories资源文件,如下图所示。

在这里插入图片描述

当然了,除了以上jar包之外,最核心的spring-boot-autoconfigure-2.4.5.jar里面也有META-INF/spring.factories资源文件,而且文件内容如下图所示。

在这里插入图片描述

我们不妨把该文件中最核心的内容贴出来给大家看一下,如下所示。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\\
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration,\\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\\
org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\\
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\\
org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\\
org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\\
org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\\
org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\\
org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\\
org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\\
org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\\
org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\\
org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\\
org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\\
org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration,\\
org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\\
org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\\
org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\\
org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\\
org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration,\\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\\
org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration,\\
org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration,\\
org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration,\\
org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration,\\
org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration,\\
org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration,\\
org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\\
org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\\
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\\
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\\
org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\\
org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\\
org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration

大家看到没有,Spring Boot应用一启动就要给容器中加载的所有配置类都在spring.factories资源文件中写死了。

也不知道你看到没有,在该资源文件中有一个配置项叫org.springframework.boot.autoconfigure.EnableAutoConfiguration,它后面跟的就是要加载的配置类的全类名,而且加载的每一个配置类都占一行,因为它们后面都有一个换行符(即\\)。除此之外,你还会看到,这些要加载的配置类的全类名起得都蛮有规律的,都叫XxxAutoConfiguration,翻译过来就是什么东西的自动配置的意思。也就是说,Spring Boot兼容的全场景的自动配置,在这全部列举出来了。

大家不妨数一数总共有多少个自动配置类,算了,我还是直接告诉大家吧!总共有130个。这130个自动配置类的全类名全写死在spring.factories资源文件中了,这样,只要咱们的Spring Boot应用一启动,那么就会给容器中加载所有的自动配置类。其实,这句话也能这么来说,且听我下面娓娓道来。

那么问题随之就来了,真要把这么多自动配置类全加载进来吗?显然不是,因为有可能容器里面都没有这么多组件,还记得我们之前在主程序类中打印所有组件定义的名字时,容器里面真的有这么多组件的名字可供我们打印吗?难道说一股脑的加载这么多,我们容器里面就真的有这么多了吗?未必吧!下面我不妨给大家看看容器里面到底有多少个组件。

package com.meimeixia.boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * 主程序类,也叫主配置类
 * @author liayun
 * @create 2021-04-19 4:02
 */
//@SpringBootApplication
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan("com.meimeixia.boot")
public class MainApplication {

    public static void main(String[] args) {
        // 1. 返回IoC容器,IoC容器里面就包含了当前应用的所有组件
        ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args); // 这是固定写法哟
        // 2. 我们可以来查看下IoC容器里面所有的组件,只要能查找到某一个组件,就说明这个组件是能工作的,至于怎么工作,这就是我们后来要阐述的原理了
        String[] names = run.getBeanDefinitionNames(); // 获取所有组件定义的名字
        for (String name : names) {
            System.out.println(name);
        }
        
        // 3. 从容器中获取组件
        int count = run.getBeanDefinitionCount(); // 获取所有bean定义的总数
        System.out.println("count = " + count);

//        String[] beanNamesForType = run.getBeanNamesForType(CacheAspectSupport.class);
//        System.out.println("======" + beanNamesForType.length);
//
//        String[] beanNamesForType1 = run.getBeanNamesForType(WebMvcProperties.class);
//        System.out.println("======" + beanNamesForType1.length);


//        Pet tom01 = run.getBean("tom", Pet.class);
//        Pet tom02 = run.getBean("tom", Pet.class);
//        System.out.println("组件是否为单实例:" + (tom01 == tom02));
//
//        // 配置类打印:com.meimeixia.boot.config.MyConfig$$EnhancerBySpringCGLIB$$4559f04d@49096b06
//        MyConfig bean = run.getBean(MyConfig.class);
//        System.out.println(bean);
//
//        // 如果@Configuration(proxyBeanMethods = true),代理对象调用方法
//        User user = bean.user01();
//        User user1 = bean.user01();
//        System.out.println("(user == user1) = " + (user == user1));
//
//        User user01 = run.getBean("user01", User.class);
//        Pet tom = run.getBean("tom", Pet.class);
//        System.out.println("用户的宠物:" + (user01.getPet() == tom));
Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之HttpEncodingAutoConfiguration自动配置类

Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之初始加载自动配置类

Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之CacheAutoConfiguration自动配置类

Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之CacheAutoConfiguration自动配置类

Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之AopAutoConfiguration自动配置类

Spring Boot 2从入门到入坟 | 自动配置篇:源码分析之AopAutoConfiguration自动配置类