JDK动态代理与Cglib动态代理

基本概念

1.什么是代理?

JDK动态代理:

JDK动态代理的实例:

cglib动态代理:

CGLIB动态代理实例


基本概念

1.什么是代理?

在阐述动态代理之前,我们很有必要先来弄明白代理的概念。代理这个词本身并不是计算机专用术语,它是生活中一个常用的概念。这里引用维基百科上的一句话对代理进行定义:
A proxy is an agent or substitute authorized to act for another person or a document which authorizes the agent so to act.
意思是说:代理指的是一个代理人(或替代品),它被授权代表另外一个人(或文档)。
从这个简明扼要的定义中,可以看出代理的一些特性:

1.代理存在的意义就是代表另一个事物。

2.代理至少需要完成(或实现)它所代表的事物的功能。

 

                                               

 

JDK动态代理:

了解了动态代理的概念后,现在我们动手写个JDK动态代理的代码样例。直观的认识下JDK动态代理技术为我们的做了什么。上面说到了,

JDK动态代理主要依靠Proxy和InvocationHandler这两个类来生成动态代理类和类的实例。这两个类都在jdk的反射包java.lang.reflect下面。Proxy是个工具类,有了它就可以为接口生成动态代理类了。如果需要进一步生成代理类实例,需要注入InvocationHandler实例。这点我们上面解释过,因为代理类最终逻辑的实现是分派给InvocationHandler实例的invoke方法的。

JDK动态代理的实例:

闲话休絮,先开始我们的第一步,定义一个接口。这个接口里面定义一个方法helloWorld()。

public interface MyIntf {
    void helloWorld();
}

第二步,编写一个我们自己的调用处理类,这个类需要实现InvocationHandler接口。

public class MyInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method);
        return null;
    }
}

InvocationHandler接口只有一个待实现的invoke方法这个方法有三个参数,proxy表示动态代理类实例,method表示调用的方法,args表示调用方法的参数。在实际应用中,invoke方法就是我们实现业务逻辑的入口。这里我们的实现逻辑就一行代码,打印当前调用的方法(在实际应用中这么做是没有意义的,不过这里我们只想解释JDK动态代理的原理,所以越简单越清晰)。

第三步,直接使用Proxy提供的方法创建一个动态代理类实例。并调用代理类实例的helloWorld方法,检测运行结果。

public class ProxyTest {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
        MyIntf proxyObj = (MyIntf)Proxy.newProxyInstance(MyIntf.class.getClassLoader(),new Class[]{MyIntf.class},new MyInvocationHandler());
        proxyObj.helloWorld();
    }
}

第三行代码是设置系统属性,把生成的代理类写入到文件。这里再强调一下,JDK动态代理技术是在运行时直接生成类的字节码,并载入到虚拟机执行的这里不存在class文件的,所以我们通过设置系统属性,把生成的字节码保存到文件,用于后面进一步分析。
第四行代码就是调用Proxy.newProxyInstance方法创建一个动态代理类实例,这个方法需要传入三个参数,第一个参数是类加载器,用于加载这个代理类。第二个参数是Class数组,里面存放的是待实现的接口信息。第三个参数是InvocationHandler实例。
第五行调用代理类的helloWorld方法,运行结果:

public abstract void com.tuniu.distribute.openapi.common.annotation.MyIntf.helloWorld()

cglib动态代理:

cglib (Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库,

它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成。

CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP为他们提供

方法的interception(拦截)。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,

因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

简单看下cglib的用法:cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数。

我之前写过一篇spring中用到cglib的文章,是关于@Configuration注解的,

地址如下:https://blog.csdn.net/weixin_43107805/article/details/102980058


CGLIB动态代理实例

实现一个业务类,注意,这个业务类并没有实现任何接口:

package com.lanhuigu.spring.proxy.cglib;
 
public class HelloService {
 
    public HelloService() {
        System.out.println("HelloService构造");
    }
 
    /**
     * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
     */
    final public String sayOthers(String name) {
        System.out.println("HelloService:sayOthers>>"+name);
        return null;
    }
 
    public void sayHello() {
        System.out.println("HelloService:sayHello");
    }
}

自定义MethodInterceptor:

package com.lanhuigu.spring.proxy.cglib;
 
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
 
import java.lang.reflect.Method;
 
/**
 * 自定义MethodInterceptor
 */
public class MyMethodInterceptor implements MethodInterceptor{
 
    /**
     * sub:cglib生成的代理对象
     * method:被代理对象方法
     * objects:方法入参
     * methodProxy: 代理方法
     */
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("======插入前置通知======");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("======插入后者通知======");
        return object;
    }
}

生成CGLIB代理对象调用目标方法:

package com.lanhuigu.spring.proxy.cglib;
 
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
 
public class Client {
    public static void main(String[] args) {
        // 代理类class文件存入本地磁盘方便我们反编译查看源码
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        // 通过CGLIB动态代理获取代理对象的过程
        Enhancer enhancer = new Enhancer();
        // 设置enhancer对象的父类
        enhancer.setSuperclass(HelloService.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new MyMethodInterceptor());
        // 创建代理对象
        HelloService proxy= (HelloService)enhancer.create();
        // 通过代理对象调用目标方法
        proxy.sayHello();
    }
}

运行结果: 

 

总结:

JDK代理是不需要第三方库支持,只需要JDK环境就可以进行代理,使用条件:

1)实现InvocationHandler 

2)使用Proxy.newProxyInstance产生代理对象

3)被代理的对象必须要实现接口

CGLib必须依赖于CGLib的类库,但是它需要类来实现任何接口代理的是指定的类生成一个子类,覆盖其中的方法,是一种继承但是针对接口编程的环境下推荐使用JDK的代理; 
 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值