工欲善其事,必先利其器。

脚手架选型

一年前,我接到为团队落地一个快速开发脚手架的任务。

在月底这节骨眼上,时间紧,任务急,有想自己撸一个脚手架的人都赶紧把这想法收起来吧!这劳民又伤身的事咱肯定是不能干的!

于是,我将目光放在了 Gitee Star比较靠前的开源项目上,这是当时调研的数据 Java Web 开发脚手架调研。

其中MCMS、lenosp、bootdo等项目,我们甚至已经有过项目落地经验,但最终我们还是选择了jeecg-boot,选择它的理由我们有这几点:

  • 前后分离架构

    1. 当时我们正在推前端工程化,前后分离的工作模式是我们团队的趋势
  • 热门技术栈

    1. 后端同学使用 Spring Boot + Mybatis Plus,能更专注理解需求与业务逻辑
    2. 前端同学使用Vue + Ant Design Vue,既能快速开发业务,还有时间对页面性能优化做研究
    3. 设计同学使用Ant Design组件库设计资源,统一了设计风格
  • 基于角色的访问控制体系

    1. 符合公司当前业务场景
  • 完善的开发文档

    1. 开发文档清晰且详细,有经验的同学肯定知道维护一份完善的开发文档往往比写这个项目花费的时间和精力还要多。
    2. 除了文档,社区还提供了视频教学,真的是贴心到了极致
  • 活跃的社区生态

    1. github issue 1300+
    2. jeecg开源社区会员20000+
    3. 活跃的社群交流

正是基于这些点,我们选择相信 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 的存储做了一些改造。

方案一:

  1. redis 中 token 的 key 不使用 username_token的形式,使用 username_UUID。
  2. 在登录接口中,不将 jwt 生成的 token 直接返回给前端,而是返回 UUID 。
  3. 调整 ShiroRealm#doGetAuthenticationInfo逻辑,先使用 token 从 redis 获取 jwt token 。
  4. 从 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极不友好。

我们的实践方案:

  1. .env
  2. .env.development
  3. .env.production
  4. .env.test
  5. ...

分环境提供配置,并在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 前端做的一些优化:

  1. 路由懒加载 component:() => import(/* webpackChunkName: "component" */ component.vue)
  2. 根据环境需要,使用splitChunks对依赖的第三方资源拆包

当然,还有更多 jeecg-boot 用户可以做的优化

  1. 文件内联(减少请求数)
  2. 摇树优化(用不到的方法或者模块就不打包)
  3. gzip压缩
  4. CDN加速

关于增加ModalMixin.js

在业务开发过程中,一个页面可能有很多Modal / Drwaer,就需要我们加一些响应式变量和方法去控制这些Modal / Drawer的 显示/关闭,而这些工作往往都是重复的。

那么,我们能不能抽象一下这些重复的工作,将更多的精力放在组件的逻辑上去呢?

我们的实践方案:

  1. 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>
  2. 在 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。

总结

  1. jeecg-boot 明显的提高了我们团队的开发效率。
  2. jeecg-boot 全套技术栈符合当前技术趋势,学习文档完善,是一个非常值得大家学习的开源项目。
  3. jeecg-boot 社区活跃,碰到问题基本上都能及时解决,有价值的问题最好再去 github 提 issue。

最后,希望 jeecg-boot 的代码质量更加优秀,希望 jeecg-boot 成为2020最最最最受欢迎的开源项目!

【JeecgBoot】关于 jeecg-boot 的项目理解、使用心得和改进建议的更多相关文章

  1. SpringCloud 微服务一:spring boot 基础项目搭建

    spring cloud是建立在spring boot的基础上的,而之前虽然听说过,也随便看了一下spring boot,却没有真正使用,因此还必须先花时间学一下spring boot. spring ...

  2. Spring Boot 商城项目

    Spring Boot 商城项目 angularJS Demo1 <html> <head> <title>angularJS Demo1</title> ...

  3. 有哪些值得学习的spring boot开源项目?

    1. awesome-spring-boot 首先给大家介绍的就是Spring Boot 中文索引,这是一个专门收集 Spring Boot 相关资料的开源项目,也有对应的导航页面. 产品主页 htt ...

  4. 15 个优秀开源的 Spring Boot 学习项目,一网打尽!

    Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受 ...

  5. Spring boot Gradle项目搭建

    Spring boot Gradle项目搭建 使用IDEA创建Gradle工程     操作大致为:File->new->Project->Gradle(在左侧选项栏中)     创 ...

  6. 【spring】1.2、Spring Boot创建项目

    Spring Boot创建项目 在1.1中,我们通过"Spring Starter Project"来创建了一个项目,实际上是使用了Pivotal团队提供的全新框架Spring B ...

  7. 15 个优秀开源的 Spring Boot 学习项目

    Spring Boot 算是目前 Java 领域最火的技术栈了,松哥年初出版的 <Spring Boot + Vue 全栈开发实战>迄今为止已经加印了 8 次,Spring Boot 的受 ...

  8. 最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/

    最近做的一个Spring Boot小项目,欢迎大家访问 http://39.97.115.152/,帮忙找找bug,网站里有源码地址 网站说明 甲壳虫社区(Beetle Community) 一个开源 ...

  9. 1.spring boot初始化项目

    初始化spring boot项目的方式非常多,如使用Spring Tool Suite.使用IntelliJ IDEA.使用NetBeans.在start.spring.io网站中.curl命令.sp ...

随机推荐

  1. GitBook简单的使用

    GitBook 是一个基于 Node.js 的命令行工具,支持 Markdown 和 AsciiDoc 两种语法格式,可以输出 HTML.PDF.eBook 等格式的电子书. 使用 GitBook 管 ...

  2. 恶补了 Python 装饰器的六种写法,你随便问~

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 今天给大家分享一下关于装饰器的知识点,内容非常干,全程高能,认真吸收看完,一定会对装饰器有更深的理解 ...

  3. 最全Python正则表达式来袭

    前言 正则表达式是对字符串(包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"))操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成 ...

  4. HCIP --- BGP属性

    传播范围                 默认值              大优或小优 1. Preference_Value     不传播                      0       ...

  5. Java日常开发的21个坑,你踩过几个?

    前言 最近看了极客时间的<Java业务开发常见错误100例>,再结合平时踩的一些代码坑,写写总结,希望对大家有帮助,感谢阅读~ 1. 六类典型空指针问题 包装类型的空指针问题 级联调用的空 ...

  6. 个人MySQL的事务特性原理学习笔记总结

    目录 个人MySQL的事务特性原理笔记总结 一.基础概念 2. 事务控制语句 3. 事务特性 二.原子性 1. 原子性定义 2. 实现 三.持久性 1. 定义 2. 实现 3. redo log存在的 ...

  7. double 转为long类型

    System.out.println(new Double(234314.999999999).longValue());//234314  System.out.println(new Double ...

  8. jdbc编程学习(1)

    一,背景 1996年,sun公司发布了第一版本的数据库连接(JDBC)API,使编程人员可以通过这个API接口连接到数据库,并使用结构化查询语言(SQL)完成对数据库的查找与更新.JDBC自此成为Ja ...

  9. JavaSwing 船只停靠管理可视化(一)

    最近抽空闲时间做了船只停靠管理系统,先看一下效果. 停靠泊位管理:实现泊位的 增删改查. JavaSwing 船只停靠管理可视化(一) JavaSwing 船只停靠管理可视化(二) JavaSwing ...

  10. Service Mesh——微服务中的流量管理中间件

    Service Mesh--微服务中的流量管理中间件 摘自-https://zhuanlan.zhihu.com/p/28794062 Service mesh 与 Cloud Native Kube ...