当前位置: 代码迷 >> 综合 >> Cglib Callback使用、如何进行反编译
  详细解决方案

Cglib Callback使用、如何进行反编译

热度:38   发布时间:2023-12-15 03:49:33.0

Cglib Callback使用、反编译

    • 6种Callback的使用
      • 统一入口
      • FixedValue
      • NoOp
      • Dispatcher
      • LazyLoader
      • InvocationHandler
      • MethodInterceptor
    • 反编译解析
      • 使用sa-jdi.jar(HSDB)反编译
      • 使用DebuggingClassWriter输入Class文件

6种Callback的使用

统一入口

原始类

//提供六种方法来分别测试六种Callback效果
public class UserService {public String forFixedValue() {return "self FixedValue";}public String forNoOp() {return "self NoOp";}public String forDispatcher() {return "self Dispatcher";}public String forLazyLoader() {return "self LazyLoader";}public String forInvocationHandler() {return "self InvocationHandler";}public String forMethodInterceptor() {return "self MethodInterceptor";}
}

测试类

public class Test {
    public static void main(String[] args) {
    Enhancer enhancer = new Enhancer();enhancer.setSuperclass(UserService.class);enhancer.setCallbacks(new Callback[]{
    new MyFixedValue(), NoOp.INSTANCE, new MyDispatcher(),new MyLazyLoader(), new MyInvocationHandler(), new MyMethodInterceptor()});enhancer.setCallbackFilter(getCallBackFilter());UserService userService = (UserService) enhancer.create();System.out.println("result:" + userService.forFixedValue());System.out.println();System.out.println("result:" + userService.forNoOp());System.out.println();System.out.println("result111111:" + userService.forDispatcher());System.out.println();System.out.println("result222222:" + userService.forDispatcher());System.out.println();System.out.println("result111111:" + userService.forLazyLoader());System.out.println();System.out.println("result222222:" + userService.forLazyLoader());System.out.println();System.out.println("result:" + userService.forInvocationHandler());System.out.println();System.out.println("result:" + userService.forMethodInterceptor());System.out.println();}static CallbackFilter getCallBackFilter() {
    return method -> {
    if (method.getName().contains("FixedValue")) {
    return 0;} else if (method.getName().contains("NoOp")) {
    return 1;} else if (method.getName().contains("Dispatcher")) {
    return 2;} else if (method.getName().contains("LazyLoader")) {
    return 3;} else if (method.getName().contains("InvocationHandler")) {
    return 4;} else if (method.getName().contains("MethodInterceptor")) {
    return 5;} else {
    return 1;}};}
}

这里使用了多种callback,所以对应使用了callbacks+callbackFilter来逐个对应六种callback方式,语法方面不再涉及。

FixedValue

public class MyFixedValue implements FixedValue {
    @Overridepublic Object loadObject() throws Exception {
    System.out.println("------exec MyFixedValue--------");return "Proxy FixedValue";}
}
//测试类的调用入口:
// UserService userService = (UserService) enhancer.create();
// System.out.println("result:" + userService.forFixedValue());
//对应执行结果
//------exec MyFixedValue--------
//result:Proxy FixedValue

使用FixedValue来作为回调,会直接替换初始类的方法,可以理解为直接替换原先的方法。
由于直接拦截的初始的所有方法,可能会出现类型异常
对于案例而言:

  • UserServiceforFixedValue方法不再被调用,而是执行MyFixedValue内部的loadObject方法;
  • loadObject的返回值将作为原始方法(即forFixedValue)的返回值。

上面这两点是针对UserService的所有方法而言的,比假如调用userService.hashCode,本来应当返回int类型,但是走了MyFixedValue的逻辑,就返回了String类型。类型不同,所以报类型异常。
ps:备注

  • 在本例中调用userService.hashcode是不会报错的,因为使用了CallbackFilter来对方法进行了CallBack的调用处理,除了UserService内部自定义的6个方法以外,都会进入到NoOp的逻辑中(不代理)。
  • 当方法如果走的是FixedValue的时候,才可能会出现类型异常。比如只使用了FixedValue作为回调,即:enhancer.setCallback(new MyFixedValue()),并且不使用callbackfilter,此时调用hashcode(),就会类型异常。

NoOp

走原本的方法调用逻辑,相当于不进行代理(调用的是原方法)。
使用时直接用NoOp.INSTANCE即可

 enhancer.setCallbacks(new Callback[]{
    new MyFixedValue(), NoOp.INSTANCE, new MyDispatcher(),new MyLazyLoader(), new MyInvocationHandler(), new MyMethodInterceptor()});

Dispatcher

创建一个类来替换初始类,然后使用替换的类的方法,每次都会重复创建。

注意loadObject放回的类得是初始类类型的。不然转型异常。从loadObject这个名字也可以猜出来这是用来加载被调用的累的。

public class MyDispatcher implements Dispatcher {
    @Overridepublic Object loadObject() throws Exception {
    System.out.println("------exec MyDispatcher--------");return new UserService();}
}
// 测试类调用方法
// System.out.println("result111111:" + userService.forDispatcher());
// System.out.println();
// System.out.println("result222222:" + userService.forDispatcher());
// System.out.println();
// 测试类调用结果
//------exec MyDispatcher--------
//result111111:self Dispatcher
//
//------exec MyDispatcher--------
//result222222:self Dispatcher

测试类中调用了两次forDispatcher,从打印结果可以看出来每次都执行了loadObject方法,每次都打印了------exec MyDispatcher--------

LazyLoader

用法和Dispatcher差不多,不过它是懒加载的。
创建一个类来替换初始类,然后使用替换的类的方法,不同于Dispatcher,只创建一次。

public class MyLazyLoader implements LazyLoader {
    @Overridepublic Object loadObject() throws Exception {
    System.out.println("------exec MyLazyLoader--------");return new UserService();}
}
// 测试类调用方法
// System.out.println("result111111:" + userService.forLazyLoader());
// System.out.println();
// System.out.println("result222222:" + userService.forLazyLoader());
// System.out.println();
// 执行结果
//------exec MyLazyLoader--------
//result111111:self LazyLoader
//
//result222222:self LazyLoader

打印结果可以看到,只有第一次的时候执行了loadObject方法,第二次的时候直接用第一次调用得到的new UserService来进行方法调用了。

InvocationHandler

通过反射的方式进行代理。
需要注意这里调用的是proxy类的父类的实例。使用method.invoke(proxy args),会造成死循环。

  • 原理是这里的proxy对象实际上初始类的子类(由cglib生成)
  • 在这个子类里面最终会调用到MyInvocationHandler的逻辑。
  • 那么如果反射的时候调用还是这个子类,相当于又走了一遍上面的逻辑,然后就造成了死循环。
public class MyInvocationHandler implements InvocationHandler {
    @Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    System.out.println("------exec MyFixedValue--------");System.out.println("before invocationHandler");Object invoke = method.invoke(proxy.getClass().getSuperclass().newInstance(), args);System.out.println("after invocationHandler");return invoke;}
}

MethodInterceptor

使用最多的Callback,可以完成几乎所有功能。
需要注意这里调用的是invokeSuper方法,使用invoke方法,会形成死循环,最终造成堆栈溢出。

public class MyMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("before methodInterceptor");Object result = methodProxy.invokeSuper(obj, args);System.out.println("after methodInterceptor");return result;}
}

反编译解析

对于如何查看cglib动态
代理生成的class类,然后进行反编译,这里提供了两种方式。

使用sa-jdi.jar(HSDB)反编译

这是jdk提供的开发工具之一,可以连接java进行,对一些jvm底层进行调试。
调试时需要java进程暂停,可以debug断点,或者System.in.read阻塞。

  1. 命令行启动:java -classpath "X:\develop\jdk\lib\sa-jdi.jar" sun.jvm.hotspot.HSDB
  2. jps -l查看当前java进程。
    在这里插入图片描述
  3. 连接
    在这里插入图片描述
  4. 搜索class并点击生成
    在这里插入图片描述
  5. 得到class:生成的class文件在执行第一步的命令行的路径下获取
    在这里插入图片描述
  6. 反编译:使用jd-gui,或者将该class直接拖入idea里面。
    idea本来就有反编译的功能,比如平常debug的时候,如果点到jar包里面的类,就能看见提示Decompiled .class file,bytecode version:52.0(java 8)

使用DebuggingClassWriter输入Class文件

cglib提供的一种方式,程序运行前设置这个变量,就会把动态生成的class放在指定文件夹下。
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://resultcallback");

  相关解决方案