首页 > 编程知识 正文

cglib代理原理,JAVA 动态代理

时间:2023-05-03 15:38:17 阅读:138225 作者:848

有两种实现Java动态代理的方法: JDK动态代理和CGLIB动态代理模型JDK动态代理CGLIB动态代理

代理模型是23种设计模式之一,是指一个对象a拥有另一个对象b,从而可以具有与b相同行为的模式。 为了对外开放协议,b往往实现一个接口,a也去实现接口。 但是b是真正的实现类,a是比较“虚”,a借助b的方法实现接口的方法。 虽然a是“伪军”,但是可以增强b,在调用b的方法前后做其他事情。 Spring AOP利用动态代理完成了代码的动态嵌入。 使用动态代理的好处还不止于此。 如果一个项目依赖于另一个项目提供的接口,但另一个项目的接口不稳定,并且经常更改协议,则可以使用其中一个代理。 界面发生变化时,只需修改代理,无需修改业务代码。 在这种意义上,您可以调整所有外部接口,以防止外部代码入侵我们的代码。 这称为防御型编程。 在上述例子中,写a类死持有b的是b的静态代理。 a如果代理的对象不确定,就是动态代理。 动态代理目前有两种常规实现: JDK动态代理和CGLIB动态代理。 JDK动态代理

JDK动态代理是JRE提供的类库,可以独立于第三方直接使用。 首先查看JDK动态代理的使用代码,了解原理。

首先,它有一个明星界面。 有唱歌的方法和跳舞的方法。

包代理; publicinterfacestar { string sing (string name ); 字符串日期(字符串名称; }另一位明星实现系-刘德华。

包代理; publicclassliudehuaimplementsstar { public string sing (string name ) { System.out.println (请给我一杯水! ' ); return '唱完'; ) publicstringdance(stringname ) { System.out.println ) (“快乐的马骥”); 返回'跳转结束'; }明星在演出前需要有人收钱。 为了准备演出,我自己不做这个工作。 一般交给喜欢的仙人掌。 很容易理解。 名字以代理结尾,但他不是代理类。 因为没有实现我们的明星接口,不能对外服务。 那只是个wrapper。

包代理; import Java.lang.reflect.invocation handler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; publicclassstarproxyimplementsinvocationhandler//目标类,被代理private Object target; publicvoidsettarget (对象目标) { this.target=target; } publicobjectinvoke (object proxy,Method method,Object[] args ) throwsthrowable(//此处为System.out.println ) ) 返回结果; //代理类public Object CreateProxyedObj () return proxy.newproxyinstance (target.getclass ).getClassLoader ), target.getclass (.getclass loader ) )的上面的示例中,从方法CreatProxyedObj返回的对象是代理类,需要三个参数。 前两个参数表示在同一个classloader下通过接口创建对象。 此对象需要属性,即第三个参数。 这是InvocationHandler。 需要注意的是,这个CreatProxyedObj方法不一定必须在我们的StarProxy类中,而是经常放在工厂类中。 上述代理的代码使用步骤一般如下。

1、new个目标对象

2、new放入一个InvocationHandler、目标对象set

3、用CreatProxyedObj创建代理对象,强烈过渡到目标对象的接口类型即可使用,实际生成的代理对象实现了目标接口。

包代理; public class client { publicstaticvoidmain (string [ ] args ) { Star ldh=new LiuDeHua; StarProxy proxy=new StarProxy (; proxy.settarget(LDH; Star proxyedObj=(Star ) proxy.CreateProxye

dObj(); proxyedObj.sing("刘德华"); }}

Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 这里增强 System.out.println("收钱"); Object result = method.invoke(target, args); return result; }

所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

CGLIB动态代理

代理的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有包装对象这一方式。

通过继承可以继承父类所有得公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是CGLIB的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以CGLIB实现的代理也是可以被正常使用的。

package proxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor{ // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中 public Object CreatProxyedObj(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable { // 这里增强 System.out.println("收钱"); return arg3.invokeSuper(arg0, arg2); } }

从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“ 父 类 方 法 名 父类方法名 父类方法名”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。