首先,编译器需要将 .java 文本文件编译为 .class 字节码,然后 JVM 执行 .class 字节码文件。流程并不复杂,本文主要记录一些在编译、运行时的相关过程。
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
	javac Hello.java
java Hello
javac M.java E.java
# 查找当前目录下的 Java 源码文件 find -name "*.java" > source.txt # 编译 javac @source.txt
执行时,只需执行包含 main 函数的类即可,例如,		java M
。	
上面的编译命令会在当前目录下生成		.class
字节码文件,也可以通过		-d
参数指定生成目录。管理这些字节码文件会是一件繁琐、麻烦的事情,而 Jar 包简化了这个过程。	
Jar 文件以 Zip 格式为基础,将多个文件聚合为一个文件。Jar 文件不仅可以用于压缩、发布,还可以用于部署、封装库、组件、插件程序,同时还可以在 JVM 上直接运行。Jar 包提供如下特性:
使用 IDE 工具,可以很方便地创建一个 Jar 文件,例如,Myeclipse,可以自行尝试。这里直接使用		jar
命令,生成 Jar 文件。	
这里以多源码文件为例,在 com/test 目录下创建两个文件:
A.java
package com.test;
public class A {
    public static void test() {
        System.out.println("A:test()");
    }
}
	B.java
package com.test;
import com.test.A;
public class B {
    public static void main(String[] argc) {
        A a = new A();
        a.test();
    }
}
	javac com/test/*.java
使用		jar
命令打包 Jar 包,与使用		tar
命令类似。	
jar cvf test.jar com/test/*.class added manifest adding: com/test/A.class(in = 388) (out= 275)(deflated 29%) adding: com/test/B.class(in = 315) (out= 236)(deflated 25%)
参考文档, Compiling the Example Programs
直接执行 Jar 文件,会报错:
java -jar test.jar no main manifest attribute, in test.jar
这是由于 JVM 找不到程序的执行入口。有两种方法可以指定程序入口:
使用		unzip
命令解压 Jar 文件,可以看到除了 .class 文件,还有一个		META-INF/MANIFEST.MF
文件。在		META-INF/MANIFEST.MF
文件中,新增:	
Main-Class: com.test.B
指向		public static void main(String[] args)
所在类。	
java -cp test.jar com.test.B A:test()
如果只有一两个源码文件,上面的打包过程尚可接受。而对于中大型项目,这种原始的方式无法满足构建和管理的需求,需要借助一定的工具。
Maven 是一个软件项目管理及自动构建工具,可以用于构建和管理各种项目,例如,Java、Ruby、Scala 等。Maven 是 Apache 软件基金会下的项目。
Maven项目使用项目对象模型(Project Object Model,POM)来配置。项目对象模型存储在名为 pom.xml 的文件中。
这里以在 CentOS 上安装为例:
yum install -y maven
查看版本:
mvn -v Apache Maven 3.0.5 (Red Hat 3.0.5-17) Maven home: /usr/share/maven Java version: 1.8.0_232, vendor: Oracle Corporation Java home: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64/jre Default locale: en_US, platform encoding: ANSI_X3.4-1968 OS name: "linux", version: "3.10.0-862.el7.x86_64", arch: "amd64", family: "unix"
以上面的 A.class,B.class 为例,新建一个 pom.xml 文件。artifactId 为构建之后生成的文件名。
在 Maven 项目中,约定主代码放到		src/main/java
目录下,而无需额外配置。这里新建		src/main/java
目录,将 com 目录移入其中。	
新建 pom.xml 文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
      <packaging>jar</packaging>
    <name> a maven project</name>
    <build>
    <plugins>
        <plugin> 
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <archive>
                <manifest>
                <!-- give full qualified name of your main class-->
                    <mainClass>com.test.B</mainClass>
                </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
    </build>
</project>
	最终的目录结构为:
tree . |-- pom.xml |-- src | `-- main | `-- java | `-- com | `-- test | |-- A.java | `-- B.java
执行命令,编译项目:
mvn clean package [INFO] Scanning for projects... [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building a maven project 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.1.0/maven-jar-plugin-3.1.0.pom Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.1.0/maven-jar-plugin-3.1.0.pom (7 KB at 5.1 KB/sec) Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.1.0/maven-jar-plugin-3.1.0.jar Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-jar-plugin/3.1.0/maven-jar-plugin-3.1.0.jar (27 KB at 49.8 KB/sec) [INFO] [INFO] --- maven-clean-plugin:2.4.1:clean (default-clean) @ test --- [INFO] Deleting /root/test-java/target [INFO] [INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ test --- [debug] execute contextualize [WARNING] Using platform encoding (ANSI_X3.4-1968 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /root/test-java/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ test --- [WARNING] File encoding has not been set, using platform encoding ANSI_X3.4-1968, i.e. build is platform dependent! [INFO] Compiling 2 source files to /root/test-java/target/classes [INFO] [INFO] --- maven-resources-plugin:2.5:testResources (default-testResources) @ test --- [debug] execute contextualize [WARNING] Using platform encoding (ANSI_X3.4-1968 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /root/test-java/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ test --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.10:test (default-test) @ test --- [INFO] No tests to run. [INFO] Surefire report directory: /root/test-java/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Results : Tests run: 0, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] --- maven-jar-plugin:3.1.0:jar (default-jar) @ test --- Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-archiver/3.5/plexus-archiver-3.5.jar Downloading: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-io/3.0.0/plexus-io-3.0.0.jar Downloading: https://repo.maven.apache.org/maven2/org/tukaani/xz/1.6/xz-1.6.jar Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-archiver/3.5/plexus-archiver-3.5.jar (183 KB at 259.7 KB/sec) Downloaded: https://repo.maven.apache.org/maven2/org/tukaani/xz/1.6/xz-1.6.jar (101 KB at 84.8 KB/sec) Downloaded: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-io/3.0.0/plexus-io-3.0.0.jar (73 KB at 59.8 KB/sec) [INFO] Building jar: /root/test-java/target/test-0.0.1-SNAPSHOT.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.352s [INFO] Finished at: Fri Dec 20 16:12:06 CST 2019 [INFO] Final Memory: 16M/249M [INFO] ------------------------------------------------------------------------
执行构建包:
java -jar target/test-0.0.1-SNAPSHOT.jar A:test()
这里可以直接		java -jar
执行的原因是,在 pom.xml 中添加了插件		maven-jar-plugin
,该插件会在		META-INF/MANIFEST.MF
中添加 Main-Class 信息。	
为了能将项目直接部署在容器平台,编译构建之后,我们还需要将生成的文件容器化。这里使用		docker-maven-plugin
插件来实现。在 pom.xml 文件		plugins
标签中新增如下内容:	
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <configuration>
        <imageName>
            shaowenchen/maven-hello-word:v1
        </imageName>
        <registryUrl></registryUrl>
        <workdir>/work</workdir>
        <rm>true</rm>
        <env>
            <TZ>Asia/Shanghai</TZ>
            <JAVA_OPTS>
                -XX:+UnlockExperimentalVMOptions /
                -XX:+UseCGroupMemoryLimitForHeap /
                -XX:MaxRAMFraction=2 /
                -XX:CICompilerCount=8 /
                -XX:ActiveProcessorCount=8 /
                -XX:+UseG1GC /
                -XX:+AggressiveOpts /
                -XX:+UseFastAccessorMethods /
                -XX:+UseStringDeduplication /
                -XX:+UseCompressedOops /
                -XX:+OptimizeStringConcat
            </JAVA_OPTS>
        </env>
        <baseImage>openjdk:8</baseImage>
        <cmd>
            java ${JAVA_OPTS} -jar ${project.build.finalName}.jar
        </cmd>
        <!--是否推送image-->
        <pushImage>false</pushImage>
        <resources>
            <resource>
                <directory>${project.build.directory}</directory>
                <include>${project.build.finalName}.jar</include>
            </resource>
        </resources>
        <serverId>docker-hub</serverId>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>build</goal>
            </goals>
        </execution>
    </executions>
</plugin>
	
再次执行编译命令		mvn package
,会看到增加了一些日志。	
[INFO] --- docker-maven-plugin:1.2.1:build (default) @ test ---
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[WARNING] No entry found in settings.xml for serverId=docker-hub, cannot configure authentication for that registry
[INFO] Using authentication suppliers: [ConfigFileRegistryAuthSupplier]
[INFO] Copying /root/test-java/target/test-0.0.1-SNAPSHOT.jar -> /root/test-java/target/docker/test-0.0.1-SNAPSHOT.jar
[INFO] Building image shaowenchen/maven-hello-word:v1
Step 1/6 : FROM openjdk:8
 ---> 09df0563bdfc
Step 2/6 : ENV JAVA_OPTS -XX:+UnlockExperimentalVMOptions                 -XX:+UseCGroupMemoryLimitForHeap                 -XX:MaxRAMFraction=2                 -XX:CICompilerCount=8                 -XX:ActiveProcessorCount=8                 -XX:+UseG1GC                 -XX:+AggressiveOpts                 -XX:+UseFastAccessorMethods                 -XX:+UseStringDeduplication                 -XX:+UseCompressedOops                 -XX:+OptimizeStringConcat
 ---> Running in b6dabde9580c
Removing intermediate container b6dabde9580c
 ---> 0664556506d3
Step 3/6 : ENV TZ Asia/Shanghai
 ---> Running in 954b264bfb35
Removing intermediate container 954b264bfb35
 ---> 334f644fa97e
Step 4/6 : WORKDIR /work
 ---> Running in 44e039f55452
Removing intermediate container 44e039f55452
 ---> 3572d77be94c
Step 5/6 : ADD test-0.0.1-SNAPSHOT.jar .
 ---> 904b5885f74a
Step 6/6 : CMD java ${JAVA_OPTS} -jar test-0.0.1-SNAPSHOT.jar
 ---> Running in b3f567a912a5
Removing intermediate container b3f567a912a5
 ---> cba070b2300d
ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}
Successfully built cba070b2300d
Successfully tagged shaowenchen/maven-hello-word:v1
[INFO] Built shaowenchen/maven-hello-word:v1
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.017s
[INFO] Finished at: Fri Dec 20 16:27:48 CST 2019
[INFO] Final Memory: 31M/508M
[INFO] ------------------------------------------------------------------------
	上面的日志是在构建镜像,如果打开推送开关,Maven 会将镜像推送到 dockerhub 仓库。
查看本地构建的镜像:
docker images|grep hello shaowenchen/maven-hello-word v1 ade23e55f848 About a minute ago 488MB