构建与部署 #
一、构建工具配置 #
1.1 Maven配置 #
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>embedded-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<pi4j.version>2.3.0</pi4j.version>
</properties>
<dependencies>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-core</artifactId>
<version>${pi4j.version}</version>
</dependency>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-plugin-raspberrypi</artifactId>
<version>${pi4j.version}</version>
</dependency>
<dependency>
<groupId>com.pi4j</groupId>
<artifactId>pi4j-plugin-pigpio</artifactId>
<version>${pi4j.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.Main</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.Main</mainClass>
</transformer>
</transformers>
<finalName>${project.artifactId}-all</finalName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
1.2 Gradle配置 #
groovy
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
application {
mainClass = 'com.example.Main'
}
repositories {
mavenCentral()
}
def pi4jVersion = '2.3.0'
dependencies {
implementation "com.pi4j:pi4j-core:${pi4jVersion}"
implementation "com.pi4j:pi4j-plugin-raspberrypi:${pi4jVersion}"
implementation "com.pi4j:pi4j-plugin-pigpio:${pi4jVersion}"
}
jar {
manifest {
attributes 'Main-Class': 'com.example.Main'
}
}
shadowJar {
archiveBaseName.set('embedded-app')
archiveClassifier.set('all')
}
task deploy(type: Exec) {
dependsOn 'shadowJar'
def host = project.findProperty('deploy.host') ?: 'raspberry-pi'
def user = project.findProperty('deploy.user') ?: 'pi'
def dest = project.findProperty('deploy.dest') ?: '/home/pi/app'
commandLine 'rsync', '-avz',
"${buildDir}/libs/embedded-app-all.jar",
"${user}@${host}:${dest}/"
}
二、部署脚本 #
2.1 部署脚本 #
bash
#!/bin/bash
set -e
APP_NAME="embedded-app"
JAR_FILE="target/embedded-app-all.jar"
REMOTE_HOST="raspberry-pi"
REMOTE_USER="pi"
REMOTE_DIR="/home/pi/${APP_NAME}"
SERVICE_NAME="${APP_NAME}.service"
echo "=== 部署 ${APP_NAME} ==="
echo "1. 构建项目..."
mvn clean package -DskipTests
echo "2. 创建远程目录..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "mkdir -p ${REMOTE_DIR}"
echo "3. 上传JAR文件..."
scp ${JAR_FILE} ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/
echo "4. 创建systemd服务..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "cat > /tmp/${SERVICE_NAME} << EOF
[Unit]
Description=${APP_NAME}
After=network.target
[Service]
Type=simple
User=${REMOTE_USER}
WorkingDirectory=${REMOTE_DIR}
ExecStart=/usr/bin/java -Xms64m -Xmx128m -jar ${REMOTE_DIR}/embedded-app-all.jar
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF"
echo "5. 安装服务..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "sudo mv /tmp/${SERVICE_NAME} /etc/systemd/system/"
echo "6. 重载并启动服务..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "sudo systemctl daemon-reload"
ssh ${REMOTE_USER}@${REMOTE_HOST} "sudo systemctl enable ${SERVICE_NAME}"
ssh ${REMOTE_USER}@${REMOTE_HOST} "sudo systemctl restart ${SERVICE_NAME}"
echo "7. 检查服务状态..."
ssh ${REMOTE_USER}@${REMOTE_HOST} "sudo systemctl status ${SERVICE_NAME}"
echo "=== 部署完成 ==="
2.2 启动脚本 #
bash
#!/bin/bash
APP_NAME="embedded-app"
JAR_FILE="embedded-app-all.jar"
LOG_DIR="/var/log/${APP_NAME}"
PID_FILE="/var/run/${APP_NAME}.pid"
JVM_OPTS="-server -Xms64m -Xmx128m \
-XX:+UseSerialGC \
-XX:+UseStringDeduplication \
-Xshare:on \
-XX:+ExitOnOutOfMemoryError"
start() {
if [ -f $PID_FILE ]; then
echo "Application is already running"
exit 1
fi
mkdir -p $LOG_DIR
echo "Starting ${APP_NAME}..."
nohup java $JVM_OPTS -jar $JAR_FILE > $LOG_DIR/app.log 2>&1 &
echo $! > $PID_FILE
echo "Started with PID $(cat $PID_FILE)"
}
stop() {
if [ ! -f $PID_FILE ]; then
echo "Application is not running"
exit 1
fi
echo "Stopping ${APP_NAME}..."
kill $(cat $PID_FILE)
rm $PID_FILE
echo "Stopped"
}
status() {
if [ -f $PID_FILE ]; then
echo "${APP_NAME} is running with PID $(cat $PID_FILE)"
else
echo "${APP_NAME} is not running"
fi
}
case "$1" in
start) start ;;
stop) stop ;;
status) status ;;
restart) stop; start ;;
*) echo "Usage: $0 {start|stop|status|restart}" ;;
esac
三、自动化CI/CD #
3.1 GitHub Actions #
yaml
name: Build and Deploy
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build with Maven
run: mvn clean package -DskipTests
- name: Run tests
run: mvn test
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: embedded-app
path: target/embedded-app-all.jar
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: embedded-app
- name: Deploy to Raspberry Pi
uses: appleboy/scp-action@v0.1.4
with:
host: ${{ secrets.RASPBERRY_PI_HOST }}
username: ${{ secrets.RASPBERRY_PI_USER }}
key: ${{ secrets.RASPBERRY_PI_KEY }}
source: "embedded-app-all.jar"
target: "/home/pi/app"
- name: Restart service
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.RASPBERRY_PI_HOST }}
username: ${{ secrets.RASPBERRY_PI_USER }}
key: ${{ secrets.RASPBERRY_PI_KEY }}
script: |
sudo systemctl restart embedded-app.service
sudo systemctl status embedded-app.service
四、Docker容器化 #
4.1 Dockerfile #
dockerfile
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY target/embedded-app-all.jar app.jar
ENV JAVA_OPTS="-Xms64m -Xmx128m -XX:+UseSerialGC"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
4.2 docker-compose.yml #
yaml
version: '3.8'
services:
embedded-app:
build: .
container_name: embedded-app
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./data:/app/data
- ./logs:/app/logs
devices:
- /dev/gpiomem:/dev/gpiomem
- /dev/i2c-1:/dev/i2c-1
- /dev/spidev0.0:/dev/spidev0.0
environment:
- TZ=Asia/Shanghai
- JAVA_OPTS=-Xms64m -Xmx128m -XX:+UseSerialGC
五、总结 #
构建与部署要点:
- 构建工具:使用Maven或Gradle管理依赖和构建
- 打包方式:选择合适的打包方式(独立JAR或依赖分离)
- 部署脚本:编写自动化部署脚本
- 服务管理:使用systemd管理应用服务
- 容器化:考虑Docker容器化部署
下一章我们将学习项目实战案例。
最后更新:2026-03-27