spring boot 启动记录

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。

相关文章
相关标签/搜索