Demon.Lee 2025-06-20 09:19

嘿,更多碎碎念在这里:墨问小程序 😄

Demon.Lee 2025-06-18 16:01

一个 web 请求,VO 类中的字段是 pCode,但 http 的响应信息却是 pcode,显然是 spring boot 在做序列化转换时出了问题,查了一下:

spring boot 默认使用 Jackson 实现的 HttpMessageConverter 进行消息转换,会将 pCode 转换成 pcode(我在 spring boot 2.1 和 2.5 下测试都是这个结果)。debug 时看到有下面这些转换器:

0 = {ByteArrayHttpMessageConverter@5529} 
1 = {StringHttpMessageConverter@5530} 
2 = {StringHttpMessageConverter@5531} 
3 = {ResourceHttpMessageConverter@5532} 
4 = {ResourceRegionHttpMessageConverter@5533} 
5 = {SourceHttpMessageConverter@5534} 
6 = {AllEncompassingFormHttpMessageConverter@5535} 
7 = {MappingJackson2HttpMessageConverter@5536} 
8 = {MappingJackson2HttpMessageConverter@5537} 

如果想正常转换 pCode(即浏览器端显示 pCode,而不是 pcode),则需要换一个 HttpMessageConverter,比如 fastjson 实现的 FastJsonHttpMessageConverter:

@Configuration
public class ComponentsConfig implements WebMvcConfigurer {

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();

        // 保持原始属性名,不进行命名转换
        SerializeConfig serializeConfig = SerializeConfig.globalInstance;
        config.setSerializeConfig(serializeConfig);
        // 添加 SerializerFeature 来支持输出 null 值字段
        config.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue  // 输出 null 字段
                // SerializerFeature.WriteNullStringAsEmpty, // 将 null 字符串输出为 ""
                // SerializerFeature.WriteNullNumberAsZero, // 将 null 数字输出为 0
                // SerializerFeature.WriteNullBooleanAsFalse // 将 null 布尔值输出为 false
        );
        converter.setFastJsonConfig(config);

        List<MediaType> supportedMediaTypes = new ArrayList<>();
        supportedMediaTypes.add(MediaType.APPLICATION_JSON);
        supportedMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        converter.setSupportedMediaTypes(supportedMediaTypes);

        converters.add(0, converter);
    }
}

另外,这类字段在 Bean Copy 时可能也会产生相关问题,需要注意,不过我在以下版本中测试都是正常的:

  • hutool 5.8.23 cn.hutool.core.bean.BeanUtil 复制正常
  • spring-beans 5.1.5 org.springframework.beans.BeanUtils 复制正常
  • cglib 3.2.4 net.sf.cglib.beans.BeanCopier 复制正常

供参考。

Demon.Lee 2025-06-05 10:07

通过 Apache Velocity 配置模板时,只要使用注释 ## 就能抑制空行的出现 🐶

##
## 输出所有生成的定义代码
#foreach($generatedDefLine in $generatedDefinitionLines)
    ${generatedDefLine}
#end
##
Demon.Lee 2025-01-11 10:09

2024 阅读 33 本,勉强追平 2023 年 🙃

Learning Summary@2024

Demon.Lee 2025-01-11 09:48

不想上班的你,像极了数据结构中的栈(Stack):来的最晚,走的最早 🐶

领导说我能力不足,我笑死~

Demon.Lee 2024-12-21 11:32

鼠与虎,都一样,没天命又不认命。

小猴子你听,那风里传来的,都是求而不得的声音。

行动是打开枷锁的钥匙,是治愈的良方。

Demon.Lee 2024-06-27 15:14

因为 Java maven 工程的 pom.xml 中配置了 <maven.test.skip>true</maven.test.skip>,导致 vs code 一直不能显示 Junit 单元测试的测试按钮……

Demon.Lee 2024-06-20 15:33

使用 hutool 中的 BeanUtil.copyProperties 转换对象时,如果有枚举类字段,转换时,会到对应枚举类中去查找自定义转换函数:

// cn.hutool.core.convert.impl.EnumConverter
private static Map<Class<?>, Method> getMethodMap(Class<?> enumClass) {
	return VALUE_OF_METHOD_CACHE.computeIfAbsent(enumClass, (key) -> Arrays.stream(enumClass.getMethods())
		.filter(ModifierUtil::isStatic)
		.filter(m -> m.getReturnType() == enumClass)
		.filter(m -> m.getParameterCount() == 1)
		.filter(m -> false == "valueOf".equals(m.getName()))
		.collect(Collectors.toMap(m -> m.getParameterTypes()[0], m -> m, (k1, k2) -> k1)));
}

所以,如果你写的自定义转换函数并不是为了用于对象转换,就需要小心了~

嗯,这并不是一个好的设计。

Demon.Lee 2023-12-26 13:49

架构的本质是分离控制和逻辑。基础设施好了,干啥都方便,所以效率自然就高。计算机软件中的各种工具、框架和平台,都是在帮我们解决控制,那我们自己干什么呢?专注于逻辑就行,也就是你的核心业务。

  • 你不用自己搭一个博客系统,微信公众号、语雀、墨问便签、flomo、journal 哪里都可以写;
  • 你不用自己搭建一个容器管理平台,kubernetes 已经帮你做好了;
  • 你不用自己管理软件进程中的内存回收,VM 虚拟机已经帮你搞定了;
  • 你不用自己管理软件对象的生命周期,对象创建、更新、销毁等,Spring 之类的框架一键托管了;
  • ……

只有计算机软件行业是这样吗?No,如果你想创业:从门店选址到门店管理,从物流到营销,从支付到配送,啥基础设施都为你准备好了。你只要想好干什么,再准备好 money,就可以开工了~

这就是真正的效率至上吧。门槛低了,干啥都快,而由此带来的副作用就是同质化竞争严重。不过,对我们这种小老百姓来说,也不是坏事,毕竟竞争可以带来低价 😄。

二十年前,会想到今天的我们,人手一部智能手机吗?
二十年后,是否会出现每人都有专属的 AI 机器人?
我觉得是很有可能的,很多事情都让机器人干,多好。然后我们干啥?躺平?🐶

Demon.Lee 2023-11-11 15:37

关于 Hotspot VM 中的 PS MarkSweep,Serial Old 和 Parallel Old:

1、https://www.jianshu.com/p/624338a8d51b

在 Parallel Scavenge 收集器架构中本身有 PS MarkSweep 收集器来进行老年代收集,但由于 PS MarkSweep 与 Serial Old 实现非常接近,因此官方的许多资料都直接以 Serial Old 代替 PS MarkSweep进行讲解。

2、https://www.zhihu.com/question/56344485

Java 7/8 默认老年代回收是 PSMarkSweep(Serial-Old) 还是Parallel Old ?
这个问题的答案取决于 JDK 版本,在 2012 年默认值改变过一次。这个改进使得 HotSpot VM 在选择使用 ParallelGC(-XX:+UseParallelGC 或者是 ergonomics 自动选择)的时候,会默认开启 -XX:+UseParallelOldGC 。这个变更应该是在JDK7u4开始的JDK7u系列与JDK8系列开始生效。

3、https://xie.infoq.cn/article/2b4358255f56a3a77939b9e84

PS MarkSweep 只是回收器的别名,他可以指代 Serial Old 和 Parallel Old,毕竟他们的实现基本一样。Java 8 上 -XX:+UseParallelGC 和 -XX:+UseParallelOldGC 结果一样,都是用的 Parallel Old。


笔者总结一下:

  • 在一些书或文章中,PS MarkSweep 指代 Serial Old,是因为它们共用了相同的代码;
  • 在 JDK7u4 之前,PS MarkSweep 指向 Serial Old,而这之后它则指向 Parallel Old;
  • 在 JDK7u4 之后,使用 -XX:+UseParallelGC 的新老代回收器是 Parallel Scavenge + Parallel Old,只不过使用 jconsole 等工具查看时,显示的都是 PS Scavenge + PS MarkSweep。