zoukankan      html  css  js  c++  java
  • SpringBoot入门之spring-boot-maven-plugin

    Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Maven操作的可能。
    Spring Boot Maven plugin能够将Spring Boot应用打包为可执行的jar或war文件,然后以通常的方式运行Spring Boot应用。
    Spring Boot Maven plugin的最新版本为2017.6.8发布的1.5.4.RELEASE,要求Java 8, Maven 3.2及以后。

    Spring Boot Maven plugin的5个Goals

    • spring-boot:repackage,默认goal。在mvn package之后,再次打包可执行的jar/war,同时保留mvn package生成的jar/war为.origin
    • spring-boot:run,运行Spring Boot应用
    • spring-boot:start,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
    • spring-boot:stop,在mvn integration-test阶段,进行Spring Boot应用生命周期的管理
    • spring-boot:build-info,生成Actuator使用的构建信息文件build-info.properties

    配置pom.xml文件

    <build>
          <plugins>
              <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.0.1.RELEASE</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
              </plugin>
          </plugins>
    </build>

    mvn package spring-boot:repackage说明

    Spring Boot Maven plugin的最主要goal就是repackage,其在Maven的package生命周期阶段,能够将mvn package生成的软件包,再次打包为可执行的软件包,并将mvn package生成的软件包重命名为*.original。

    基于上述配置,对一个生成Jar软件包的项目执行如下命令

    可以看到生成的两个jar文件,一个是*.jar,另一个是*.jar.original。在执行上述命令的过程中,Maven首先在package阶段打包生成*.jar文件;然后执行spring-boot:repackage重新打包,查找Manifest文件中配置的Main-Class属性,如下所示:

    Manifest-Version: 1.0  
    Implementation-Title: gs-consuming-rest  
    Implementation-Version: 0.1.0  
    Archiver-Version: Plexus Archiver  
    Built-By: exihaxi  
    Implementation-Vendor-Id: org.springframework  
    Spring-Boot-Version: 1.5.3.RELEASE  
    Implementation-Vendor: Pivotal Software, Inc.  
    Main-Class: org.springframework.boot.loader.JarLauncher  
    Start-Class: com.ericsson.ramltest.MyApplication  
    Spring-Boot-Classes: BOOT-INF/classes/  
    Spring-Boot-Lib: BOOT-INF/lib/  
    Created-By: Apache Maven 3.5.0  
    Build-Jdk: 1.8.0_131

    注意,其中的Main-Class属性值为org.springframework.boot.loader.JarLauncher;

    Start-Class属性值为com.ericsson.ramltest.MyApplication。

    其中com.ericsson.ramltest.MyApplication类中定义了main()方法,是程序的入口。

    通常,Spring Boot Maven plugin会在打包过程中自动为Manifest文件设置Main-Class属性,事实上该属性究竟作用几何,还可以受Spring Boot Maven plugin的配置属性layout控制的,示例如下

        <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>1.5.4.RELEASE</version>
        <configuration>
        <mainClass>${start-class}</mainClass>
        <layout>ZIP</layout>
        </configuration>
        <executions>
        <execution>
        <goals>
        <goal>repackage</goal>
        </goals>
        </execution>
        </executions>
        </plugin>

    layout属性的值可以如下:

    • JAR,即通常的可执行jar

    Main-Class: org.springframework.boot.loader.JarLauncher

    • WAR,即通常的可执行war,需要的servlet容器依赖位于WEB-INF/lib-provided

    Main-Class: org.springframework.boot.loader.warLauncher

    • ZIP,即DIR,类似于JAR

    Main-Class: org.springframework.boot.loader.PropertiesLauncher

    • MODULE,将所有的依赖库打包(scope为provided的除外),但是不打包Spring Boot的任何Launcher
    • NONE,将所有的依赖库打包,但是不打包Spring Boot的任何Launcher

    RepackageMojo的关键方法execute:

        @Override
        public void execute() throws MojoExecutionException, MojoFailureException {
            if (this.project.getPackaging().equals("pom")) {
                getLog().debug("repackage goal could not be applied to pom project.");
                return;
            }
            if (this.skip) {
                getLog().debug("skipping repackaging as per configuration.");
                return;
            }
            //得到项目中的原始的jar,就是使用maven-jar-plugin生成的jar
            File source = this.project.getArtifact().getFile();
            //要写入的目标文件,就是fat jar
            File target = getTargetFile();
            Repackager repackager = new Repackager(source) {
                //从source中寻找spring boot 应用程序入口的main方法。
                @Override
                protected String findMainMethod(JarFile source) throws IOException {
                    long startTime = System.currentTimeMillis();
                    try {
                        return super.findMainMethod(source);
                    }
                    finally {
                        long duration = System.currentTimeMillis() - startTime;
                        if (duration > FIND_WARNING_TIMEOUT) {
                            getLog().warn("Searching for the main-class is taking some time, "
                                    + "consider using the mainClass configuration "
                                    + "parameter");
                        }
                    }
                }
            };
            //如果插件中指定了mainClass就直接使用
            repackager.setMainClass(this.mainClass);
            if (this.layout != null) {
                getLog().info("Layout: " + this.layout);
                repackager.setLayout(this.layout.layout());
            }
            //寻找项目运行时依赖的jar,过滤后
            Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(),
                    getFilters(getAdditionalFilters()));
            //将Artifact转化成Libraries 
            Libraries libraries = new ArtifactsLibraries(artifacts, this.requiresUnpack,
                    getLog());
            try {
                LaunchScript launchScript = getLaunchScript();
                //进行repackage
                repackager.repackage(target, libraries, launchScript);
            }
            catch (IOException ex) {
                throw new MojoExecutionException(ex.getMessage(), ex);
            }
            if (this.classifier != null) {
                getLog().info("Attaching archive: " + target + ", with classifier: "
                        + this.classifier);
                this.projectHelper.attachArtifact(this.project, this.project.getPackaging(),
                        this.classifier, target);
            }
            else if (!source.equals(target)) {
                this.project.getArtifact().setFile(target);
                getLog().info("Replacing main artifact " + source + " to " + target);
            }

    基本上重要的步骤都有注释,应该不难理解的。再来看下面,当然也不是重点,看看就行

    public void repackage(File destination, Libraries libraries,
                LaunchScript launchScript) throws IOException {
            if (destination == null || destination.isDirectory()) {
                throw new IllegalArgumentException("Invalid destination");
            }
            if (libraries == null) {
                throw new IllegalArgumentException("Libraries must not be null");
            }
            if (alreadyRepackaged()) {
                return;
            }
            destination = destination.getAbsoluteFile();
            File workingSource = this.source;
            //如果源jar与目标jar的文件路径及名称是一致的
            if (this.source.equals(destination)) {
                //将源jar重新命名为原名称+.original,同时删除原来的源jar
                workingSource = new File(this.source.getParentFile(),
                        this.source.getName() + ".original");
                workingSource.delete();
                renameFile(this.source, workingSource);
            }
            destination.delete();
            try {
                //将源jar变成JarFile 
                JarFile jarFileSource = new JarFile(workingSource);
                try {
                    repackage(jarFileSource, destination, libraries, launchScript);
                }
                finally {
                    jarFileSource.close();
                }
            }
            finally {
                if (!this.backupSource && !this.source.equals(workingSource)) {
                    deleteFile(workingSource);
                }
            }
        }

    这一步所做的是清理工作,如果源jar同目标文件路径名称等一致,将源jar重命名,原来的文件删除。为目标文件腾位置。下面的重点来了。

        private void repackage(JarFile sourceJar, File destination, Libraries libraries,
                LaunchScript launchScript) throws IOException {
            JarWriter writer = new JarWriter(destination, launchScript);
            try {
                final List<Library> unpackLibraries = new ArrayList<Library>();
                final List<Library> standardLibraries = new ArrayList<Library>();
                libraries.doWithLibraries(new LibraryCallback() {
                    @Override
                    public void library(Library library) throws IOException {
                        File file = library.getFile();
                        if (isZip(file)) {
                            if (library.isUnpackRequired()) {
                                unpackLibraries.add(library);
                            }
                            else {
                                standardLibraries.add(library);
                            }
                        }
                    }
                });
                //按照规则写入manifest文件
                writer.writeManifest(buildManifest(sourceJar));
                Set<String> seen = new HashSet<String>();
                writeNestedLibraries(unpackLibraries, seen, writer);
               //写入源jar中的内容
                writer.writeEntries(sourceJar);
               //写入标准的jar,依赖的jar
                writeNestedLibraries(standardLibraries, seen, writer);
                if (this.layout.isExecutable()) {
                   //写入spring boot loader的类
                    writer.writeLoaderClasses();
                }
            }
            finally {
                try {
                    writer.close();
                }
                catch (Exception ex) {
                    // Ignore
                }
            }

     

  • 相关阅读:
    CF995A Tesla
    CF961D Pair Of Lines
    P1186 玛丽卡
    CF986B Petr and Permutations
    hdu6331 Problem M. Walking Plan
    Edison UVALive3488
    Be a Smart Raftsman SGU475
    100198H Royal Federation
    100197G Robbers
    Evil Book -- CodeChef
  • 原文地址:https://www.cnblogs.com/zouhong/p/12189319.html
Copyright © 2011-2022 走看看