文章目錄
spring boot自動配置原理
前言
閱讀本文最好懂得spring源碼,springmvc源碼知識,以及零配置mvc+內嵌tomcat,servlet的spi機制
本文springboot源碼版本說明:2.0.2.RELEASE
2.2.4.RELEASE的版本筆者也閱讀過,相較於2.0.2來說更合理,更清晰,但是爲了入門簡單,還是選擇早期版本
自動配置原理
@SpringBootApplication -->
@EnableAutoConfiguration -->
@Import({AutoConfigurationImportSelector.class}) -->
class AutoConfigurationImportSelector implements DeferredImportSelector{
//讀過spring源碼的都知道ImportSelector會把selectImports返回的這些類都放到spring容器中
//annotationMetadata是你帶有@SpringBootApplication的類的註解描述
public String[] selectImports(AnnotationMetadata annotationMetadata) {
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
//exclude,excludeName
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//從META-INF/spring.factories文件讀取
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = this.removeDuplicates(configurations);
//看看你的@SpringBootApplication的類有沒有exclusions
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//這裏過濾掉一些
configurations = this.filter(configurations, autoConfigurationMetadata);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
}
總結一下原理就是從spring.factories文件讀取自動配置類放到spring容器中,下面我們以一個自動配置類的實際例子看看
DispatcherServletAutoConfiguration
該類可以從spring.factories文件找到,
DispatcherServlet 是如何交給tomcat的,以及是如何與spring發生聯繫的,都在這裏完成
//省略
@AutoConfigureAfter({ServletWebServerFactoryAutoConfiguration.class})
@EnableConfigurationProperties({ServerProperties.class})
//上面註解實質上只有@AutoConfigureAfter是boot新增的,其他註解都是spring本來有的或者在spring基礎上做的擴展
public class DispatcherServletAutoConfiguration {
/*******DispatcherServlet如何與spring發生聯繫********/
//省略註解
protected static class DispatcherServletConfiguration {
private final WebMvcProperties webMvcProperties;
private final ServerProperties serverProperties;
//DispatcherServletConfiguration 類只有一個有參構造器,在裝配模式NO的情況下,determinCandidateConstructors的時候就可以確定該構造器,並且spring會把兩個參數也給這個構造器
public DispatcherServletConfiguration(WebMvcProperties webMvcProperties, ServerProperties serverProperties) {
this.webMvcProperties = webMvcProperties;
this.serverProperties = serverProperties;
}
//把DispatcherServlet 放入spring容器
//在mvc零配置的時候我們是把appContext當作DispatcherServlet 的構造器入參傳給DispatcherServlet
//boot中DispatcherServlet 是如何使用到appContext的呢?
//其實是DispatcherServlet 的父類FrameworkServlet實現了ApplicationContextAware接口,spring在初始化的時候會回調此接口把自己的上下文環境傳給他
@Bean(name = {"dispatcherServlet"})
public DispatcherServlet dispatcherServlet() {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
//省略
return dispatcherServlet;
}
/*******DispatcherServlet如何與spring發生聯繫********/
/*******DispatcherServlet 是如何交給tomcat的********/
//省略
protected static class DispatcherServletRegistrationConfiguration {
//構造器原理同上
//省略
@Bean(name = {"dispatcherServletRegistration"})
//方法參數是上面這個內部類@Bean放到spring容器的
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
//把dispatcherServlet和ServerProperties(其實是我們yml文件配置的server.address,server.port等屬性)通過構造器給ServletRegistrationBean
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean(dispatcherServlet, new String[]{this.serverProperties.getServlet().getServletMapping()});
//省略
return registration;
}
}
//到現在爲止我們還不知道dispatcherServlet是如何交給tomcat的,注意ServletRegistrationBean這個類
//他的頂級父類如下,一看ServletContextInitializer的onStartup我就懂了,servlet的spi機制而已
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = this.getDescription();
this.register(description, servletContext);
}
this.register(description, servletContext);//這行代碼一直點下去會發現如下熟悉的代碼
{
String name = this.getServletName();
return servletContext.addServlet(name, this.servlet);
}
}
/*******DispatcherServlet 是如何交給tomcat的********/
}
}
@AutoConfigureAfter註解
DispatcherServletAutoConfiguration 要在ServletWebServerFactoryAutoConfiguration之後被spring實例化, @AutoConfigureAfter是如何控制這些類的順序的
AutoConfigurationImportSelector類的如下代碼
//selectImports方法是由spring的初始化過程的invokeBeanFactoryPostProcessors方法回調的
//該方法在這裏就是給傳過來的類排個序
public Iterable<Entry> selectImports() {
return (Iterable)this.sortAutoConfigurations().stream().map((importClassName) -> {
return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
}).collect(Collectors.toList());
}
private List<String> sortAutoConfigurations() {
//省略
return (new AutoConfigurationSorter(this.getMetadataReaderFactory(), autoConfigurationMetadata)).getInPriorityOrder(autoConfigurations);
}
}
public List<String> getInPriorityOrder(Collection<String> classNames) {
//省略
//這裏處理@AutoConfigureAfter註解
List<String> orderedClassNames = this.sortByAnnotation(classes, orderedClassNames);
return orderedClassNames;
}
@EnableConfigurationProperties註解
@EnableConfigurationProperties({ServerProperties.class}) --->
@Import({EnableConfigurationPropertiesImportSelector.class})
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
class EnableConfigurationPropertiesImportSelector implements ImportSelector {
private static final String[] IMPORTS = new String[]{EnableConfigurationPropertiesImportSelector.ConfigurationPropertiesBeanRegistrar.class.getName()};
public String[] selectImports(AnnotationMetadata metadata) {
return IMPORTS;
}
//該類被EnableConfigurationPropertiesImportSelector import了
//並且該類是ImportBeanDefinitionRegistrar ,會回調registerBeanDefinitions函數
public static class ConfigurationPropertiesBeanRegistrar implements ImportBeanDefinitionRegistrar {
public ConfigurationPropertiesBeanRegistrar() {
}
//metadata在這裏是DispatcherServletAutoConfiguration的註解信息
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//拿到配置集合遍歷
this.getTypes(metadata).forEach((type) -> {
//爲每一個配置文件註冊bd
this.register(registry, (ConfigurableListableBeanFactory)registry, type);
});
}
private List<Class<?>> getTypes(AnnotationMetadata metadata) {
//只關心DispatcherServletAutoConfiguration的EnableConfigurationProperties的註解信息
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(EnableConfigurationProperties.class.getName(), false);
//返回@EnableConfigurationProperties裏的怕配置文件集合
return this.collectClasses(attributes != null ? (List)attributes.get("value") : Collections.emptyList());
}
}
}
總結
自動配置原理:spring.factories 用ImportSelector放入spring容器
DispatcherServlet關聯spring:@Bean將DS放入spring容器,ApplicationContextAware接口將spring的appContext給DS
DispatcherServlet關聯tomcat:servlet3.0 SPI擴展機制