Java动态代理

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比较合适。

你可能感兴趣的:(动态代理)