全面解析若依框架(springboot-vue前后分离--后端部分)

news2025/1/20 5:58:46

1、 若依框架分解

- 启动配置

  • 前端启动
# 进入项目目录
cd ruoyi-ui

# 安装依赖
npm install

# 强烈建议不要用直接使用 cnpm 安装,会有各种诡异的 bug,可以通过重新指定 registry 来解决 npm 安装速度慢的问题。
npm install --registry=https://registry.npmmirror.com

# 本地开发 启动项目
npm run dev
  • 后端启动

创建数据库ry-vue,导入ry_2021xxxx.sql,quartz.sql,加载好依赖直接启动。

在这里插入图片描述
在这里插入图片描述

- 使用技术

后端技术
SpringBoot
Spring Security
JWT
MyBatis
Druid
Fastjson

- 功能分解

  • 分页实现
  • 导入导出
  • 上传下载(框架使用的简单,不做讲解)
  • 权限控制
  • 事务管理(这里使用@Transactional,不做讲解,具体和Spring的8种事务有关)
  • 异常处理
  • 系统日志
  • 数据权限
  • 多数据源
  • 定时任务
  • 系统接口(系统接口采用swagger2来生成文档以及注释,很简单,不做讲解)
  • 防重复提交
  • 国际化支持(这个不做讲解)

2、功能详解

分页实现(使用PageHelper)

根据文档,对于每个list的方法,采用startPage()实现分页,该方法为BaseController(基础控制类)的一个方法。startPage()方法使用了TableSupport获取请求中的对应的分页参数

分页查询条件

(注意resonable为合理化查询参数,当请求pageNum>maxpageNum,page为最后一页,当pageNum<0,page为第一页)

在这里插入图片描述

注意坑点

PageHelper只对最近的sql起作用,分页条件消费一次就消失,所有使用startPage()方法要紧跟着分页的方法,并且只要使用了startPage()方法就必须有分页方法,否则会使其他线程的方法莫名奇妙的分页。

导入导出实现(ExcelUtil.java)

excel是对org.apache.poi.xssf.usermodel进行封装实现的excel的导入导出功能。

导出

在ExcelUtil中的exportExceL()方法中,init()方法对参数进行初始化,init的方法中初始化了很多参数,例如实体的Workbook、Fields等,Fields主要用于后面获取@Excel注解然后进行获取注解中的内容绘制Excel,然后使用Workbook对象进行数据的Excel导出。

在这里插入图片描述

导入

ExcelUtil的importExcel(String sheetName, InputStream is, int titleNum)方法中进行了解解析Excel表的操作,并且将导入的数据对照的着实体做了一个类型的转换,最后返回一个list集合。

拓展

数据处理

  • 如果我们想对数据进行额外处理,需要配置字段的@Excel中的参数hanlder,需要继承ExcelHandlerAdapter实现format方法。该方法会在导出的时候使用dataFormatHandlerAdapter(val, attr)通过反射对数据进行最后的处理。
    在这里插入图片描述

导出自定义隐藏列

自定义隐藏列通过excludeFields数组来进行控制,在List<Object[]> getFields获取字段信息的时候将通过excludeFields来进行字段排除,使用hideColumn(“userId”)方法来隐藏,使用方法如下。

在这里插入图片描述

上传下载

上传下载功能位于CommonController,这个很简单不做讲解。

权限控制

根据文档所述,若依采用shiro的注解来控制权限,采用的是RBAC角色权限控制模型,但实际上看了源码(可能文档不一致),它采用的是springSecurity的PreAuthorize注解来实现。但是他的角色权限控制是处于一个静态的,无法动态控制某个方法需要哪些权限。

示例

@PreAuthorize("@ss.hasPermi('system:dept:list')")
    @GetMapping("/list")
    public AjaxResult list(SysDept dept)
    {
        List<SysDept> depts = deptService.selectDeptList(dept);
        return success(depts);
    }

  • 解析
    ss为spring容器中的PermissionService,它为一个实例化的Bean,我们可以自定义权限的实现,若依框架自定义的权限在com.ruoyi.framework.web.service.PermissionService下,需要实现hasPermi()方法,同样的其他方法也能使用,只需要在注解中标注@ss.hasPermi(‘system:dept:list’),hasPermi可以替代为其他方法,参数为括号中的内容。

异常处理

异常处理使用的@ControllerAdvice,对于异常,我们不需要在业务层捕捉异常,统一抛出给控制层。这个没有做封装,比较简单。

参数验证

参数验证使用的是spring boot的Validated,只需要在请求的参数加上@Validated注解,对应的实体的字段上加上对应的验证注解。具体查询 所有的验证注解

拓展

我们可以进行自定义验证,若依自定义了XSS跨站脚本验证的注解XSS,注解@Constraint(validatedBy = { XssValidator.class }),指定验证的类。

在这里插入图片描述

在这里插入图片描述

系统日志

对于系统日志,若依采用的是自定@Log注解结合Aop进行实现,Log注解类在
com.ruoyi.common.annotation.Log,对应的Aop切面类在
com.ruoyi.framework.aspectj.LogAspect。系统采用了两个方式进行切面日志的实现,第一种为AfterReturning(返回前方法增强),第二种为AfterThrowing(抛出异常前方法增强)。系统的日志的实体为com.ruoyi.system.domainSysOperLog

AfterReturning和AfterThrowing

具体实现在handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult)方法中,具体日志里处理的就是日志实体所需的参数,除了注解中的参数外,还有ip、用户、请求方式以及路径等,这个很简单不做讲解。

数据权限

数据权限主要是为了一些敏感数据不能跨部门访问,admin为默认角色拥有所有数据权限,注解为@DataScope(deptAlias = “d”),切面类为com.ruoyi.framework.aspectj.DataScopeAspect。

数据的具体过滤

数据过滤的方法在dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission),根据文档知道数据过滤主要是通过注入BaseEntity一个过滤sql,然后在mapper.xml将sql以参数的方式进行拼接完成数据过滤。(前提是过滤的数据的实体需要继承BaseEntity)

在这里插入图片描述

多数据源

多数据源主要采用的是Aop,使用的是自定义的@DataSource注解。value用来表示数据源名称。

使用

  • 首先要配置application-druid.yml
# 从库数据源
slave:
	# 从数据源开关/默认关闭
	enabled: true
	url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
	username: root
	password: password
  • 在DataSourceType类添加数据源枚举
/**
 * 从库
 */
SLAVE
  • 在DruidConfig配置读取数据源

此操作会将yaml中的数据源的数据读取到DruidProperties,完成数据源的属性注入。

@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource(DruidProperties druidProperties)
{
	DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
	return druidProperties.dataSource(dataSource);
}

  • 在dataSource(DataSource masterDataSource)将数据源注入到容器中
 setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource");

内部具体实现

  • 具体实现在com.ruoyi.framework.aspectj.DataSourceAspect中,这使用的是一个环绕增强,通过一个动态的数据源上下文对象DynamicDataSourceContextHolder去设置数据源的bean的名字,具体通过ThreadLocal完成参数的传递,并且使用完需要移除ThreadLocal中的值避免内存泄漏。
  • 最核心的实现是位于com.ruoyi.framework.datasource.DynamicDataSource,它继承了Springboot的AbstractRoutingDataSource,需要设置defaultTargetDataSource和targetDataSources,这个实现类是在DruidConfig中进行初始化,在请求进来时,会根据determineCurrentLookupKey()这个方法区去获取数据源的bean名字来获取数据源。
 public Connection getConnection() throws SQLException {
        return this.determineTargetDataSource().getConnection();
    }
  protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = this.determineCurrentLookupKey();
        DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }

        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        } else {
            return dataSource;
        }
    }

这是一段AbstractRoutingDataSource中的连接代码,在连接会使用determineCurrentLookupKey()从resolvedDataSources容器中获取数据源。

定时任务

定时任务底层采用的是quartz。任务实体为com.ruoyi.quartz.domain.SysJob,这个实体主要的字段有invokeTarget以及cronExpression来控制执行内容和时间。定时任务工具类在com.ruoyi.quartz.util.ScheduleUtils

使用

在这里插入图片描述

调用方法为Component指定名称,指定方法和参数。然后使用cron表达式确定定时的时间。具体的cron表达式可以看
cron表达式

具体实现

  • 新增定时任务首先会判断定时任务的执行表达式以及cron表达式,创建定时任务首先判断是否并发执行,选择合适的执行对象Job。QuartzJobExecution为并发执行的对象,QuartzDisallowConcurrentExecution相反。
  • 传入定时任务的一些组和id以及执行类对象构造表达式调度构建器对象,实际上所有的定时任务是通过表达式调度对象scheduler来调度所有任务。
  • scheduler为调度对象,里面有调度资源对象QuartzSchedulerResources来控制所有的任务资源,例如pauseJob方法,会使用this.notifySchedulerThread()来唤起定时任务线程,this.notifySchedulerListenersPausedJob()来唤醒监听器来停止任务。
  • 在添加调度任务,底层会使用this.resources.getJobStore().storeJobAndTrigger(jobDetail, trig);进行调度资源的添加,同样会唤起线程和监听器来监听执行任务。定时任务调度对象的具体实现类在org.quartz.core.QuartzScheduler,父接口为org.quartz.core.RemotableQuartzScheduler

防重复提交

防重复提交的实现在com.ruoyi.framework.interceptor.RepeatSubmitInterceptor下,是通过使用拦截器,获取执行方法的方法注解@RepeatSubmit,注解通过提交的间隔时间来进行判断重复提交

具体实现(通过redis的过期时间实现)

  • 具体实现的方法在isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation)中,RepeatedlyRequestWrapper是为了设置请求和响应的编解码格式为UTF-8。
  • 首先这个方法将请求的数据以字符串的形式获取,这里做了不同请求参数的获取处理,通过指定key + url + 消息头来拼成字符串组成key,使用map为value,map为url->datamap的结构,当key不存在时,则为新的请求数据,若不存在,则证明上一次存在相同方式的数据,则对数据以及请求的间隔时间进行判断。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/93024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法刷题打卡第47天:排序数组---归并排序

排序数组 难度&#xff1a;中等 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,3,1] 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;nums [5,1,1,2,0,0] 输出&#xff1a;[0,0,1,1,2,5]归并排…

用CSS给健身的侣朋友做一个喝水记录本

前言 事情是这样的&#xff0c;由于七八月份的晚上时不时就坐在地摊上开始了喝酒撸串的一系列放肆的长肉肉项目。 这不&#xff0c;前段时间女朋友痛下决心&#xff08;心血来潮&#xff09;地就去报了一个健身的私教班&#xff0c;按照教练给的饮食计划中&#xff0c;其中有一…

卵巢早衰与微生物群,营养治疗新进展

卵巢早衰 卵巢早衰&#xff08;premature ovarian insufficiency&#xff0c;简称POI&#xff09;在生殖系统疾病中位居首位&#xff0c;这些疾病可能会损害多个功能系统&#xff0c;降低生活质量&#xff0c;最终剥夺女性患者的生育能力。 目前的激素替代疗法不能改善受孕或降…

NR PDSCH(七) DL SPS

非动态调度&#xff0c;除了PUSCH configured grant type 1和2的传输&#xff0c;还有PDSCH SPS 传输&#xff0c;两者的流程基本类似&#xff0c;也有些小区别。在实网并没有见过配置DL SPS PDSCH传输的log&#xff0c;但还是按顺序理一遍相关内容。 RRC/MAC 先看下MAC 38.32…

文件上传,还存储在应用服务器?

一般项目开发中都会有文件、图片、视频等文件上传并能够访问的场景。要实现这样的场景&#xff0c;要么把文件存储在应用服务器上&#xff0c;要么搭建文件服务来存储。但是这两种方式也有不少的缺点&#xff0c;增加运维的成本。 因此&#xff0c;追求用户体验的项目可能会考…

Tomcat安装配置全解

&#x1f44c; 棒棒有言&#xff1a;也许我一直照着别人的方向飞&#xff0c;可是这次&#xff0c;我想要用我的方式飞翔一次&#xff01;人生&#xff0c;既要淡&#xff0c;又要有味。凡事不必太在意&#xff0c;一切随缘&#xff0c;缘深多聚聚&#xff0c;缘浅随它去。凡事…

数据库分库分表

文章目录为什么要分库分表&#xff1f;数据切分垂直切分水平切分&#xff08;每个表的结构相同&#xff09;范围拆分取模拆分&#xff08;一般为业务主键&#xff09;分库分表带来的问题数据倾斜问题热点问题事务问题聚合查询问题分页问题非分区业务查询分库分表实现或工具hash…

DSP篇--C6701功能调试系列之 UART串口测试

目录 1、原理 2、测试 调试的前期准备可以参考前面的博文&#xff1a;DSP篇--C6701功能调试系列之前期准备_nanke_yh的博客-CSDN博客 UART串口收发数据存在两种模式&#xff1a;通常的串口模式&#xff08;McBSP in Serial Port Mode&#xff09;和GPIO模式&#xff08;McBS…

哈希表及其与Java类集的关系

目录 1.哈希表的概念 2.哈希冲突 3.如何避免哈希冲突? 3.1哈希函数设计 3.2 负载因子的调节 4.解决哈希冲突 4.1闭散列 4.1.1线性探测 4.1.2二次探测 4.2开散列(哈希桶) 5.HashMap 6.HashSet 1.哈希表的概念 假设有一组数据,要让你去搜索其中的一个关键码,这种场…

JWT快速入门及所需依赖

目录 1.JWT 1.1什么是JWT 1.2JWT的构成 jwt的头部 payload signature 1.3JWT快速入门案例 2Jwt认证&#xff08;微服务&#xff09; 2.1微服务下统一权限认证 2.2应用认证 3.无状态的JWT令牌如何实现续签功能&#xff1f; 3.1不允许改变Token令牌实现续签 3.2允许改…

计算机毕业设计django基于python大学生多媒体学习系统

项目介绍 随着计算机多媒体技术的发展和网络的普及。采用当前流行的B/S模式以及3层架构的设计思想通过Python技术来开发此系统的目的是建立一个配合网络环境的大学生多媒体学习系统的平台,这样可以有效地解决数据学习系统混乱的局面。 本文首先介绍了大学生多媒体学习系统的发…

eslint Parsing error: The keyword ‘export‘ is reserved

报错 原因 ECMAScript modules(import/export) 是 es6 的语法。 根据 eslint 官方文档 Configure language options &#xff0c;eslint 默认使用 es5 语法&#xff1a; 解决 要让 eslint 知道我在使用 es6 的 modules 语法。有下面几种方法&#xff1a; 设置 env 为 es6&am…

喜讯 | 第三届国际科创节,企企通喜提两项大奖

近日&#xff0c;第三届国际科创节暨数服会STIF奖评选活动重磅揭晓&#xff0c;旨在向科技创新与数字化转型引领者致敬。企企通作为作为数字化采购平台领军者&#xff0c;凭借业内领先的技术实力与优秀的服务口碑&#xff0c;经过层层筛选和专业评审&#xff0c;企企通最终荣膺…

【LeetCode每日一题:1785. 构成特定和需要添加的最少元素~~~数组公式推导+防止整型溢出+向上取整+贪心】

题目描述 给你一个整数数组 nums &#xff0c;和两个整数 limit 与 goal 。数组 nums 有一条重要属性&#xff1a;abs(nums[i]) < limit 。 返回使数组元素总和等于 goal 所需要向数组中添加的 最少元素数量 &#xff0c;添加元素 不应改变 数组中 abs(nums[i]) < limi…

内存管理:虚拟地址空间和堆

准备用一个系列来总结一下内存管理涉及到的相关知识&#xff0c;范围从底层的数据结构和算法&#xff0c;到上层的API的使用&#xff0c;这里的内存管理&#xff0c;目前打算主要是侧重在堆的管理&#xff0c;本文作为一个引子&#xff0c;先粗略讲一下虚拟地址空间、堆管理、a…

​合并PDF文件什么方法很简单?看完你就明白了

想要将几个PDF文件合并到一起&#xff0c;什么方法使用起来是很简单的呢&#xff1f;PDF文件作为大家经常使用的文件之一&#xff0c;对它的编辑需求也很多&#xff0c;除了需要编辑文件的内容之外&#xff0c;还有需要将几个文件合并到一起使用的需求。那么我们如果遇到这种情…

traffic-forward

traffic-forward traffic-forward 是一款python开发的流量转发工具&#xff0c;可以使用python脚本行运行&#xff0c;也可以封装使用命令行&#xff0c;同样可以使用pyinstaller等工具进行封装成Macos&#xff0c;Linux, Windows 下的可执行文件运行&#xff0c;可用于本地流量…

简单理解HTML区块_HTML学习第七篇区块元素和内联元素

简单理解HTML区块_区块元素和内联元素HTML篇_第七章、区块一、区块元素和内联元素1.1块级元素1.2内联元素二、<div>元素三、<span>元素HTML篇_第七章、区块 一、区块元素和内联元素 HTML元素可以通过<div>和<span>元素组合起来&#xff0c;大多数 HT…

固定行数的纵向分栏

【问题】 what can ı configure the jasper report detail heapriider layout ? ı want to print datas side by side and every sides have 4 datas sub bottom 1 data1 5 data5 2 data2 6 data6 3 data3 4 data4 【回答】 整张报表纵向分栏可在 jasper 中设置分栏数&a…

性能高、上手快,实体类转换工具 MapStruct 到底有多强大

1.什么是MapStruct 1.1 JavaBean 的困扰 对于代码中 JavaBean之间的转换&#xff0c; 一直是困扰我很久的事情。在开发的时候我看到业务代码之间有很多的 JavaBean 之间的相互转化&#xff0c; 非常的影响观感&#xff0c;却又不得不存在。我后来想的一个办法就是通过反射&…