【Javassist】快速入门系列12 当检测到catch语句时在catch前插入代码

系列文章目录

01 在方法体的开头或结尾插入代码
02 使用Javassist实现方法执行时间统计
03 使用Javassist实现方法异常处理
04 使用Javassist更改整个方法体
05 当有指定方法调用时替换方法调用的内容
06 当有构造方法调用时替换方法调用的内容
07 当检测到字段被访问时使用语句块替换访问
08 当检测到对象(不包括数组)创建时用代码块替换
09 当检测到对象(不包括数组)创建时用代码块替换
10 当检测到instanceof表达式时用代码块替换
11 当检测到显示类型转换时用代码块替换
12 当检测到catch语句时在catch前插入代码



前言

上一章我们介绍了当检测到显示类型转换时用代码块替换,学习了 method.instrument的用法。以及参数为Cast的重载方法edit的含义。本章主要介绍当检测到catch语句时在catch前插入代码。


引入Javassist jar包

在上几篇文章已经引入了javassist的jar包,如果你是第一次观看本系列文章,也可以复制以下maven依赖将jar包导入工程。

        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.28.0-GA</version>
        </dependency>

当检测到catch语句时在catch前插入代码

/**
 * 【Javassist】快速入门系列11 当检测到显示类型转换时用代码块替换
 * 公众号&B站:精致的王同学
 * @author 精致的王同学
 * @date 2022/12/28 18:18
 */
public class Basic12Handler {
    public static void main(String[] args) throws Exception{
        // 获取javassist默认类池
        ClassPool pool = ClassPool.getDefault();
        // 获取basic.Basic12Test的ctclass类
        CtClass ctClass = pool.get("basic.Basic12Test");
        // 获取basic.Basic12Test的main方法
        CtMethod method = ctClass.getDeclaredMethod("main");
        // 当检测到catch语句时在catch前插入代码
        method.instrument(new ExprEditor() {
            @Override
            public void edit(Handler h) throws CannotCompileException {
                try {
                    if (h.getType().equals(pool.get("java.lang.Exception"))) {
                        h.insertBefore("System.out.println(\"Handler\");");
                    }

                } catch (NotFoundException e) {
                    e.printStackTrace();
                }
            }
        });
        // 将类写成文件
        ctClass.writeFile();
        // 获取修改后的class文件
        Class<?> clazz = ctClass.toClass();
        // 获取修改后的类实例
        Object obj = clazz.newInstance();
        // 获取修改后的main方法
        Method main = clazz.getDeclaredMethod("main", String[].class);
        // 调用修改后的main方法
        main.invoke(obj,(Object) new String[0]);
    }
}

以上Basic12Handler 类创建了一个main方法,该方法中首先获取javassist的类池pool,然后调用pool.get(“basic.Basic12Test”)方法获取到basic包下的Basic12Test类。Basic12Test类源码如下:

/**
 * 第12节测试类
 * 公众号&B站:精致的王同学
 * @author 精致的王同学
 * @date 2022/12/28 17:54
 */
public class Basic12Test {
    public static void main(String[] args) {
        SkuService skuService = new SkuService();
        try {
            skuService.getSkuProfitRate(1L);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

该类中有一个main方法,创建了一个skuService 对象。然后将调用其getSkuProfitRate方法。如果有异常则打印异常,SkuService 类源码如下:

/**
 * 商品业务类
 *
 * @author 精致的王同学
 * @date 2022/12/19 23:38
 */
public class SkuService {
    public BigDecimal getSkuProfitRate(Long skuId){
        // 模拟调用查价格接口获取商品原价和商品销售价
        BigDecimal originalPrice = null;
        originalPrice = getOriginalPrice(skuId);
        BigDecimal salePrice = getSalePrice(skuId);
        BigDecimal profit = salePrice.subtract(originalPrice);
        return profit.divide(originalPrice);
    }

    private BigDecimal getSalePrice(Long skuId) {
        return new BigDecimal(130.00);
    }

    private BigDecimal getOriginalPrice(Long skuId) {
        // 模拟sku原始价格为0,价格未初始化的情况
        BigDecimal originalPrice = new BigDecimal(0.00);
        if (BigDecimal.ZERO.compareTo(originalPrice) == 0) {
            throw new ErrorCodeException(10001,"sku原始价格不能等于0!");
        }
        return originalPrice;
    }
}

SkuService类的getSkuProfitRate方法根据skuId获取其sku利润率。其中调用getOriginalPrice方法抛出一个异常。

回到Basic12Test的main方法,在获取到Basic12Test类的ctClass的对象之后,获取其main方法的方法对象。

接着调用method.instrument(ExprEditor editor)方法搜索method内的catch语句。调用h.getType()方法获取异常类型的ctclas文件判断是否是java.lang.Exception类型的,如果是则调用h.insertBefore(“System.out.println(“Handler”);”);在catch前插入代码。

instrument方法接收一个ExprEditor 类型的对象,该类有很多重载的edit方法,其中参数为Handler的重载方法代表搜索方法内的catch语句。

在replace的代码块中,以下符号有特殊含义:

$1  catch子句捕获的异常对象。
$r  catch子句捕获的异常的类型。它用于强制转换表达式中。
$w  包装器类型。它用于强制转换表达式中。
$type  java.lang.Class对象表示catch子句捕获的异常的类型。

最后模拟调用修改后的main方法结果如下:
main方法结果

总结

本篇文章介绍了使用Javassist当检测到catch语句时在catch前插入代码,学习了 method.instrument的用法。以及参数为Handler的重载方法edit的含义。

说明

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值