博主是在 2018 年中就接触了 RuoYi 我的项目 这个我的项目,对于过后国内的开源后盾管理系统来说,RuoYi 算是一个完成度较高,易读易懂、界面简洁好看的前后端不拆散我的项目。
对于过后刚入行还在写 jsp 模板的博主来说,RuoYi 我的项目在后盾根底性能、模块划分、易用性和页面好看度上,比照同期用 Java 开源的前后端不拆散后盾我的项目整体上是高了一个等级的。并且我的项目 commit 频繁,代码品质一直进步、bug 一直修复,使得这个我的项目在明天来说任然是具备学习价值的。
本文博主尽量用一个感性视角率领大家由浅入深看 RuoYi 我的项目 v4.7.6 版本的优良设计。
- RuoYi 仓库地址:https://gitee.com/y_project/RuoYi
- 版本:v4.7.6
一、疾速理解
RuoYi 我的项目是一个基于 SpringBoot + Mybatis + Shiro
开发的轻量级 Java 疾速开发框架,它蕴含根底的后盾治理性能以及权限管制。我的项目作者对于 RuoYi 我的项目的定调是这样的:
RuoYi 是一款基于 SpringBoot+Bootstrap 的极速后盾开发框架。
RuoYi 是一个 Java EE 企业级疾速开发平台,基于经典技术组合(Spring Boot、Apache Shiro、MyBatis、Thymeleaf、Bootstrap)。内置模块如:部门治理、角色用户、菜单及按钮受权、数据权限、零碎参数、日志治理、告诉布告等。在线定时工作配置;反对集群,反对多数据源,反对分布式事务。
二、多模块设计
如果想疾速理解一个我的项目的设计理念那间接下载这个我的项目,查看我的项目构造即可略知一二。这里参考官网给出的我的项目构造:
com.ruoyi
├── ruoyi-common // 工具类
│ └── annotation // 自定义注解
│ └── config // 全局配置
│ └── constant // 通用常量
│ └── core // 外围管制
│ └── enums // 通用枚举
│ └── exception // 通用异样
│ └── json // JSON 数据处理
│ └── utils // 通用类解决
│ └── xss // XSS 过滤解决
├── ruoyi-framework // 框架外围
│ └── aspectj // 注解实现
│ └── config // 系统配置
│ └── datasource // 数据权限
│ └── interceptor // 拦截器
│ └── manager // 异步解决
│ └── shiro // 权限管制
│ └── web // 前端管制
├── ruoyi-generator // 代码生成(不必可移除)├── ruoyi-quartz // 定时工作(不必可移除)├── ruoyi-system // 零碎代码
├── ruoyi-admin // 后盾服务
├── ruoyi-xxxxxx // 其余模块
由上可知,RuoYi 前后端不拆散我的项目依照模块划分成了七个模块
- ruoyi-common 蕴含了整个我的项目根底的注解、枚举、异样、帮忙类的定义以及在 core 包中定义的根底用户、角色、菜单、字典类的 entity 对象以及其余 ajax 响应后果、分页参数、文本处理等一众根底类
- ruoyi-framework 是整个我的项目的外围模块,因为这外面有整个我的项目的外围配置代码,全副在 config 目录下
其中
ShiroConfig
是最外围的配置,整合了 shiro 框架,给我的项目提供了权限治理性能 - ruoyi-generator 次要用作代码生成,目蕴含一个对外提供服务模块所需的
contrller、domain、mapper、service、util、config
等包。如果增加Spring Boot
启动类就能够间接作为独立我的项目启动。作为 ruoyi-admin 模块的插件存在,通过削减 pom 依赖来管制插件是否开启 - ruoyi-quartz 次要用作定时工作,集成了分布式定时任务调度框架 quartz,目录与 ruoyi-generator 相似,也是作为 ruoyi-admin 模块的插件存在,通过削减 pom 依赖来管制插件是否开启
- ruoyi-system 蕴含后盾零碎中非核心用户、角色、菜单、字典类实体对象之外的
mapper、service
层性能代码 - ruoyi-admin 用作后盾 web 服务,蕴含后盾零碎的
controlelr
层代码以及配置文件。也是整个 RuoYi 我的项目后盾的启动入口 - ruoyi-xxxxxx 作为由开发人员引入的其余模块,个别是新业务模块代码
最初再列出我的项目 ruoyi-admin 的模块依赖图,简略解说下各个模块间的依赖关系
- ruoyi-common 根底通用模块
- ruoyi-system依赖 ruoyi-common 模块
- ruoyi-framework依赖 ruoyi-system 模块
- ruoyi-generator依赖 ruoyi-common 模块
- ruoyi-quartz依赖 ruoyi-common 模块
-
ruoyi-admin依赖ruoyi-framework、ruoyi-generator、ruoyi-quartz
看完了 RuoYi 的我的项目构造与模块依赖关系,大家能够看看本人日常开发业务后盾的我的项目构造。或多或少,大家都可能遇到过那种一把梭所以代码都全副放在同一个 Maven 模块的我的项目。比照 RuoYi 的我的项目构造,置信大家都会感觉多模块设计是比单模块更优的设计。
拆分出 ruoyi-common 模块后,其余插件模块能够只援用 ruoyi-common 的通用代码就能实现插件性能开发。拆分出 ruoyi-framework 模块后,我的项目中的外围配置代码全副放在 ruoyi-framework 中与 ruoyi-admin 拆散,避免对 ruoyi-admin 的批改影响到我的项目外围配置。博主认为正当的模块拆分能够缩小模块间的耦合与改变模块所带来的影响范畴。
通过多模块设计将我的项目划分成 common -> system -> framework -> admin
由低到高的外围模块以及插件模式的 common -> ruoyi-generator|ruoyi-quartz
模块。模块之间尽量松耦合,不便模块降级、增减模块。
三、优雅的操作日志记录
在 RuoYi 我的项目中通过 com.ruoyi.framework.aspectj.LogAspect
日志切面,以自定义日志注解作为切点来记录日志信息,这样能够防止在接口中进行反复的操作日志记录代码编写,以及日志记录产生异样也不影响接口返回。
自定义日志注解如下:
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
*/
public String title() default "";
/**
* 性能
*/
public BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
public OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保留申请的参数
*/
public boolean isSaveRequestData() default true;
/**
* 是否保留响应的参数
*/
public boolean isSaveResponseData() default true;
/**
* 排除指定的申请参数
*/
public String[] excludeParamNames() default {};}
能够看到 LogAspect
注解类中定义了模块名称、业务操作类型(新增、批改、删除、导出等业务操作)、操作人类别(其余、后盾、手机等)、是否保留申请的参数、是否保留响应的参数、排除指定的申请参数等六个属性。咱们在应用自定义注解时,通常只用依据接口作用指定模块名称和业务操作类型就能够,日志注解应用如下:
@Log(title = "参数治理", businessType = BusinessType.INSERT)
@PostMapping("/add")
@ResponseBody
public AjaxResult addSave(@Validated SysConfig config) {...}
自定义日志注解切面代码如下:
/**
* 操作日志记录解决
*
* @author ruoyi
*/
@Aspect
@Component
public class LogAspect {private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
/** 排除敏感属性字段 */
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword" ...};
/** 计算操作耗费工夫 */
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<Long>("Cost Time");
/**
* 解决申请前执行
*/
@Before(value = "@annotation(controllerLog)")
public void boBefore(JoinPoint joinPoint, Log controllerLog) {TIME_THREADLOCAL.set(System.currentTimeMillis());
}
/**
* 解决完申请后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 拦挡异样操作
*
* @param joinPoint 切点
* @param e 异样
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {handleLog(joinPoint, controllerLog, e, null);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog,
final Exception e, Object jsonResult)
...
}
}
通过 aop 切面对应用了日志注解的办法进行三个方面的切入:
@Before(value = "@annotation(controllerLog)")
解决申请前执行 记录日志记录开始工夫。@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
解决完申请后执行 记录日志完结工夫,填充操作日志最初异步插入。@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
以及解决完申请产生异样后执行 记录日志完结工夫,填充操作日志、异样起因最初异步插入日志。
在应用了日志切面后,操作日志记录的逻辑与后盾各性能接口的业务逻辑相拆散,缩小了日志记录代码的的反复编写,前期批改日志记录逻辑只用批改切面代码,进步了操作日志记录的可维护性,也防止了日志记录产生异样时影响业务接口,应用线程池插入日志记录还能够缩短接口响应时长。能够看到通过切面实现日志记录有这么多益处。
其实 RuoYi 中不仅仅只有日志记录应用了切面解决,像是日常开发中数据过滤权限、多数据源切换等也都应用了切面解决。应用切面能够让咱们集中处理繁多逻辑、不便削减关注点、缩小反复代码、对管制层零侵入性以及进步可维护性。
四、总结
本文目前从模块设计、操作日志记录等两个方面对 RuoYi 我的项目进行了解说。如果大家也应用过 RuoYi 我的项目,欢送大家探讨发言给出想法,最初心愿本文对大家日常我的项目开发有所帮忙,喜爱的敌人们能够点赞加关注😘。