Java 动态代理

1、动态代理的目的                                                                      

  动态代理用于代码的分离;将通用代码从各个业务模块中分离出来,不必在每个业务模块中都去实现,这样既减少了开发工作量,也便于维护;

  eg: “吃饭”、“说话”这两个业务都需要用到“张嘴”  “闭嘴”这两行代码,但是张嘴之后和闭嘴之前所做的事情是不一样的,这样将业务模块和通用模块(即 张嘴、闭嘴)分离出来,每次在说话 或者 吃饭的时候,使用通用模块+业务模块 实现吃饭和说话这两件事;

2、动态代理的应用                          

   最典型的应用是基于动态代理技术实现spring框架中的AOP(面向切面编程);  面向切面编程,通过切面代码层,实现业务代码和通用代码的分离,这样各个业务模块可以共用一个通用代码。大概原理如下:切面层代码提供了一个调用其他方法的接口(姑且命名为 invoke),通过反射机制将类 和 方法等参数传入后,便在invoke 里面执行此方法,传入的方法仅仅是invoke方法的一部分,另一部分便是通用代码,这样就将通用代码和业务代码编制在了一起。(实际上sping中的增强就是这么回事)

3、动态代理的原理及实现                                                              

   java提供了两种动态代理方式,一种是JDK动态代理,另一种是cglib动态代理;JDK动态代理仅仅实现了对接口的代理,cglib实现的是对对象的代理;二者相比,chlib综合性能更好一点;

  (1) JDK动态代理

  JDK动态代理是基于 “InvocationHandler接口”  和  “Proxy类” 实现的,其技术基础是 JAVA中的反射机制 及 面向对象中的重写;其中 代理类就是继承于InvocationHandler这个接口的,主要是继承他的invoke 方法;Proxy类用于获取反射对象,调用方法。虽然在本来的业务类中没有invoke方法,但是会在Java虚拟机中生成一个$Proxy0文件,这个文件是继承于本来的业务类,在这个文件中会调用invoke方法。

                     

 下面就根据上图中的代理模型,来写一个简单的JDK动态代理

    ① 首先构建业务层接口 java代码如下:

package Proxy.JDK;
import java.lang.*;
public interface SayHelloWorld {
    public void say(String language);
}
业务层接口

    ② 基于构建的接口层,创建业务类

package Proxy.JDK;

public class menSay implements SayHelloWorld {

    @Override
    public void say(String language) {
        // TODO Auto-generated method stub
        System.out.println("HelloWorld!");
    }

}
实际的业务类

    ③ 构建代理类

package Proxy.JDK;

import java.lang.reflect.*;;

public class proxyClass implements InvocationHandler {

    private Object VR_class;
    
    /**
     * 返回代理类
     * @method: proxyClass() -by fjt
     * @TODO:  利用反射机制,被代理业务类产生代理对象
     * @param obj 被代理的业务类
     * @return 
     * @return Object
     */
    public Object getProxy(Object obj)
    {
        this.VR_class = obj;
        return Proxy.newProxyInstance(VR_class.getClass().getClassLoader(), VR_class.getClass().getInterfaces(), this);
    }
    
    @Override
    /**
     * 当调用业务层方法的时候  就会进入这个接口,所以可以认为是覆盖了原先的方法
     * 至于是怎么实现覆盖的,为什么会跳到这儿,还不是很清楚
     */
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("张嘴");
        method.invoke(this.VR_class, args);
        System.out.println("闭嘴");
        return null;
    }

}
代理类

    ④ 代理的应用

package Proxy.JDK;

public class jdk_proxyTest {
    public static void main(String[] arges)
    {
        SayHelloWorld manSay = new menSay();
        proxyClass proxyMan = new proxyClass();
        //JDK实现的动态代理 是基于接口实现的,因此下面的语句  如果将SayHelloWorld 改为 menSay就会出错
        //$Proxy0 cannot be cast to Proxy.JDK.menSay (因为menSay是一个类 Proxy0)
        SayHelloWorld proxyMenSay = (SayHelloWorld)proxyMan.getProxy(manSay);  
        proxyMenSay.say("hi!");
        
    }
}
代理的应用

   通过上述例子不难看出,JDK动态代理,实际上就是接口注入,也仅仅是接口注入,通过代理返回的对象也是接口,所以代理的也只能是接口。

  (2) CGLIB动态代理

   JDK动态代理使用起来不太方便,需要写接口、服务类、代理类等,而且只能代理接口,繁琐许多。相比之下,CGLIB就简单许多了。然而CGLIB创建代理对象的时间要花费的比JDK多。所以如果需要频繁的创建对象,并且是接口代理,使用JDK会更好一点。CGLIB是动态创建子类,所以不能代理final变量和方法。

  CGLIB的使用,需要引入如下几个包:

        

  下面先看一下CGLIB的动态代理的实现吧!

package Proxy.CGLIB;

public class sayHello {
    public int a = 0;
    public int b = 0;
    public void add()
    {
        System.out.println("计算结果: " + Integer.toString(a+b));
    }
}
被代理类
package Proxy.CGLIB;

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 {

    private Object obj;   //被代理的原始对象
    public Object creatProxy(Object target)
    {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.obj.getClass());  //设置代理目标
        enhancer.setCallback(this);
        enhancer.setClassLoader(target.getClass().getClassLoader());
        return enhancer.create();
    }
    //阻塞被代理的类的方法调用
    @Override
    public Object intercept(Object arg0, Method arg1, Object[] arg2,
            MethodProxy arg3) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("调用之前");
        arg3.invokeSuper(arg0, arg2);
        System.out.println("调用之后");
        return null;
    }

}
代理类
package Proxy.CGLIB;

public class cglib_proxyTest {

     public static void main(String[] args) {
         sayHello calcu = new sayHello();
         CglibProxy cglib_proxy = new CglibProxy();
         sayHello cal_proxy = (sayHello)cglib_proxy.creatProxy(calcu);
         cal_proxy.a = 1;
         cal_proxy.b = 1;
         cal_proxy.add();
     }
}
测试代码

  看起来很简单吧。

  CGLIB实现的是代理类,JDK实现的是代理接口;CGLIB是通过intercept这个函数来阻塞代理类函数的调用,也就是说调用被代理类的方法的时候,执行的实际上是intercept这个方法。

原文地址:https://www.cnblogs.com/tengpan-cn/p/4841033.html