杰瑞科技汇

如何用shell脚本启动Java程序?

java 命令

无论在哪种 Shell 中,启动 Java 程序的核心都是 java 命令,它的基本语法如下:

java [选项] 类名 [参数]
  • [选项]: JVM 的启动参数,例如设置内存大小 (-Xmx)、垃圾回收器 (-XX:+UseG1GC) 等。
  • 类名: 你想要执行的 Java 主类的全限定名,com.example.MyApp
  • [参数]: 传递给 Java 程序 main 方法的参数。

第1步:最简单的启动方式

假设你已经:

  1. 安装了 Java 并配置好了 JAVA_HOME
  2. 你的 Java 程序已经被编译成了 .class 文件,或者打包成了 .jar 文件。

场景1:直接运行 .class 文件

  1. 编译 Java 源码:

    # 假设你有一个 MyProgram.java 文件
    javac MyProgram.java

    这会生成一个 MyProgram.class 文件。

  2. 运行编译后的文件:

    # 运行当前目录下的 MyProgram.class
    java MyProgram

    注意: 这里只写类名 MyProgram,不要带 .class 后缀。

场景2:运行 .jar 文件 (最常见的方式)

Java 程序通常被打包成 .jar (Java Archive) 文件以便分发。

  1. 创建 JAR 文件:

    # 创建一个可执行的 JAR 文件,指定 Main-Class
    jar cvfe MyApp.jar com.example.MainClass com/example/*.class
    • c: 创建新的 JAR 文件。
    • v: 生成详细输出。
    • f: 指定 JAR 文件名。
    • e: 指定入口点(主类)。
  2. 运行 JAR 文件:

    # 使用 -jar 选项来运行 JAR 文件
    java -jar MyApp.jar

    如果你的 JAR 文件没有在 META-INF/MANIFEST.MF 中正确配置 Main-Class,你也可以通过以下方式指定:

    java -cp MyApp.jar com.example.MainClass

第2步:传递参数给 Java 程序

你的 Java 程序的 main 方法可以接收字符串数组参数,在 Shell 中,只需在类名或 JAR 文件名后跟上这些参数即可。

Java 代码示例 (ArgsDemo.java):

public class ArgsDemo {
    public static void main(String[] args) {
        System.out.println("程序接收到 " + args.length + " 个参数:");
        for (int i = 0; i < args.length; i++) {
            System.out.println("参数 " + (i + 1) + ": " + args[i]);
        }
    }
}

Shell 中运行并传参:

# 编译
javac ArgsDemo.java
# 运行并传递三个参数
java ArgsDemo "Hello World" 123 --verbose

输出:

程序接收到 3 个参数:
参数 1: Hello World
参数 2: 123
参数 3: --verbose

对 JAR 文件传参:

java -jar MyApp.jar config.properties mode=production

第3步:设置类路径 (-cp-classpath)

当你的 Java 程序依赖了第三方库(如 .jar 文件或 .class 文件)时,你需要使用 -cp (Classpath) 选项来告诉 JVM 去哪里查找这些依赖。

场景1:依赖当前目录下的其他 JAR

假设你的项目结构如下:

.
├── MyApp.jar
├── lib/
│   ├── library1.jar
│   └── library2.jar
└── ...

运行方式:

# 使用冒号 : 分隔多个路径(Linux/macOS)
java -jar MyApp.jar -cp ./lib/library1.jar:./lib/library2.jar
# 或者不使用 -jar,而是用 -cp 指定所有依赖
java -cp ".:./lib/library1.jar:./lib/library2.jar:MyApp.jar" com.example.MainClass

场景2:依赖系统路径下的库

如果你已经将库文件(如 mysql-connector-java.jar)放到了 /usr/local/lib/,可以这样使用:

java -cp "/usr/local/lib/mysql-connector-java.jar:." com.example.DatabaseApp

场景3:使用通配符 (Linux/macOS)

现代 Java 版本支持在类路径中使用通配符 来简化命令。

# * 会自动替换为 lib 目录下所有的 .jar 文件
java -jar MyApp.jar -cp "./lib/*"

注意: Windows 的命令提示符不支持 通配符,需要使用第三方工具如 ClasspathUtils 或手动列出所有 JAR。


第4步:JVM 选项(内存、GC 等)

在生产环境中,为 Java 程序分配正确的内存至关重要,这些选项直接跟在 java 命令之后,类名或 -jar 之前。

常用选项示例:

# 设置最大堆内存为 2GB,初始堆内存为 512MB
java -Xmx2g -Xms512m -jar my-big-app.jar
# 使用 G1 垃圾回收器
java -XX:+UseG1GC -jar my-app.jar
# 设置元空间大小 (Java 8+)
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar my-app.jar
# 启用 JMX 以便使用 VisualVM 或 JConsole 监控
java -Dcom.sun.management.jmxremote.port=9001 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -jar my-app.jar

第5步:在 Shell 脚本中启动(生产环境实践)

在实际生产环境中,我们通常不会直接在命令行运行,而是编写一个 Shell 脚本,这样做的好处是:

  • 可以方便地管理启动参数。
  • 可以配置日志输出。
  • 可以设置运行环境(如 JAVA_HOME)。
  • 可以实现后台运行、日志轮转等功能。

示例 start_app.sh 脚本:

#!/bin/bash
# 1. 设置环境变量
# 强制使用特定版本的 Java
export JAVA_HOME=/opt/java/jdk-17
export PATH=$JAVA_HOME/bin:$PATH
# 2. 定义应用变量
APP_NAME="MyAwesomeApp"
APP_JAR="target/MyAwesomeApp-1.0.0.jar"
APP_LOG_FILE="logs/app.log"
APP_PID_FILE="logs/app.pid"
JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC -Dspring.profiles.active=prod"
# 3. 检查 JAR 文件是否存在
if [ ! -f "$APP_JAR" ]; then
    echo "错误: JAR 文件 $APP_JAR 不存在!"
    exit 1
fi
# 4. (可选) 创建日志和PID文件目录
mkdir -p logs
# 5. 检查应用是否已经在运行
if [ -f "$APP_PID_FILE" ]; then
    PID=$(cat "$APP_PID_FILE")
    if ps -p $PID > /dev/null; then
        echo "$APP_NAME 已经在运行,PID: $PID"
        exit 1
    else
        echo "清理过期的 PID 文件: $APP_PID_FILE"
        rm -f "$APP_PID_FILE"
    fi
fi
# 6. 启动应用
echo "正在启动 $APP_NAME..."
# nohup 表示即使终端关闭,程序也会继续运行
# > $APP_LOG_FILE 2>&1 表示将标准输出和标准错误都重定向到日志文件
# & 表示在后台运行
nohup java $JAVA_OPTS -jar $APP_JAR > $APP_LOG_FILE 2>&1 &
# 7. 记录 PID
echo $! > $APP_PID_FILE
echo "$APP_NAME 启动成功,PID: $!,日志文件: $APP_LOG_FILE"

如何使用这个脚本:

  1. 保存为 start_app.sh
  2. 赋予执行权限:chmod +x start_app.sh
  3. 运行脚本:./start_app.sh

配套的 stop_app.sh 脚本:

#!/bin/bash
APP_PID_FILE="logs/app.pid"
if [ -f "$APP_PID_FILE" ]; then
    PID=$(cat "$APP_PID_FILE")
    echo "正在停止 PID 为 $PID 的进程..."
    kill $PID
    # 等待进程优雅退出
    for i in {1..10}; do
        if ! ps -p $PID > /dev/null; then
            echo "$APP_NAME 已停止。"
            rm -f "$APP_PID_FILE"
            exit 0
        fi
        sleep 1
    done
    # 如果超时,强制杀死
    echo "超时,强制杀死进程..."
    kill -9 $PID
    rm -f "$APP_PID_FILE"
else
    echo "$APP_PID_FILE 文件不存在,可能应用未运行。"
fi

常见问题与排查

  1. 'java' command not found

    • 原因: 系统没有找到 java 命令。
    • 解决: 确保 Java 已正确安装,JAVA_HOME 环境变量和 PATH 环境变量已正确配置。
  2. Error: Could not find or load main class com.example.MyApp

    • 原因:
      1. 类名写错了。
      2. 类所在的包目录结构不正确。
      3. -cp (类路径) 没有包含 .class 文件所在的目录。
      4. 在 Windows 上,路径分隔符是分号 而不是冒号 。
    • 解决: 仔细检查类名和类路径,可以使用 echo $CLASSPATH (Linux/macOS) 或 echo %CLASSPATH% (Windows) 查看当前类路径。
  3. OutOfMemoryError: Java Heap Space

    • 原因: Java 堆内存不足。
    • 解决: 增加 JVM 的最大堆内存,使用 -Xmx 参数,-Xmx4g
任务 命令示例
运行 .class java MyClass
运行 .jar java -jar myapp.jar
传参 java MyClass arg1 arg2java -jar myapp.jar arg1 arg2
设置类路径 java -cp ".:lib/*" com.example.Main
设置内存 java -Xmx2g -Xms512m -jar myapp.jar
后台运行 nohup java -jar myapp.jar > app.log 2>&1 &

掌握这些基本操作和脚本编写技巧,你就可以在 Shell 中灵活、稳定地管理和启动你的 Java 应用程序了。

分享:
扫描分享到社交APP
上一篇
下一篇