kubernetes构建微服务-Springboot启动原理分析篇

本文深入探讨了SpringBoot应用的启动原理,从JarLauncher、ExecutableArchiveLauncher到MainMethodRunner,揭示了如何通过MANIFEST.MF中的Start-Class启动应用。接着分析了SpringApplication.run()方法在创建ApplicationContext并启动内嵌的Tomcat Container过程中的作用,展现了SpringBoot在Kubernetes微服务构建中的便利性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

上篇文章Springboot/Python之helloworld,只要简单的一个命令

java -jar target/demo-0.0.1.jar  

应用就启动起来了,很惊奇,下面我们来分析下具体的原因。

JarLauncher

把demo-0.0.1.jar解压后,有三个目录:
  • BOOT-INF
  • META-INF
  • org
在META-INFO目录下,有个MANIFEST.MF文件,内容如下:

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

Launcher是ExecutableArchiveLauncher的父类,JarLauncher类中执行main方法的时候,就会调用父类的launcher方法,抽取主要的几个方法:launch(String[] args) 、launch(String[] args, String mainClass, ClassLoader classLoader)、createMainMethodRunner(String mainClass, String[] args,ClassLoader classLoader)
	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

EmbeddedServletContainer是一个接口,实现类有:Jetty、Tomcat、Undertow,默认的tomcat的实现方式。

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的内容。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值