4 篇文章

Java

Kryo序列化问题
 • 分类:java • 标签:Kryo

深藏不露的Bug:当Kryo序列化遇上toString()

在软件开发中,我们时常与各种Bug不期而遇。有些Bug显而易见,错误日志直指病灶;而另一些则如冰山一角,表面现象可能误导我们偏离真正的根源。

本文将详细复盘一个在Java项目中,因错误处理Kryo序列化字节流而导致的Bug: ClassNotFoundException,揭示一个使用Kryo序列化后又使用 toString() 导致的Bug。


项目背景和问题分析

AI工具日趋强大,为了熟悉Spring AI和体验现在AI编程工具的潜力,我选择了使用AI来辅助我开发一个基于Spring AI的项目。

一个令人困惑的Bug

在使用Spring AI构建聊天功能时,实现对话记忆(ChatMemory)时计划采用Kryo进行序列化,并存储于Redis中。

于是我在IDEA中通过 AI 插件实现了 RedisChatMemory 类,以实现基于Redis的AI对话记忆功能。代码如下:

java
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.io.Input;
import org.objenesis.strategy.StdInstantiatorStrategy;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

@Component
public class RedisChatMemory implements ChatMemory {

    private final StringRedisTemplate redisTemplate;
    private static final Kryo kryo = new Kryo();
    
    static {
        kryo.setRegistrationRequired(false);
        // 设置实例化策略
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
    }

    @Autowired
    public RedisChatMemory(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void add(String conversationId, List<Message> messages) {
        List<Message> conversationMessages = getOrCreateConversation(conversationId);
        conversationMessages.addAll(messages);
        saveConversation(conversationId, conversationMessages);
    }

    @Override
    public List<Message> get(String conversationId, int lastN) {
        List<Message> allMessages = getOrCreateConversation(conversationId);
        return allMessages.stream()
                .skip(Math.max(0, allMessages.size() - lastN))
                .toList();
    }

    @Override
    public void clear(String conversationId) {
        redisTemplate.delete(conversationId);
    }

    private List<Message> getOrCreateConversation(String conversationId) {
        String data = redisTemplate.opsForValue().get(conversationId);
        if (data != null && !data.isEmpty()) {
            try (Input input = new Input(new ByteArrayInputStream(data.getBytes()))) {
                return kryo.readObject(input, ArrayList.class);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return new ArrayList<>();
    }

    private void saveConversation(String conversationId, List<Message> messages) {
        try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
             Output output = new Output(bos)) {
            kryo.writeObject(output, messages);
            output.flush();
            redisTemplate.opsForValue().set(conversationId, bos.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

然而,系统在运行中突然抛出异常:

阅读更多 »
Jar包的导入
 • 分类:java • 标签:jar

普通Java项目如何导入Jar包

虽然maven导入jar包更简单实用,但也存在需要在普通java项目中导入jar包的需求。

注:演示的IDEA为2021版,其他版本可能略有差异

  1. 项目目录下新建 lib 目录,并将需要导入的 jar 包复制到该文件夹 如:(以导入 mysql驱动包为例)

  2. 将 lib 目录下的所有依赖导入到指定模块

打开 File -> Project Structure (Ctrl + Shift + Alt + S)-> 点击 加号 -> 选择我们创建的 lib 目录 -> 确认即可

然后选择需要该依赖的模块,点击 OK

此时我们打开该模块的依赖,可以看到 lib 下的依赖已经导入成功

导入成功后该jar包还可以打开查看源码 (^▽^)

  1. 项目中途导入新的jar包

新版IDEA直接将jar包复制到 lib 文件夹即可

阅读更多 »
Java简介及发展史
 • 分类:java • 标签:jdk

一 Java的起源和发展史

1. Java的起源

2. JDK1-7

  1. 1996年1月23日,JDK 1.0发布,Java语言有了第一个正式版本的运行环境。JDK 1.0提供了一个纯解释执行的Java虚拟机实现(Sun Classic VM)。JDK 1.0版本的代表技术包括:Java虚拟机、Applet、AWT等。

    1996年4月,十个最主要的操作系统和计算机供应商声明将在其产品中嵌入Java技术。同年9月,已有大约8.3万个网页应用了Java技术来制作。在1996年5月底,Sun于美国旧金山举行了首届JavaOne大会,从此JavaOne成为全世界数百万Java语言开发者每年一度的技术盛会。

    1997年2月19日,Sun公司发布了JDK 1.1,Java里许多最基础的技术支撑点(如JDBC等)都是在JDK 1.1版本中提出的,JDK 1.1版的技术代表有:JAR文件格式、JDBC、JavaBeans、RMI等。Java语言的语法也有了一定的增强,如内部类(Inner Class)和反射(Reflection)都是在这时候出现的。 直到1999年4月8日,JDK 1.1一共发布了1.1.0至1.1.8这9个版本。从1.1.4以后,每个JDK版本都有一个属于自己的名字(工程代号),分别为:JDK 1.1.4-Sparkler(宝石)、JDK 1.1.5-Pumpkin(南瓜)、JDK 1.1.6-Abigail(阿比盖尔,女子名)、JDK 1.1.7-Brutus(布鲁图,古罗马政治家和将军)和JDK 1.1.8-Chelsea(切尔西,城市名)。

  2. 1998年12月4日,JDK迎来了一个里程碑式的重要版本:工程代号为Playground(竞技场)的JDK 1.2,Sun在这个版本中把Java技术体系拆分为三个方向,分别是面向桌面应用开发的J2SE(Java 2 Platform,Standard Edition)、面向企业级开发的J2EE(Java 2 Platform,Enterprise Edition)和面向手机等移动终端开发的J2ME(Java 2 Platform,Micro Edition)。在这个版本中出现的代表性技术非常多,如EJB、Java Plug-in、Java IDL、Swing等,并且这个版本中Java虚拟机第一次内置了JIT(Just In Time)即时编译器(JDK 1.2中曾并存过三个虚拟机,Classic VM、HotSpot VM和Exact VM,其中Exact VM只在Solaris平台出现过;后面两款虚拟机都是内置了JIT即时编译器的,而之前版本所带的Classic VM只能以外挂的形式使用即时编译器)。在语言和API层面上,Java添加了strictfp关键字,Java类库添加了现在Java编码之中极为常用的一系列Collections集合类等。在1999年3月和7月,分别有JDK 1.2.1和JDK 1.2.2两个小升级版本发布。

    1999年4月27日,HotSpot虚拟机诞生。HotSpot最初由一家名为“Longview Techno-logies”的小公司开发,由于HotSpot的优异表现,这家公司在1997年被Sun公司收购。Hot-Spot虚拟机刚发布时是作为JDK 1.2的附加程序提供的,后来它成为JDK 1.3及之后所有JDK版本的默认Java虚拟机。

  3. 2000年5月8日,工程代号为Kestrel(美洲红隼)的JDK 1.3发布。相对于JDK 1.2,JDK1.3的改进主要体现在Java类库上(如数学运算和新的Timer API等),JNDI服务从JDK 1.3开始被作为一项平台级服务提供(以前JNDI仅仅是一项扩展服务),使用CORBA IIOP来实现RMI的通信协议,等等。这个版本还对Java 2D做了很多改进,提供了大量新的Java 2D API,并且新添加了JavaSound类库。JDK 1.3有1个修正版本JDK 1.3.1,工程代号为Ladybird(瓢虫),于2001年5月17日发布。 自从JDK 1.3开始,Sun公司维持着稳定的研发节奏:大约每隔两年发布一个JDK的主版本,以动物命名,期间发布的各个修正版本则以昆虫作为工程代号。

阅读更多 »
Java开发环境
 • 分类:java • 标签:jdk

Linux环境

解压安装jdk: jdk 8u202之前的版本下载地址   jdk全版本下载地址

  • 先检查是否已经安装jdk
shell
java -version

rpm -qa|grep openjdk -i   # 检查系统安装的openjdk

rpm -e --nodeps XXX(需要删除的软件名) #如果存在openjdk,就用这个命令逐一删除
  • 创建jdk安装目录和软件包存储目录,并上传jdk文件。将文件解压剪贴到jdk安装目录后配置环境变量即可。
shell
mkdir /usr/java
mkdir /home/software

tar -zxvf jdk-8u191-linux-x64.tar.gz
mv jdk1.8.0_191/ /usr/java/
  • 配置环境变量,(修改profile文件)
shell
vim /etc/profile  #配置环境变量,加入如下信息:(按esc退出插入模式后 :wq 保存退出)
text
export JAVA_HOME=/usr/java/jdk1.8.0_191
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$PATH
  • 刷新profile,使其生效
shell
source /etc/profile

Windows

下载安装JDK

oracle官网 下载适合的JDK安装包,注意要选择自己系统对应的版本

阅读更多 »