@java
SpringBoot是Spring的包裝,經過自動配置使得SpringBoot能夠作到開箱即用,上手成本很是低,可是學習其實現原理的成本大大增長,須要先了解熟悉Spring原理。若是還不清楚Spring原理的,能夠先查看博主以前的文章,本篇主要分析SpringBoot的啓動、自動配置、Condition、事件驅動原理。web
SpringBoot啓動很是簡單,因其內置了Tomcat,因此只須要經過下面幾種方式啓動便可:spring
@SpringBootApplication(scanBasePackages = {"cn.dark"}) public class SpringbootDemo { public static void main(String[] args) { // 第一種 SpringApplication.run(SpringbootDemo .class, args); // 第二種 new SpringApplicationBuilder(SpringbootDemo .class)).run(args); // 第三種 SpringApplication springApplication = new SpringApplication(SpringbootDemo.class); springApplication.run(); } }
能夠看到第一種是最簡單的,也是最經常使用的方式,須要注意類上面須要標註@SpringBootApplication註解,這是自動配置的核心實現,稍後分析,先來看看SpringBoot啓動作了些什麼?
在往下以前,不妨先猜想一下,run方法中須要作什麼?對比Spring源碼,咱們知道,Spring的啓動都會建立一個ApplicationContext的應用上下文對象,並調用其refresh方法啓動容器,SpringBoot只是Spring的一層殼,確定也避免不了這樣的操做。另外一方面,之前經過Spring搭建的項目,都須要打成War包發佈到Tomcat才行,而如今SpringBoot已經內置了Tomcat,只須要打成Jar包啓動便可,因此在run方法中確定也會建立對應的Tomcat對象並啓動。以上只是咱們的猜測,下面就來驗證,進入run方法:apache
public ConfigurableApplicationContext run(String... args) { // 統計時間用的工具類 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); // 獲取實現了SpringApplicationRunListener接口的實現類,經過SPI機制加載 // META-INF/spring.factories文件下的類 SpringApplicationRunListeners listeners = getRunListeners(args); // 首先調用SpringApplicationRunListener的starting方法 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); // 處理配置數據 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); // 啓動時打印banner Banner printedBanner = printBanner(environment); // 建立上下文對象 context = createApplicationContext(); // 獲取SpringBootExceptionReporter接口的類,異常報告 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); // 核心方法,啓動spring容器 refreshContext(context); afterRefresh(context, applicationArguments); // 統計結束 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } // 調用started listeners.started(context); // ApplicationRunner // CommandLineRunner // 獲取這兩個接口的實現類,並調用其run方法 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { // 最後調用running方法 listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
SpringBoot的啓動流程就是這個方法,先看getRunListeners方法,這個方法就是去拿到全部的SpringApplicationRunListener實現類,這些類是用於SpringBoot事件發佈的,關於事件驅動稍後分析,這裏主要看這個方法的實現原理:tomcat
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); } private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 加載上來後反射實例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; } public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); } public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryTypeName, factoryImplementationName.trim()); } } } cache.put(classLoader, result); return result; } }
一步步追蹤下去能夠看到最終就是經過SPI機制根據接口類型從META-INF/spring.factories文件中加載對應的實現類並實例化,SpringBoot的自動配置也是這樣實現的。爲何要這樣實現呢?經過註解掃描不能夠麼?固然不行,這些類都在第三方jar包中,註解掃描實現是很麻煩的,固然你也能夠經過@Import註解導入,可是這種方式不適合擴展類特別多的狀況,因此這裏採用SPI的優勢就顯而易見了。
回到run方法中,能夠看到調用了createApplicationContext方法,見名知意,這個就是去建立應用上下文對象:app
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot." + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext"; protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
注意這裏經過反射實例化了一個新的沒見過的上下文對象AnnotationConfigServletWebServerApplicationContext,這個是SpringBoot擴展的,看看其構造方法:框架
public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
若是你有看過Spring註解驅動的實現原理,這兩個對象確定不會陌生,一個實支持註解解析的,另一個是掃描包用的。
上下文建立好了,下一步天然就是調用refresh方法啓動容器:less
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } } protected void refresh(ApplicationContext applicationContext) { Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext); ((AbstractApplicationContext) applicationContext).refresh(); }
這裏首先會調用到其父類中ServletWebServerApplicationContext:ide
public final void refresh() throws BeansException, IllegalStateException { try { super.refresh(); } catch (RuntimeException ex) { stopAndReleaseWebServer(); throw ex; } }
能夠看到是直接委託給了父類:spring-boot
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
這個方法不會陌生吧,以前已經分析過了,這裏再也不贅述,至此SpringBoot的容器就啓動了,可是Tomcat啓動是在哪裏呢?run方法中也沒有看到。實際上Tomcat的啓動也是在refresh流程中,這個方法其中一步是調用了onRefresh方法,在Spring中這是一個沒有實現的模板方法,而SpringBoot就經過這個方法完成了Tomcat的啓動:
protected void onRefresh() { super.onRefresh(); try { createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } } private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { ServletWebServerFactory factory = getWebServerFactory(); // 主要看這個方法 this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
這裏首先拿到TomcatServletWebServerFactory對象,經過該對象再去建立和啓動Tomcat:
public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); tomcat.setBaseDir(baseDir.getAbsolutePath()); Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); return getTomcatWebServer(tomcat); }
上面的每一步均可以對比Tomcat的配置文件,須要注意默認只支持了http協議:
Connector connector = new Connector(this.protocol); private String protocol = DEFAULT_PROTOCOL; public static final String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol";
若是想要擴展的話則能夠對additionalTomcatConnectors屬性設置值,須要注意這個屬性沒有對應的setter方法,只有addAdditionalTomcatConnectors方法,也就是說咱們只能經過實現BeanFactoryPostProcessor接口的postProcessBeanFactory方法,而不能經過BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,由於前者能夠經過傳入的BeanFactory對象提早獲取到TomcatServletWebServerFactory對象調用addAdditionalTomcatConnectors便可;然後者只能拿到BeanDefinition對象,該對象只能經過setter方法設置值。
Spring本來就提供了事件機制,而在SpringBoot中又對其進行擴展,經過發佈訂閱事件在容器的整個生命週期的不一樣階段進行不一樣的操做。咱們先來看看SpringBoot啓動關閉的過程當中默認會發布哪些事件,使用下面的代碼便可:
@SpringBootApplication public class SpringEventDemo { public static void main(String[] args) { new SpringApplicationBuilder(SpringEventDemo.class) .listeners(event -> { System.err.println("接收到事件:" + event.getClass().getSimpleName()); }) .run() .close(); } }
這段代碼會在控制檯打印全部的事件名稱,按照順序以下:
以上是正常啓動關閉,若是發生異常還有發佈ApplicationFailedEvent事件。事件的發佈遍及在整個容器的啓動關閉週期中,事件發佈對象剛剛咱們也看到了是經過SPI加載的SpringApplicationRunListener實現類EventPublishingRunListener,一樣事件監聽器也是在spring.factories文件中配置的,默認實現瞭如下監聽器:
org.springframework.context.ApplicationListener=\ org.springframework.boot.ClearCachesApplicationListener,\ org.springframework.boot.builder.ParentContextCloserApplicationListener,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\ org.springframework.boot.context.FileEncodingApplicationListener,\ org.springframework.boot.context.config.AnsiOutputApplicationListener,\ org.springframework.boot.context.config.ConfigFileApplicationListener,\ org.springframework.boot.context.config.DelegatingApplicationListener,\ org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ org.springframework.boot.context.logging.LoggingApplicationListener,\ org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
能夠看到有用於文件編碼的(FileEncodingApplicationListener),有加載日誌框架的(LoggingApplicationListener),還有加載配置的(ConfigFileApplicationListener)等等一系列監聽器,SpringBoot也就是經過這系列監聽器將必要的配置和組件加載到容器中來,這裏再也不詳細分析,感興趣的讀者能夠經過其實現的onApplicationEvent方法看到每一個監聽器到底是監聽的哪個事件,固然事件發佈和監聽咱們本身也是能夠擴展的。
SpringBoot最核心的仍是自動配置,爲何它能作到開箱即用,再也不須要咱們手動使用@EnableXXX等註解來開啓?這一切的答案就在@SpringBootApplication註解中:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {}
這裏重要的註解有三個:@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan。@ComponentScan就不用再說了,@SpringBootConfiguration等同於@Configuration,而@EnableAutoConfiguration就是開啓自動配置:
@AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration { } @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
@AutoConfigurationPackage註解的做用就是將該註解所標記類所在的包做爲自動配置的包,簡單看看就行,主要看AutoConfigurationImportSelector,這個就是實現自動配置的核心類,注意這個類是實現的DeferredImportSelector接口。
在這個類中有一個selectImports方法。這個方法在我以前的文章這一次搞懂Spring事務註解的解析也有分析過,只是實現類不一樣,它一樣會被ConfigurationClassPostProcessor類調用,先來看這個方法作了些什麼:
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); // SPI獲取EnableAutoConfiguration爲key的全部實現類 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 把某些自動配置類過濾掉 configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); // 包裝成自動配置實體類 return new AutoConfigurationEntry(configurations, exclusions); } protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // SPI獲取EnableAutoConfiguration爲key的全部實現類 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; }
追蹤源碼最終能夠看到也是從META-INF/spring.factories文件中拿到全部EnableAutoConfiguration對應的值(在spring-boot-autoconfigure中)並經過反射實例化,過濾後包裝成AutoConfigurationEntry對象返回。
看到這裏你應該會以爲自動配置的實現就是經過這個selectImports方法,但實際上這個方法一般並不會被調用到,而是會調用該類的內部類AutoConfigurationGroup的process和selectImports方法,前者一樣是經過getAutoConfigurationEntry拿到全部的自動配置類,然後者這是過濾排序幷包裝後返回。
下面就來分析ConfigurationClassPostProcessor是怎麼調用到這裏的,直接進入processConfigBeanDefinitions方法:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton( AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); // 省略。。。。 }
前面一大段主要是拿到合格的Configuration配置類,主要邏輯是在ConfigurationClassParser.parse方法中,該方法完成了對@Component、@Bean、@Import、@ComponentScans等註解的解析,這裏主要看對@Import的解析,其它的讀者可自行分析。一步步追蹤,最終會進入到processConfigurationClass方法:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
這裏須要注意this.conditionEvaluator.shouldSkip方法的調用,這個方法就是進行Bean加載過濾的,即根據@Condition註解的匹配值判斷是否加載該Bean,具體實現稍後分析,繼續跟蹤主流程doProcessConfigurationClass:
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { 省略.... // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); 省略.... return null; }
這裏就是完成對一系列註解的支撐,我省略掉了,主要看processImports方法,這個方法就是處理@Import註解的:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { 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)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); if (selector instanceof DeferredImportSelector) { this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); processImports(configClass, currentSourceClass, importSourceClasses, false); } } else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class, this.environment, this.resourceLoader, this.registry); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } } }
剛剛我提醒過AutoConfigurationImportSelector是實現DeferredImportSelector接口的,若是不是該接口的實現類則是直接調用selectImports方法,反之則是調用DeferredImportSelectorHandler.handle方法:
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>(); public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) { DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder( configClass, importSelector); if (this.deferredImportSelectors == null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); handler.register(holder); handler.processGroupImports(); } else { this.deferredImportSelectors.add(holder); } }
首先建立了一個DeferredImportSelectorHolder對象,若是是第一次執行則是添加到deferredImportSelectors屬性中,等到ConfigurationClassParser.parse的最後調用process方法:
public void parse(Set<BeanDefinitionHolder> configCandidates) { 省略..... this.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<>(); } }
反之則是直接執行,首先經過register拿到AutoConfigurationGroup對象:
public void register(DeferredImportSelectorHolder deferredImport) { Class<? extends Group> group = deferredImport.getImportSelector() .getImportGroup(); DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent( (group != null ? group : deferredImport), key -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } public Class<? extends Group> getImportGroup() { return AutoConfigurationGroup.class; }
而後在processGroupImports方法中進行真正的處理:
public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { processImports(configurationClass, asSourceClass(configurationClass), asSourceClasses(entry.getImportClassName()), false); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configurationClass.getMetadata().getClassName() + "]", ex); } }); } } public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); }
在getImports方法中就完成了對process和selectImports方法的調用,拿到自動配置類後再遞歸調用調用processImports方法完成對自動配置類的加載。至此,自動配置的加載過程就分析完了,下面是時序圖:
在自動配置類中有不少Condition相關的註解,以AOP爲例:
Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Advice.class) static class AspectJAutoProxyingConfiguration { @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) static class JdkDynamicAutoProxyConfiguration { } @Configuration(proxyBeanMethods = false) @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class CglibAutoProxyConfiguration { } } @Configuration(proxyBeanMethods = false) @ConditionalOnMissingClass("org.aspectj.weaver.Advice") @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) static class ClassProxyingConfiguration { ClassProxyingConfiguration(BeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } } } }
這裏就能看到@ConditionalOnProperty、@ConditionalOnClass、@ConditionalOnMissingClass,另外還有@ConditionalOnBean、@ConditionalOnMissingBean等等不少條件匹配註解。這些註解表示條件匹配纔會加載該Bean,以@ConditionalOnProperty爲例,代表配置文件中符合條件纔會加載對應的Bean,prefix表示在配置文件中的前綴,name表示配置的名稱,havingValue表示配置爲該值時才匹配,matchIfMissing則是表示沒有該配置是否默認加載對應的Bean。其它註解可類比理解記憶,下面主要來分析該註解的實現原理。
這裏註解點進去看會發現每一個註解上都標註了@Conditional註解,而且value值都對應一個類,好比OnBeanCondition,而這些類都實現了Condition接口,看看其繼承體系:
上面只展現了幾個實現類,但實際上Condition的實現類是很是多的,咱們還能夠本身實現該接口來擴展@Condition註解。
Condition接口中有一個matches方法,這個方法返回true則表示匹配。該方法在ConfigurationClassParser中多處都有調用,也就是剛剛我提醒過的shouldSkip方法,具體實現是在ConditionEvaluator類中:
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) { if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) { return false; } if (phase == null) { if (metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) { return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION); } return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN); } List<Condition> conditions = new ArrayList<>(); for (String[] conditionClasses : getConditionClasses(metadata)) { for (String conditionClass : conditionClasses) { Condition condition = getCondition(conditionClass, this.context.getClassLoader()); conditions.add(condition); } } AnnotationAwareOrderComparator.sort(conditions); for (Condition condition : conditions) { ConfigurationPhase requiredPhase = null; if (condition instanceof ConfigurationCondition) { requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase(); } if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) { return true; } } return false; }
再來看看matches的實現,但OnBeanCondition類中沒有實現該方法,而是在其父類SpringBootCondition中:
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String classOrMethodName = getClassOrMethodName(metadata); try { ConditionOutcome outcome = getMatchOutcome(context, metadata); logOutcome(classOrMethodName, outcome); recordEvaluation(context, classOrMethodName, outcome); return outcome.isMatch(); }
getMatchOutcome方法也是一個模板方法,具體的匹配邏輯就在這個方法中實現,該方法返回的ConditionOutcome對象就包含了是否匹配和日誌消息兩個字段。進入到OnBeanCondition類中:
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { ConditionMessage matchMessage = ConditionMessage.empty(); MergedAnnotations annotations = metadata.getAnnotations(); if (annotations.isPresent(ConditionalOnBean.class)) { Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class); MatchResult matchResult = getMatchingBeans(context, spec); if (!matchResult.isAllMatched()) { String reason = createOnBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(spec.message().because(reason)); } matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches()); } if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) { Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations); MatchResult matchResult = getMatchingBeans(context, spec); if (!matchResult.isAllMatched()) { return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll()); } else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(), spec.getStrategy() == SearchStrategy.ALL)) { return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans") .items(Style.QUOTE, matchResult.getNamesOfAllMatches())); } matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE, matchResult.getNamesOfAllMatches()); } if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) { Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnMissingBean.class); MatchResult matchResult = getMatchingBeans(context, spec); if (matchResult.isAnyMatched()) { String reason = createOnMissingBeanNoMatchReason(matchResult); return ConditionOutcome.noMatch(spec.message().because(reason)); } matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll(); } return ConditionOutcome.match(matchMessage); }
能夠看到該類支持了@ConditionalOnBean、@ConditionalOnSingleCandidate、@ConditionalOnMissingBean註解,主要的匹配邏輯在getMatchingBeans方法中:
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) { ClassLoader classLoader = context.getClassLoader(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT; Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers(); if (spec.getStrategy() == SearchStrategy.ANCESTORS) { BeanFactory parent = beanFactory.getParentBeanFactory(); Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.ANCESTORS"); beanFactory = (ConfigurableListableBeanFactory) parent; } MatchResult result = new MatchResult(); Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy, spec.getIgnoredTypes(), parameterizedContainers); for (String type : spec.getTypes()) { Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type, parameterizedContainers); typeMatches.removeAll(beansIgnoredByType); if (typeMatches.isEmpty()) { result.recordUnmatchedType(type); } else { result.recordMatchedType(type, typeMatches); } } for (String annotation : spec.getAnnotations()) { Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation, considerHierarchy); annotationMatches.removeAll(beansIgnoredByType); if (annotationMatches.isEmpty()) { result.recordUnmatchedAnnotation(annotation); } else { result.recordMatchedAnnotation(annotation, annotationMatches); } } for (String beanName : spec.getNames()) { if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) { result.recordMatchedName(beanName); } else { result.recordUnmatchedName(beanName); } } return result; }
這裏邏輯看起來比較複雜,但實際上就作了兩件事,首先經過getNamesOfBeansIgnoredByType方法調用beanFactory.getBeanNamesForType拿到容器中對應的Bean實例,而後根據返回的結果判斷哪些Bean存在,哪些Bean不存在(Condition註解中是能夠配置多個值的)並返回MatchResult對象,而MatchResult中只要有一個Bean沒有匹配上就返回false,也就決定了當前Bean是否須要實例化。
本篇分析了SpringBoot核心原理的實現,經過本篇相信讀者也將能更加熟練地使用和擴展SpringBoot。另外還有一些經常使用的組件我沒有展開分析,如事務、MVC、監聽器的自動配置,這些咱們有了Spring源碼基礎的話下來看一下就明白了,這裏就不贅述了。最後讀者能夠思考一下咱們應該如何自定義starter啓動器,相信看完本篇應該難不倒你。