目录
- 一、maven-compiler-plugin 插件详解
- 二、org.apache.maven.plugins系列插件简介
- 三、springboot 打包插件spring-boot-maven-plugin打包机制及内部结构分析
- 四、springboot增量打包更新–静态资源分离打包
一、maven-compiler-plugin 插件详解
1.maven插件介绍
maven
是个项目管理工具,如果我们不告诉它代码要使用什么样的jdk
版本编译的话,它就会用maven-compiler-plugin
默认的jdk
版本来进行编译处理,这样就容易出现编译版本与源代码不匹配,以至于可能导致编译不通过的问题。例如代码中要是使用上了Java 8的新特性,比如函数式编程,但是maven在编译的时候使用的是Java 7,那这一段代码是完全不可能编译成.class文件的,但如果相反maven编译时使用的是java11则可以编译通过,java高版本的jdk可以运行低版本的java语法的,也既是向下兼容的。为了避免出现mvne使用低版本jdk编译高版本java语法这种情况,在构建maven项目的时候,推荐第一步就是配置maven-compiler-plugin插件,指定项目源码的 jdk 版本,编译后的 jdk 版本,以及编码方式。从 maven-compiler-plugin 3.8.0 之后,默认JDK 版本就由 1.5 改为 1.6 了。但是这仍然跟不上 JDK 的更新速度,目前大多数系统都在使用 JDK 1.8。Apache Maven Project 对 maven-compiler-plugin中compiler:compile有如下关于jdk版本变更的描述:
<plugin>
<!-- 指定maven编译的jdk版本,如果不指定,maven3默认用jdk 1.5 maven2默认用jdk1.3 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<!-- 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行
(比如:源代码使用的是1.5的语法,但是想在1.8的jdk环境运行,由低刀高是允许的)
(对于低版本目标jdk,源代码中不能使用低版本jdk中不支持的语法),会存在target不同于source的情况 -->
<!-- 源代码使用的JDK版本 -->
<source>1.8</source>
<!-- 需要生成的目标class文件的编译版本 -->
<target>1.8</target>
<!-- 字符集编码 -->
<encoding>UTF-8</encoding>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
<verbose>true</verbose>
<showWarnings>true</showWarnings>
<!-- 要使compilerVersion标签生效,还需要将fork设为true,用于明确表示编译版本配置的可用 -->
<fork>true</fork>
<!-- 使用指定的javac命令,例如:<executable>${JAVA_1_4_HOME}/bin/javac</executable> -->
<executable><!-- path-to-javac --></executable>
<!-- 指定插件将使用的编译器的版本 -->
<compilerVersion>1.3</compilerVersion>
<!-- 编译器使用的初始内存 -->
<meminitial>128m</meminitial>
<!-- 编译器使用的最大内存 -->
<maxmem>512m</maxmem>
<!-- 这个选项用来传递编译器自身不包含但是却支持的参数选项 -->
<compilerArgument>-verbose -bootclasspath ${java.home}/lib/rt.jar</compilerArgument>
</configuration>
</plugin>
参考:https://blog.csdn.net/liupeifeng3514/article/details/80236077
2.全局配置maven JDK编译版本
- 在公司开发时一劳永逸的配置
此种设置方式将影响由maven创建的所有项目。
找到maven安装目录的conf文件夹,修改其中的settings.xml文件,profiles标签加入如下配置:
<!-- ${MAVEN_HOME}/conf/settins.xml 文件添加如下profile配置,建立的java项目默认使用java 8/11/13 -->
<profiles>
<profile>
<id>jdk-8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>8</jdk>
</activation>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.compilerVersion>8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
这样全局设置后,项目编译时就会默认使用jdk 13。全局配置的好处就是省事、方便,从而避免对每个项目都去设置。
3.参考
https://www.cnblogs.com/east7/p/13363069.html?utm_source=tuicool
二、org.apache.maven.plugins系列插件简介
1.插件知识简介
compiler(maven-compiler-plugin)插件3.0之前,默认的Java编译器就的JDK自带的javac。但是从Compiler(maven-compiler-plugin)插件3.0开始(需要JDK1.6),默认的Java编译器 是javax.tools.JavaCompiler。如果仍然希望使用JDK自带的javac编译源代码,就需要为mvn命令配置forceJavacCompilerUse启动参数如:-Dmaven.compiler.forceJavacCompilerUse=true
Compiler插件编译时和编译后运行的JVM版本目前默认的设置为1.5,默认用此版本,而不是根据你在IDEA项目结构中指定的工程JDK版本编译。如果想改变这些默认设置,应该设置编译源和目标中java编译器的目标,通过设置Java源代码兼容的JVM版本,标明Java源代码开发过程中使用的Java版本,通过设置编译后的类库拟运行的JVM版本,给出编译后的类库将要运行的Java环境(一般都会设置,因为很少有项目再用1.7以下的版本了):
同时,命令mvn的运行需要依赖JDK,Compiler插件默认使用当前运行mvn命令的JDK去编译Java源代码。如果想使用其他版本的JDK(比如本地java环境的,而非maven自带的)编译Java源代码,则需要设置如下(重点fork 、executable、compilerVersion):
上述配置中,用以编译Java源代码的是JDK 1.8,运行mvn命令时指定maven所使用的是JDK为1.6
Compiler插件提供了如下2个goal,默认都已经绑定到Maven的生命周期阶段,无需单独指出。
compiler:compile,绑定到compile 阶段,用以编译main/路径下的源代码
compiler:testCompile,绑定到test-compile阶段,用以编译test/路径下的源代码
mvn compile:
2.官网也建议指定编译的jdk版本
如果是web项目,就需要打war包,那就需要这个插件:
强制字符集编码:${project.build.sourceEncoding}
platformwar包名字——platform.war
产生war前,用于存放构建war包的目录——target/platform。
${project.build.directory}/platform
使用maven工具链:
使用不同的JDK的最好方法是使用工具链方式。在建立一个项目,如编译java源文件,生成Javadoc,运行单元测试、打包,这些插件中的每一个都需要一个JDK工具来对应操作:Javac、JavaDoc、JaveNeR等。使用Maven工具链插件,您可以为所有相关的Maven插件配置1个默认JDK工具链也可以各自配置不同的jdk,用法略。
配置编译插件:
除工具链方式之外,也可以在编译过程中使用的特定JDK。这样的配置对这个插件是特定的,不会影响其他插件。compilerVersion参数可以用来指定插件使用的编译器版本,但是需要将fork设置为true才能工作,此为非常用配置不做详细了解。
针对不同的编译器设置source和target选项:
有时编译某个项目时需要使用的jdk与当前maven所使用的版本不同。Javac可以使用source和target参数来接受这样的命令。编译器插件也可以被配置为在编译期间提供这些选项。如刚才上述:官网也建议指定编译的jdk版本
传递编译参数:
有时需要传递其他编译器参数,这些编译器参数本身不是插件本身需要处理的,而是需要将编译器参数传递给Javac编译器,如下图
3.POM简介
所有的 POM 都继承自一个父 POM(无论是否显式定义了这个父 POM),父POM包含一些可以被继承的默认设置。Maven 使用 effective pom(Super pom 加上工程自己的配置)来执行相关,目的为了使开发者在pom.xml中做尽可能少的配置,且在子配置中可以被方便的覆盖:
比如不指定packaging
时,即默认打jar包时打开effective.pom
:
再看另一种举例:当指定packaging
为war,指定打war包时打开effective pom
所以我们只需要指定packaging打包类型,maven插件可以自动加载并继承父pom相关配置。
如果父pom中的默认配置不符合现有项目要求,而在我们的pom中有没有覆盖,则会报错,如删除pom中的maven-compiler-plugin,使其不覆盖父pom中的maven-compiler-plugin,这样就是使用的父pom中的配置:
上图删除了pom中的maven-compiler-plugin,再看下图effecrive pom中的:
看到这里就使用了默认父pom的maven-compiler-plugin2.3.2版本,此版本默认的jdk是1.5,maven编译时报:
然后修改自己的pom中的maven-compiler-plugin覆盖父pom中的maven-compiler-plugin,使用自定义覆盖默认配置:
上图pom中增加了maven-compiler-plugin,再看下图effecrive pom中的:
则使用jdk1.7编译成功了
4.打开Show Effective POM的两种方式
- maven 命令行方式:
mvn help:effective-pom -Doutput=EffectivePom.xml
我们都知道maven是约定大于配置,也就是默认有很多约定好的配置,如果你不改变,那么就使用这些配置,比如java源代码放置在src/main/java下, 资源文件放置在src/main/resources下, 所以当我们把源代码,资源按约定的结构建立起来后,pom.xml配置很少就可以build jar/war/ear 包, 那么如果你想知道pom.xml的默认配置有哪些,分别设置了哪些值,那么你可以通过上面的goal来生成一个完整的EffectivePom.xml文件,这里面有完整的配置.
打开Show Effective POM
(最终生效POM配置,包含了继承的内容)
- IDEA方式:
5.常用插件
Maven官网插件地址:https://maven.apache.org/plugins/index.html
maven的属性值的占位符,类似EL,类似ant的属性,比如${X},可用于pom文件任何赋值的位置。有以下分类:
env.X:操作系统环境变量,比如${env.PATH}
project.x:pom文件中的属性,比如:1.0,引用方式:${project.version}
settings.xml文件中的属性,比如:false,引用方式:${settings.offline}
Java System Properties:java.lang.System.getProperties()中的属性,比如java.home,引用方式:${java.home}
自定义在pom文件中可以:c:/apps/cargo-installs定义属性及属性的值,而引用方式为:${installDir}
6.工作机制
Maven强大的一个重要的原因是它有一个十分完善的生命周期模型(lifecycle),它有三套相互独立的生命周期,请注意这里说的是“三套”,请别将Maven的生命周期看成一个整体哦,三个生命周期是独立线性执行,分别是:
Clean Lifecycle 在进行真正的构建之前进行一些清理工作。
Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。
Site Lifecycle 生成项目报告,站点,发布站点。
每个生命周期包含一些阶段(phase),这些阶段(phase)是有顺序的,每个阶段蕴含一个或多个目标(goal),并且后面的阶段依赖于前面的阶段,我们和Maven最直接的交互方式就是调用这些生命周期阶段。较之于生命周期阶段的前后依赖关系,三套生命周期本身是相互独立的,用户可以仅仅调用clean生命周期的某个阶段,或者仅仅调用default生命周期的某个阶段,而不会对其他生命周期产生任何影响。例如,当用户调用clean生命周期的clean阶段的时候,不会触发default生命周期的任何阶段。其中deault是最重要的生命周期,拥有validate 、compile 、test 、package、integration、verify、install、deploy等等阶段。
看一下Maven的编译阶段,让maven进行编译代码,使用的是声明的方式来告知Maven如何做的。看似一个简单的命令,但其实它后面执行了一系列的工作。mvn compile如不指定compile阶段的goal,所以complie阶段所有goal,compile和test compile都会执行。
Maven是如何知道从哪里找到要编译的源文件?并且Maven如何知道将编译好的类文件放到哪里?这里就是由Mave基础工作之一“通过配置进行约定”所解决的问题。一般情况下,源文件放在src/main/java路径下,这种默认设置(虽然在上面的POM文件中并没看到)是从父 POM继承来的,即使最简单的POM也知道源文件的默认位置:
当首次执行compile命令或其它命令时,maven会下载所有插件和相关的文件,而之后再执行同一个命令的时候会发现比第一次快很多,这就为什么首次执行命令时候会比较慢的原因。
7.自定义maven插件
大家有没有想过,如果maven自带的插件满足不了我们的需求时候,该怎么办呢?其实不难办,可以通过自己写插件来实现。下面给大家讲一下如何写插件。
1、首先需要创建一个maven项目tinaproject,然后把pom里的packaging改成maven-plugin
然后把版本改为你自己使用的版本,一般现在都是用maven2
接着添加依赖
2、pom修改完了之后就开始创建mojo类了,maven插件里每一个goal所对应的功能都是一个Mojo,比如说eclipse:clean和eclipse:eclipse就是两个Mojo
写完了一个最简单的mojo类之后就来测试下能否正确运行,把mvn install把它发布到本地maven仓库,然后在pom里再增加一个plugin(上面写的这个)
最后再运行mvn compile 就能看到输出了
8.参考
https://www.jianshu.com/p/3c75b3225724
三、springboot 打包插件spring-boot-maven-plugin打包机制及内部结构分析
1.前言
许多公司都会选择使用springboot作为服务应用开发框架,springboot框架提供了一套自己的打包机制,是通过spring-boot-maven-plugin插件来实现的。
1、spring-boot-maven-plugin引入pom
对于新建的一个springboot项目来说,pom中会加入插件:
通过idea可以看到maven中包含了spring-boot-maven-plugin插件:
功能说明:
- build-info:生成项目的构建信息文件 build-info.properties
- repackage:这个是默认
goal
,在mvn package
执行之后,这个命令再次打包生成可执行的 jar,同时将mvn package
生成的 jar 重命名为*.origin
- run:这个可以用来运行 Spring Boot 应用
- start:这个在
mvn integration-test
阶段,进行Spring Boot
应用生命周期的管理 - stop:这个在
mvn integration-test
阶段,进行Spring Boot
应用生命周期的管理
spring-boot-maven-plugin插件默认在父工程sprint-boot-starter-parent中被指定为repackage,可以点击sprint-boot-starter-parent进入父pom进行查看,如下图:
如果需要设置其他属性,需要在当前应用的pom中进行设置去覆盖父pom默认的值去改变行为。
2.执行打包命令
mvn clean package
或者通过开发工具如idea执行clean和package俩命令:
执行以上命令时会自动触发spring-boot-maven-plugin插件的repackage目标,完后可以在target目录下看到生成的jar,如下图:
这里可以看到生成了两个jar相关文件,其中common.jar是spring-boot-maven-plugin插件重新打包后生成的可执行jar,即可以通过java -jar common.jar命令启动。common.jar.original这个则是mvn package打包的原始jar,在spring-boot-maven-plugin插件repackage命令操作时重命名为xxx.original,这个是一个普通的jar,可以被引用在其他服务中。
3.jar内部结构
对这两个jar文件解压看看里面的结构差异:
3.1 common.jar目录结构如下:
其中BOOT-INF主要是一些启动信息,包含classes和lib文件,classes文件放的是项目里生成的字节文件class和配置文件,lib文件是项目所需要的jar依赖。
META-INF目录下主要是maven的一些元数据信息,MANIFEST.MF文件内容如下:
MANIFEST.MF:这个manifest文件定义了与扩展和包相关的数据。单词“manifest”的意思是“显示”。
Manifest-Version: 1.0
Implementation-Title: java-common-utils
Implementation-Version: 0.0.1-SNAPSHOT
Start-Class: com.common.util.CommonUtilsApplication
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Build-Jdk-Spec: 1.8
Spring-Boot-Version: 2.1.9.RELEASE
Created-By: Maven Archiver 3.4.0
Main-Class: org.springframework.boot.loader.JarLauncher
其中Start-Class是项目的主程序入口
,即main方法。Springboot-Boot-Classes和Spring-Boot-Lib指向的是生成的BOOT-INF下的对应位置。
Main-Class属性值为org.springframework.boot.loader.JarLauncher,这个值可以通过设置属性layout来控制,如下:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!--使用-Dloader.path需要在打包的时候增加<layout>ZIP</layout>,不指定的话-Dloader.path不生效-->
<layout>ZIP</layout>
<!-- 指定该jar包启动时的主类[建议] -->
<mainClass>com.common.util.CommonUtilsApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
设置<layout>ZIP</layout>
时Main-Class为org.springframework.boot.loader.PropertiesLauncher,具体layout值对应Main-Class关系如下:
- 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
common.jar之所以可以使用java -jar运行,和MANIFEST.MF文件里的配置关系密切
3.2 common.jar.original目录结构
可以看到通过mvn package构建的jar是一个普通的jar,包含的都是项目的字节文件和一些配置文件,没有将项目依赖的第三方jar包含进来。再看下MANIFEST.MF文件:
Manifest-Version: 1.0
Implementation-Title: java-common-utils
Implementation-Version: 0.0.1-SNAPSHOT
Build-Jdk-Spec: 1.8
Created-By: Maven Archiver 3.4.0
其中没有包含Start-Class、Main-Class等信息,这个与可执行jar的该文件存在很多差异,而且目录结构也有很大差异。
一般对使用spring-boot-maven-plugin插件打出的可执行jar不建议作为jar给其他服务引用,因为可能出现访问可执行jar中的一些配置文件找不到的问题。如果想让构建出来的原始jar不被重新打包,可以对spring-boot-maven-plugin插件配置classifier属性,自定义一个可运行jar名称,这样该插件就不会对原始的jar重命名操作了。
例如:
<configuration>
<!-- [建议]指定该jar包启动时的主类 -->
<mainClass>com.common.util.CommonUtilsApplication</mainClass>
<!--配置的 classifier 表示可执行 jar 的名字,配置了这个之后,在插件执行 repackage 命令时,
就不会给 mvn package 所打成的 jar 重命名了,这样就可以被其他项目引用了,classifier命名的为可执行jar-->
<classifier>myexec</classifier>
</configuration>
效果如下:
以上是对spring-boot-maven-plugin插件的打包机制和jar包结构的一些分析。
4.参考
https://blog.csdn.net/iss_jin/article/details/122463390
四、springboot增量打包更新–静态资源分离打包
1.前言
springboot部署打包为jar,一般都是全量打包,生成的jar包因包含大量三方库通常都是超过100M的,并且在进行一般的页面html微调、js修改、img替换、css样式修改时也需要重新打包进行部署;每次微小的调整都需要重新打包就太烦了,一般在项目开发稳定以后项目中引用的jar就不再改变
为了方便进行静态资源管理及增量部署,对项目引用jar包以及静态资源分离打包,提高打包的效率及部分前端微调项修改后及时进行无重启更新;
2.具体步骤
1、初次打包进行全量打包,对打包的jar进行解压,解压后的文件如下图展示:
2、分离引用的jar包:进入BOOT-INF中,copy文件夹lib到指定目录下,如jar包运行目录;
3分离静态文件:在lib同目录下创建resource文件夹,进入classes文件夹内copy文件夹static
及templates
文件夹到resource文件下;如图:
4、删除jar包及解压文件,当前目录结构如下:
5、增量打包,打包不再将引用jar及static文件夹、templates文件夹打到jar包中:首先修改pom.xml文件中打包相关配置,如下图:
打包配置增加了如下代码:
<layout>ZIP</layout>
<includes>
<include>
<groupId>non-exists</groupId>
<artifactId>non-exists</artifactId>
</include>
</includes>
resource打包配置增加如下过滤:
<exclude>static/**/*</exclude>
<exclude>templates/**/*</exclude>
最终pom.xml中打包配置如下:
<build>
<finalName>web</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
<mainClass>com.XXX.Application</mainClass>
<!--增量打包配置【start】-->
<layout>ZIP</layout>
<includes>
<include>
<groupId>non-exists</groupId>
<artifactId>non-exists</artifactId>
</include>
</includes>
<!--增量打包配置【end】-->
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.4.RELEASE</version>
<configuration>
<fork>false</fork>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<!--【增加静态文件过滤】-->
<exclude>static/**/*</exclude>
<exclude>templates/**/*</exclude>
</excludes>
</resource>
</resources>
</build>
6、执行maven clean install,获得最终jar包,如下图所示,只有5M大小;
7、最终运行时,jar的执行命令中增加lib及resource的路径指向,否则项目无法正常运行;
java -Dloader.path=./lib,./resource -jar ./web.jar
8、如上进行增量打包后,如果前端有不涉及到后端的修改时都可以对resource中的文件进行替换进行实时更新,不再进行重启;后端有变动也不用上传100多M的jar到服务器,影响效率;
3.参考
转自:https://blog.csdn.net/qq_35611143/article/details/107083164
参考:https://www.jb51.net/article/186316.htm
原创文章,作者:kepupublish,如若转载,请注明出处:https://blog.ytso.com/245540.html