在Java企业级开发领域,拥有一套功能完备、架构清晰的快速开发平台是每个开发者和团队的梦想。若依(RuoYi)作为国内Spring Boot生态中最炙手可热的开源后台管理系统之一,因其优雅的架构、丰富的功能和详尽的中文文档而备受青睐。前后端分离版本更是契合了现代微服务与分布式开发的潮流。
一、 核心架构原理剖析
若依前后端分离版的成功,源于其对经典技术栈的合理运用和精妙的架构设计。
1、后端(Spring Boot)核心原理
1.1、权限控制:Spring Security + JWT
摒弃了传统的Session模式,采用无状态的JWT(JSON Web Token)进行身份认证。
具体流程:
- 用户登录,后端验证用户名密码。
- 验证成功后,生成一个包含用户信息(如用户ID、角色)的JWT Token返回给前端。
public String login(String username, String password, String code, String uuid) {
validateCaptcha(username, code, uuid);// 验证码校验
loginPreCheck(username, password);// 登录前置校验
// 用户验证
Authentication authentication = null;
try
{
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
...省略
// 生成token
return tokenService.createToken(loginUser);
}
- 前端后续所有请求都在HTTP Header(通常是 Authorization: Bearer <token>)中携带此Token。
- 后端通过自定义的 JwtAuthenticationTokenFilter 拦截请求,解析并验证Token的合法性。
- 验证通过后,将用户信息和权限数据存入SecurityContext,供后续的权限注解(如 @PreAuthorize("@ss.hasPermi('system:user:list')"))进行校验。
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
LoginUser loginUser = tokenService.getLoginUser(request);
// 判断是否登录并验证token
if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
tokenService.verifyToken(loginUser);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
chain.doFilter(request, response);
}
1.2、数据流转:MyBatis + 分页插件
- ORM:使用MyBatis作为数据持久层框架,SQL写在XML中,灵活且性能可控。
- 分页:集成了PageHelper插件,通过ThreadLocal机制,只需一句 PageHelper.startPage(pageNum, pageSize) 即可实现物理分页,与MyBatis的查询完美融合。
public class PageUtils extends PageHelper {
// 设置请求分页数据
public static void startPage() {
PageDomain pageDomain = TableSupport.buildPageRequest();
Integer pageNum = pageDomain.getPageNum();
Integer pageSize = pageDomain.getPageSize();
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
Boolean reasonable = pageDomain.getReasonable();
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
}
}
1.3、全局异常处理:@ControllerAdvice
- 通过此注解定义全局异常处理器(GlobalExceptionHandler),将系统的各类异常(业务异常、运行时异常等)统一捕获,并转换为结构化的JSON数据返回给前端,保证了API的友好性和一致性。
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
... 省略其他
// 权限校验异常
@ExceptionHandler(A***essDeniedException.class)
public AjaxResult handleA***essDeniedException(A***essDeniedException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权");
}
// 系统异常
@ExceptionHandler(Exception.class)
public AjaxResult handleException(Exception e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
return AjaxResult.error(e.getMessage());
}
}
2、 前端(Vue)核心原理
2.1、状态管理:Vuex(vue 2版本),Pinia (vue3版本)
用于集中式存储管理所有组件的状态。例如用户的登录状态、token、侧边栏的展开收起状态等,都存储在Vuex的Store中,使得状态变化可预测、可追踪。
当前文件store/index.js
const store = createPinia()
export default store
2.2、路由与权限:Vue Router + 动态路由
核心机制:用户登录后,根据其角色权限,向后端请求其有权访问的菜单列表。
动态加载:前端接收到菜单数据后,通过 router.addRoute() 方法动态地将路由规则添加到Vue Router实例中。这样,不同权限的用户登录后,看到的路由和菜单是完全不同的,实现了前端层面的权限过滤。
当前文件:router/index.js
import { createWebHistory, createRouter } from 'vue-router'
/* Layout */
import Layout from '@/layout'
// Note: 路由配置项
// 公共路由
export const constantRoutes = []
// 动态路由,基于用户权限动态去加载
export const dynamicRoutes = []
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition
}
return { top: 0 }
},
})
export default router
2.3、请求拦截:Axios 拦截器
请求拦截器:在所有请求发出前,自动从Vuex中获取Token并添加到请求头中。
响应拦截器:统一处理后端返回的数据。例如,遇到 401 状态码自动跳转到登录页;遇到 500 错误则弹出统一错误提示。
当前文件:utils/request.js
import axios from 'axios'
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: import.meta.env.VITE_APP_BASE_API,
// 超时
timeout: 10000
})
// request拦截器
service.interceptors.request.use(config => {})
二、 从Spring Boot 2 + Vue 2 到 Spring Boot 3 + Vue 3 的迁移与升级
2.1 后端升级:Spring Boot 2.x -> 3.x
Spring Boot 3是一个重大版本升级,其基石是Spring Framework 6和Java 17。
2.1.1、升级前提
确保你的JDK版本 >= 17。
2.1.2、核心变化与适配
-
Jakarta EE:这是最大的 breaking change。所有
javax.*包名被替换为jakarta.*。 -
影响范围:Servlet API, JPA, JAXB, JWS等。
-
修改点:需要全局替换代码中的
import javax.servlet...为import jakarta.servlet...。若依中与过滤器、Servlet相关的配置都需要调整。 -
依赖升级:内嵌的Tomcat、Spring Security、Spring Data等版本随之大幅升级,需关注其新特性和废弃的API。
-
GraalVM原生镜像支持:Spring Boot 3为GraalVM原生镜像提供了更好的支持,这为若依应用追求极致启动速度和内存占用提供了可能(尽管若依功能庞大,完全原生化有挑战)。
-
改进的Micrometer观测性:对微服务观测(Metrics, Tracing)的支持更完善。
2.2 前端升级:Vue 2 -> Vue 3
Vue 3带来了全新的组合式API(***position API)和性能提升。
2.2.1、升级策略
-
选项式API -> 组合式API:Vue 3完全兼容Vue 2的选项式API,但推荐使用新的组合式API。若依的升级过程,可以逐步将大型组件重构为使用
setup语法糖和ref,reactive,***puted等组合式函数。 -
Vuex -> Pinia:Vue 官方现在推荐使用 Pinia 作为状态管理库。Pinia的API更简洁,对TypeScript的支持更好。若依升级时,可以考虑将原有的Vuex Store迁移到Pinia。
-
Element-UI -> Element Plus:Vue 2配套的Element-UI已不再维护,必须升级到其Vue 3版本——Element Plus。这涉及大量组件标签和API的变更(如
el-button的type值等)。 -
构建工具:从Vue CLI逐步迁移到功能更强大、性能更好的Vite,将获得闪电般的冷启动和模块热更新(HMR)体验。