前言
上篇文章Springboot/Python之helloworld,只要简单的一个命令
java -jar target/demo-0.0.1.jar
应用就启动起来了,很惊奇,下面我们来分析下具体的原因。
JarLauncher
- BOOT-INF
- META-INF
- org
Manifest-Version: 1.0
Implementation-Title: spring-restdocs
Implementation-Version: 0.0.1
Archiver-Version: Plexus Archiver
Built-By: Peihai_Chen
Implementation-Vendor-Id: com.chen
Spring-Boot-Version: 1.5.10.RELEASE
Implementation-Vendor: Pivotal Software, Inc.
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.chen.demo.SpringRestdocsApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Created-By: Apache Maven 3.5.0
Build-Jdk: 1.8.0_40
Implementation-URL: http://projects.spring.io/spring-boot/demo/
Main-Class中指定了执行的入口是JarLauncher,那么来看看JarLauncher的主要实现逻辑。
public class JarLauncher extends ExecutableArchiveLauncher {
static final String BOOT_INF_CLASSES = "BOOT-INF/classes/";
static final String BOOT_INF_LIB = "BOOT-INF/lib/";
public JarLauncher() {
}
protected JarLauncher(Archive archive) {
super(archive);
}
@Override
protected boolean isNestedArchive(Archive.Entry entry) {
if (entry.isDirectory()) {
return entry.getName().equals(BOOT_INF_CLASSES);
}
return entry.getName().startsWith(BOOT_INF_LIB);
}
public static void main(String[] args) throws Exception {
new JarLauncher().launch(args);
}
}
JarLauncher有个main方法,继承了ExecutableArchiveLauncher,同时定义了Spring-Boot-Classes和BOOT-INF/lib/路径。
ExecutableArchiveLauncher
ExecutableArchiveLauncher是JarLauncher的父类,最主要的方法是:getMainClass,如下
@Override
protected String getMainClass() throws Exception {
Manifest manifest = this.archive.getManifest();
String mainClass = null;
if (manifest != null) {
mainClass = manifest.getMainAttributes().getValue("Start-Class");
}
if (mainClass == null) {
throw new IllegalStateException(
"No 'Start-Class' manifest entry specified in " + this);
}
return mainClass;
}
可以看到,返回的mainClass是MANIFEST.MF中Start-Class
Start-Class: com.chen.demo.SpringRestdocsApplication
Launcher
protected void launch(String[] args) throws Exception {
JarFile.registerUrlProtocolHandler();
ClassLoader classLoader = createClassLoader(getClassPathArchives());
launch(args, getMainClass(), classLoader);
}
protected void launch(String[] args, String mainClass, ClassLoader classLoader)
throws Exception {
Thread.currentThread().setContextClassLoader(classLoader);
createMainMethodRunner(mainClass, args, classLoader).run();
}
protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args,
ClassLoader classLoader) {
return new MainMethodRunner(mainClass, args);
}
这几个方法主要加载demo-0.0.1\BOOT-INF\lib下的jar包,通过LaunchedURLClassLoader创建ClassLoader,调用ExecutableArchiveLauncher中的getMainClass()方法,创建并启动MainMethodRunner的run方法,run方法通过反射的方式启动SpringRestdocsApplication中的main方法。
MainMethodRunner
package org.springframework.boot.loader;
import java.lang.reflect.Method;
/**
* Utility class that is used by {@link Launcher}s to call a main method. The class
* containing the main method is loaded using the thread context class loader.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class MainMethodRunner {
private final String mainClassName;
private final String[] args;
/**
* Create a new {@link MainMethodRunner} instance.
* @param mainClass the main class
* @param args incoming arguments
*/
public MainMethodRunner(String mainClass, String[] args) {
this.mainClassName = mainClass;
this.args = (args != null ? args.clone() : null);
}
public void run() throws Exception {
Class<?> mainClass = Thread.currentThread().getContextClassLoader()
.loadClass(this.mainClassName);
Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
mainMethod.invoke(null, new Object[] { this.args });
}
}
run()方法中的mainclass就是MANIFEST.MF中Start-Class的com.chen.demo.SpringRestdocsApplication,通过反射的方式执行main方法。
Tomcat启动分析
SpringRestdocsApplication中的main方法如下:
package com.chen.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringRestdocsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringRestdocsApplication.class, args);
}
}
SpringApplication.run()方法中,会生成一个SpringApplication对象,并继续执行run方法,run方法如下:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
在context = createApplicationContext();这行代码中,创建了AnnotationConfigEmbeddedWebApplicationContext。
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
AnnotationConfigEmbeddedWebApplicationContext继承了EmbeddedWebApplicationContext,EmbeddedWebApplicationContext中的EmbeddedServletContainer有个createEmbeddedServletContainer方法,创建了Tomcat Container。
EmbeddedServletContainer
EmbeddedWebApplicationContext的createEmbeddedServletContainer方法:如下
private void createEmbeddedServletContainer() {
EmbeddedServletContainer localContainer = this.embeddedServletContainer;
ServletContext localServletContext = getServletContext();
if (localContainer == null && localServletContext == null) {
EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
this.embeddedServletContainer = containerFactory
.getEmbeddedServletContainer(getSelfInitializer());
}
else if (localServletContext != null) {
try {
getSelfInitializer().onStartup(localServletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
通过TomcatEmbeddedServletContainerFactory工厂创建一个EmbeddedServletContainer,
@Override
public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null ? this.baseDirectory
: createTempDir("tomcat"));
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
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 getTomcatEmbeddedServletContainer(tomcat);
}
该方法创建了一个Tomcat对象,设置属性并启动tomcat。
结束语
至此,kubernetes构建微服务中关于Springboot方便的介绍告一段落,下面的博客继续更关于kubernetes的内容。