SpringBoot是Spring主推的基于"习惯优于配置"的原则,快速搭建应用的框架,它实现了jar in jar的加载方式。java
spring boot应用打包以后,生成一个fat jar,里面包含了应用依赖的jar包,还有Spring boot loader相关的类react
其中META-INF/MANIFEST.MF文件下的两个Class:
Main-Class是org.springframework.boot.loader.JarLauncher ,这个是jar启动的Main函数。
Start-Class是应用本身的Main函数。git
程序启动时JarLauncher先找到本身所在的jar,而后建立了一个Archive(在spring boot里,一个archive能够是一个jar:JarFileArchive,也能够是一个文件目录:ExplodedArchive)。再获取到<工程>.jar/lib下面的全部jar文件。<工程>.jar既fat jar。
获取到全部Archive的URL以后,会构造一个自定义的ClassLoader:LaunchedURLClassLoader。
它从MANIFEST.MF里读取到Start-Class,建立一个新的线程来启动应用的Main函数。github
工程以fat jar运行时,应用的main函数的ClassLoader是LaunchedURLClassLoader,它的parent是SystemClassLoader。
而且LaunchedURLClassLoader的urls是 fat jar里的BOOT-INF/classes!/目录和BOOT-INF/lib里的全部jar。
SystemClassLoader的urls是fat jar自己。web
工程在IDE里启动,由于依赖的Jar都让IDE放到classpath里了,因此Spring boot直接启动了。
Spring的ClassLoader直接是SystemClassLoader。ClassLoader的urls包含所有的jar和本身的target/classesspring
工程在一个开放目录下启动Spring boot启动。所谓的开放目录就是把fat jar解压,而后直接启动应用。
Spring boot会判断当前是否在一个目录里,若是是的,则构造一个ExplodedArchive(前面在jar里时是JarFileArchive),后面的启动流程相似fat jar的。apache
执行应用的main函数的ClassLoader是LaunchedURLClassLoader,它的parent是SystemClassLoader。
LaunchedURLClassLoader的urls是解压目录里的BOOT-INF/classes/和/BOOT-INF/lib/下面的jar包。
SystemClassLoader的urls只有当前目录
目录形式会有更好的兼容性。编程
Start-Class的代码里基本上就一句话
SpringApplication.run(Application.class,args);tomcat
这个main方法中,调用了SpringApplication的静态run方法,并将Application类对象和main方法的参数args做为参数传递了进去。app
核心代码以下:
/** * Run the Spring application, creating and refreshing a new * {@link ApplicationContext}. * @param args the application arguments (usually passed from a Java main method) * @return a running {@link ApplicationContext} */ 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; }
一、SpringApplicationRunListeners (初始化监听器)
二、配置参数和环境
三、打印Banner
四、建立ApplicationContext(),在里面会判断webApplicationType,而后具体Class.forName哪一个ApplicationContext(AnnotationConfigApplicationContext、 AnnotationConfigServletWebServerApplicationContext、 AnnotationConfigReactiveWebServerApplicationContext)
五、使用扩展机制加载其余configure
六、准备context
七、刷新context
。。。。。。
Spring Boot里用于解耦的扩展机制:Spring Factories。这种扩展机制其实是仿照Java中的SPI扩展机制来实现的。
其主要功能就是从指定的配置文件(SpringBoot的autoconfigure依赖包)中的META-INF/spring.factories加载配置。
在Spring中也有一种相似与Java SPI的加载机制。它从spring.factories文件中配置接口的实现类名称,而后在程序中读取这些配置文件并实例化。
这种自定义的SPI机制是Spring Boot Starter实现的基础。
spring-core包里定义了SpringFactoriesLoader类,这个类实现了检索META-INF/spring.factories文件,并获取指定接口的配置的功能。spring.factories的是经过Properties解析获得的,因此咱们在写文件中的内容都是安装下面这种方式配置的:com.xxx.interface=com.xxx.classname
若是一个接口但愿配置多个实现类,可使用','进行分割。
Factories机制可让SDK或者Starter的使用只须要不多或者不须要进行配置,只须要在服务中引入咱们的jar包。
XXXAutoConfiguration上会根据@Conditional标注来判断类会不会实例化
spring boot embeded container AutoConfiguration 代码以下:
package org.springframework.boot.autoconfigure.web.embedded; import io.undertow.Undertow; import org.apache.catalina.startup.Tomcat; import org.apache.coyote.UpgradeProtocol; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.webapp.WebAppContext; import org.xnio.SslClientAuthMode; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; /** * {@link EnableAutoConfiguration Auto-configuration} for embedded servlet and reactive * web servers customizations. * * @author Phillip Webb * @since 2.0.0 */ @Configuration @EnableConfigurationProperties(ServerProperties.class) public class EmbeddedWebServerFactoryCustomizerAutoConfiguration { @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) public static class TomcatWebServerFactoryCustomizerConfiguration { @Bean public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new TomcatWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Jetty is being used. */ @Configuration @ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class }) public static class JettyWebServerFactoryCustomizerConfiguration { @Bean public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new JettyWebServerFactoryCustomizer(environment, serverProperties); } } /** * Nested configuration if Undertow is being used. */ @Configuration @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class }) public static class UndertowWebServerFactoryCustomizerConfiguration { @Bean public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer( Environment environment, ServerProperties serverProperties) { return new UndertowWebServerFactoryCustomizer(environment, serverProperties); } } }
@conditionalonclass:某个class位于类路径上,才会实例化这个Bean ,根据上面的configuration,jar包里若是能找到 Tomcat.class, UpgradeProtocol.class
就会实例化 TomcatWebServerFactoryCustomizerConfiguration
Java SPI:
咱们系统里抽象的各个模块,每每有不少不一样的实现方案,好比日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,咱们通常推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。
一旦代码里涉及具体的实现类,就违反了可拔插的原则,若是须要替换一种实现,就须要修改代码。为了实如今模块装配的时候能不在程序里动态指明,这就须要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点相似IOC的思想,就是将装配的控制权移到程序以外,在模块化设计中这个机制尤为重要。
Java SPI约定:
当服务的提供者,提供了服务接口的一种实现以后,在jar包的META-INF/services/目录里同时建立一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能经过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。 基于这样一个约定就能很好的找到服务接口的实现类,而不须要再代码里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader。