JDK动态代理与Cglib动态代理使用详解

一、JDK动态代理

准备

JDK动态代理只能代理接口,所以我们需要先准备一个接口,以及一个接口的实现类

// 需要代理的接口
public interface InvokeInterface {
    void test1(String test,int[] ints);

    String test3(String test);
}

//代理接口的实现类
public class InvokeBean implements InvokeInterface{

    public void test1(String test,int[] ints){
        System.out.println("代理实现方法1 :"+test + JSON.toJSON(ints));
    }

    public String test3(String test){
        System.out.println("代理实现方法1 :"+test);
        return test;
    }
}

有了代理的目标对象了,我们自然还需要一个处理方法,毕竟我们需要实现额外的逻辑呀,不然代理干嘛?对吧,这个处理方法可不能乱写,需要实现InvocationHandler接口,里面有个invoke方法,就是给我们自己写逻辑的地方,我们就可以对方法执行前、后填充自己的逻辑了

注意: 这个类实例化的时候传了一个Object参数,这个就是上面的实现类,为什么要传进来?因为一个接口是可以有多个实现类的,你不传一个具体的实现类,我怎么知道要执行哪个实现类里面的方法呢?

public class DemoInvokeHandler implements InvocationHandler {

    private Object object;

    public DemoInvokeHandler(Object object){
        this.object=object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");
        
        // 这个就是我们原本的方法执行
        Object invoke=method.invoke(object,args);
        
        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return invoke;
    }
}

像以上这样呢,整个接口中的方法都会被代理,如果我们只想代理某个方法呢?就需要对方法判断一下如:

这里只是对方法名过滤了,还可以结合注解、参数等过滤

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(method.getName().equals("test1")){
            // 所以可以在这里  方法执行前写逻辑
            System.out.println("方法执行前执行");

            // 这个就是我们原本的方法执行
            Object invoke=method.invoke(object,args);

            // 所以可以在这里  方法执行后写逻辑
            System.out.println("方法执行后执行");
            return invoke;
        }else {
            return method.invoke(object,args);
        }
    }

使用

以上我们的准备工作就完成了,我们只需要在实际使用中,获取代理对象,执行代理对象即可,通过

Proxy.newProxyInstance获取代理对象,有三个传参:

  1. 类加载器
  2. 被代理的接口
  3. 需要做的处理类(也就是实现了InvocationHandler的类)
// 获取代理对象
InvokeInterface invokeBean = (InvokeInterface) Proxy.newProxyInstance(InvokeBean.class.getClassLoader(),
    new Class[]{InvokeInterface.class}, new DemoInvokeHandler(new InvokeBean()));
invokeBean.test1("test1",new int[]{1,2});
String test3 = invokeBean.test3("test3");
System.out.println(test3);

二、Cglib动态代理

准备

这个我们需要先导入依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.1</version>
</dependency>

Cglib和JDK实现起来差不多,但是Cglib代理的是类,所以我们需要先准备个目标类

public class CglibTarget {

    public void test(){
        System.out.println("test 本方法执行");
    }
}

同样的还需要自己的处理方法,这个要实现的接口就不一样。是Callback接口,而该接口有几种:

在这里插入图片描述

我们只看一下MethodInterceptorInvocationHandler两种,其他毕竟用得少:

InvocationHandler:用法和JDK动态代理一样,参考JDK的即可

MethodInterceptor

注意: 是net.sf.cglib.proxy.MethodInterceptor包下的,同时这个处理类实例化的时候就不需要再传参了

public class CglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");

        // 这个就是我们原本的方法执行
        Object o1 = methodProxy.invokeSuper(o, objects);

        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return o1;
    }


}

使用

Cglib的使用同样是需要先生成一个代理对象,一般是靠Enhancer.create() 方法,但该方法有几个重载,我们一个一个介绍

先是最简单也是最常用的

Enhancer.create(Class type, Callback callback)

  • type: 需要代理的类
  • callback:处理方法类
// MethodInterceptor 方式
CglibTarget cglibTarget = (CglibTarget) Enhancer.create(CglibTarget.class, new CglibProxy());
cglibTarget.test();

// InvocationHandler 方式  与JDK一样,不需要类加载器了
Interface cglibTarget = (Interface) Enhancer.create(Interface.class, new CglibProxy(new 实现类));
cglibTarget.test();

结果:
在这里插入图片描述

Enhancer.create((Class superclass, Class[] interfaces, Callback callback))

  • superclass: 需要代理的类
  • interfaces:需要实现的接口
  • callback:处理方法类

这个就比上面那个更厉害了,上面那个只是代理原有类里面的方法,这个可以对代理原有的类帮他实现接口,并实现接口逻辑,这等于一个类凭空多了几个方法出来,怎么用?

上述的目标类不需要改,处理方法需要改一下,然后新建一个接口:

// 我们新建一个接口不需要任何实现类  
public interface TestTarget {
    public void test1();
}

// 修改处理方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
    // 这里新增对上面接口里面方法的处理,其他的不需要变
    if(method.getName().equals("test1")){
        // 这里可以实现那个接口方法的逻辑 
        System.out.println("这里写实现那个接口的逻辑方法");
        return "我是那个接口返回的";
    }else {
        // 所以可以在这里  方法执行前写逻辑
        System.out.println("方法执行前执行");

        // 这个就是我们原本的方法执行
        Object o1 = methodProxy.invokeSuper(o, objects);

        // 所以可以在这里  方法执行后写逻辑
        System.out.println("方法执行后执行");
        return o1;
    }
}


调用:

// 先用接口生成器 对我们要实现的接口做一个处理
InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
// 然后我们就可以正常的传参调用了 
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibProxy());
// 类中方法正常调用
o.test();
// 接口方法调用需要用反射 因为毕竟那接口的方法不存在对象方法里面
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);

结果:

在这里插入图片描述

Enhancer.create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks)

  • superclass: 需要代理的类
  • interfaces:需要实现的接口
  • filter: 这个就是用来选择处理类的,毕竟处理类有多个了
  • callbacks:处理方法类数组(处理类可以有多个)

为了演示效果,我们新建一个处理类CglibProxyOther,上面的都不需要变

public class CglibProxyOther implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if(method.getName().equals("test1")){
            System.out.println("新建处理类的逻辑方法");
            return "我是那个新建处理类返回的";
        }else {
            // 所以可以在这里  方法执行前写逻辑
            System.out.println("新建处理类——方法执行前执行");

            // 这个就是我们原本的方法执行
            Object o1 = methodProxy.invokeSuper(o, objects);

            // 所以可以在这里  方法执行后写逻辑
            System.out.println("新建处理类——方法执行后执行");
            return o1;
        }
    }
}

然后新增一个过滤选择器filter

实现CallbackFilter接口即可,返回值是处理类数组的下标(我这里用方面名称来选择,实际还可以用其他)

public class CglibFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        // test方法用下标为0的处理类
        if(method.getName().equals("test")){
            return 0;
        }
        // 其他方法用下标为1的处理类
        return 1;
    }
}

调用:

InterfaceMaker interfaceMaker=new InterfaceMaker();
interfaceMaker.add(TestTarget.class);
Class aClass = interfaceMaker.create();
CglibTarget o = (CglibTarget)new Enhancer().create(CglibTarget.class, new Class[]{aClass},new CglibFilter(),new Callback[]{new CglibProxy(),new CglibProxyOther()});
o.test();
Method test1 = o.getClass().getMethod("test1");
test1.invoke(o);


结果: 很明显的看到test1方法走了第二个处理类

在这里插入图片描述

至此重载方法全介绍完毕!

三、总结对比

  • 拓展性:要是考虑使用角度Cglib无疑是更好的,因为JDK只能代理接口
  • 原理:JDK代理是利用反射机制生成匿名类,调用也是通过反射来调用
    Cglib是采用字节码技术,通过修改字节码生成子类
  • 效率:JDK创建对象效率较高,但执行较慢,Cglib创建对象效率低,但执行较快
  • 局限性: Cglib需要额外导入第三方包,而Jdk代理不需要,但JDK局限于代理接口

到底用什么相信大家有选择了,如果没特殊需求,就直接JDK得了,有特殊需求不用Cglib,JDK能满足吗?至于效率,这种效率的差距都体现在一定量往上的程度,没到这个量无需考虑太多!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值