java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java 动态代理

java中的动态代理(jdk和Cglib)实现详解

作者:自律的kkk

这篇文章主要介绍了java中的动态代理(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获取代理对象,有三个传参:

// 获取代理对象
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接口,而该接口有几种:

img

我们只看一下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)

// 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();

结果:

img

####Enhancer.create((Class superclass, Class[] interfaces, Callback 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)

为了演示效果,我们新建一个处理类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方法走了第二个处理类

img

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

三、总结对比

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

到此这篇关于java中的动态代理(jdk和Cglib)实现详解的文章就介绍到这了,更多相关java 动态代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文