Spring Boot被设计用来简化spring应用的搭建和开发,其使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。这里主要介绍下springBoot的启动和自动化配置,以及如何集成Tomcat服务的。
示例 如果想要搭建一个类似spring mvc的web应用,可以如下:
pom.xml 1 2 3 4 5 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > <version > 2.1.1.RELEASE</version > </dependency >
Boot.java 1 2 3 4 5 6 @SpringBootApplication public class Boot { public static void main (String[] args) { SpringApplication.run(Boot.class , args ) ; } }
TestController.java 1 2 3 4 5 6 7 8 9 10 @Controller @RequestMapping ("/test" )public class TestController { @RequestMapping ("/hello" ) @ResponseBody public String hello () { return "hello spring boot" ; } }
starter 上面的示例非常简洁,不过可以注意到它依赖很简单,就一个spring-boot-starter-web
。starter模式的提出,使得模块的开发更加独立化,相互之间以更加松散的方式进行集成。
启动 SpringApplication 对于spring应用的启动,无非是创建spring应用上下文,然后进行容器加载,再refresh,如下代码所示,如果从前面的介绍一步步看过来,可以体会到一个层次感,BeanFactory
负责Bean
的创建,ApplicationContext
负责BeanFactory
的创建,而springMvc或者springBoot则负责了ApplicationContext
的创建
org.springframework.boot.SpringApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public ConfigurableApplicationContext run (String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null ; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments,printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this .logStartupInfo) { new StartupInfoLogger(this .mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); 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; }
这里创建的应用上下文都是基于AnnotationConfigApplicationContext
的扩展,其在之前[spring. 应用上下文 ]中已经有过详细介绍
org.springframework.boot.SpringApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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); }
创建了基于注解的上下文之后,便是对其进行一些设置以及各种事件回调,另外加载spring的各种资源配置文件,最近进行refresh
org.springframework.boot.SpringApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 private void prepareContext (ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this .logStartupInfo) { logStartupInfo(context.getParent() == null ); logStartupProfileInfo(context); } ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments" , applicationArguments); if (printedBanner != null ) { beanFactory.registerSingleton("springBootBanner" , printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory)beanFactory) .setAllowBeanDefinitionOverriding(this .allowBeanDefinitionOverriding); } Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty" ); load(context, sources.toArray(new Object[0 ])); listeners.contextLoaded(context); }
这里对于容器的加载统一交给了BeanDefinitionLoader
org.springframework.boot.SpringApplication 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected void load (ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this .beanNameGenerator != null ) { loader.setBeanNameGenerator(this .beanNameGenerator); } if (this .resourceLoader != null ) { loader.setResourceLoader(this .resourceLoader); } if (this .environment != null ) { loader.setEnvironment(this .environment); } loader.load(); } protected BeanDefinitionLoader createBeanDefinitionLoader (BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }
BeanDefinitionLoader
则将加载又委托给了AnnotatedBeanDefinitionReader
和XmlBeanDefinitionReader
,具体分别参考前面的笔记:[spring. BeanDefinition解析(XmlBeanDefinitionReader) ]以及[spring. 应用上下文(ApplicationContext) ],这里不再赘述org.springframework.boot.BeanDefinitionLoader 1 2 3 4 5 6 7 8 9 10 11 12 BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null" ); Assert.notEmpty(sources, "Sources must not be empty" ); this .sources = sources; this .annotatedReader = new AnnotatedBeanDefinitionReader(registry); this .xmlReader = new XmlBeanDefinitionReader(registry); if (isGroovyPresent()) { this .groovyReader = new GroovyBeanDefinitionReader(registry); } this .scanner = new ClassPathBeanDefinitionScanner(registry); this .scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
自动化配置 @SpringBootApplication 上面的分析基本描述了springBoot启动时对于容器的创建和加载,但是对于各个starter模块是如何加载的呢,这里就从注解@SpringBootApplication入手
org.springframework.boot.autoconfigure.SpringBootApplication 1 2 3 4 5 6 7 8 9 10 @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
由于注解的传递性,再跟到注解@EnableAutoConfiguration,而其又使用@Import进行了声明,关于@Import在之前[spring. 应用上下文(ApplicationContext) ]已经做过详细介绍,这里看下AutoConfigurationImportSelector
中具体的处理
org.springframework.boot.autoconfigure.EnableAutoConfiguration 1 2 3 4 5 6 7 @Target (ElementType.TYPE)@Retention (RetentionPolicy.RUNTIME)@Documented @Inherited @AutoConfigurationPackage @Import (AutoConfigurationImportSelector.class ) public @interface EnableAutoConfiguration
AutoConfigurationImportSelector 关于ImportSelector
的调用流程,之前已经介绍过,这里从selectImports
作为入口
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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); 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; }
最终对于starter的加载交给SpringFactoriesLoader
,其通过硬编码的方式加载所有文件:META-INF/spring.factories,剩下的就与之前一样交给ConfigurationClassPostProcessor
处理了
org.springframework.core.io.support.SpringFactoriesLoader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static List<String> loadFactoryNames (Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } 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 factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; }catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]" , ex); } }
Tomcat启动 结合之前spring应用上下文的介绍,其在模板方法refresh
中提供了onRefresh()
供子类扩展,而springBoot正是在onRefresh()
进行了web容器的初始化。
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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(); }
然后在finishRefresh()
中进行启动
org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected void finishRefresh () { super .finishRefresh(); WebServer webServer = startWebServer(); if (webServer != null ) { publishEvent(new ServletWebServerInitializedEvent(webServer, this )); } } private WebServer startWebServer () { WebServer webServer = this .webServer; if (webServer != null ) { webServer.start(); } return webServer; }
参考:
《spring源码深度解析》 郝佳