![](https://img-blog.csdnimg.cn/20201014180756918.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
Java
文章平均质量分 81
倪琛
程序员
展开
-
为什么应该少用静态(static)方法:静态方法的三大问题
静态方法非常常见,比如很多工具类中都会有大量静态方法。之前我对这种现象习以为常,直到最近发生的几件事让我决定以后要尽量避免用静态方法。下面就来看看静态方法的两大问题。(这里以 Java 为例,但相信很多语言应该都一样。)相比常规方法,在 Java 中对静态方法进行 mock 要复杂一些。Mockito 直到 3.4.0 版本才支持 mock 静态方法。不过,这实在是三个问题里最轻的一个了。面向对象语言的精髓就是依赖抽象,而不是依赖实现。在 Java 中,可以通过接口或者抽象类来实现这一点。而静态方法则不具备原创 2022-07-13 13:27:24 · 3632 阅读 · 0 评论 -
Mockito 实现原理(4):@Mock 注解
上篇文章 研究了一下 JUnit 的基础原理,这篇来看看 Mockito 和 JUnit 交界的部分: 注解。 注解可以看做一个语法糖,用更少的代码实现相同的 mock 功能。例如:这样声明 更为清晰直观,胜过在测试里写 ,特别是在许多测试方法中都要用到这样一个 mock 对象的时候。那为什么说 注解是 Mockito 和 JUnit 交界的部分呢?注意最上面那行 ,如果把这行去掉, 注解就失效了。(当然,还有别的方式也可以实现同样功能,比如增加 JUnit 的 注解。)毕竟, 注解需要由 Mock原创 2022-06-14 11:59:19 · 7345 阅读 · 0 评论 -
JUnit 实现原理(1):测试是如何跑起来的
继上一个系列初步研究了单元测试神器 Mockito 的实现原理之后,这个系列关注一下单元测试框架本身:JUnit。相信大家对于在 IDE 中执行单元测试毫不陌生。除了单元测试,我也经常会用 JUnit 验证一些 Java 的语法机制,毕竟 Java 不是脚本语言,总得有个入口才能执行程序呀!总不能每次都新建一个 project 吧。如此说来,JUnit 真的是一个非常方便的工具。下面就来看看,当你点击 IntelliJ 中的“执行测试”的按钮时,JUnit 中到底发生了什么。(本文基于 JUnit 4.13原创 2022-06-11 20:37:21 · 2791 阅读 · 3 评论 -
浅谈 Java 注解(annotation)
之前我一直以为,Java 注解(annotation)和 Python 或者 JavaScript 里的装饰器(decorator)是类似的东西,毕竟看起来很像嘛,都是一个 符号;甚至功能也很像,就是给原本的对象“添加”一些效果。今天才发现,虽然 Python 和 JavaScript 里的装饰器确实是一回事,但 Java 注解和它们完全不同!先来看看 Python 和 JavaScript 里的装饰器吧。之所以叫“装饰器”,的确是因为实现了设计模式里的“装饰器模式”(decorator pattern)原创 2022-06-06 15:51:13 · 366 阅读 · 0 评论 -
Mockito 实现原理(3):如何对 final 类进行 mock
前面两篇提到,Mockito 默认基于创建派生类(subclass)来实现 mock(包括 spy)。那么问题来了,如果我的类标记为 final,明确禁止创建派生类,那不就没法 mock 了吗?为了解决这个问题,Mockito 2 中引入了 。和前面讨论过的默认的 相比,这个 同样基于 Byte Buddy 这个提供 Java 字节码操作功能的第三方库,但会尽量不通过创建派生类来实现 mock。对 final 类进行 mock,需要用 替换掉默认的 。替换方法是通过创建一个配置文件。按照这篇教程,应该原创 2022-06-05 21:48:24 · 5321 阅读 · 0 评论 -
Mockito 实现原理(2):spy 的原理
上一篇讲了 的基本原理,这一篇简单谈谈 。可以这么理解这两者之间的区别: 的一个典型用法就是,你想测试某个对象 的某个方法 A,但 A 会调用它的另一个方法 B,而你想把 B mock 掉。这时候就可以基于 创建一个 ,然后把它的 B 方法 mock 掉就可以了。(注:本系列基于 Mockito 4.6.1 源码)可以看到,在创建 对象时,Mockito 会指定 作为 :这里的 并非一个字符串,而是某个对象。总之就是,根据这个配置,在对 mock 对象进行方法调用时,默认会执行真正的方法,而不原创 2022-06-04 19:06:33 · 687 阅读 · 0 评论 -
使用 Optional 写出更简洁清晰的 Java 代码
Optional 是 Java 8 中引入的功能,很早就听说过,但一直没实际用过。直到这两天在某开源项目里看到 Optional,没看懂,查了一下用法,这才恍然大悟,原来这东西这么有用!简单来说,如果你的代码里有很多 这样的判断,那么 Optional 能帮你把它们都除掉!写出更简洁清晰的代码。(这个功能类似于 和 中的可选链(Optional Chaining))下面举个例子。假设需要根据客户端传来的 查找相应的 data,有两点要求:如果不用 Optional,一般情况下我们会这样写:使用 O原创 2022-06-04 12:30:43 · 261 阅读 · 0 评论 -
Mockito 实现原理(1):基本原理
最近开始读 Mockito 的源码,还挺有意思。先简单说说它的基本原理吧!(注:本系列基于 Mockito 4.6.1 源码)Mockito 最基本的用法就是 mock 一个对象,然后在调用其方法时返回指定结果。以一段最简单的代码为例:这个效果是如何实现的呢?事实上,Mockito 提供了不止一种方法来实现上面的 mock 效果。在 中定义了一个 接口:这个接口有好多个实现类。目前我执行时发现,默认用的是 这个实现,所以下面就讨论该实现的具体步骤。第一行调用 时, 首先会做的就是创建一个 moc原创 2022-06-03 20:30:26 · 1451 阅读 · 0 评论 -
浅谈设计模式(2):装饰模式
继上一篇讲了简单工厂模式之后,这一篇谈谈装饰模式(Decorator Pattern),也叫装饰器模式。这也是继简单工厂模式之后,我实际工作中用到的第二种模式。装饰模式极为常见,一个很典型的例子就是缓存。很多时候一个业务组件已经实现了其功能,但我们希望给它加上一层缓存。大多数情况下,缓存的逻辑和该业务组件的内部逻辑完全无关,所以当然应该分开来写。甚至,这个业务组件根本就不应该知道缓存的存在!为什么呢?因为业务组件是业务逻辑,缓存只是技术细节,不应该用技术细节去“沾染”业务逻辑。这种情况下,装饰模式是当之无愧原创 2022-06-02 20:51:52 · 180 阅读 · 0 评论 -
浅谈 Mockito:Java 单元测试神器
背景背景Mockito 解决的问题Mockito 的优点优点 1:简单易用优点 2:功能强大优点 3:语法优美小结背景记得刚开始学 Java 那会儿看过一个讲解 Mockito 的视频,当时不理解,感觉这东西不过如此,平时不太会需要用到,就把它忘到九霄云外了。两年过去了,翻过来看到了同一个视频,这才意识到这东西的重要性!简直就是开发效率提升神器好不好!两年间,是我的编程习惯发生了天翻地覆的变化。之前的我喜欢学新技术,喜欢把功能快速实现(当然,也是产品的压力在那里),不太在意代码的可维护性,单元测试更原创 2022-05-28 18:30:44 · 569 阅读 · 0 评论 -
如何在 Spring Boot 启动之后触发代码逻辑
目录背景Spring Boot 启动之后执行代码逻辑参考链接背景这周遇到一件很棘手的事情。在启动某个 service 时,我需要初始化一个缓存。按照之前的逻辑,初始化只需要从本地存储中加载一个文件就行了,当时用 Spring 的 @PostConstruct 注解成功解决了这个问题:@Servicepublic class MyService { @Resource SomeDao someDao; // 数据库的 DAO @PostConstruct private void原创 2022-01-31 18:10:39 · 1956 阅读 · 0 评论 -
Java 并发编程基础实例:Runnable 与固定大小线程池
目录背景RunnableExecutors.newFixedThreadPool小结参考链接背景之前虽然在书里学过 Java 并发编程的基础,但一直没有在实践中用到过。直到上周,终于在项目看到了一个活生生的例子。这个例子也非常基础,需求大概是这样的:我们基于 Lucene 做站内搜索引擎,每天都需要重新构建一次索引,因为每天都会产生新内容(之所以彻底重新构建一遍,是因为内容量也没有那么大,与其在原有索引后面追加新索引,不如重新构建,因为这样查询起来更快);在构建索引时,需要请求 CMS 数据库,原创 2022-01-10 01:07:03 · 594 阅读 · 0 评论 -
Java stream 中 peek() 的合理用法
目录背景最终操作(terminal operation)peek() vs forEach()peek() 的典型用法:协助调试参考链接背景这周遇到了一件很有意思的事情。在看项目代码时,发现了这么一段:return objects.stream() .peek(object -> addInfo(object, someParams)) .collect(Collectors.toList());因为之前没接触过 peek(),这段代码看得我云里雾里。后来在原创 2022-01-08 19:12:39 · 46613 阅读 · 11 评论 -
Java/Maven:解决依赖冲突引起的 ClassNotFoundException
目录背景寻找原因表面原因心急如焚根本原因解决办法参考连接背景这周做的一件事情是,在我们原有的 REST API 服务上加一层 GraphQL Server。其实这件事本身挺简单,我们用的 Netflix 的 DGS 框架 非常好上手。我新建了一个 Spring Boot 项目,使用示例代码很快把 GraphQL Server 跑起来了。但问题来了:同样的示例代码,只要是加到我们原有的 Spring Boot 服务里,运行时就会报错。错误如下:... # 省略很多行Caused by: java.l原创 2021-11-08 00:09:08 · 1999 阅读 · 0 评论 -
Java 泛型 Selfbounded:SomeClass<T extends SomeClass<T>>
这里写目录标题背景Bounded Genericsasdfpoiupoipoiu背景这周在新公司熟悉代码,看到一个莫名其妙的 class 声明:public abstract class SomeClass<T extends SomeClass<T>> implements Cloneable { // ... // 这里面没有出现过 T}之前对 Java 泛型了解得不是很多,于是去学了一下。Bounded Generics下面这种是非常常见的一种泛原创 2021-09-19 11:08:32 · 241 阅读 · 0 评论 -
【AES 算法】实现服务端 Java 加密,前端 JS 解密
背景AES CBC 加解密算法代码实现Java 生成 key 和 ivJava 加密 & 解密JavaScript 解密小结原创 2021-07-27 19:51:37 · 1229 阅读 · 0 评论 -
jstack 命令检查 Spring MVC 应用启动时的各个线程
目录背景jstack 是什么?使用 jstack 查看 Spring Boot 启动时线程【2】Druid 数据库连接线程【1】MySQL Statement Cancellation Timer 线程【2】RMI TCP 连接线程【1】DestroyJavaVM 线程【14】端口 8080 的 HTTP 相关线程【1】HTTP NIO Acceptor 线程【2】HTTP NIO ClientPoller 线程【10】HTTP NIO 执行线程【1】BlockPoller 线程【1】调度线程【14】端口8原创 2021-07-24 17:24:37 · 1208 阅读 · 0 评论 -
Spring MVC 中的线程安全
目录背景实验实验 1:@Component 中的私有变量默认不是线程安全的实验 2:ThreadLocal实验 3:AtomicInteger背景Spring MVC 这个框架我前前后后用了也有快半年了,但还真的从来没有遇到过线程安全相关的问题。直到这周做了这样一个需求:我们的平台出售充值码,需要定期检查每款产品的充值码剩余库存,库存过低则报警 / 下架;查询剩余库存需要遍历记录,比较耗费性能,不能每次发奖都查询;常规的方案是,每隔固定时间轮询一次,比如每分钟查询一次,但我觉得不如每发固定个充原创 2021-07-23 19:50:04 · 888 阅读 · 0 评论 -
使用 Charles 抓取 Java 应用内 HTTP/HTTPS 请求
目录背景操作步骤步骤 1:步骤 2:背景操作步骤步骤 1:链接步骤 2:System.setProperty("http.proxyHost", "127.0.0.1");System.setProperty("https.proxyHost", "127.0.0.1");System.setProperty("http.proxyPort", "8889");System.setProperty("https.proxyPort", "8889");...原创 2021-07-08 23:14:13 · 1650 阅读 · 1 评论 -
Java 运行时 Jar 中无法读取 resources 目录下文件
背景(技术栈:普通的 SSM)目前在做一个充值发奖的需求,用户在我们的平台上支付成功之后,需要给用户发送包含充值码的邮件。邮件模板是一个 HTML 文件(CSS 全部内联),发奖时需要把相关数据(充值码、用户信息等)注入到模板里,得到邮件体的字符串,然后调用发邮件服务。遇到的问题本来应该是一件很简单的事情。HTML 文件保存在 resources/templates/myEmailTemplate.html,在发奖服务的 init() 方法中,需要把 HTML 模板加载为字符串,我是这么写的:@原创 2021-06-19 22:26:45 · 1732 阅读 · 3 评论