SpringBoot自动配置原理

SpringBoot自动配置原理

自动配置到底配置了些什么?

对于一个Spring项目,主要就是有两种配置:

  1. 一种是类似端口号,数据库地址,用户名密码等
  2. 一种是各种Bean,比如整合Mybatis需要配置的MapperFactoryBean,比如整合事务需要配置的DataSourceTransactionManager

关于SpringBoot的Servlet容器是如何加载

ServletWebServerApplicationContext类中有个createWebServer()方法,该方法调用时机在Spring容器完全初始化后。

java 复制代码
private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = this.getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
            createWebServer.end();
            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var5) {
                throw new ApplicationContextException("Cannot initialize servlet context", var5);
            }
        }

        this.initPropertySources();
    }
如何通过引入的serlvet容器starter来动态获取servlet 容器
createWebServer中如何获取
java 复制代码
ServletWebServerFactory factory = this.getWebServerFactory();
关于ServletWebServerFactory
java 复制代码
public interface ServletWebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

可以看到里面是一个函数式接口,提供工厂的形式来获取WebServer。

ServletWebServerFactory的实现类
  1. JettyServletWebServerFactory
  2. TomcatServletWebServerFactory
  3. UndertowServletWebServerFactory

SpringBoot分别提供了三个实现类分别作为Tomcat,jetty,underTow的工厂类。

getWebServerFactory
java 复制代码
protected ServletWebServerFactory getWebServerFactory() {
        String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
        } else if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        } else {
            return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
        }
    }

利用BeanFactory的getBeanNamesByType(ServletWebServerFactory.class)来获取Spring容器的ServletWebServerFactory对象。

产生疑问,ServletWebServerFacotry的实现类是什么时候交给Spring管理的呢?

在ServletWebServerFactoryAutoConfiguration中

java 复制代码
@Configuration(
    proxyBeanMethods = false
)
@AutoConfigureOrder(Integer.MIN_VALUE)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
    type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
@Import({BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
    .....
}

分别导入了EmbeddedTomcat,EmbeddedJetty,EmbeddedUnderTow等几个类,如果该应用满足上面的条件则会导入该类。

EmbeddedTomcat
java 复制代码
	@Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedTomcat {
        EmbeddedTomcat() {
        }

        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }

该类的职责主要还是在满足一定的条件下往Spring容器中注入TomcatServletWebServerFactory ,这就可以让上一步中获取到ServletWebServerFactory。从而实现开启tomcat 容器。

自动配置

SpringBoot提供了一种机制,会自动配置某些必须的类,不需要程序员再自己配置。如原生Spring项目中需要手动配置aop并开启。SpringBoot中只需要引入spring-boot-starter-aop,便自动开启了aop功能。

EnableAutoConfiguration

java 复制代码
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

EnableAutoConfiguration表示开启SpringBoot的自动配置,并可以配置排除不需要哪些Configuration类配置。

EnableAutoConfiguration导入了AutoConfigurationImportSelector,则Spring容器会直接将该类放进容器中。

关于ImportSelector机制

ImportSelector 是 Spring 框架中的一个接口,用于动态地定义和注册 Bean 到 Spring 容器中。它允许开发者根据一定的条件或逻辑,选择性地导入(import)特定的配置类或 Bean 定义。

主要作用和用途:
  1. 动态注册 BeanImportSelector 主要用于在 Spring 的配置类(Configuration)中动态地注册 Bean 或者导入其他配置类。通过实现 selectImports() 方法,可以根据一些条件或逻辑返回一个字符串数组,这些字符串数组表示要导入的 Bean 定义类的全限定名。
  2. 条件化配置 : 可以结合条件注解(如 @Conditional 注解)和 ImportSelector 实现类,根据运行时环境的条件来决定是否导入某些 Bean 定义或配置类,从而实现更灵活和动态的配置管理。
  3. 自定义扩展 : 开发者可以根据具体需求自定义实现 ImportSelector 接口,实现特定的逻辑来选择性地导入 Bean 或配置类。这种方式适合于需要根据不同情况动态调整 Spring 容器配置的场景。

通过ImportSelector机制,SpringBoot通过AutoConfigurationImportSelector实现ImortSelector来动态的将其他jar包的bean放入Spring容器中

AutoConfigurationImportSelector

AutoConfigurationImportSelector实现了ImportSelector。核心查找配置的方法如下

通过一系列解析,将@Configuration注解的类全限定名称返回

复制代码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
        	//annotationMetadata代表SpringBootApplica传入的那个类元信息,通过一系列解析,将@Configuration注解的类全限定名称返回
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }
getAutoConfigurationEntry
java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationEntry(configurations, exclusions);
        }
    }
  • getCandidateConfigurations()将META-INF/spring.factories配置下EnableAutoConfiguration节点下的全限定名称解析出来,并返回
  • removeDuplicates()对返回的configurations进行去重,因为这里有可能某个配置的名称完全相同
  • getExclusions对EnableAutoConfiguration配置的exclude进行统计
  • checkExcludedClasses 对排除的内容进行校验
  • removeAll 对EnableAutoConfiguration配置的exclude进行排除
  • getConfigurationClassFilter 获取初步的排除配置类不符合条件注解的那些。

注意,这里只会对以下条件进行初步排除

复制代码
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
  • fireAutoConfigurationImportEvents这一步只是记录哪些合格,哪些是被排除掉的。

可以看到,最后排除掉,从133个配置类变成21个符合要求的。

  • AutoConfigurationEntry 封装Entry类并返回
相关推荐
旷野说几秒前
为什么 MyBatis 原生二级缓存“难以修复”?
java·java-ee·mybatis
8***23553 分钟前
【wiki知识库】07.用户管理后端SpringBoot部分
java
bcbnb4 分钟前
iOS 性能测试的工程化方法,构建从底层诊断到真机监控的多工具测试体系
后端
开心就好20258 分钟前
iOS 上架 TestFlight 的真实流程复盘 从构建、上传到审核的团队协作方式
后端
小周在成长16 分钟前
Java 泛型支持的类型
后端
aiopencode16 分钟前
Charles 抓不到包怎么办?HTTPS 抓包失败、TCP 数据流异常与底层补抓方案全解析
后端
阿蔹19 分钟前
JavaWeb-Selenium 配置以及Selenim classnotfound问题解决
java·软件测试·python·selenium·测试工具·自动化
稚辉君.MCA_P8_Java21 分钟前
Gemini永久会员 C++返回最长有效子串长度
开发语言·数据结构·c++·后端·算法
Penge66640 分钟前
Redis-bgsave浅析
redis·后端
阿白的白日梦1 小时前
Windows下c/c++编译器MinGW-w64下载和安装
c语言·后端