死磕cglib系列之一 cglib简介与callback解析「建议收藏」

死磕cglib系列之一 cglib简介与callback解析「建议收藏」简介cglib是一套java动态代理实现框架,cglib被应用到springapp,hibernate等高级业务框架,spring事务在业务实现类未实现接口的情况下也会使用该技术。实际上,cglib基于继承实现,这也就意味着final,private相关的method无法被代理。基于asm框架对class字节码编辑改动,从而达到动态代理的目的,总之,被代理类没有实现接口的情况下cglib为首…

大家好,又见面了,我是你们的朋友全栈君。

简介

cglib是一套java动态代理实现框架,cglib被应用到spring app,hibernate等高级业务框架,spring事务在业务实现类未实现接口的情况下也会使用该技术。

实际上,cglib基于继承实现,这也就意味着final,private相关的method无法被代理。基于asm框架对class字节码编辑改动,从而达到动态代理的目的,总之,被代理类没有实现接口的情况下cglib为首选

引用官网介绍的一段话:

Byte Code Generation Library is high level API to generate and transform JAVA byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.

github地址:https://github.com/cglib/cglib

cglib作者博客:http://mydailyjava.blogspot.no/2013/11/cglib-missing-manual.html

以下案例在我的github中可以clone到,分支为howtocglib

github路径:https://github.com/zhang6622056/cglib

测试用例:cglib-sample/src/main/test/callback/Callbacktest

实现动态代理的几种方案

  • javasisit
  • jdk 动态代理
  • cglib
  • asm(cglib 内部也是用asm更改其字节码)

callbacks简介

这里的callback可以认为是cglib用于生成字节码的实现手段,cglib一共实现了6种callback,用于对代理类目标进行不同手段的代理,非常灵活,分别为:

  • FixedValue
  • InvocationHandler
  • LazyLoader
  • MethodInterceptor
  • Dispatcher
  • NoOp

编写实现案例

callbacks代码编写


Dispatcher

实现Dispatcher接口,要求实现loadObject方法,返回期望的代理类。值的一提的是,loadobject方法在每次调用被拦截方法的时候都会被调用一次

/**** * 与lazy不同的是,每一次调用代理方法的时候,都会调用一次Dispatcher的loadObject获取对象 * 而lazy则会缓存下来。 */
public class DispatcherCallBack implements Dispatcher { 
   
    public Object loadObject() throws Exception { 
   
        CallbackBean callbackBean = new CallbackBean();
        return callbackBean;
    }
}

FixedValue

实现FixedValue接口,该callback同样要求实现一个loadobject方法,只不过需要注意的是该loadobject方法相同与重写了被代理类的相应方法,因为在被代理之后,FixedValue callback只会调用loadobject,而不会再调用代理目标类的相应方法!

/***** * 该callback相当于重写了相应的函数实现。并不会调用原函数 */
public class FixValueCallback implements FixedValue { 
   


    /***** * 被代理方法的指定函数将会无条件的返回改object,动态的变更返回值 * @return * @throws Exception */
    public Object loadObject() throws Exception { 
   
        System.out.println("this is fixvalue callback ..... overwrite the code....");
        return true;
    }
}

InvocationHandler

需要实现InvocationHandler接口,实现invoke对象,该拦截传入了proxy对象,用于自定义实现,与MethodInterceptor相似,慎用method的invoke方法。切忌不要造成循环调用

public class InvocationHandlerCallback implements InvocationHandler { 
   
    /***** * invocationHandler的invoke方法传入的method和proxy都是代理本身对象 * 切忌重复调用,会循环调用 * @param proxy 代理类本身 * @param method 代理类内部的方法 * @param args 参数 * @return * @throws Throwable */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
   
        System.out.println("invocationHandlerCallback Before....");
        method.invoke(proxy.getClass().getSuperclass().newInstance(),args);
        //会无限循环
        //method.invoke(proxy,args);
        System.out.println("invocationHandlerCallback after....");
        return null;
    }
}

LazyLoader

实现LazyLoader的loadObject方法,返回对象实例,该实例只有第一次调用的时候进行初始化,之后不再重新调用,proxy类初始化时进行了成员的赋值,之后使用该成员进行调用父类方法

/**** * * 延迟加载初始化 * 类似于spring prototype的singleton ,在第一次调用的时候进行初始化,并且将此实例存储起来,之后都将返回改实例 * 可参考资料: * https://shensy.iteye.com/blog/1881277 */
public class LazyLoaderCallback implements LazyLoader { 
   
    public Object loadObject() throws Exception { 
   
        CallbackBean callbackBean = new CallbackBean();
        return callbackBean;
    }
}

MethodInterceptor

实现MethodInterceptor的intercept,实现被代理对象的逻辑植入。也是最常用的callback

/**** * 生成代理类字节码。对方法进行拦截调用 */
public class MethodInterceptorCallback implements MethodInterceptor { 
   
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 
   
        System.out.println("before invoke........");
        proxy.invokeSuper(obj,args);
        System.out.println("after invoke.........");
        return null;
    }
}

NoOp

通过接口声明了一个单例对象,该代理不对被代理类执行任何操作

/** * Methods using this {@link Enhancer} callback will delegate directly to the * default (super) implementation in the base class. */
public interface NoOp extends Callback
{ 
   
    /** * A thread-safe singleton instance of the <code>NoOp</code> callback. */
    public static final NoOp INSTANCE = new NoOp() { 
    };
}

被代理类code


public class CallbackBean { 
   

    public void methodForDispatcher(){ 
   
        System.out.println("methodForDispatcher...");
    }

    public void methodForFixValue(){ 
   
        System.out.println("methodForFixValue...");
    }

    public void methodForInvocationHandler(){ 
   
        System.out.println("methodForInvocationHandler...");
    }

    public void methodForLazy(){ 
   
        System.out.println("methodForLazy...");
    }

    public void methodForInterceptor(){ 
   
        System.out.println("methodForInterceptor...");
    }

    public void methodForNoop(){ 
   
        System.out.println("methodForNoop...");
    }
}

编写Enhancer调用


import callbacks.*;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import org.junit.Test;
import proxybean.CallbackBean;
import java.lang.reflect.Method;
public class CallbackTest { 

@Test
public void testCallback(){ 

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CallbackBean.class);
enhancer.setCallbacks(initCallBacks());
enhancer.setCallbackFilter(initCallbackFilter());
CallbackBean callbackBean = (CallbackBean) enhancer.create();
callbackBean.methodForNoop();
callbackBean.methodForInterceptor();
callbackBean.methodForLazy();
callbackBean.methodForDispatcher();
callbackBean.methodForInvocationHandler();
callbackBean.methodForFixValue();
}
/**** * 初始化callback拦截数组 * @return */
private static final Callback[] initCallBacks(){ 

MethodInterceptorCallback methodInterceptorCallback = new MethodInterceptorCallback();
LazyLoaderCallback lazyLoaderCallback = new LazyLoaderCallback();
InvocationHandlerCallback invocationHandlerCallback = new InvocationHandlerCallback();
FixValueCallback fixValueCallback = new FixValueCallback();
DispatcherCallBack dispatcherCallBack = new DispatcherCallBack();
Callback[] callbacks = new Callback[]{ 
NoOp.INSTANCE,methodInterceptorCallback,lazyLoaderCallback,dispatcherCallBack,invocationHandlerCallback,fixValueCallback};
return callbacks;
}
private static final CallbackFilter initCallbackFilter(){ 

return new CallbackFilter() { 

public int accept(Method method) { 

if (method.getName().equals("methodForNoop")){ 

return 0;
}
if (method.getName().equals("methodForInterceptor")){ 

return 1;
}
if (method.getName().equals("methodForLazy")){ 

return 2;
}
if (method.getName().equals("methodForDispatcher")){ 

return 3;
}
if (method.getName().equals("methodForInvocationHandler")){ 

return 4;
}
if (method.getName().equals("methodForFixValue")){ 

return 5;
}
return 0;
}
};
}
}

proxy class反编译分析(请看关键注释)

Dispatcher

public final void methodForDispatcher() { 

Dispatcher var10000 = this.CGLIB$CALLBACK_3;
if (var10000 == null) { 

CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_3;
}
//每次都调用一次loadObject,获取对象,并调用对象的相应方法
//这样的实现,相当于loadObject可以很灵活的返回相应的实现类或者子类
((CallbackBean)var10000.loadObject()).methodForDispatcher();
}

FixedValue

public final void methodForFixValue() { 

FixedValue var10000 = this.CGLIB$CALLBACK_5;
if (var10000 == null) { 

CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_5;
}
//直接调用了FixValue callback的loadObject,相当于重写逻辑
var10000.loadObject();
}

InvocationHandler

public final void methodForInvocationHandler() { 

try { 

InvocationHandler var10000 = this.CGLIB$CALLBACK_4;
if (var10000 == null) { 

CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_4;
}
//调用InvocationHandler相应的invoke实现
var10000.invoke(this, CGLIB$methodForInvocationHandler$4, new Object[0]);
} catch (Error | RuntimeException var1) { 

throw var1;
} catch (Throwable var2) { 

throw new UndeclaredThrowableException(var2);
}
}

LazyLoader

public final void methodForLazy() { 

//CGLIB$LOAD_PRIVATE_2在proxy初始化的时候初始化一次,并维护了该成员
//与Dispatcher不同,每次调用都是运用proxy成员变量进行调用
((CallbackBean)this.CGLIB$LOAD_PRIVATE_2()).methodForLazy();
}

MethodInterceptor

public final void methodForInterceptor() { 

MethodInterceptor var10000 = this.CGLIB$CALLBACK_1;
if (var10000 == null) { 

CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_1;
}
if (var10000 != null) { 

//调用callback的intercept方法,用于实现拦截
var10000.intercept(this, CGLIB$methodForInterceptor$1$Method, CGLIB$emptyArgs, CGLIB$methodForInterceptor$1$Proxy);
} else { 

super.methodForInterceptor();
}
}

NoOp


此处不做NoOp的讨论,理解为调用原类方法即可

运行结果

在这里插入图片描述

反编译遇到问题

如jd-gui反编译遇到错误,请尝试直接将class文件拖进idea

总结

以上即为本篇的全部内容,主要简单介绍了cglib与几种callback的使用。比较简单、基础。

当然,cglib不只是这些内容。在实现的生成proxy的同时,作者抽象了很多东西,这包括

  • 缓存机制

  • 生成策略

  • 弱引用WeakHashMap,WeakReference

  • LoadingCache搭配FutureTask线程竞争模型

  • 对反射的封装

  • 对asm的封装

  • 类加载的ClassLoader

  • 灵活的Function设计

总之,先会用,我们在逐一深入,下一篇正式开始撸源码,敬请期待。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/139892.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • ubuntu安装goland_ubuntu安装kali工具集

    ubuntu安装goland_ubuntu安装kali工具集1下载地址可以从go语言中文网下载最新的Linux包,地址如下:https://studygolang.com/dl/golang/go1.17.3.linux-amd64.tar.gzhttps://studygolang.com/dl/golang/go1.17.3.linux-amd64.tar.gz2安装进入home目录,使用wget下载子并解压,修改名字,然后创建链接(这个目的是方便后续切换golang版本的时候,只需要修改链接即可),脚本如下:cd~wgethttps

    2022年10月10日
  • java中break和continue的用法「建议收藏」

    java中break和continue的用法「建议收藏」**break和continue的用法**break的用法:1.break用于switch语句中,终止switch语句2.break用于循环时,跳出循环3.break用于其他位置,毫无意义1.break用于switch语句中,终止switch语句inta=4;switch(a){case1:…

  • 博弈论学习笔记(六)纳什均衡之约会游戏与古诺模型

    博弈论学习笔记(六)纳什均衡之约会游戏与古诺模型可以将纳什均衡看成一种自我实施的协议,假设每个人都相信大家都会遵守协议,那么大家就都会遵守。纳什均衡是和领导力紧密联系的。在协调博弈中,领导力的作用就是促成人们达到某个特定均衡而不是其他均衡。尤其是某些缺乏领导的混乱状态,在这类博弈中领导力的作用举足轻重。–领导力的用武之地。举个简单的例子,如下表,很显然协调能够起到作用。αβα1,10…

    2022年10月15日
  • 蚂蚁金服通信框架SOFABolt解析 | 连接管理剖析

    蚂蚁金服通信框架SOFABolt解析 | 连接管理剖析

  • 笔记29-MySQL多表&事务

    笔记29-MySQL多表&事务今日内容1.多表查询2.事务3.DCL多表查询:*查询语法: select 列名列表 from 表名列表 where….*准备sql #创建部门表 CREATETABLEdept( idINTPRIMARYKEYAUTO_INCREMENT, NAMEVARCHAR(20) ); INSERTINTOdept(NAME)VALUES(‘开发部’),(‘市场部’),(‘财务部’); #创建员工表 CREATETAB

  • 漏洞扫描 渗透测试_什么是渗透

    漏洞扫描 渗透测试_什么是渗透渗透测试阶段信息收集完成后,需根据所收集的信息,扫描目标站点可能存在的漏洞,包括SQL注入漏洞、跨站脚本漏洞、文件上传漏洞、文件包含漏洞及命令执行漏洞等,然后通过这些已知的漏洞,寻找目标站点存在攻击的入口。那么今天我们就介绍几款常用的WEB应用漏洞扫描工具。一、AWVSAcunetixWebVulnerabilityScanner(简称AWVS)是一款知名的网络漏洞扫描工具,它通过网络爬虫测试你的网站安全,检测流行安全漏洞。在漏洞扫描实战过程中,一般会首选AWVS,因为这个能扫描出来的漏洞很多,而

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号