This page looks best with JavaScript enabled

How to Build a Java Project

 ·  ☕ 4 min read

First, the compiler needs to compile .java text files into .class bytecode, and then the JVM executes the .class bytecode files. The process is not complicated; this article mainly records some of the related steps during compilation and runtime.

1. A Single Source File

  1. Create a text file Hello.java
1
2
3
4
5
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
  1. Compile the source
1
javac Hello.java
  1. Execute the bytecode
1
java Hello

2. Multiple Source Files

  • Specify multiple files on the command line
1
javac M.java E.java
  • Specify multiple files with a text file
1
2
3
4
# 查找当前目录下的 Java 源码文件
find -name "*.java" > source.txt
# 编译
javac @source.txt

When running, you only need to run the class that contains the main function, for example, java M.

The compile command above generates .class bytecode files in the current directory; you can also use the -d parameter to specify the output directory. Managing these bytecode files becomes a tedious and troublesome affair, and Jar files simplify that process.

3. Jar Files

A Jar file is based on the Zip format and aggregates multiple files into one file. Jar files can be used not only for compression and distribution, but also for deployment, packaging libraries, components, and plugin programs, and they can also be run directly on the JVM. Jar packages provide the following features:

  • Security. The file contents carry a digital signature
  • Compresses files and reduces network transfer time
  • Platform extension. Use Jar files to extend the core Java platform with additional functionality
  • Package sealing. Packages stored in a Jar file can be sealed to strengthen version consistency and security
  • Package versioning. Jar files can contain version and developer-related information
  • Portability. The Java platform core standardizes how Jar files are handled

With IDE tools you can create a Jar file very conveniently, for example MyEclipse; you can try it yourself. Here we use the jar command directly to generate the Jar file.

3.1 Preparing the Java Source

Here we take multiple source files as an example, creating two files under the com/test directory:

A.java

1
2
3
4
5
6
7
package com.test;

public class A {
    public static void test() {
        System.out.println("A:test()");
    }
}

B.java

1
2
3
4
5
6
7
8
9
package com.test;

import com.test.A;
public class B {
    public static void main(String[] argc) {
        A a = new A();
        a.test();
    }
}

3.2 Compiling the Java Source

1
javac com/test/*.java

3.3 Packaging the Jar File

Use the jar command to package a Jar, similar to using the tar command.

1
2
3
4
5
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%)

Reference documentation, Compiling the Example Programs

3.4 Running the Jar File

Running the Jar file directly reports an error:

1
2
3
java -jar test.jar

no main manifest attribute, in test.jar

This is because the JVM cannot find the program’s entry point. There are two ways to specify the program entry point:

  • Specify it in the META-INF/MANIFEST.MF file

Use the unzip command to extract the Jar file, and you will see that besides the .class files there is also a META-INF/MANIFEST.MF file. In the META-INF/MANIFEST.MF file, add:

1
Main-Class: com.test.B

pointing to the class that contains public static void main(String[] args).

  • Specify the class containing the main function on the command line
1
2
3
java -cp test.jar com.test.B

A:test()

4. Maven

If there are only one or two source files, the packaging process above is still acceptable. But for medium and large projects, this primitive approach cannot meet the needs of building and management, so some tooling is required.

Maven is a software project management and automated build tool, usable for building and managing various projects such as Java, Ruby, Scala, and so on. Maven is a project under the Apache Software Foundation.

Maven projects are configured using a Project Object Model (POM). The project object model is stored in a file named pom.xml.

4.1 Installing Maven

Here we take installing on CentOS as an example:

1
yum install -y maven

Check the version:

1
2
3
4
5
6
7
8
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"

4.2 Creating a pom.xml File

Using the A.class and B.class above as an example, create a new pom.xml file. The artifactId is the filename generated after the build.

In a Maven project, the convention is to put the main code under the src/main/java directory without additional configuration. Here we create the src/main/java directory and move the com directory into it.

The newly created pom.xml file is as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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>

The final directory structure is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
tree

.
|-- pom.xml
|-- src
|   `-- main
|       `-- java
|           `-- com
|               `-- test
|                   |-- A.java
|                   `-- B.java

Run the command to compile the project:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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] ------------------------------------------------------------------------

Run the built package:

1
2
3
java -jar target/test-0.0.1-SNAPSHOT.jar

A:test()

The reason it can be run directly with java -jar is that the maven-jar-plugin plugin was added to pom.xml, and this plugin adds the Main-Class information to META-INF/MANIFEST.MF.

4.3 Packaging the Project into an Image

To deploy the project directly on a container platform, after compiling and building we still need to containerize the generated files. Here we use the docker-maven-plugin plugin to do this. Add the following content to the plugins tag in the pom.xml file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<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>

Run the compile command mvn package again, and you will see some additional log output.

[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] ------------------------------------------------------------------------

The log above is building the image; if you turn on the push switch, Maven will push the image to the dockerhub registry.

View the locally built image:

1
2
3
docker images|grep hello

shaowenchen/maven-hello-word                                     v1                   ade23e55f848        About a minute ago   488MB

5. References


微信公众号
WRITTEN BY
微信公众号