Java框架快速入门: Spring Security+OAuth2之前后端分离多模块工程配置

概述

在前后端分离架构中,后端提供RESTful API,前端使用Vue、React等框架独立开发,最后需要整合为一个可部署的应用。

本文基于一个使用Spring Security与OAuth2的后端项目,介绍如何将前端工程与后端工程合并为Maven多模块结构,实现统一编译、打包和运行。

纲要

  • 前后端分离的多模块工程概览
  • Maven父子工程配置
    • 父POM:管理公共依赖与模块顺序
    • 后端模块(UA):拷贝前端静态资源到classpath
    • 前端模块(UA-UI):借助Maven插件执行Node构建
  • 前端项目关键文件
    • package.json:脚本与依赖管理
    • 前端开发依赖与运行时依赖
  • 工程启动与验证
  • 架构图与代码示例

多模块工程结构

我们有两个独立工程:后端UA和前端UA-UI。通过在它们的上层目录新建一个聚合POM,形成父子工程,便于统一构建。

项目目录结构如下:

dir 复制代码
parent/
├── pom.xml                  # 父POM
├── UA/                      # 后端Spring Boot工程
│   ├── pom.xml
│   └── src/main/resources/public/  # 前端编译产物拷贝至此
└── UA-UI/                   # 前端Vue工程
    ├── pom.xml
    └── package.json

模块构建顺序:前端先编译生成HTML/JS/CSS,再由后端模块在package阶段拷贝到resources/public,最终打包成一个可执行的Jar。
#mermaid-svg-XcMMknxkoePPuWFm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XcMMknxkoePPuWFm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XcMMknxkoePPuWFm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XcMMknxkoePPuWFm .error-icon{fill:#552222;}#mermaid-svg-XcMMknxkoePPuWFm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XcMMknxkoePPuWFm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XcMMknxkoePPuWFm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XcMMknxkoePPuWFm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XcMMknxkoePPuWFm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XcMMknxkoePPuWFm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XcMMknxkoePPuWFm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XcMMknxkoePPuWFm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XcMMknxkoePPuWFm .marker.cross{stroke:#333333;}#mermaid-svg-XcMMknxkoePPuWFm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XcMMknxkoePPuWFm p{margin:0;}#mermaid-svg-XcMMknxkoePPuWFm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XcMMknxkoePPuWFm .cluster-label text{fill:#333;}#mermaid-svg-XcMMknxkoePPuWFm .cluster-label span{color:#333;}#mermaid-svg-XcMMknxkoePPuWFm .cluster-label span p{background-color:transparent;}#mermaid-svg-XcMMknxkoePPuWFm .label text,#mermaid-svg-XcMMknxkoePPuWFm span{fill:#333;color:#333;}#mermaid-svg-XcMMknxkoePPuWFm .node rect,#mermaid-svg-XcMMknxkoePPuWFm .node circle,#mermaid-svg-XcMMknxkoePPuWFm .node ellipse,#mermaid-svg-XcMMknxkoePPuWFm .node polygon,#mermaid-svg-XcMMknxkoePPuWFm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XcMMknxkoePPuWFm .rough-node .label text,#mermaid-svg-XcMMknxkoePPuWFm .node .label text,#mermaid-svg-XcMMknxkoePPuWFm .image-shape .label,#mermaid-svg-XcMMknxkoePPuWFm .icon-shape .label{text-anchor:middle;}#mermaid-svg-XcMMknxkoePPuWFm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XcMMknxkoePPuWFm .rough-node .label,#mermaid-svg-XcMMknxkoePPuWFm .node .label,#mermaid-svg-XcMMknxkoePPuWFm .image-shape .label,#mermaid-svg-XcMMknxkoePPuWFm .icon-shape .label{text-align:center;}#mermaid-svg-XcMMknxkoePPuWFm .node.clickable{cursor:pointer;}#mermaid-svg-XcMMknxkoePPuWFm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XcMMknxkoePPuWFm .arrowheadPath{fill:#333333;}#mermaid-svg-XcMMknxkoePPuWFm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XcMMknxkoePPuWFm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XcMMknxkoePPuWFm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XcMMknxkoePPuWFm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XcMMknxkoePPuWFm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XcMMknxkoePPuWFm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XcMMknxkoePPuWFm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XcMMknxkoePPuWFm .cluster text{fill:#333;}#mermaid-svg-XcMMknxkoePPuWFm .cluster span{color:#333;}#mermaid-svg-XcMMknxkoePPuWFm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XcMMknxkoePPuWFm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XcMMknxkoePPuWFm rect.text{fill:none;stroke-width:0;}#mermaid-svg-XcMMknxkoePPuWFm .icon-shape,#mermaid-svg-XcMMknxkoePPuWFm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XcMMknxkoePPuWFm .icon-shape p,#mermaid-svg-XcMMknxkoePPuWFm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XcMMknxkoePPuWFm .icon-shape .label rect,#mermaid-svg-XcMMknxkoePPuWFm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XcMMknxkoePPuWFm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XcMMknxkoePPuWFm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XcMMknxkoePPuWFm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 父POM
UA-UI 前端模块
UA 后端模块
maven-exec-plugin: npm install & build
target/dist
maven-resources-plugin 拷贝
spring-boot:repackage
可运行Jar

父POM配置

父POM继承自spring-boot-starter-parent,统一管理Spring Boot版本与公共属性,并在<modules>中声明两个子模块,前端模块放在第一位以保证编译顺序。

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>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>ua-parent</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <properties>
        <java.version>11</java.version>
        <node.version>v16.14.2</node.version>
        <npm.version>8.5.0</npm.version>
    </properties>

    <modules>
        <module>UA-UI</module>
        <module>UA</module>
    </modules>
</project>

通过父POM,子模块无需重复声明Spring Boot的版本继承关系,公共属性也可复用。

后端模块配置

后端UA模块的pom.xml原本是独立Spring Boot项目,现在其<parent>指向父POM。为接收前端产物,添加maven-resources-plugin,在validate阶段将UA-UI编译输出目录的内容拷贝到src/main/resources/public,确保打包时静态资源位于classpath下。

xml 复制代码
<!-- UA/pom.xml -->
<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>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>ua-parent</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>UA</artifactId>
    <packaging>jar</packaging>

    <dependencies>
        <!-- Spring Boot, Security, OAuth2 等依赖 -->
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-frontend</id>
                        <phase>validate</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${basedir}/src/main/resources/public</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${basedir}/../UA-UI/target/dist</directory>
                                    <filtering>false</filtering>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

确保UA-UI模块已执行构建,target/dist目录存在,否则拷贝会失败。通过这种方式,最终Jar包中的/public目录即前端静态文件,可直接通过浏览器访问。

前端模块配置

前端模块本质上是一个Node项目,Maven通过exec-maven-plugin调用系统安装的Node和npm,或者使用frontend-maven-plugin自动下载Node并执行构建。这里展示自动下载的方案,避免CI环境缺少Node的问题。

xml 复制代码
<!-- UA-UI/pom.xml -->
<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>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>ua-parent</artifactId>
        <version>1.0.0</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>UA-UI</artifactId>
    <packaging>pom</packaging>

    <build>
        <plugins>
            <plugin>
                <groupId>com.github.eirslett</groupId>
                <artifactId>frontend-maven-plugin</artifactId>
                <version>1.12.1</version>
                <configuration>
                    <nodeVersion>${node.version}</nodeVersion>
                    <npmVersion>${npm.version}</npmVersion>
                    <workingDirectory>${basedir}</workingDirectory>
                </configuration>
                <executions>
                    <execution>
                        <id>install node and npm</id>
                        <goals>
                            <goal>install-node-and-npm</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>npm install</id>
                        <goals>
                            <goal>npm</goal>
                        </goals>
                        <configuration>
                            <arguments>install</arguments>
                        </configuration>
                    </execution>
                    <execution>
                        <id>npm run build</id>
                        <goals>
                            <goal>npm</goal>
                        </goals>
                        <configuration>
                            <arguments>run build</arguments>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

该插件会自动下载指定版本的Node和npm,然后执行npm install和npm run build。npm run build命令对应前端package.json中定义的构建脚本。

前端关键文件:package.json

前端依赖和脚本通过package.json管理,类似于Java中的pom.xml。典型配置如下:

json 复制代码
{
  "name": "ua-ui",
  "version": "1.0.0",
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "vue": "^2.6.14",
    "vue-router": "^3.5.1",
    "vuex": "^3.6.2",
    "axios": "^0.27.2",
    "ant-design-vue": "^1.7.8"
  },
  "devDependencies": {
    "@vue/cli-service": "^5.0.8",
    "vue-template-compiler": "^2.6.14"
  }
}
  • scripts 定义了可执行命令,serve用于本地开发,build用于生产构建。
  • dependencies 是运行时依赖,如Vue全家桶、axios HTTP客户端、Ant Design Vue UI库。
  • devDependencies 是开发编译依赖,如Vue CLI构建工具和模板编译器。

前端项目启动时,开发者需预先安装Node环境,然后执行npm run serve即可在本地热加载开发。Maven整合后,npm run build会自动生成静态文件到target/dist目录。

启动与验证

调整目录结构后,确保后端application.properties或application.yml中的资源路径配置正确,并且Redis等外部依赖已启动。

在工程根目录执行统一构建:

bash 复制代码
mvn clean package

构建过程:

  1. 父POM触发UA-UI模块构建,插件安装Node并执行npm install与npm run build。
  2. UA模块在validate阶段拷贝前端产物到src/main/resources/public,随后编译打包。
  3. 生成的Jar包包含后端逻辑与前端静态资源,可直接运行:
bash 复制代码
java -jar UA/target/UA-1.0.0.jar

访问 http://localhost:8080 即可看到前端页面,通过登录接口与后端OAuth2认证交互。前端使用axios发送HTTP请求,与后端的Spring Security安全体系对接,具体的认证流程将在后续章节展开。

总结

本文介绍了将独立的前后端工程整合为Maven多模块项目的方法,利用父POM统一管理依赖版本,借助frontend-maven-plugin实现Node环境标准化,通过资源拷贝将前端编译产物嵌入后端Jar包。

这种结构便于持续集成,使全栈应用成为一个整体交付单元。

读者可根据实际框架调整前端构建工具和目录布局,实现类似的一体化构建。

相关推荐
殷色玫瑰5 分钟前
C++入门基础复习:从命名空间到引用与nullptr,一篇重新捡回C++基础
java·开发语言·c++
大侠归来1 小时前
C语言素数判断:从入门到进阶
java·c语言·算法
谢亮_vipxieliang1 小时前
Spring Boot 自动配置原理:从 @SpringBootApplication 到自定义 Starter
java·spring boot·后端
朝朝辞暮i2 小时前
C++ 第 25 课:getter / setter + this
java·javascript·c++
蜗牛互联网2 小时前
Python接入Gemini 3.8 Flash实现票据视觉抽取与规则校验
java·javascript·网络·人工智能·python
萧瑟余晖2 小时前
Spring 事务管理与数据访问详解
spring
用户094248568032 小时前
第22章:JIT编译分层——Interpreter、C1、C2与热点探测
java·jvm
周杰偷奶茶2 小时前
【JavaSE】类和对象基础
java·开发语言
ly76892 小时前
Redis 主从复制全解析:从 PSYNC 协议到复制积压缓冲区的故障切换边界
java·数据库·redis·主从复制·故障切换·psync
Alice-YUE2 小时前
提示词工程:工业级 Prompt 写法与分层组装
java·开发语言·大模型·prompt·提示词工程·system prompt