今天學習一下SpringBoot的啟動及自動配置,由於沒有參與過springBoot項目開發,
所以初次學習的主要目標:將SpringBoot中的自動配置與啟動與之前學習的Spring與SpringMVC實現聯系起來。弄清楚SpringBoot中的:
- SpringIOC容器初始化(怎樣實現自動配置的)
- SpringAOP支持
- Spring事務支持
- SpringMVC組件初始化
- Tomcat啟動
一、SpringBoot啟動流程
@SpringBootApplication public class App { public static void main( String[] args ) { //springboot應用啟動 SpringApplication.run(App.class,args); } } /*
* org.springframework.boot.SpringApplication#run()
*/ public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
1、SpringApplication的初始化
初始化了6個屬性:
- resourceLoader:資源加載器,初始化時一般為null
- primarySources:啟動時配置文件Configuration
- webApplicationType:容器類型;
- initalizers:實例化多個與容器初始化有關的組件;
- listeners:實例化多個應用監聽器;
- mainApplicationClass:main方法所在的類的Class實例。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //null this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //啟動時配置Configuration this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //容器類型,三個枚舉類型,判斷順序 // ① 反應式web容器:WebApplicationType.REACTIVE:項目工程中包含DispatcherHandler.class,不包含DispatcherServlet.class、ServletContainer.class // ② 不是web應用:WebApplicationType.NONE:項目工程中不包含ConfigurableWebApplicationContext.class、Servlet.class // ③ 響應式web容器:WebApplicationType.SERVLET:不是上面兩種情況,則響應式web容器啟動 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //實例化多個與IOC容器初始化有關的組件 // 具體實例化所有jar包下的/META-INF/spring.factories文件中,ApplicationContextInitializer.class全限定名指定的class數組中所以Class setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //實例化多個應用監聽器 // 具體實例化所有jar包下的/META-INF/spring.factories文件中,ApplicationListener.class全限定名指定的class數組中所以Class setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //main方法所在的類的Class實例 this.mainApplicationClass = deduceMainApplicationClass(); }
先介紹下getSpringFactorieInstances()方法:springboot中SPI(服務發現接口)機制的實現,主要是根據/META-INF/spring.factories中尋找並創建對應的服務實例。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { //獲取類加載器,(SPI打破類加載的雙親委派模型) ClassLoader classLoader = getClassLoader(); // 從所用/META-INF/spring.factories中找到type對應的class集合,后面SpringFactoriesLoader是Spring-core.java中的類。 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 實例化class類型集合(parameterTypers:class的有參構造器中參數類型;args:構造器傳參) List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //優先級排序(BeanFactoryPostProcessor時提到過的順序:先PriorityOrdered,后Ordered,其他) AnnotationAwareOrderComparator.sort(instances); return instances; }
例如上面的:getSpringFactoriesInstances(ApplicationListener.class)
//spring-boot-autoconfigure.jar/META-INF/spring.factories //Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer
2、SpringApplication.run()運行的流程
public ConfigurableApplicationContext run(String... args) { // ① 簡單的秒表,記錄服務啟動到關閉的時間 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; // ② 異常記錄器 spring.factories中SpringBootExceptionReporter.class對應的類型實例 // org.springframework.boot.diagnostics.FailureAnalyzers的實例 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); // ③ 啟動抽象窗口工具包支持:java.awt.headless = true // 無顯示設備,鼠標,鍵盤時,awt+Swing可進行窗口編程(window+frame等) configureHeadlessProperty(); // ③ 應用監聽器 spring.factories中SpringApplicationRunListener.class對應的類型實例 // org.springframework.boot.context.event.EventPublishingRunListener實例 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // ④ 初始化環境變量 // 根據上面容器類型創建對應的創建變量實例 // 設置defaultProperties(springApplication.set)、args(命令行參數)、profiles(spring.profiles.active)屬性 // 綁定環境變量到監聽器+spring.main(默認當前SpringApplication實例) ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); // 設置spring.beaninfo.ignore(默認true)到環境變量中 configureIgnoreBeanInfo(environment); // 打印/不打印環境到控制台、日志 Banner printedBanner = printBanner(environment); // ⑤ 根據對應容器類型創建容器 // 響應式web容器:AnnotationConfigServletWebServerApplicationContext // ① 父類的構造方法中創建DefaultListableBeanFactory // ② reader 注冊注解注入的Bean后置處理器到IOC容器中AutowiredAnnotationBeanPostProcessor
// BeanFactory后置處理器ConfigurationClassPostProcessor,后面發現這個就是自動配置實現的類 // ③ scanner 設置環境變量、resourceLoader、includeFilters(Spring的@Component+JDK的@Named) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //⑥ 初始化容器context的一部分屬性 // ① 環境變量覆蓋 // ② 將容器綁定到初始化組件(initializers)、監聽器(listeners)中// ③ 注冊單例 命令行參數的包裝實例applicationArguments、printedBanner // ④ 設置懶加載則注冊懶加載BeanFactory后置處理器 LazyInitializationBeanFactoryPostProcessor // ⑤ 不允許BeanDefinition覆蓋 // ⑥ 將配置resource(包括MainClass)加載到容器中,MainClass(App.class)解析成一個AnnotatedGenericBeanDefinition // ⑦ 將listeners中ApplicationContextListener類型的監聽器注冊到容器中 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //⑦ 容器初始化 主要是AnnotationConfigServletWebServerApplicationContext的父類 // ServletWebServerApplicationContext,這個第三節研究 refreshContext(context); //⑧ 空方法 afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { // 打印日志 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //監聽器啟動 listeners.started(context); //喚醒runners callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { //監聽器運行 listeners.running(context); } catch (Throwable ex) { //監聽器關閉 handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
小結:熟悉了大致流程:發現核心的實現還是在⑦ 容器初始化中:refreshContext()。
- 容器的初始化
- tomcat的嵌入
- 兩個組件initalizers、listeners的作用
3、Springboot容器初始化——refreshContext()
refreshContext() : 容器初始化+優雅停機
/* org.springframework.boot.SpringApplication#refreshContext */ private void refreshContext(ConfigurableApplicationContext context) { //容器初始化 refresh((ApplicationContext) context); if (this.registerShutdownHook) { try { // JDK的shutdownHook關閉鈎子, // 作用:優雅停機。調用系統退出方法(關機注銷)時,線程還在跑,但不提供服務,設定一個超時時間,到時間后停機。 // kill pid(注意:不能kill -9 pid) context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext); //容器初始化 refresh((ConfigurableApplicationContext) applicationContext); } protected void refresh(ConfigurableApplicationContext applicationContext) { //容器初始化 //AnnotationConfigServletWebServerApplicationContext.refresh() applicationContext.refresh(); }
AnnotationConfigServletWebServerApplicationContext.refresh(),
查看AnnotationConfigServletWebServerApplicationContext的類圖,發現springboot相比於spring實現了一個ServletWebServerApplicationContext
如果觀看AnnotationConfigservletWebServerApplicationContext源碼,發現具體邏輯實現都在ServletWebServerApplicationContext中,所以主要學習ServletWebServerApplicationContext的源碼:ServletWebServerApplicationContext.refresh();
/* org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#refresh */ public final void refresh() throws BeansException, IllegalStateException { try { //AbstractApplicationContext.refresh() super.refresh(); } catch (RuntimeException ex) { //關閉釋放web服務(tomcat、jetty等) stopAndReleaseWebServer(); throw ex; } }
結合之前的SpringIOC——refresh()分析,裝飾者模式(繼承實現)有對哪些模塊有增強。先給出AbstractApplicationContext.refresh()。僅說明增強功能。
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // ② IOC初始化前的裝備(配置環境參數、創建監聽器、事件容器) // 增強:scanner緩存清理 prepareRefresh(); ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); prepareBeanFactory(beanFactory); try { // 徹底重寫 postProcessBeanFactory(beanFactory); invokeBeanFactoryPostProcessors(beanFactory); registerBeanPostProcessors(beanFactory); initMessageSource(); initApplicationEventMulticaster(); //增強:初始化了一個web服務:createWebServer(tomcat、jetty等),並啟動 onRefresh(); registerListeners(); finishBeanFactoryInitialization(beanFactory); //增強:webServer未啟動時,重新啟動 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } destroyBeans(); cancelRefresh(ex); throw ex; } finally { resetCommonCaches(); } } }
綜上:找到了tomcat啟動的位置,但是沒有找到自動配置的位置,可能是postProcessBeanFactory(beanFactory)
/* org/springframework/boot/web/servlet/context/ServletWebServerApplicationContext */ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { //注冊了一個Bean后置處理器WebApplicationContextServletContextAwareProcessor //這個Bean后置處理器邏輯很簡單:僅有一個前置方法 // servletContextAware類型的bean,bean.set(servletContext) // servletConfigAware類型的bean,bean.set(servletConfig) beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this)); //忽略實現接口ServletContextAware的Bean //跟上面的Bean后置處理器沖突,應該是版本升級加的吧 beanFactory.ignoreDependencyInterface(ServletContextAware.class); registerWebApplicationScopes(); } private void registerWebApplicationScopes() { // 允許用戶自定義scope(不能與自帶的singleton,prototype沖突) // 注冊web的作用域scope(session、request) // 將scope注冊進IOC容器中 ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory()); WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory()); existingScopes.restore(); }
綜上:還是沒有找到自動配置實現的地方,只能IDEA debug一行一行的找了,最終發現
invokeBeanFactoryPostProcessors(beanFactory):這一行執行完,容器中多了100多個BeanDefinition,也就是說是BeanFactory的后置處理器實現自動配置的。
最終找到了ConfigurationClassPostProcessor,這個BeanFactoryPostProcessor是在AnnotationConfigservletWebServerApplicationContext初始化時注冊入到容器中的
/* org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext#AnnotationConfigServletWebServerApplicationContext() */ public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } /* * 下面是spring-context.jar中的源碼,也就是自動加載還是依托於spring源碼實現的 */ /* org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.core.env.Environment) */ public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); } /*org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) */ public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); } if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) { beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver()); } } Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8); if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { //BeanFactory后置處理器:會對class上的@Configuration、@ComponentScan、@Component、@Import、@ImportResource注解解析 RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { //Bean后置處理器:在bean實例化后實現依賴注入的(@Autowired注解) RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor. if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)); } // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor. if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(); try { def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader())); } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex); } def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME)); } if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME)); } return beanDefs; }
ConfigurationClassPostProcessor.processConfigBeanDefinitions():解析@Configuration注解類中的@Bean到容器BeanDefinition容器中(屬於Spring的源碼)
二、自動配置
自動配置配置代碼有點復雜,主要看看@SpringBootApplication的實現
1、@SpringBootApplication注解
@SpringBootApplication是一個組合注解,主要是@SpringBootConfiguration+@EnableAutoConfiguration+@ComponentScan
@SpringBootConfiguration == @Configuration
@EnableAutoConfiguration = @AutoConfigurationPackage +@Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage = @Import(AutoConfigurationPackages.Registrar.class)
綜上:@SpringBootApplication = @Configuration + @Import(AutoConfigurationPackages.Registrar.class)+@Import(AutoConfigurationImportSelector.class)+@ComponentScan
順序ConfigurationClassPostProcessor找到@ComponentScan、@Import的解析
ConfigurationClassPostProcessor.processConfigBeanDefinitions()-->parser.parse(candidates)
ConfigurationClassParser.parse-->ConfigurationClassParser.processConfigurationClass->ConfigurationClassParser.doProcessConfigurationClass()
protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass, filter); } // 處理@PropertySource注解 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // 處理componentScans注解 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { //如果bdCand也被@Configuration注解,解析
parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // 處理Import注解 processImports(configClass, sourceClass, getImports(sourceClass), filter, true); // 處理@ImportResource注解 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // 處理@Configuration中的@Bean注解 Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 處理接口默認的方法 @Import(AutoConfigurationPackages.Registrar.class)
// AutoConfigurationPackages.Registrar 的接口有默認方法,
// 將MainClass的basePackage封裝成一個BeanDefinition放入到IOC容器中,並不是掃描basePackage,僅僅是一個記錄的作用 processInterfaces(configClass, sourceClass); // 處理超類 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } return null; }
@ComponentScan注解解析:
basePackage/basePackageClasses未設置時,掃描@ComponentScan所注解的類所在的包下的所有class文件,
@Component注解的class文件,生成BeanDefinition放入IOC容器中
SpringBoot未配置basePackage、basePackageClasses,所以會掃描MainClass所在包下的所有class文件。
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { //@ComponentScan注解對應一個ClassPathBeanDefinitionScanner實例 //setUserDefaultFilters:是否使用默認過濾器Filters,默認使用 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); //setBeanNameGenerator:設置默認的beanName生成器,默認使用BeanNameGenerator.class Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); //setScopedProxyMode:設置是否為檢測組件生成代理 ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } //setResourcePattern:掃描包下源文件格式 默認"**/*.class" scanner.setResourcePattern(componentScan.getString("resourcePattern")); //includeFilter:掃描組件過濾器,符合includeFilter條件的類,生成BeanDefinition for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } //excludeFilter:掃描組件反向過濾器,符合excludeFilter條件的類,不生成BeanDefinition for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } //lazyInit:懶加載 默認關閉 boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } //掃描的包basePackages下class文件 Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } //掃描的basePackageClasses所在包下class文件 for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } //如果basePackages,basePackageClasses未設置, // 掃描@ComponentScan所注解的類所在包下的class文件: // 這里就是Springboot掃描MainClass同包下的Bean的原因 if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); //scanner.do(basePackages):熟悉的spring源碼,掃描basePackages下的class文件(includeFilters = @Component)生成組件 return scanner.doScan(StringUtils.toStringArray(basePackages)); }
@Import解析:
- DeferredImportSelector放入容器ConfigurationClassParser中的deferredImportSelectors中
- ImportBeanDefinitionRegistrar放入ConfigurationClass的importBeanDefinitionRegistrars容器中
- 其他類型@Import==@Configuration處理
/* org.springframework.context.annotation.ConfigurationClassParser#processImports */ private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter, boolean checkForCircularImports) { //校驗@Import()如果括號中值為空直接返回 if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { //@Import()指定類型是ImportSelector類型 Class<?> candidateClass = candidate.loadClass(); //初始化ImportSelector實例 ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); Predicate<String> selectorFilter = selector.getExclusionFilter(); if (selectorFilter != null) { exclusionFilter = exclusionFilter.or(selectorFilter); } if (selector instanceof DeferredImportSelector) { // 指定類型再細分為DeferredImportSelector類型 // springboot的注解@Import(AutoConfigurationImportSelector.class) // 將AutoConfigurationImportSelector實例放入到 // ConfigurationClassParser中的deferredImportSelectors容器中 this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter); processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { //@Import()指定類型是ImportBeanDefinitionRegistrar類型時 //springboot的注解@Import(AutoConfigurationPackages.Registrar.class) Class<?> candidateClass = candidate.loadClass(); //創建一個ImportBeanDefinitionRegistrar實例, // 放入到ConfigurationClass的importBeanDefinitionRegistrars容器中 // ConfigurationClass是@Configuration注解的類的元數據+beanName的封裝類型 ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // 指定類不是上兩種類型,將@Import視為@Configuration處理 this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
@Import+@ComponentScan解析完,后退到ConfigurationClassParser.parse方法
public void parse(Set<BeanDefinitionHolder> configCandidates) { for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { if (bd instanceof AnnotatedBeanDefinition) { //@注解解析(@PropertySource、@ComponentScan、@Import、@ImportResource) parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName()); } else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex); } } //springboot自動配置 this.deferredImportSelectorHandler.process(); }
this.deferredImportSelectorHandler.process();
調用AutoConfigurationImportSelector.selectImports()方法返回/META-INF/spring.factories中EnableAutoConfiguration對應的class數組
將每個文件視作為一個@Configuration注解的class文件進行解析,解析生成BeanDefinition
/* org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process */ public void process() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); deferredImports.forEach(handler::register); handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } /* org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#register*/ public void register(DeferredImportSelectorHolder deferredImport) { //調用ImportSelector.getImportGroup返回group類型 //springboot: AutoConfigurationImportSelector.getImportGroup==AutoConfigurationGroup.class Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); //將group轉換成一個DeferredImportSelectorGrouping類型grouping DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); //將deferredImport放入到grouping.deferredImports容器中 //這里還是回憶一下springboot的deferredImport是AutoConfigurationImportSelector、ConfigurationClass的封裝器 grouping.add(deferredImport); //將ConfigurationClass放入到ConfigurationClasses容器中, //springboot:MainClass放入到ConfigurationClasses容器中 this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } /* org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports */ public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate<String> exclusionFilter = grouping.getCandidateFilter(); //grouping.getImports() == <configurationClass,importSelect.selectImports(configurationClass)> //selectImports(configurationClass) ==SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, getBeanClassLoader()) //即這里遍歷的是/MATE-INF/spring.factories中EnableAutoConfiguration對應的class數組 //將每個class視作為一個@Import注解解析 // ① class是ImportSelector類型,加入到ConfigurationClassParser中的deferredImportSelectors容器中 // ② ImportBeanDefinitionRegistrar,放入到ConfigurationClass的importBeanDefinitionRegistrars容器中 // ③ 不是上面兩種類型,就視為一個@Configuration注解的配置文件類解析 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } }
最后看下AutoConfigurationImportSelector.selectImports()實現
/* org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports */ public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } 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); //@Condition注解篩選 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
spring-boot-autoconfigure-2.1.3.RELEASE.jar!/META-INF/spring.factories中自動配置class數組
總共有118個class,列舉spring相關的自動配置
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
118個class並不是都有效,還需要經過@Condition中條件篩選
/* org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#filter */ private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) { long startTime = System.nanoTime(); String[] candidates = StringUtils.toStringArray(configurations); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; //注意filters也是在spring.factories中指定的 //默認三個OnBeanCondition,OnClassCondition,OnWebApplicationCondition即只有三個注解有效@ConditionOnBean @ConditionOnClass @ConditionOnWebApplication for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) { invokeAwareMethods(filter); //Condition條件判斷 boolean[] match = filter.match(candidates, autoConfigurationMetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { skip[i] = true;//沒通過的打標true candidates[i] = null; skipped = true; } } } if (!skipped) { //所有class都滿足各自的Condition return configurations; } List<String> result = new ArrayList<>(candidates.length); for (int i = 0; i < candidates.length; i++) { if (!skip[i]) { //滿足條件的Bean才會被視為配置文件(@Configuration)解析 result.add(candidates[i]); } } if (logger.isTraceEnabled()) { int numberFiltered = configurations.size() - result.size(); logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms"); } return new ArrayList<>(result); }
條件過濾是有順序:(追究這個好像沒什么意義,最終Condition都生效了......)
1、先select過濾:在調用AutoConfigurationImportSelector.filters時僅僅只檢測@ConditionOnClass @ConditionOnBean @ConditionOnWebApplication三個條件
2、后class上的@Condition過濾:這個時候類上的@ConditionalOnProperty才會生效
以AopAutoConfiguration為例
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { ... }
將spring.aop.auto = false,依然掃描進了configurations中,后續類加載時,@ConditionOnProperty才生效
三、總結
1、springboot主動注解配置,是依賴spring源碼中BeanFactory的后置處理器ConfigurationClassPostProcessor實現的,這個后置處理器是在ApplicationContext的構造方法中注入到IOC容器中的,ConfigurationClassPostProcessor會處理解析5個注解@Configuration、@Import、@ImportResource、@Component、@ComponentScan。
2、@SpringBootApplication = @Configuration+@ComponentScan+@Import(AutoConfigurationImportSelector.class) +@Import(AutoConfigurationPackages.Registrar.class)
- @Configuration:主要是一個屬性proxyBeanMethods:默認開啟(true),允許其他類調用@Bean注解的方法,為false的話,不允許則想要一個@Bean注解的類型只能自己初始化一個。
- @ComponentScan:springboot掃描MainClass所在包下的class文件。這個標簽basePackages、basePackageClasses未指定時,掃描注解類(MainClass)所在package下的所有class文件到IOC容器中。
- @Import(AutoConfigurationImportSelector.class):自動配置的核心。會調用selector.selectImports(medata)方法,獲取/META-INF/spring.factories文件中的EnableAutoConfiguration映射的class數組,class數組就是配置類的集合。(可視為每個類被@Configuration注解)。
- @Import(AutoConfigurationPackages.Registrar.class):注冊basePackage為一個BeanDefinition到容器中。注意僅僅是記錄basePackage,不會進行包掃描,包掃描由上面的@ComponentScan實現
3、springboot的SPI機制:對應文件/META-INF/spring.factories,所有的自動發現並加載的類都配置在這里面。這也是springboot熱拔插的原因,直接依賴jar包,就可以使用redis,kafka等中間件,通過spring.factories+@ConditionOnClass(判斷Class是否存在 == 是否引入jar包)來發現中間件的Bean,例如RedisTemplate等
4、spring的啟動順序:
① initalizers+listeners+webType
② 異常記錄器exceptionReporters +headless模式
③ 初始化環境變量environment(命令行參數+spring.profiles.active+系統環境變量path)
④ printbanner日志橫幅
⑤ 根據webType創建對應的容器ApplicationContext,
⑥ initalizers+listeners+environment與容器ApplicationContext的互相綁定
⑦ 容器啟動(允許自定義scope,重寫AbstarctApplicationContext的onRefresh,開啟tomcat服務)
四、遺留問題
1、知道了怎么找到配置類,但是屬性自動注入還沒有弄清楚
2、知道tomcat啟動的地方,但嵌入細節不知道,開啟一個后台線程嗎?
五、補充
1、SpringBoot自定義的注解@ConditionOnClass等