Java动态代理

来源:互联网 发布:2017淘宝注册用户数 编辑:程序博客网 时间:2024/06/18 14:45

Java主要有两种代理,JDK和Cglib动态代理。

Java的JDK动态代理

一个接口

public interface Advice {      public void before();      public void after();  }

有两个横切逻辑类,如下:

public class TimeAdvice implements Advice {      long startTime;      long endTime;      @Override      public void before() {          startTime = System.nanoTime(); // 获取开始时间          System.out.println("开始计算程序运行时间");      }      @Override      public void after() {          endTime = System.nanoTime(); // 获取结束时间          System.out.println("计算程序运行时间: " + (endTime - startTime) + "ns");      }  }  
public class ControlAdvice extends TimeAdvice {      @Override      public void before() {          super.before();          System.out.println("提前 判断系统的权限 ");      }      @Override      public void after() {          super.after();          System.out.println("判断系统的权限 完毕");      }  }  

一个目标类,如下:

public interface SalaryInterface {       public void doSalary();  }  public class Salary  implements SalaryInterface{       public void doSalary() {             System.out.println("进行薪资计算的逻辑处理");         }  }  

实现最重要的代理类,这个类需要实现一个特定的接口。如下:

package com.test.javaproxy;  import java.lang.reflect.InvocationHandler;  import java.lang.reflect.Method;  import java.lang.reflect.Proxy;  /*  * 每一个代理实例都必须指定一个调用处理器,代理对象调用方法时,  * 该方法会指派到调用处理器的invoke()中去。代理的方法封装成  * invoke中的method对象,其中的参数封装成Object[].  */  public class MyProxy implements InvocationHandler{       private Object obj;   // 希望被代理的对象       private Advice advice;          //绑定代理对象          public Object bind(Object obj, Advice advice) {              this.obj = obj;              this.advice = advice;              return Proxy.newProxyInstance(                      obj.getClass().getClassLoader(), // 类加载器                        obj.getClass().getInterfaces(), // 创建目标类所需要使用的一组接口                      this  // 一个实现InvocationHandler的实例,用来整合横切与业务逻辑              );          }          //实现代理          /*          * method为方法名,args为代理实例某一方法的入参数组,而obj为所属的实例对象          */          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {              Object result = null;              try {                  advice.before();                  result = method.invoke(obj, args);                   advice.after();              } catch (Exception e){                  e.printStackTrace();              }              return result;          }      }  

进行测试如下:

/*          *  效果一样 ControlAdvice adv1 = new ControlAdvice(); 但是这时候ControlAdvice还是在          *  继承接口Advice,如果去掉这样的写就会报错。          *  所以说它只能为接口创建代理实例          */          Advice adv1 = new ControlAdvice();           SalaryInterface p = new Salary();          MyProxy proxy = new MyProxy();          SalaryInterface y = (SalaryInterface)proxy.bind(p, adv1);          y.doSalary();// 相当于调用proxy.invoke(proxy, "doSalary, null);  

最终运行结果为:

开始计算程序运行时间  提前 判断系统的权限   进行薪资计算的逻辑处理  计算程序运行时间: 743454ns  判断系统的权限 完毕  

可以看到实现了Advic接口的代理类都进行了运行。

* Java的CGLib动态代理*

CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并顺势织入横切逻辑。

public class BookFacadeImpl {        public void addBook() {            System.out.println("增加图书方法。。。");        }    }    
import java.lang.reflect.Method;    import net.sf.cglib.proxy.Enhancer;    import net.sf.cglib.proxy.MethodInterceptor;    import net.sf.cglib.proxy.MethodProxy;    /**   * 使用cglib动态代理   */    public class BookFacadeCglib implements MethodInterceptor {        private Object target;        /**       * 创建代理对象       * @param target       * @return       */        public Object getInstance(Object target) {            this.target = target;            Enhancer enhancer = new Enhancer();            enhancer.setSuperclass(this.target.getClass());  // 设置需要创建子类的类           // 回调方法            enhancer.setCallback(this);            // 通过字节码技术动态创建子类实例            return enhancer.create();        }        @Override        // 回调方法 ,拦截所有的父类方法调用       public Object intercept(Object obj, Method method, Object[] args,                MethodProxy proxy) throws Throwable {            System.out.println("事物开始");            Object result = proxy.invokeSuper(obj, args);   // 通过代码类调用父类中的方法          System.out.println("事物结束");            return result;        }    }    
BookFacadeCglib cglib=new BookFacadeCglib();           BookFacadeImpl bookCglib=(BookFacadeImpl)cglib.getInstance(new BookFacadeImpl());           bookCglib.addBook();    

最终运行的结果如下:

事物开始  增加图书方法。。。  事物结束  

注意加入cglib和asm的jar包,如不加asm包会报出如下错误:

Exception in thread "main" java.lang.NoClassDefFoundError: org/objectweb/asm/Type  

CGGlib创建的代理对象要比JDK的性能高很多,但是创建时所花费的时间却比JDK动态代理要多。所以对于singleton的代理对象或者具有实例池的代码,由于无须频繁创建代码对象,用CGLib比较合适。
也就是生命周期长的实例用CGLib比较合适。

0 0
原创粉丝点击