工作六年,看到这样的代码,内心五味杂陈...

软考中级-软件设计师 知识点整理(一篇就过了 建议收藏) 软考中级-软件设计师上午题知识点总结 阅读详情

👉 这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料: 

de44f46f8479ee4a213c28a4d2066fd9.gif

👉这是一个或许对你有用的开源项目

国产 Star 破 10w+ 的开源项目,前端包括管理后台 + 微信小程序,后端支持单体和微服务架构。

功能涵盖 RBAC 权限、SaaS 多租户、数据权限、商城、支付、工作流、大屏报表、微信公众号、CRM 等等功能:

  • Boot 仓库:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • Cloud 仓库:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK 21 + SpringBoot 3.2.2、JDK 8 + Spring Boot 2.7.18 双版本 

来源:juejin.cn/post/
7294844864020430902

4224c350345041dda1cdf6eaf1257ae6.jpeg


看到这样的代码,内心五味杂陈......

那天下午,看到了令我终生难忘的代码,那一刻破防了......

🔊 本文记录那些年的 Java 代码轶事

ヾ(•ω•`)🫥 故事还得从半年前数据隔离的那个事情说起......

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

历史背景

数据隔离

预发,灰度,线上环境共用一个数据库。每一张表有一个 env 字段,环境不同值不同。特别说明:env 字段即环境字段。如下图所示:

49522c03f9787620aac8af803c97255f.jpeg

隔离之前

🖌️插曲:一开始只有 1 个核心表有 env 字段,其他表均无该字段;有一天预发环境的操作影响到客户线上的数据。为了彻底隔离,剩余的二十几个表均要添加上环境隔离字段。

「当时二十几张表已经大量生产数据,隔离需要做好兼容过渡,保障数据安全。」

隔离改造

其他表历史数据很难做区分,于是新增加的字段 env 初始化 all ,表示预发线上都能访问。以此达到历史数据的兼容。

每一个环境都有一个自己独立标志;从 application.properties 中读该字段;最终到数据库执行的语句如下:

SELECT XXX FROM tableName WHERE env = ${环境字段值} and ${condition}

隔离方案

「最拉胯的做法:每一张表涉及到的 DO、Mapper、XML等挨个添加 env 字段。但我指定不能这么干!!!」

197ebdeb66feb95b2e9871807a1946e6.jpeg

具体方案:自定义 mybatis 拦截器进行统一处理。通过这个方案可以解决以下几个问题:

  • 业务代码不用修改,包括 DO、Mapper、XML等。只修改 mybatis 拦截的逻辑。

  • 挨个添加补充字段,工程量很多,出错概率极高

  • 后续扩展容易

最终落地

在 mybatis 拦截器中, 通过改写 SQL。新增时填充环境字段值,查询时添加环境字段条件。真正实现改一处即可。考虑历史数据过渡,将 env = ${当前环境} 修改成 env in (${当前环境},'all')

SELECT xxx FROM ${tableName} WHERE env in (${当前环境},'all') AND ${其他条件}

具体实现逻辑如下图所示:

e9c566bd77f9875fcb6411fb3fd3ae48.jpeg
  1. 其中 env 字段是从 application.properties 配置获取,全局唯一,只要环境不同,env 值不同

  2. 借助 JSqlParser 开源工具,改写 sql 语句,修改重新填充、查询拼接条件即可。链接https://github.com/JSQLParser/JSqlParser

思路:自定义拦截器,填充环境参数,修改 sql 语句,下面是部分代码示例:

@Intercepts(
        {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})}
)
@Component
public class EnvIsolationInterceptor implements Interceptor {
    ......
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        ......
        if (SqlCommandType.INSERT == sqlCommandType) {
            try {
                // 重写 sql 执行语句,填充环境参数等
                insertMethodProcess(invocation, boundSql);
            } catch (Exception exception) {
                log.error("parser insert sql exception, boundSql is:" + JSON.toJSONString(boundSql), exception);
                throw exception;
            }
        }

        return invocation.proceed();
    }
}

一气呵成,完美上线。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

发展演变

业务需求

「随着业务发展,出现了以下需求:」

  • 上下游合作,我们的 PRC 接口在匹配环境上与他们有差异,需要改造

SELECT * FROM ${tableName} WHERE bizId = ${bizId} and env in (?,'all')
  • 有一些环境的数据相互相共享,比如预发和灰度等

  • 开发人员的部分后面,希望在预发能纠正线上数据等

初步沟通

这个需求的落地交给了来了快两年的小鲜肉。在开始做之前,他也问我该怎么做;我简单说了一些想法,比如可以跳过环境字段检查,不拼接条件;或者拼接所有条件,这样都能查询;亦或者看一下能不能注解来标志特定方法,你想一想如何实现......

(●ˇ∀ˇ●)年纪大了需要给年轻人机会。

勤劳能干

小鲜肉,没多久就实现了。不过有一天下午他遇到了麻烦。他填充的环境字段取出来为 null,看来很久没找到原因,让我帮他看看。(不久前也还教过他 Arthas 如何使用呢,这种问题应该不在话下吧🤔)

具体实现

大致逻辑:在需要跳过环境条件判断的方法前后做硬编码处理,同环切面逻辑, 一加一删。填充颜色部分为小鲜肉的改造逻辑。

27c5529ec66b62330ac1691f0fd8a494.jpeg

大概逻辑就是:将 env 字段填充所有环境。条件过滤的忽略的目的。

SELECT * FROM ${tableName} WHERE env in ('pre','gray','online','all') AND ${其他条件}

错误原因

经过排查是因为 API 里面有多处对 threadLoal 进行处理的逻辑,方法之间存在调用。简化举例:A 和 B 方法都是独立的方法, A 在调用 B 的过程,B 结束时把上下文环境字段删除, A 在获取时得到 null。具体如下:

521bebbfac998961803a4402cf600e60.jpeg

五味杂陈

当我看到代码的一瞬间,彻底破防了......

a675d6cddb1cc8b432d977384bd394a8.jpeg

「queryProject 方法里面调用 findProjectWithOutEnv, 在两个方法中,都有填充处理 env 的代码。」

遍地开花

然而,这三行代码,随处可见,在业务代码中遍地开花.......

// 1. 变量保存 oriFilterEnv
String oriFilterEnv = UserHolder.getUser().getFilterEnv();

// 2. 设置值到应用上下文
UserHolder.getUser().setFilterEnv(globalConfigDTO.getAllEnv());

//....... 业务代码 ....

// 3. 结束复原
UserHolder.getUser().setFilterEnv(oriFilterEnv);
edc96fbeeee7f5632dba49389f2c16bf.jpeg

改了个遍,很勤劳👍......

灵魂开问

「难道真的就只能这么做吗,当然还有......」

  • 开闭原则符合了吗

  • 改漏了应该办呢

  • 其他人遇到跳过的检查的场景也加这样的代码吗

  • 业务代码和功能代码分离了吗

  • 填充到应用上下文对象 user 合适吗

  • .......

「大量魔法值,单行字符超500,方法长度拖几个屏幕也都睁一眼闭一只眼了,但整这一出,还是破防......」

内心涌动😥,我觉得要重构一下。

重构一下

困难之处

「在 mybatis intercept 中不能直接精准地获取到 service 层的接口调用。只能通过栈帧查询到调用链。」

问题列表

  • 尽量不要修改已有方法,保证不影响原有逻辑;

  • 尽量不要在业务方法中修改功能代码;关注点分离;

  • 尽量最小改动,修改一处即可实现逻辑;

  • 改造后复用能力,而不是依葫芦画瓢地添加这种代码

实现分析

  1. 用独立的 ThreadLocal,不与当前用户信息上下文混合使用

  2. 注解+APO,通过注解参数解析,达到目标功能

  3. 对于方法之间的调用或者循环调用,要考虑优化

「同一份代码,在多个环境运行,不管如何,一定要考虑线上数据安全性。」

使用案例

改造后的使用案例如下,案例说明:「project 表在预发环境校验跳过」 。

@InvokeChainSkipEnvRule(skipEnvList = {"pre"}, skipTableList = {"project"})
@SneakyThrows
@GetMapping("/importSignedUserData")
@InvokeChainSkipEnvRule(skipEnvList = {"pre"}, skipTableList = {"project"})
public void importSignedUserData(
    ......
    HttpServletRequest request,
    HttpServletResponse response) {
    ......
}

「在使用的调用入口处添加注解。」

具体实现

  1. 方法上标记注解, 注解参数定义规则

  2. 切面读取方法上面的注解规则,并传递到应用上下文

  3. 拦截器从应用上下文读取规则进行规则判断

a7009a4edf955a217548ab7af576b51c.jpeg

注解代码

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InvokeChainSkipEnvRule {

    /**
     * 是否跳过环境。 默认 true,不推荐设置 false
     *
     * @return
     */
    boolean isKip() default true;

    /**
     * 赋值则判断规则,否则不判断
     *
     * @return
     */
    String[] skipEnvList() default {};

    /**
     * 赋值则判断规则,否则不判断
     *
     * @return
     */
    String[] skipTableList() default {};
}

不足之处

  1. 整个链路上的这个表操作都会跳过,颗粒度还是比较粗

  2. 注解只能在入口处使用,公共方法调用尽量避免

🤔那还要不要完善一下,还有什么没有考虑到的点呢?拿起手机看到快12点的那一刻,我还是选择先回家了......

总结思考

隔离总结

这是一个很好参考案例:在应用中既做了数据隔离,也做了数据共享。通过自定义拦截器做数据隔离,通过自定注解切面实现数据共享。

编码总结

「同样的代码写两次就应该考虑重构了」

  • 尽量修改一个地方,不要写这种边边角角的代码

  • 善用自定义注解,解决这种通用逻辑

  • 可以妥协,但是要有底线

  • ......

场景总结

「简单梳理,自定义注解 + AOP 的场景」

db639b0c723e22df1886bf2085eababb.png

自定义注解很灵活,应用场景广泛,可以多多挖掘。

反思总结

  • 如果一开始就做好技术方案或者直接使用不同的数据库

  • 是否可以拒绝那个所谓的需求

  • 先有设计再有编码,别瞎搞

最后感想

在这个只讲业务结果,不讲技术氛围的环境里,突然有一些伤感;身体已经开始吃不消了,好像也过了那个对技术较真死抠的年纪;突然一想,这么做的意义又有多大呢?


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,“长按”或“扫描”下方二维码噢:

1740f58248ab41c2fe1e51cf254e11ee.png

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

926b082071a5487a610d1638feb66a69.png

90fe5284b1c325fad76fccf4e0568819.png03f517b41b261672c914219798f49740.pnge6f028bca1fdf9fd8da6a5cdeffd525b.png7f738526576310306a9b3a2bc84a6dd2.png

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
2026华为OD机考新系统真题 + 双机位C卷 + 往年真题 - 题库目录分值说明 + 考点说明+在线OJ 2026年4月1日起,华为OD机试正式切换至新系统模式!采用力扣模式,无需自己写输入! 难度大幅度降低。正在备考华为OD机试的同学,优先刷新系统机试真题,可大幅提升通过率。 新系统考题考点与旧版一致,无需重新适应。本专栏所有算法题均配有详细思路解析,并提供C语言、JS、C++、Python、Go、Java多语言源码实现。订阅后私信即可开通专属OJ刷题权限,高效备考一次上岸! 阅读详情

相关推荐

基于ST32 DAC7311数模转换器驱动代码及电路设计完整版

基于ST32的DAC7311模数转换器的电路设计及驱动代码完整版

qq_39626643的博客 3722

免费获取芋道源码ruoyi-vue-pro和yudao-cloud的sql脚本,持续更新!

ruoyi-vue-pro和yudao-cloud都属于芋道源码,但用过的人都知道,这两个开源项目下有商城、ERP、CRM等模块,这些模块的SQL需要加入芋道源码的知识星球才能获取,加入费用为199元,有点小贵!芋道源码的ruoyi-vue-pro和yudao-cloud都已经发布到GoldPanKit上,通过GoldPanKit构建项目不仅可以免费获取到SQL,还能选配功能模块,调整包名,自动配置数据库。

Caesar's blog 1万+

UReport2 报表设计器 在线表格

UReport2官网 一、UReport2报表设计器 UReport2是一个开源的可视化报表设计器,功能强大,操作简单,可以实现复杂的报表统计,有各种各样的导出和打印功能,支持导入Excel表格! 1、报表设计器 2、设计一个简单的用户列表展示 二、可编辑表格 扩展了layui的表格,支持多元化数据编辑(单选,多些、日期、下拉联动),开发简单、高效、便捷。有了它就可以轻松开发类似进销存系统的表格录入功能了,用户拿来即用! 三、主从表录......

ejinxian的专栏 2万+

工作六年,看到这样的代码,内心五味杂陈......

????这是一个或许对你有用的社群????一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:《项目实战(视频)》:从书中学,往事中“练”《互联网高频面试题》:面朝简历学习,春暖花开《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题《精进 Java 学习指南》:系统学习,互联网主流技术栈《必读 Java 源码专栏》:知其然,知其所以然????这是一个或许...

weixin_44421461的博客 799

刚入职,软件测试岗,有点迷茫不知道下一步怎么提升自己

软件测试前景和发展都是不错,已经入了软件测试这行业就坚挺下去

大白的博客 2220

UE5新手必看:如何用蓝图系统快速打造赛车游戏(含Chaos Physics配置)

本文为UE5新手提供使用蓝图系统快速打造赛车游戏的完整指南,涵盖Chaos Physics配置、载具蓝图架构设计、控制逻辑实现等关键步骤。通过详细的实战案例和优化技巧,帮助开发者高效构建具备专业物理反馈的赛车游戏原型。

weixin_29001327的博客 387

同事写的代码,太垃圾!破防了......内心五味杂陈

点击关注公众号:互联网架构师,后台回复2T获取2TB学习资源!上一篇:2T架构师学习资料干货分享大家好,我是互联网架构师!工作六年,看到这样的代码,内心五味杂陈......那天下午,看到了令我终生难忘的代码,那一刻破防了......ヾ(•ω•`)???? 故事还得从半年前数据隔离的那个事情说起......一、历史背景1.1 数据隔离预发,灰度,线上环境共用一个数据库。每一张表有一个 env 字段,环境...

emprere的博客 213

在Modelsim中手动添加仿真库

缘由:在使用rom ip核的时候发现仿真需要altera_mf库的支持,而我的ModelSim没有这个库,所以想写一篇手动添加仿真库的文章方便自己以后使用:首先新建一个库,名字叫做altera_mf:然后编译对应的库文件:这里altera_mf.v文件是在quartus安装路径下quartus\eda\sim_lib文件夹下,点击编译后退出:最后在modelsim->example文件夹下可以找到编译好的库文件:

欢迎一起交流问题! 3402

同事写的代码,太垃圾!破大防了......

本文系转载,原文作者:uzong。原文地址:https://juejin.cn/post/7294844864020430902工作六年,看到这样的代码,内心五味杂陈......那天下午,看到了令我终生难忘的代码,那一刻破防了......ヾ(•ω•`)???? 故事还得从半年前数据隔离的那个事情说起......一、历史背景1.1 数据隔离预发,灰度,线上环境共用一个数据库。每一张表有一个 env 字段,...

HollisChuang's Blog 333

Open3D点云数据处理(二):读写点云,你真的会吗?

本文详细介绍了open3d加载点云、保存点云的方法,同时打印点云坐标、颜色、法向量等字段信息。

孙悟空 2604

uni-app - 最详细 H5 网页接入微信登录功能,提供公众号配置与详细注释示例代码(移动端网页实现点击登录按钮后 调用微信公众号授权登录功能 详细讲解接入流程与详细示例代码)官方最新超级详细教程

uniapp中H5微信登录,uniapp H5微信授权登录,微信公众号第三方网页(H5)授权登录(OAuth2.0)流程及逻辑详细教程,UNIAPP 开发H5 怎么实现微信公众号登录功能,uniapp如何实现H5网页的公众号微信登录,怎么做uniapp微信登录,uniapp h5项目怎么实现微信登录的功能,uniapp微信浏览器H5授权微信登录,UniApp开发H5接入微信登录的全过程,uniapp的H5页面使用微信登录,uniapp H5微信授权登录,H5之微信授权登陆 (uniapp网页版微信授权登录)

🏆 前端领域优质创作者 7410

反思

10年8月进了一家外包公司,开始从事我的java工程师工作。 15年6月经项目经理介绍来进了一家互联网创业公司。 16年2月因为投资人不愿再砸钱,团队宣布解散,于是我面临着找工作的问题。 花了一个星期时间找工作,面试了将近8家企业,都没有拿到offer,这对于我一个工作6年来的java工程师来说,打击是沉痛的。 印象最深刻的是当时面试的一家小的创业公司,面试官比我小两岁,面完后我听到他在外...

weixin_30379531的博客 87

Vue - 简约大气 404 页面

效果图 实现代码 复制即可。 <template> <div> <div class="error"> <!-- 图片可保存到本地 --> <img src="https://img-blog.csdnimg.cn/20210715111039922.png"> <p>抱歉!你访问的页面不存在或已删除 :( </p> <span class="back" @click="go(

🏆 前端领域优质创作者 3371

以后程序出问题,可以甩锅给AI了

这些代码来自于一个 GitHub 仓库,目的就是为了让 AI 拿去训练,然后给使用者程序中加后门,具体是主动投喂给GPT的,还是GPT自己主动吃的,不得而知,总之,就是恰好被GPT训练用了,然后有用户碰到类似的需求,正好就拿到了带后门的代码,然后就被骗了。但是这个人否认是他加的这行代码,钱包也不是他自己的,最后甩锅给了 AI,说是AI加上去的,自己没发现,Code Review 时也没发现。说是AI干的,这确实是属于甩锅了。AI时代,写代码是方便读了,但是万一AI被利用了,写出的代码安全性就成了大问题。

Java后端技术 462

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

用泛型实现优雅:Spring 框架事件监听详解

前面我们在实现 ResolvableTypeProvider 的时候,就重写了 getResolvableType 方法,调用了 ResolvableType.forClassWithGenerics,然后用 data 对应的真正的 T 对象实例的类型,作为返回值,这样泛型对应的真正的对象类型,就在运行期被动态的获取到了,从而解决了编译阶段泛型擦除的问题。当你一个类中注入了大量的 Service 的时候,你就要考虑考虑,是不是有什么做的不合适的地方了,是不是有些 Service 其实不应该注入进来的。

Java后端技术 296

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

目前工资最高的几家外包公司汇总!(2024最新版)

往期热门文章: 1、工作中最常见的6种OOM问题 2、千万不要滥用Stream.toList(),有坑! 3、强烈建议你不要再使用Date类了!!! 4、IDEA 接口调试神器,贼好用! 5、Java内部类有坑,100%内存泄露!最近,很多小伙伴问:只有外包的 offer 能去吗?大环境不行,面试太少了,很多本科生想进外包都没机会。非常时期,不需要在意那么多,外包作为过渡也是没问题的,很多外包其...

Java后端技术 226

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

一个现代化轻量级的跨平台 Redis 桌面客户端,支持 Mac、Windows 和 Linux

往期热门文章: 1、能用到“退休”的 600条 Linux 命令,可以解决日常99%的问题~ 2、工作六年,看到这样的代码,内心五味杂陈... 3、拒绝写重复代码,试试这套开源的 SpringBoot 组件 4、一次生产事故,来来回回搞了一个月,人麻了! 5、面试官:Git 如何撤回已 Push 的代码?问倒一大片。。。今天给大家推荐的这个开源项目是一个现代化轻量级的跨平台 Redis 桌面客户...

Java后端技术 385

中国程序员独立开发9年、最受欢迎的开源Redis客户端——被Redis公司收购了!...

往期热门文章: 1、我在代码里面故意留个漏洞,违法吗? 2、一个现代化轻量级的跨平台 Redis 桌面客户端,支持 Mac、Windows 和 Linux 3、能用到“退休”的 600条 Linux 命令,可以解决日常99%的问题~ 4、工作六年,看到这样的代码,内心五味杂陈... 5、拒绝写重复代码,试试这套开源的 SpringBoot 组件ioredis 作者 @Luin 宣布该项目已被 R...

Java后端技术 253
上一篇: Spring Boot 实现热插拔 AOP,非常实用!
下一篇: 推荐一款免费好用的远程桌面软件
公众号-芋道源码
博客等级 码龄10年 1634粉丝 377原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值