惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
V
V2EX
GbyAI
GbyAI
Recent Announcements
Recent Announcements
Microsoft Security Blog
Microsoft Security Blog
阮一峰的网络日志
阮一峰的网络日志
Hugging Face - Blog
Hugging Face - Blog
T
Tailwind CSS Blog
Y
Y Combinator Blog
C
Check Point Blog
爱范儿
爱范儿
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
美团技术团队
雷峰网
雷峰网
IT之家
IT之家
WordPress大学
WordPress大学
V
Visual Studio Blog
Microsoft Azure Blog
Microsoft Azure Blog
MyScale Blog
MyScale Blog
N
News and Events Feed by Topic
罗磊的独立博客
S
SegmentFault 最新的问题
S
Security Affairs
aimingoo的专栏
aimingoo的专栏
F
Fortinet All Blogs
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
H
Hacker News: Front Page
Google DeepMind News
Google DeepMind News
B
Blog
O
OpenAI News
C
Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
The Last Watchdog
The Last Watchdog
Hacker News: Ask HN
Hacker News: Ask HN
博客园_首页
人人都是产品经理
人人都是产品经理
C
Cybersecurity and Infrastructure Security Agency CISA
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Help Net Security
Help Net Security
月光博客
月光博客
J
Java Code Geeks
L
LangChain Blog
博客园 - 司徒正美
Stack Overflow Blog
Stack Overflow Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Apple Machine Learning Research
Apple Machine Learning Research
T
The Exploit Database - CXSecurity.com
N
News and Events Feed by Topic
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

陈少文的网站

巨变与机遇的未来十年 Kubernetes 平台管理软件压力测试方案 使用镜像部署 Hexo 静态页面 终于等到你 - GitHub 镜像仓库服务(ghcr.io) 一起来学 Go --(6)Interface 一起来学 Go --(5)Goroutine 和 Channel 什么是函数式编程 如何在 Kubernetes 集群集成 Kata 柯里化与偏函数 使用 PyGithub 自动创建 Label 软件产品是团队能力的输出 Helm 2 、Helm 3 比较 IoT 变现 Kubernetes 中的 DNS 服务 国内的 Helm 镜像源 Harbor 使用自签证书支持 Https 访问 DevOps 工具链之 Prow 如何使用 kfctl 安装 Kubeflow VS Code 无法下载 Go 插件的工具包 工程师更应具有服务精神 你不知道的 Docker 使用技巧 使用 Docker 运行 Tensorflow 论中国 什么是左移 如何清空 Git 仓库全部历史记录 一禅小和尚 有风吹过厨房 时间的玫瑰 如何在 CentOS 安装 GPU 驱动 开发 Tips(19) 使用 Velero 备份 Kubernetes 集群 Kubernetes Cheat Sheet 开发 Tips(18) 开发 Tips(17) KubeSpray 安装 Kubernetes 报错 ip in ansible_all_ipv4_addresses 基于 Kubernetes 和 Jenkins 搭建自动化测试系统 在 Kubernetes 上动态创建 Jenkins Slave 使用 Jenkins 进行服务拨测 开发 Tips(16) Kubernetes 签发 Ingress 证书及日常故障运维 Kubernetes 中 Deployment 的基本操作 Kubernetes 中的证书 如何使用 KubeBuilder 开发一个 Operator Kubernetes 1.6.0 安装问题汇总 镜像管理工具 -- Harbor 开发 Tips(15) Docker 如何拉取镜像 开发 Tips(14) 使用 Helm 安装 harbor 开发 Tips(13) 使用 S2I 构建云原生应用 在 Kubernetes 中使用 emptyDir、hostPath、localVolume 开发 Tips(12) 开发 Tips(11) 代码质量分析工具 SonarQube 使用 Kubeadm 安装 Kubernetes 集群 一起来学 Go --(4)常用函数 Kubernetes 中的 Ceph Kubernetes 之 Volumes Kubernetes 之 Labels、Selectors 开发 Tips(10) 开源正在重构商业模式 Kubernetes 之网络 Kubernetes 之 API 使用 Helm 和 Operator 快速部署 Prometheus Kubernetes 复杂有状态应用管理框架 -- Operator Kubernetes 的包管理器 -- Helm 一起来学 Go --(3)Go Modules 如何一步一步地优化博客方案 kubectl 实用指南 Kubernetes 中的基本概念 搭建远程 Kubernetes 开发环境 大公司和小公司的 ToB 思路 开发 Tips(9) Go 入门指南 一起来学 Go --(2)数据与逻辑结构 如何预防 Web 富文本中的 XSS 攻击 django-xss-cleaner 云工作时代 一起来学 Go --(1)背景与特点 SaaS 开发团队的不同阶段 你不知道的 Git 使用技巧 输出既服务 微服务设计 继续奔跑 开发 Tips(8) 从账户安全到二次验证 Django 性能之数据库查询优化 Django 性能之分库分表 敏捷开发之研发流程 打造一致性的团队 开发 Tips(7) Pytest 进阶学习之 Mock PaaS 部署之 buildpack Go 开发配置 领域输出才是 PaaS 的核心竞争力 Pytest 入门学习 开发 Tips(6) 如何使用 Jenkins、Docker、GitLab 搭建 Django 自动化部署流程 开发 Tips(5)
如何构建一个 Java 工程
微信公众号 · 2019-12-20 · via 陈少文的网站

首先,编译器需要将 .java 文本文件编译为 .class 字节码,然后 JVM 执行 .class 字节码文件。流程并不复杂,本文主要记录一些在编译、运行时的相关过程。

1. 单个文件源代码

  1. 新建文本文件 Hello.java
1
2
3
4
5
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
  1. 编译源码
  1. 执行字节码

2. 多个源码文件

  • 使用命令行指定多个文件
  • 使用文本指定多个文件
1
2
3
4
# 查找当前目录下的 Java 源码文件
find -name "*.java" > source.txt
# 编译
javac @source.txt

执行时,只需执行包含 main 函数的类即可,例如,java M

上面的编译命令会在当前目录下生成 .class 字节码文件,也可以通过 -d 参数指定生成目录。管理这些字节码文件会是一件繁琐、麻烦的事情,而 Jar 包简化了这个过程。

3. Jar 文件

Jar 文件以 Zip 格式为基础,将多个文件聚合为一个文件。Jar 文件不仅可以用于压缩、发布,还可以用于部署、封装库、组件、插件程序,同时还可以在 JVM 上直接运行。Jar 包提供如下特性:

  • 安全性。文件内容具有数字化签名
  • 压缩文件、减少网络传输时间
  • 平台扩展。使用 Jar 文件向 Java 平台核心扩展功能
  • 包密封。存储在 Jar 文件中的包可以进行密封,以增强版本一致性和安全性
  • 包版本控制。Jar 文件可以包含版本、开发者相关信息
  • 可移植性。Java 平台核心对 Jar 文件的处理进行了规范

使用 IDE 工具,可以很方便地创建一个 Jar 文件,例如,Myeclipse,可以自行尝试。这里直接使用 jar 命令,生成 Jar 文件。

3.1 准备 Java 源码

这里以多源码文件为例,在 com/test 目录下创建两个文件:

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 编译 Java 源码

3.3 打包 Jar 文件

使用 jar 命令打包 Jar 包,与使用 tar 命令类似。

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%)

参考文档,Compiling the Example Programs

3.4 执行 Jar 文件

直接执行 Jar 文件,会报错:

1
2
3
java -jar test.jar

no main manifest attribute, in test.jar

这是由于 JVM 找不到程序的执行入口。有两种方法可以指定程序入口:

  • 在 META-INF/MANIFEST.MF 文件中指定

使用 unzip 命令解压 Jar 文件,可以看到除了 .class 文件,还有一个 META-INF/MANIFEST.MF 文件。在 META-INF/MANIFEST.MF 文件中,新增:

指向 public static void main(String[] args) 所在类。

  • 通过命令行指定包含 main 函数的类
1
2
3
java -cp test.jar com.test.B

A:test()

4. Maven

如果只有一两个源码文件,上面的打包过程尚可接受。而对于中大型项目,这种原始的方式无法满足构建和管理的需求,需要借助一定的工具。

Maven 是一个软件项目管理及自动构建工具,可以用于构建和管理各种项目,例如,Java、Ruby、Scala 等。Maven 是 Apache 软件基金会下的项目。

Maven项目使用项目对象模型(Project Object Model,POM)来配置。项目对象模型存储在名为 pom.xml 的文件中。

4.1 安装 Maven

这里以在 CentOS 上安装为例:

查看版本:

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 创建一个 pom.xml 文件

以上面的 A.class,B.class 为例,新建一个 pom.xml 文件。artifactId 为构建之后生成的文件名。

在 Maven 项目中,约定主代码放到 src/main/java 目录下,而无需额外配置。这里新建 src/main/java 目录,将 com 目录移入其中。

新建 pom.xml 文件如下:

 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>

最终的目录结构为:

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

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

执行命令,编译项目:

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

执行构建包:

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

A:test()

这里可以直接 java -jar 执行的原因是,在 pom.xml 中添加了插件 maven-jar-plugin,该插件会在 META-INF/MANIFEST.MF 中添加 Main-Class 信息。

4.3 将项目打包到镜像

为了能将项目直接部署在容器平台,编译构建之后,我们还需要将生成的文件容器化。这里使用 docker-maven-plugin 插件来实现。在 pom.xml 文件 plugins 标签中新增如下内容:

 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>

再次执行编译命令 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 仓库。

查看本地构建的镜像:

1
2
3
docker images|grep hello

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

5. 参考