【JeecgBoot】关于 jeecg-boot 的项目理解、使用心得和改进建议
工欲善其事,必先利其器。
脚手架选型
一年前,我接到为团队落地一个快速开发脚手架
的任务。
在月底这节骨眼上,时间紧,任务急,有想自己撸一个脚手架的人都赶紧把这想法收起来吧!这劳民又伤身的事咱肯定是不能干的!
于是,我将目光放在了 Gitee Star
比较靠前的开源项目上,这是当时调研的数据 Java Web 开发脚手架调研。
其中MCMS、lenosp、bootdo等项目,我们甚至已经有过项目落地经验,但最终我们还是选择了jeecg-boot,选择它的理由我们有这几点:
前后分离架构
- 当时我们正在推前端工程化,前后分离的工作模式是我们团队的趋势
热门技术栈
- 后端同学使用
Spring Boot + Mybatis Plus
,能更专注理解需求与业务逻辑 - 前端同学使用
Vue + Ant Design Vue
,既能快速开发业务,还有时间对页面性能优化做研究 - 设计同学使用
Ant Design
组件库设计资源,统一了设计风格
- 后端同学使用
基于角色的访问控制体系
- 符合公司当前业务场景
完善的开发文档
活跃的社区生态
- github issue
1300+
- jeecg开源社区会员
20000+
- 活跃的社群交流
- github issue
正是基于这些点,我们选择相信 jeecg-boot!
经过一年多,我们见证了 jeecg-boot 在 github 从 star 2000+
到现在的 star 14.7k
,
而我们团队也已经有 5 个服务是基于 jeecg-boot 2.0.0 进行开发,并有 4 个服务已投入生产使用。
改进建议
后端
关于分层领域模型
- 【建议】将 分层领域模型 落地
相比我们使用的 2.0.0 版本,jeecg-boot 2.2.1
在功能上已经非常完善,且已经形成了稳定的代码风格,在代码分层的工作上也细化了很多。
但是,在分层领域模型
方面,始终是使用Entity
贯穿各层,如果能将分层领域模型
落地,jeecg-boot 一定会更加优秀。
我们在分层领域模型规约的一些实践: 遵循 JAVA开发手册
# 对象模型
Model (接口入参 表单验证 swagger注解)
VO (返回页面对象) View Object
BO (业务层对象)
DO (数据库返回结果集)
DTO (远程调用传输对象)
实体类 (与数据表一一对应)
# 接口入参接收对象:XXXModel
【推荐】不包含`id、updateBy、updateTime、createBy、createTime`等属性,通过sql拦截注入这一系列字段
【推荐】必传字段校验
【推荐】字段长度校验
【推荐】格式校验
【推荐】时间格式转换 `@DateTimeFormat` (入参格式化,将字符串时间格式化为Date对象)
# 接口返回值:XXXVO
【强制】不包含敏感字段(手机号、密码、邮箱、身份证号)
【强制】包含敏感字段时对数据加密
【推荐】使用`@JsonView`注解控制返回值的字段可见性
【推荐】字典字段转换 如,`@Dict(dicCode = "sex")`
【推荐】时间格式转换 `@JsonFormat` (出参格式化,将Date对象时间格式化为字符串)
关于签发给前端的 token
- 【建议】不将 jwt 生成的 token 直接返回给前端
这里之所以有这个建议,是因为我们安全部门的同学找了开发同学好几次麻烦,最后我们将 token 的存储做了一些改造。
方案一:
- redis 中 token 的 key 不使用
username_token
的形式,使用username_UUID
。 - 在登录接口中,不将 jwt 生成的 token 直接返回给前端,而是返回 UUID 。
- 调整
ShiroRealm#doGetAuthenticationInfo
逻辑,先使用 token 从 redis 获取 jwt token 。 - 从 request 中获取 token 并使用
JwtUtil.getUsername(token)
之前需要先从 redis 获取 jwt token 。
方案二:
当然,还可以将 jwt token 进行加密,向前端返回加密后的 token,而后端只需增加一个对被加密的token
进行解密
的过滤器。
既可满足不将 jwt token 返回给前端,又不会对原有逻辑进行调整,满足开闭原则
。
关于防暴力破解
- 【建议】增加账户锁定策略
这也是安全部门同学找开发同学谈过话的案例!
我们的实践方案:
使用 redis 对单位时间内(我们是 5 分钟)的用户登录失败次数进行计数。
失败次数达到 5 次后,将对该账户进行冻结(5 分钟),5 分钟后 redis key 过期,该用户即可正常登录。
关于 jeecg 配置
- 增加
JeecgPorperties.java
对前缀为jeecg
的配置进行管理,避免使用@Value
获取属性值的行为。 - 引入
spring-boot-configuration-processor
依赖,为已声明的配置项增加提示。
前端
关于环境配置文件
- 【建议】引入
.env.*
配置文件,将配置与环境隔离
目前 jeecg-boot 的前端,一直使用的是 window._CONFIG
来挂载相关全局变量,它有一个缺点就是没有将配置与环境隔离
。
比如:
在开发环境下window._CONFIG['domianURL'] = 'http://127.0.0.1:8080/jeecg-boot'
在测试环境下window._CONFIG['domianURL'] = 'http://test.product.com:8080/jeecg-boot'
在生产环境下window._CONFIG['domianURL'] = 'http://prod.product.com:8080/jeecg-boot'
打包不同的环境,都需要人为的去改动这些配置,对CI/CD
极不友好。
我们的实践方案:
- .env
- .env.development
- .env.production
- .env.test
- ...
分环境提供配置,并在package.json
增加相应打包脚本,提高部署效率。
类似于后端项目中的:
application.yml
application-dev.yml
application-prod.yml
application-test.yml
关于增强 JeecgListMixin.js
【建议】围绕 JeecgListMixin 向下提供一些勾子函数
我们可以看到
initDictConfig
是一个空方法,它就是一个勾子函数,子组件如果重写了该方法,则会在执行created生命周期函数
时,执行子组件中重写的逻辑。所以,我们可以向
JeecgListMixin
的赋能更多勾子函数,满足更多场景,提高灵活性,提高开发效率。
我们的实践方案:
// created生命周期函数中执行 应用场景:加载异步词典项等
initDictConfig() {}
// 搜索之前执行 应用场景:对 queryParam 对象内的数据,做一些数据转换工作等
beforeSearch() {}
// 重置之前执行 应用场景:重置一些没有绑定在 queryParam 对象内的数据等
beforeReset() {}
// loadData加载数据成功之后执行 应用场景:需要在该时机下做一些数据的转换工作等
afterLoadDataSuccess() {}
// 在 loadData 内执行 应用场景:自定义数据加载逻辑
// 执行 loadData 时,会先判断是否重写了 customLoadData 方法
customLoadData() {}
关于页面性能的优化
- 【建议】路由懒加载
需要注意使用路由懒加载后所带来的问题,即刷新时会触发两次路由守卫的beforeEach函数
- 【建议】对体积较大的第三方资源拆包,或使用CDN引入
当前 jeecg-boot 2.2.1 是一次性加载所有资源,无论是开发还是生产阶段都是不利的!
对于开发阶段,开发同学每次刷新页面需要等待 3 ~ 4 秒,
对于生产阶段,就会涉及到首屏加载
这个指标的考验。
对资源做按需加载/缩减资源体积
能极大的优化加载性能,也是一件很有意义的事。
我们可以对 jeecg-boot 前端做的一些优化:
- 路由懒加载
component:() => import(/* webpackChunkName: "component" */ component.vue)
- 根据环境需要,使用
splitChunks
对依赖的第三方资源拆包
当然,还有更多 jeecg-boot 用户可以做的优化
- 文件内联(减少请求数)
- 摇树优化(用不到的方法或者模块就不打包)
- gzip压缩
- CDN加速
关于增加ModalMixin.js
在业务开发过程中,一个页面可能有很多Modal / Drwaer
,就需要我们加一些响应式变量和方法去控制这些Modal / Drawer
的 显示/关闭,而这些工作往往都是重复的。
那么,我们能不能抽象一下这些重复的工作,将更多的精力放在组件的逻辑上去呢?
我们的实践方案:
- Modal / Drawer 的父组件引入
@/mixins/ModalParentMixin
,并在mounted
生命周期函数中使用register
方法对 ref 的值进行注册<template>
<div>
<a-button type="primary" @click="showModal('addModal')">新增</a-button>
<a-button type="primary" @click="showModal('editModal')">编辑</a-button>
<a-button type="primary" @click="showModal('detailModal')">详情</a-button> <!--新增-->
<modal-add ref="addModal" @ok="searchReset"/> <!--编辑-->
<modal-edit ref="editModal" @ok="searchReset"/> <!--详情-->
<!--条件渲染,将v-if="isShow('detailModal')" @close="closeModal"一起使用即可-->
<modal-detail ref="detailModal" @ok="searchReset" v-if="isShow('detailModal')" @close="closeModal"/>
</div>
</template> <script>
import ModalParentMixin from '@/mixins/ModalParentMixin' export default {
mixins: [ ModalParentMixin],
mounted() {
// 注册当前页面有哪些modal
this.register(['addModal', 'editModal', 'detailModal'])
},
}
</script>
- 在 Modal / Drawer 组件中引入
@/mixins/ModalMixin
现在我们控制页面上所有的 Modal / Drawer 是不是要轻松很多了呢?
再也不用声明各种不同的 xxxVisible
来控制这些子组件的显示/关闭
。
关于修复 Ant Table 的 BUG
这个本应该去 Ant Design Vue 提 issue 的,但是跟了好几个版本之后,他们并没有做修复,得到的答案似乎是:设计如此
!
既然这样,我们就自己来做一些处理,去规避掉这个 BUG。
复现步骤
删除数据前{ total: 11, current: 2, pageSize: 10 }
。
删除数据后{ total: 10, current: 2, pageSize: 10 }
,显示暂无数据
。
修复
在JeecgListMixin.js
的handleDelete
、batchDel
等方法,在操作成功后,对current
进行重新计算。修复后效果
删除数据前{ total: 11, current: 2, pageSize: 10 }
。
删除数据后{ total: 10, current: 1, pageSize: 10 }
,正常显示第一页的数据。
其他
关于更新计划
- 【建议】增加
更新计划
菜单
能方便大家了解到 JEECG团队 对新版本的迭代方向,增加用户粘度。
关于这个建议已经有 issue,相信在近期内就会看到这个菜单!
关于平滑升级
很多社区的朋友都有反应这个问题。确实,当我们的业务代码与 jeecg-boot 的源码融合在一起的时候,做版本升级是比较头疼的。
特别是大版本的升级,除了源码的变动,可能还有数据库表的变更,升级会不会对我们的业务产生影响,谁也说不清楚!
我们的实践方案:
依赖 jeecg-spring-boot-starter,解决业务代码与 jeecg-boot 源码融合的在一起的窘境。
- 适用人群
- 快速开发人群
- 不需对 jeecg-boot 的源代码进行改动的人群
- 只需对 jeecg-boot 配置项进行调整即可满足开发的人群
有兴趣的同学,可以去 github 了解它 https://github.com/tanpenggood/jeecg-spring-boot-starter。
总结
- jeecg-boot 明显的提高了我们团队的开发效率。
- jeecg-boot 全套技术栈符合当前技术趋势,学习文档完善,是一个非常值得大家学习的开源项目。
- jeecg-boot 社区活跃,碰到问题基本上都能及时解决,有价值的问题最好再去 github 提 issue。
最后,希望 jeecg-boot 的代码质量更加优秀,希望 jeecg-boot 成为2020最最最最受欢迎的开源项目!
【JeecgBoot】关于 jeecg-boot 的项目理解、使用心得和改进建议的更多相关文章
- SpringCloud 微服务一:spring boot 基础项目搭建
spring cloud是建立在spring boot的基础上的,而之前虽然听说过,也随便看了一下spring boot,却没有真正使用,因此还必须先花时间学一下spring boot. spring ...
- Spring Boot 商城项目
Spring Boot 商城项目 angularJS Demo1 <html> <head> <title>angularJS Demo1</title> ...
- 有哪些值得学习的spring boot开源项目?
1. awesome-spring-boot 首先给大家介绍的就是Spring Boot 中文索引,这是一个专门收集 Spring Boot 相关资料的开源项目,也有对应的导航页面. 产品主页 htt ...
- 15 个优秀开源的 Spring Boot 学习项目,一网打尽!
Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受 ...
- Spring boot Gradle项目搭建
Spring boot Gradle项目搭建 使用IDEA创建Gradle工程 操作大致为:File->new->Project->Gradle(在左侧选项栏中) 创 ...
- 【spring】1.2、Spring Boot创建项目
Spring Boot创建项目 在1.1中,我们通过"Spring Starter Project"来创建了一个项目,实际上是使用了Pivotal团队提供的全新框架Spring B ...
- 15 个优秀开源的 Spring Boot 学习项目
Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受 ...
- 最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/
最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/,帮忙找找bug,网站里有源码地址 网站说明 甲壳虫社区(Beetle Community) 一个开源 ...
- 1.spring boot初始化项目
初始化spring boot项目的方式非常多,如使用Spring Tool Suite.使用IntelliJ IDEA.使用NetBeans.在start.spring.io网站中.curl命令.sp ...
随机推荐
- 【Java】Java Win10环境搭建--JDK、Eclipse
win10安装Java JDK环境及Eclipse安装使用(Hello world) win10环境下安装Java环境,对于小白来说简直是头疼,因为Java内部环境有着JDK和JRE两块,互相牵扯着很 ...
- 高可用K8S构建3master+3node+keepalived+haproxy
视频地址:https://www.bilibili.com/video/BV1w4411y7Go?p=66 所需安装包在视频评论区 安装准备 系统: CentOS-7-x86_64-Minimal-1 ...
- Python常用配置文件ini、json、yaml读写总结
开发项目时,为了维护一些经常需要变更的数据,比如数据库的连接信息.请求的url.测试数据等,需要将这些数据写入配置文件,将数据和代码分离,只需要修改配置文件的参数,就可以快速完成环境的切换或者测试数据 ...
- python k-means聚类实例
port sys reload(sys) sys.setdefaultencoding('utf-8') import matplotlib.pyplot as plt import numpy as ...
- Python之猜拳游戏
第一次写这东西,主要是为了记录自己的学习历程,或者说是为了忘记的时候找回来看看. 今天是参加风变编程培训第10天.昨天晚上完成了第10关关底的猜拳小游戏. 要求:人和电脑轮流出拳.判断输赢. 给出列表 ...
- Weblogic命令执行漏洞(CVE-2018-2628)复现
一.漏洞环境搭建 CVE-2018-2628影响的软件版本为: Weblogic 10.3.6.0 Weblogic 12.1.3.0 Weblogic 12.2.1.2 Weblogic 12.2. ...
- feig中调用其他微服务接口无反应
1.调用微服务时get请求接口中不能使用@RequestBody注解,不然接口调用无反应.post接口中可以使用@RequestBody注解
- [leetcode]Next Greater Element
第一题:寻找子集合中每个元素在原集合中右边第一个比它大的数. 想到了用哈希表存这个数的位置,但是没有想到可以直接用哈希表存next great,用栈存还没找到的数,没遍历一个数就考察栈中的元素小,小的 ...
- php中require与include的区别
描述:require, include三者都是引入文件,其中require_once,include_once只引入一次,即之前引入过的就不再引入. include与require的区别: 加载失败的 ...
- Eclipse导入外部jar包的步骤
(1)首先在项目的跟目录下先建一个名字为lib的文件夹,通常外部导入的jar包都放在这个文件夹下面. (2)将需要用到的jar包复制到lib文件夹下面. (3)在项目中导入jar包 右键项目,选择Bu ...