做者筆記倉庫:https://github.com/seazean/javanotesjava
歡迎各位關注個人筆記倉庫,clone 倉庫到本地後使用 Typora 閱讀效果更好。git
若是你們只關注 SpringBoot 如何自動裝配,能夠只看「註解分析」和「裝配流程」兩個小節github
應用啓動:web
@SpringBootApplication public class BootApplication { public static void main(String[] args) { // 啓動代碼 SpringApplication.run(BootApplication.class, args); } }
SpringApplication 構造方法:spring
this.resourceLoader = resourceLoader
:資源加載器,初始爲 null編程
this.webApplicationType = WebApplicationType.deduceFromClasspath()
:判斷當前應用的類型,是響應式仍是 Web 類bootstrap
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories()
:獲取引導器tomcat
META-INF/spring.factories
文件中找 org.springframework.boot.BootstrappersetInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class))
:獲取初始化器springboot
META-INF/spring.factories
文件中找 org.springframework.context.ApplicationContextInitializersetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))
:獲取監聽器服務器
META-INF/spring.factories
文件中找 org.springframework.context.ApplicationListenerthis.mainApplicationClass = deduceMainApplicationClass()
:獲取出 main 程序類
SpringApplication#run(String... args):
StopWatch stopWatch = new StopWatch()
:中止監聽器,監控整個應用的啓停
stopWatch.start()
:記錄應用的啓動時間
bootstrapContext = createBootstrapContext()
:建立引導上下文環境
bootstrapContext = new DefaultBootstrapContext()
:建立默認的引導類環境this.bootstrapRegistryInitializers.forEach()
:遍歷全部的引導器調用 initialize 方法完成初始化設置configureHeadlessProperty()
:讓當前應用進入 headless 模式
listeners = getRunListeners(args)
:獲取全部 RunListener(運行監聽器)
META-INF/spring.factories
文件中找 org.springframework.boot.SpringApplicationRunListenerlisteners.starting(bootstrapContext, this.mainApplicationClass)
:遍歷全部的運行監聽器調用 starting 方法
applicationArguments = new DefaultApplicationArguments(args)
:獲取全部的命令行參數
environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments)
:準備環境
environment = getOrCreateEnvironment()
:返回或建立基礎環境信息對象
switch (this.webApplicationType)
:根據當前應用的類型建立環境
case SERVLET
:Web 應用環境對應 ApplicationServletEnvironmentcase REACTIVE
:響應式編程對應 ApplicationReactiveWebEnvironmentdefault
:默認爲 Spring 環境 ApplicationEnvironmentconfigureEnvironment(environment, applicationArguments.getSourceArgs())
:讀取全部配置源的屬性值配置環境
ConfigurationPropertySources.attach(environment)
:屬性值綁定環境信息
sources.addFirst(ATTACHED_PROPERTY_SOURCE_NAME,..)
:把 configurationProperties 放入環境的屬性信息頭部listeners.environmentPrepared(bootstrapContext, environment)
:運行監聽器調用 environmentPrepared(),EventPublishingRunListener 發佈事件通知全部的監聽器當前環境準備完成
DefaultPropertiesPropertySource.moveToEnd(environment)
:移動 defaultProperties 屬性源到環境中的最後一個源
bindToSpringApplication(environment)
:與容器綁定當前環境
ConfigurationPropertySources.attach(environment)
:從新將屬性值綁定環境信息
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME)
:從環境信息中移除 configurationProperties
sources.addFirst(ATTACHED_PROPERTY_SOURCE_NAME,..)
:把 configurationProperties 從新放入環境信息
configureIgnoreBeanInfo(environment)
:配置忽略的 bean
printedBanner = printBanner(environment)
:打印 SpringBoot 標誌
context = createApplicationContext()
:建立 IOC 容器
switch (this.webApplicationType)
:根據當前應用的類型建立 IOC 容器
case SERVLET
:Web 應用環境對應 AnnotationConfigServletWebServerApplicationContextcase REACTIVE
:響應式編程對應 AnnotationConfigReactiveWebServerApplicationContextdefault
:默認爲 Spring 環境 AnnotationConfigApplicationContextcontext.setApplicationStartup(this.applicationStartup)
:設置一個啓動器
prepareContext()
:配置 IOC 容器的基本信息
postProcessApplicationContext(context)
:後置處理流程
applyInitializers(context)
:獲取全部的初始化器調用 initialize() 方法進行初始化
listeners.contextPrepared(context)
:全部的運行監聽器調用 environmentPrepared() 方法,EventPublishingRunListener 發佈事件通知 IOC 容器準備完成
listeners.contextLoaded(context)
:全部的運行監聽器調用 contextLoaded() 方法,通知 IOC 加載完成
refreshContext(context)
:刷新 IOC 容器
invokeBeanFactoryPostProcessors(beanFactory)
:實現了自動裝配onRefresh()
:建立 WebServer 使用該接口afterRefresh(context, applicationArguments)
:留給用戶自定義容器刷新完成後的處理邏輯
stopWatch.stop()
:記錄應用啓動完成的時間
callRunners(context, applicationArguments)
:調用全部 runners
listeners.started(context)
:全部的運行監聽器調用 started() 方法
listeners.running(context)
:全部的運行監聽器調用 running() 方法
獲取容器中的 ApplicationRunner、CommandLineRunner
AnnotationAwareOrderComparator.sort(runners)
:合併全部 runner 而且按照 @Order 進行排序
callRunner()
:遍歷全部的 runner,調用 run 方法
handleRunFailure(context, ex, listeners)
:處理異常,出現異常進入該邏輯
handleExitCode(context, exception)
:處理錯誤代碼listeners.failed(context, exception)
:運行監聽器調用 failed() 方法reportFailure(getExceptionReporters(context), exception)
:通知異常SpringBoot 定義了一套接口規範,這套規範規定 SpringBoot 在啓動時會掃描外部引用 jar 包中的 META-INF/spring.factories
文件,將文件中配置的類型信息加載到 Spring 容器,並執行類中定義的各類操做,對於外部的 jar 包,直接引入一個 starter 便可
@SpringBootApplication 註解是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
註解的集合
@SpringBootApplication 註解
@Inherited @SpringBootConfiguration //表明 @SpringBootApplication 擁有了該註解的功能 @EnableAutoConfiguration //同理 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) // 掃描被 @Component (@Service,@Controller)註解的 bean,容器中將排除TypeExcludeFilter 和 AutoConfigurationExcludeFilter public @interface SpringBootApplication { }
@SpringBootConfiguration 註解:
@Configuration // 表明是配置類 @Indexed public @interface SpringBootConfiguration { @AliasFor(annotation = Configuration.class) boolean proxyBeanMethods() default true; }
@AliasFor 註解:表示別名,能夠註解到自定義註解的兩個屬性上表示這兩個互爲別名,兩個屬性實際上是同一個含義相互替代
@ComponentScan 註解:默認掃描當前包及其子級包下的全部文件
@EnableAutoConfiguration 註解:啓用 SpringBoot 的自動配置機制
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; }
@AutoConfigurationPackage:將添加該註解的類所在的 package 做爲自動配置 package 進行管理,把啓動類所在的包設置一次,爲了給各類自動配置的第三方庫掃描用,好比帶 @Mapper 註解的類,Spring 自身是不能識別的,但自動配置的 Mybatis 須要掃描用到,而 ComponentScan 只是用來掃描註解類,並無提供接口給三方使用
@Import(AutoConfigurationPackages.Registrar.class) // 利用 Registrar 給容器中導入組件 public @interface AutoConfigurationPackage { String[] basePackages() default {}; //自動配置包,指定了配置類的包 Class<?>[] basePackageClasses() default {}; }
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]))
:註冊 BD
new PackageImports(metadata).getPackageNames()
:獲取添加當前註解的類的所在包registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames))
:存放到容器中
new BasePackagesBeanDefinition(packageNames)
:把當前主類所在的包名封裝到該對象中@Import(AutoConfigurationImportSelector.class):首先自動裝配的核心類
容器刷新時執行:invokeBeanFactoryPostProcessors() → invokeBeanDefinitionRegistryPostProcessors() → postProcessBeanDefinitionRegistry() → processConfigBeanDefinitions() → parse() → process() → processGroupImports() → getImports() → process() → AutoConfigurationImportSelector#getAutoConfigurationEntry()
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 獲取註解屬性,@SpringBootApplication 註解的 exclude 屬性和 excludeName 屬性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 獲取全部須要自動裝配的候選項 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去除重複的選項 configurations = removeDuplicates(configurations); // 獲取註解配置的排除的自動裝配類 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); // 移除全部的配置的不須要自動裝配的類 configurations.removeAll(exclusions); // 過濾,條件裝配 configurations = getConfigurationClassFilter().filter(configurations); // 獲取 AutoConfigurationImportListener 類的監聽器調用 onAutoConfigurationImportEvent 方法 fireAutoConfigurationImportEvents(configurations, exclusions); // 包裝成 AutoConfigurationEntry 返回 return new AutoConfigurationEntry(configurations, exclusions); }
AutoConfigurationImportSelector#getCandidateConfigurations:獲取自動配置的候選項
List<String> configurations = SpringFactoriesLoader.loadFactoryNames()
:加載自動配置類
參數一:getSpringFactoriesLoaderFactoryClass()
獲取 @EnableAutoConfiguration 註解類
參數二:getBeanClassLoader()
獲取類加載器
factoryTypeName = factoryType.getName()
:@EnableAutoConfiguration 註解的全類名return loadSpringFactories(classLoaderToUse).getOrDefault()
:加載資源
urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION)
:獲取資源類FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
:加載的資源的位置return configurations
:返回全部自動裝配類的候選項
從 spring-boot-autoconfigure-2.5.3.jar/META-INF/spring.factories 文件中獲取自動裝配類,進行條件裝配,按需裝配
Spring Boot 經過 @EnableAutoConfiguration
開啓自動裝配,經過 SpringFactoriesLoader 加載 META-INF/spring.factories
中的自動配置類實現自動裝配,自動配置類其實就是經過 @Conditional
註解按需加載的配置類(JVM 類加載機制),想要其生效必須引入 spring-boot-starter-xxx
包實現起步依賴
以 DispatcherServletAutoConfiguration 爲例:
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) // 類中的 Bean 默認不是單例 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) // 條件裝配,環境中有 DispatcherServlet 類才進行自動裝配 @ConditionalOnClass(DispatcherServlet.class) @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) public class DispatcherServletAutoConfiguration { // 註冊的 DispatcherServlet 的 BeanName public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet"; @Configuration(proxyBeanMethods = false) @Conditional(DefaultDispatcherServletCondition.class) @ConditionalOnClass(ServletRegistration.class) // 綁定配置文件的屬性,從配置文件中獲取配置項 @EnableConfigurationProperties(WebMvcProperties.class) protected static class DispatcherServletConfiguration { // 給容器註冊一個 DispatcherServlet,起名字爲 dispatcherServlet @Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) { // 新建一個 DispatcherServlet 設置相關屬性 DispatcherServlet dispatcherServlet = new DispatcherServlet(); // spring.mvc 中的配置項獲取注入,沒有就填充默認值 dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest()); // ...... // 返回該對象註冊到容器內 return dispatcherServlet; } @Bean // 容器中有這個類型組件才進行裝配 @ConditionalOnBean(MultipartResolver.class) // 容器中沒有這個名字 multipartResolver 的組件 @ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME) // 方法名就是 BeanName public MultipartResolver multipartResolver(MultipartResolver resolver) { // 給 @Bean 標註的方法傳入了對象參數,這個參數就會從容器中找,由於用戶自定義了該類型,以用戶配置的優先 // 可是名字不符合規範,因此獲取到該 Bean 並返回到容器一個規範的名稱:multipartResolver return resolver; } } }
//將配置文件中的 spring.mvc 前綴的屬性與該類綁定 @ConfigurationProperties(prefix = "spring.mvc") public class WebMvcProperties { }
(補充內容,WebServer)
SpringBoot 嵌入式 Servlet 容器,默認支持的 webServe:Tomcat、Jetty、Undertow
配置方式:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <!--必需要把內嵌的 Tomcat 容器--> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
建立 Web 容器:
SpringApplication.run(BootApplication.class, args)
:應用啓動
ConfigurableApplicationContext.run()
:
context = createApplicationContext()
:建立容器
applicationContextFactory = ApplicationContextFactory.DEFAULT
ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: // Servlet 容器,繼承自 ServletWebServerApplicationContext return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: // 響應式編程 return new AnnotationConfigReactiveWebServerApplicationContext(); default: // 普通 Spring 容器 return new AnnotationConfigApplicationContext(); } } catch (Exception ex) { throw new IllegalStateException(); } }
applicationContextFactory.create(this.webApplicationType)
:根據應用類型建立容器
refreshContext(context)
:容器啓動
內嵌容器工做流程:
ServletWebServerApplicationContext 容器啓動時進入 refresh() 邏輯,Spring 容器啓動邏輯中,在實例化非懶加載的單例 Bean 以前有一個方法 onRefresh(),留給子類去擴展,該容器就是重寫這個方法建立 WebServer
protected void onRefresh() { //省略.... createWebServer(); } private void createWebServer() { ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); }
獲取 WebServer 工廠 ServletWebServerFactory,而且獲取的數量不等於 1 會報錯,Spring 底層有三種:
TomcatServletWebServerFactory
、JettyServletWebServerFactory
、UndertowServletWebServerFactory
自動配置類 ServletWebServerFactoryAutoConfiguration 導入了 ServletWebServerFactoryConfiguration(配置類),根據條件裝配判斷系統中到底導入了哪一個 Web 服務器的包,建立出服務器並啓動
默認是 web-starter 導入 tomcat 包,容器中就有 TomcatServletWebServerFactory,建立出 Tomcat 服務器並啓動,
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) { // 初始化 initialize(); }
初始化方法 initialize 中有啓動方法:this.tomcat.start()