月度归档: 2023年3月

Mysql临时表CREATE TEMPORARY TABLE

群友的一个小问题,大意如下。我手中目前已知有若干ID列表,如[1,2,3,4,5]的ID,需要到某张表中查询出表中不存在的ID。

好像有点绕,实际列举数据说明下,比如此刻表中有ID为[2,4,5,6,7]的数据,那么最终查询出来的结果应当为[1,3],因为[1,3]在我们已知的列表[1,2,3,4,5]中,但是不在数据库的表[2,4,5,6,7]中,而[6,7]因为不在[1,2,3,4,5]中,所以也不符合要求。

其实这个问题很好解的,最直观的来说我们先拿[1,2,3,4,5],到表中查询[2,4,5,6,7]用in语句得到[2,4,5]的结果,之后再拿[1,2,3,4,5]到[2,4,5]的结果集中用not in语句就可以得到[1,3]。

但,问题不是这里,而是[1,2,3,4,5]不是一个结果集,而是入参,所以我们这里我们需要用到MYSQL的临时表。而如果[1,2,3,4,5]是在一张表里的结果的话,那么我们就可以换另一个思路了,用临时表[1,2,3,4,5]LEFT JOIN目标表使用id关联,并保留当前的[1,2,3,4,5]的列和目标表的ID列,关联后的结果如果目标ID列为空的数据可以再做一次过滤剔除。

Continue reading

Javassist结合JavaAgent修改Java.class文件字节码实践

续上一篇Javassist动态修改jar包内java类字节码,有了上一篇的基础之后我们再尝试结合JavaAgent来实现更加强大的AOP功能。

首先提一嘴JavaAgent。在 jdk 1.5 之后引入了 java.lang.instrument 包,该包提供了检测 java 程序的 Api,比如用于监控、收集性能信息、诊断问题,通过 java.lang.instrument 实现的工具我们称之为 Java Agent。

其基本原理可以理解为,当JVM在加载.class类文件的时候会触发ClassFileLoadHook回调JNI执行sun.instrument.InstrumentationImpl#transform方法,如果我们自己实现了ClassFileTransformer的transform方法,则会用transform方法返回的字节码内容替换掉原来读取到的.class文件的字节码。https://www.cnblogs.com/yichengtech/p/15854415.html

接下来我们来自己做一个简单的实现,顺便结合之前文章中的Javassist工具做一点“有意思”的事情。

创建Agent Jar包

创建一个新的工程,代码结构基本如下。一个MyAgent入口类、一个名为MyClassFileTransformer的ClassFileTransformer接口实现类

两个类的对应实现代码,首先是MyAgent类,在这里写一个premain方法,并在这里引入我们下面要实现的MyClassFileTransformer类

package com.myagent;

import java.lang.instrument.Instrumentation;

/**
 * MyAgent<br>
 *
 * @author CheungQ
 * @date 2023/3/15 16:43
 */
public class MyAgent {
    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("premain begin");
        inst.addTransformer(new MyClassFileTransformer());
    }
}

而在MyClassFileTransformer类中实现了ClassFileTransformer接口,并在transform方法中实现我们自己的逻辑。先判断一下当前加载的类,如果不需要调整修改的则直接return null即可。此处我们假设需要修改com.cheungq.demo.service.RedisService这个类

Continue reading

Javassist动态修改jar包内java类字节码

之前在es聚合导出(不是),Map#merge方法使用一文里提到过调用jar包里的代码,但是jar包里的代码有问题,需要修改才行的问题。另外之前也跟朋友聊天的时候提到过他调用的jar包里的源码有bug,但是没法修改的事情。今天摸会儿<・)))><<,试用下Javassist(http://www.javassist.org/)的强大功能,可以动态的直接修改编译后的.class文件,这样的好处是在不修改源码的情况下即可修改现有逻辑。不光是修改,他还能动态创建新的类,并编写相应逻辑,实现对现有代码的零侵入。

同时结合上文Redis结合AOP实现限流注解的开发中提到的Aspect切面编程相关技术实现更加强大的方法增强效果。

下面就来做一个简单实现试试看,目标仍然是修改jar包里的代码逻辑,首先第一步自己先写个类,并打成jar包。

打包jar包

取一段现有项目里的代码,打成一个包。新建一个项目,按如下结构填入代码,并进行package打包操作

现有分库操作中常用的一个方法,根据订单号模12之后的值进行分库存取操作
public class Mod12 {
    public static int mod12(String number){
        return modBy(number,12);
    }

    public static int modBy(String number, int modNum){
        BigInteger numBig = new BigInteger(number);
        return numBig.mod(BigInteger.valueOf(modNum)).intValue();
    }
}

pom.xml文件内容也很干净,如下,分别定义groupId、artifactId、version即可

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mymod</groupId>
    <artifactId>mod12</artifactId>
    <version>1.0-SNAPSHOT</version>
</project>

执行package后在项目的target目录下得到一个mod12-1.0-SNAPSHOT.jar文件。

题外话,这样的操作其实也是在日常工作中常用的一种方式。将一些通用的工具类,接口类等独立一个项目,并打包成jar包,再在其他各业务系统中引入这个对应的包调用即可。打一个比方,A系统在集群内提供了一系列rpc服务接口,此时A系统的开发人员将接口单独定义单独抽出来打成一个jar包,其他系统在需要调用A系统的接口的时候引入这个jar包,根据jar包内定义的方法调用A系统的接口。如果A系统某天新增了某个接口,而某个调用方需要用这个新增的接口,则更新下jar包的版本号即可。

Continue reading