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类并返回
相关推荐
Abladol-aj20 分钟前
并发和并行的基础知识
java·linux·windows
清水白石00820 分钟前
从一个“支付状态不一致“的bug,看大型分布式系统的“隐藏杀机“
java·数据库·bug
吾日三省吾码6 小时前
JVM 性能调优
java
Estar.Lee6 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
弗拉唐7 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi777 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
2401_857610038 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
少说多做3438 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀8 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员