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 installnpm run buildnpm 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.propertiesapplication.yml中的资源路径配置正确,并且Redis等外部依赖已启动。

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

bash 复制代码
mvn clean package

构建过程:

  1. 父POM触发UA-UI模块构建,插件安装Node并执行npm installnpm 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包。

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

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

相关推荐
梅梅绵绵冰1 小时前
SpringCloud网关
java·spring·spring cloud
敲代码的嘎仔1 小时前
互动问答系统实战:两级评论模型、ES 搜索集成、Caffeine 多级缓存全记录
java·开发语言·数据库·elasticsearch·缓存·mybatis·高并发
QQ_21696290961 小时前
基于微服务架构的店铺管理系统的设计与实现
大数据·spring boot·后端·spring·微服务·小程序·架构
CoderYanger1 小时前
Java EE 进阶:2.3 JavaScript 综合案例
java·前端·javascript·css·职场和发展·java-ee·html
starzy19902 小时前
Flink TimeWindow 详解及代码实现:从三种时间语义到滚动与滑动窗口
java·服务器·flink
重生之小比特2 小时前
【Java SE】类和对象(一)
java·ide·intellij-idea
captain3762 小时前
网络原理(4)-TCP
java·服务器·网络·网络协议·tcp/ip·java-ee
青山木2 小时前
枚举类DP进阶:完全平方数与零钱兑换
java·数据结构·算法·leetcode·动态规划
小七在进步2 小时前
类和对象(二)
java·开发语言