创建 Spring Boot 3 项目目录结构脚本
这份脚本适合在任意目录下直接生成一个新的 Spring Boot 项目骨架,并补齐最基础的 Gradle Kotlin DSL 文件。
脚本
spring_init.sh:
#!/bin/bash
if [ $# -eq 0 ]; then
echo "错误: 请提供包名"
echo "使用方法: $0 com.example.myapp [project-name]"
exit 1
fi
PACKAGE_NAME=$1
if [[ "$PACKAGE_NAME" == *-* ]]; then
echo "错误: 包名不能包含连字符 -"
echo "如果项目目录需要使用连字符,请作为第二个参数传入"
echo "示例: $0 io.github.lookoutldz.arkmusic ark-music"
exit 1
fi
PACKAGE_PATH=$(echo $PACKAGE_NAME | sed 's/\./\//g')
LAST_PART=$(echo $PACKAGE_NAME | awk -F. '{print $NF}')
PROJECT_NAME="${2:-$LAST_PART}"
to_pascal_case() {
echo "$1" | awk -F '[-_. ]+' '{
for (i = 1; i <= NF; i++) {
if ($i != "") {
printf "%s%s", toupper(substr($i, 1, 1)), substr($i, 2)
}
}
printf "\n"
}'
}
MAIN_CLASS_NAME=$(to_pascal_case "$PROJECT_NAME")
PROJECT_ROOT="$(pwd)/$PROJECT_NAME"
MAIN_SRC="$PROJECT_ROOT/src/main/kotlin"
MAIN_RESOURCES="$PROJECT_ROOT/src/main/resources"
TEST_SRC="$PROJECT_ROOT/src/test/kotlin"
TEST_RESOURCES="$PROJECT_ROOT/src/test/resources"
CURRENT_DATE=$(date +"%Y/%-m/%-d")
ENV_FILE="$PROJECT_ROOT/.env"
APP_YAML_FILE="$MAIN_RESOURCES/application.yaml"
COMPOSE_FILE="$PROJECT_ROOT/docker-compose.local.yaml"
SETTINGS_FILE="$PROJECT_ROOT/settings.gradle.kts"
BUILD_FILE="$PROJECT_ROOT/build.gradle.kts"
GITIGNORE_FILE="$PROJECT_ROOT/.gitignore"
if [ -e "$PROJECT_ROOT" ]; then
echo "错误: 目标目录已存在: $PROJECT_ROOT"
exit 1
fi
echo "即将创建 Spring Boot 项目目录结构:"
echo " 包名: $PACKAGE_NAME"
echo " 项目名: $PROJECT_NAME"
echo " 目标目录: $PROJECT_ROOT"
read -r -p "确认继续创建? [y/N] " CONFIRM
case "$CONFIRM" in
y|Y|yes|YES)
;;
*)
echo "已取消创建"
exit 0
;;
esac
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/bootstrap"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/component"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/config"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/controller"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/dto"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/entity"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/enumeration"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/exception"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/repository"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/service/impl"
mkdir -p "$MAIN_SRC/$PACKAGE_PATH/util"
mkdir -p "$MAIN_RESOURCES/static"
mkdir -p "$MAIN_RESOURCES/templates"
mkdir -p "$MAIN_RESOURCES/db/migration"
mkdir -p "$MAIN_RESOURCES/META-INF"
mkdir -p "$TEST_SRC/$PACKAGE_PATH/controller"
mkdir -p "$TEST_SRC/$PACKAGE_PATH/service"
mkdir -p "$TEST_SRC/$PACKAGE_PATH/repository"
mkdir -p "$TEST_SRC/$PACKAGE_PATH/util"
mkdir -p "$TEST_RESOURCES"
MAIN_CLASS_FILE="$MAIN_SRC/$PACKAGE_PATH/${MAIN_CLASS_NAME}Application.kt"
TEST_CLASS_FILE="$TEST_SRC/$PACKAGE_PATH/${MAIN_CLASS_NAME}ApplicationTests.kt"
cat > "$MAIN_CLASS_FILE" << EOF
package $PACKAGE_NAME
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
@SpringBootApplication
class ${MAIN_CLASS_NAME}Application
fun main(args: Array<String>) {
runApplication<${MAIN_CLASS_NAME}Application>(*args)
}
EOF
cat > "$TEST_CLASS_FILE" << EOF
package $PACKAGE_NAME
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
/**
* @author looko
* @date $CURRENT_DATE
*
*/
@SpringBootTest
class ${MAIN_CLASS_NAME}ApplicationTests {
@Test
fun contextLoads() {}
}
EOF
cat > "$ENV_FILE" << EOF
############# used by docker compose and spring yml #############
# postgres
PG_IMAGE=nexusserver:8083/postgres:18-alpine
DB_HOST=localhost
DB_NAME=${PROJECT_NAME}
DB_USER=postgres
DB_PASS=postgres
EOF
cat > "$APP_YAML_FILE" << EOF
server:
address: 0.0.0.0
port: 8080
spring:
application:
name: "$PROJECT_NAME"
# 本地靠 .env 给 Spring 补变量,生产靠 Compose 给进程注入变量;JAR 最终只认 Spring 的配置键。
config:
import: optional:file:./.env[.properties]
# 数据库配置
datasource:
url: \${SPRING_DATASOURCE_URL:jdbc:postgresql://\${DB_HOST:127.0.0.1}:\${DB_PORT:5432}/\${DB_NAME:$PROJECT_NAME}?sslmode=disable}
username: \${SPRING_DATASOURCE_USERNAME:\${DB_USER}}
password: \${SPRING_DATASOURCE_PASSWORD:\${DB_PASS}}
driver-class-name: org.postgresql.Driver
# JPA配置
jpa:
hibernate:
ddl-auto: update
show-sql: false
open-in-view: true
properties:
hibernate:
format_sql: true
EOF
cat > "$COMPOSE_FILE" << EOF
services:
postgres:
image: \${PG_IMAGE}
environment:
TZ: Asia/Shanghai
POSTGRES_USER: \${DB_USER}
POSTGRES_PASSWORD: \${DB_PASS}
POSTGRES_DB: \${DB_NAME}
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C.UTF-8"
volumes:
- postgres_data:/var/lib/postgresql
ports:
- "5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
logging:
options:
max-size: "16m"
max-file: "4"
volumes:
postgres_data:
EOF
cat > "$SETTINGS_FILE" << EOF
rootProject.name = "$PROJECT_NAME"
EOF
cat > "$BUILD_FILE" << EOF
plugins {
kotlin("jvm") version "2.1.21"
kotlin("plugin.spring") version "2.1.21"
id("org.springframework.boot") version "3.5.16"
id("io.spring.dependency-management") version "1.1.7"
}
group = "$PACKAGE_NAME"
version = "0.0.1-SNAPSHOT"
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
repositories {
maven {
url = uri("http://nexusserver:8081/repository/maven-public/")
isAllowInsecureProtocol = true
}
}
dependencies {
// Spring
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-web")
// Kotlin
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("org.jetbrains.kotlin:kotlin-reflect")
// Driver
runtimeOnly("org.postgresql:postgresql")
// Test
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict", "-Xannotation-default-target=param-property")
}
}
allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
annotation("jakarta.persistence.Embeddable")
}
tasks.withType<Test> {
useJUnitPlatform()
}
EOF
cat > "$GITIGNORE_FILE" << EOF
.DS_Store
HELP.md
.gradle
.env
.env.*
!.env.sample
application-*.yml
!application-test.yml
build/
temp/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Kotlin ###
.kotlin
gradle.properties
docker-compose.local.yml
EOF
cat > .gitattributes << EOF
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary
EOF
echo "SpringBoot项目目录结构已创建:"
echo "包名: $PACKAGE_NAME"
echo "主应用类: ${MAIN_CLASS_NAME}Application.kt"
echo "测试类: ${MAIN_CLASS_NAME}ApplicationTests.kt"
echo "项目名: $PROJECT_NAME"
echo "项目目录: $PROJECT_ROOT"
echo "Gradle 文件: settings.gradle.kts, build.gradle.kts"
echo "Git 文件: .gitignore"
find "$PROJECT_ROOT/src" -type d | sort
示例
./spring_init.sh io.github.lookoutldz.arkmusic ark-music