SpringBoot自动配置原理
自动配置到底配置了些什么?
对于一个Spring项目,主要就是有两种配置:
- 一种是类似端口号,数据库地址,用户名密码等
- 一种是各种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的实现类
- JettyServletWebServerFactory
- TomcatServletWebServerFactory
- 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 定义。
主要作用和用途:
- 动态注册 Bean :
ImportSelector
主要用于在 Spring 的配置类(Configuration)中动态地注册 Bean 或者导入其他配置类。通过实现selectImports()
方法,可以根据一些条件或逻辑返回一个字符串数组,这些字符串数组表示要导入的 Bean 定义类的全限定名。 - 条件化配置 : 可以结合条件注解(如
@Conditional
注解)和ImportSelector
实现类,根据运行时环境的条件来决定是否导入某些 Bean 定义或配置类,从而实现更灵活和动态的配置管理。 - 自定义扩展 : 开发者可以根据具体需求自定义实现
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类并返回