zoukankan      html  css  js  c++  java
  • SpringBoot项目的spring-boot-maven-plugin插件打包原理

    前言

    在使用SpringBoot的项目中,我们需要配置spring-boot-maven-plugin插件

    <plugin>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-maven-plugin</artifactId>             
    </plugin>
    

    这是SpringBoot自己提供的插件,它可以在maven的生命周期package后重新打包,生成自己的jar包结构。插件提供的功能如下

    核心为repackage,它会在package之后执行,生成一个新的jar包,将之前的jar包命名为 xxx.original。

    打包原理

    生成的xxx.original的jar包结构为

    不包含项目依赖的第三方jar包,MANIFEST.MF文件内容为

    Manifest-Version: 1.0
    Created-By: Maven Archiver 3.4.0
    Build-Jdk-Spec: 11
    Implementation-Title: springbootfirst
    Implementation-Version: 0.0.1-SNAPSHOT
    

    SpringBoot插件生成的jar包结构为

    BOOT-INF/classes 中包含项目所有的class文件,BOOT-INF/lib 下包含项目依赖的第三方jar包,MANIFEST.MF文件内容为

    Manifest-Version: 1.0
    Created-By: Maven Archiver 3.4.0
    Build-Jdk-Spec: 11
    Implementation-Title: springbootfirst
    Implementation-Version: 0.0.1-SNAPSHOT
    Main-Class: org.springframework.boot.loader.JarLauncher
    Start-Class: com.imooc.springbootfirst.SpringbootfirstApplication
    Spring-Boot-Version: 2.1.6.RELEASE
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    

    插件是如何找到启动类的

    描述文件中的Start-Class就是我们项目的启动类,我们可以查看插件源码来分析

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>2.2.1.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-loader</artifactId>
      <version>2.2.1.RELEASE</version>
    </dependency>
    

    先引入插件的maven依赖

    RepackageMojo就是repackage打包要执行的逻辑,核心类为Repackager

    在插件创建MANIFEST.MF文件过程中,如果我们没有配置MainClass属性,就会通过MainClassFinder类来查找MainClass

    private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
    
    protected String findMainMethod(JarFile source) throws IOException {
    		return MainClassFinder.findSingleMainClass(source, this.layout.getClassesLocation(),
    				SPRING_BOOT_APPLICATION_CLASS_NAME);
    	}
    

    在所有类中查找包含SpringBootApplication注解且包含main方法的类,并当做启动类,内部通过ASM字节码库来解析class文件得到类信息。

    启动流程

    java -jar springbootfirst-0.0.1-SNAPSHOT.jar
    

    java启动jar包会找META-INF/MANIFEST.MF文件中的Main-Class来启动,SpringBoot插件最终生成的Main-Class为 org.springframework.boot.loader.JarLauncher类。

    /**
     * jar包类型的启动器
     *
     * @author Phillip Webb
     * @author Andy Wilkinson
     * @since 1.0.0
     */
    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);
    	}
    
    }
    

    最终运行的是MANIFEST.MF文件中Start-Class,值为com.imooc.springbootfirst.SpringbootfirstApplication,其实就是我们项目中配置的启动类。

    /**
     * 启动器
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @since 1.0.0
     */
    public abstract class Launcher {
    
    	/**
    	 * 启动流程
    	 */
    	protected void launch(String[] args) throws Exception {
    		JarFile.registerUrlProtocolHandler();
    		//根据BOOT-INF/classes下的class文件和BOOT-INF/lib下的第三方jar包创建Archive
    		//创建的ClassLoader为LaunchedURLClassLoader类型
    		ClassLoader classLoader = createClassLoader(getClassPathArchives());
    		launch(args, getMainClass(), classLoader);
    	}
    
    	/**
    	 * 创建新的LaunchedURLClassLoader,从多个URL中加载class
    	 */
    	protected ClassLoader createClassLoader(URL[] urls) throws Exception {
    		return new LaunchedURLClassLoader(urls, getClass().getClassLoader());
    	}
    
    	/**
    	 * 将新的类加载器设置到线程上下文中,并启动应用程序
    	 */
    	protected void launch(String[] args, String mainClass, ClassLoader classLoader) throws Exception {
    		Thread.currentThread().setContextClassLoader(classLoader);
    		createMainMethodRunner(mainClass, args, classLoader).run();
    	}
    
    	/**
    	 * 创建一个Main方法运行器来启动应用程序
    	 */
    	protected MainMethodRunner createMainMethodRunner(String mainClass, String[] args, ClassLoader classLoader) {
    		return new MainMethodRunner(mainClass, args);
    	}
    
    }
    

    创建新的ClassLoader类型LaunchedURLClassLoader,从BOOT-INF/classes下和BOOT-INF/lib下的所有jar包中加载class。加载我们整个项目的都是LaunchedURLClassLoader类加载器。

    /**
     * 一个执行Main方法的工具类
     *
     * @author Phillip Webb
     * @author Andy Wilkinson
     * @since 1.0.0
     */
    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 {
    		//使用线程上下文类加载器加载MainClass,就是我们项目中的SpringbootfirstApplication
    		Class<?> mainClass = Thread.currentThread().getContextClassLoader().loadClass(this.mainClassName);
    		//执行Main方法
    		Method mainMethod = mainClass.getDeclaredMethod("main", String[].class);
    		mainMethod.invoke(null, new Object[] { this.args });
    	}
    }
    

    参考

    springboot 打包插件spring-boot-maven-plugin打包机制及内部结构分析

  • 相关阅读:
    Linux 杂记
    Hadoop多硬盘配置时的注意事项
    spark-env.sh 配置示例
    Nagios监控ganglia的指标
    ELK日志解决方案安装配置与使用
    zookeeper 相关学习资料
    Hive分析hadoop进程日志
    hadoop2.3cdh5.0.2 upgrade to hadoop2.5cdh5.5.0
    Nagios check_logfiles插件的使用记录
    Hadoop Kernel tunning
  • 原文地址:https://www.cnblogs.com/strongmore/p/15485109.html
Copyright © 2011-2022 走看看