Java动态代理

2016-11-02  本文已影响20人  米刀灵

给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象。思考代理模式中的代理Proxy角色。Proxy角色在执行代理业务的时候,无非是在调用真正业务之前或者之后做一些“额外”业务。



由上图可以看出,代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。换一种思路就是:在触发(invoke)真实角色的方法之前或者之后做一些额外的业务。

首先看静态代理:
代理模式上,基本上有Subject角色,RealSubject角色,Proxy角色。

    public class ProxyDemo {
    public static void main(String args[]){
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
        }
    }

    interface Subject{
        void request();
    }

    class RealSubject implements Subject{
        public void request(){
            System.out.println("request");
        }
    }

    class Proxy implements Subject{
        private Subject subject;
        public Proxy(Subject subject){
            this.subject = subject;
        }
        public void request(){
            System.out.println("PreProcess");
            subject.request();
            System.out.println("PostProcess");
        }
    }

上面的这幅代理结构图是典型的静态的代理模式:
当在代码阶段规定这种代理关系,Proxy类通过编译器编译成class文件,当系统运行时,此class已经存在了。这种静态的代理模式固然在访问无法访问的资源,增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护;并且由于Proxy和RealSubject的功能 本质上是相同的,Proxy只是起到了中介的作用,这种代理在系统中的存在,导致系统结构比较臃肿和松散。

动态代理:
动态地创建Proxy,在运行状态中,需要代理的地方,根据Subject 和RealSubject,动态地创建一个Proxy,用完之后销毁,就可以避免了Proxy 角色的class在系统中冗杂的问题了。
动态代理模式的结构跟上面的静态代理模式稍微有所不同,多引入了一个InvocationHandler角色。

动态代理分为两种:

JDK的动态代理:
现在定义两个接口Vehicle和Rechargable,Vehicle表示交通工具类,有drive()方法;Rechargable接口表示可充电的(工具),有recharge() 方法。定义一个实现两个接口的类ElectricCar。

package com.foo.proxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Proxy;  
  
public class Test {  
  
    public static void main(String[] args) {  
  
        ElectricCar car = new ElectricCar();  
        // 1.获取对应的ClassLoader  
        ClassLoader classLoader = car.getClass().getClassLoader();  
  
        // 2.获取ElectricCar 所实现的所有接口  
        Class[] interfaces = car.getClass().getInterfaces();  
        // 3.设置一个来自代理传过来的方法调用请求处理器,处理所有的代理对象上的方法调用  
        InvocationHandler handler = new InvocationHandlerImpl(car);  
        /* 
          4.根据上面提供的信息,创建代理对象 在这个过程中,  
          a.JDK会通过根据传入的参数信息动态地在内存中创建和.class 文件等同的字节码 
          b.然后根据相应的字节码转换成对应的class,  
          c.然后调用newInstance()创建实例 
         */  
        Object o = Proxy.newProxyInstance(classLoader, interfaces, handler);  
        Vehicle vehicle = (Vehicle) o;  
        vehicle.drive();  
        Rechargable rechargeable = (Rechargable) o;  
        rechargeable.recharge();  
    }  
}  

交通工具接口:

package com.foo.proxy;  
/** 
 * 交通工具接口 
 * @author louluan 
 */  
public interface Vehicle {  
    public void drive();  
}  

可充电设备接口:

package com.foo.proxy;  
/** 
 * 可充电设备接口 
 * @author louluan 
 */  
public interface Rechargable {  
  
    public void recharge();  
}  

电能车类:

package com.foo.proxy;  
/** 
 * 电能车类,实现Rechargable,Vehicle接口 
 * @author louluan 
 */  
public class ElectricCar implements Rechargable, Vehicle {  
  
    @Override  
    public void drive() {  
        System.out.println("Electric Car is Moving silently...");  
    }  
  
    @Override  
    public void recharge() {  
        System.out.println("Electric Car is Recharging...");  
    }  
  
}  

InvocationHandler:

package com.foo.proxy;  
  
import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  
  
public class InvocationHandlerImpl implements InvocationHandler {  
  
    private ElectricCar car;  
      
    public InvocationHandlerImpl(ElectricCar car)  
    {  
        this.car=car;  
    }  
      
    @Override  
    public Object invoke(Object paramObject, Method paramMethod,  
            Object[] paramArrayOfObject) throws Throwable {  
        System.out.println("You are going to invoke "+paramMethod.getName()+" ...");  
        paramMethod.invoke(car, null);  
        System.out.println(paramMethod.getName()+" invocation Has Been finished...");  
        return null;  
    }  
  
}  

执行后的结果:

cglib的动态代理:
JDK中提供的生成动态代理类的机制有个鲜明的特点是: 某个类必须有实现的接口,而生成的代理类也只能代理某个类接口定义的方法,比如:如果上面例子的ElectricCar实现了继承自两个接口的方法外,另外实现了方法bee() ,则在产生的动态代理类中不会有这个方法了!更极端的情况是:如果某个类没有实现接口,那么这个类就不能同JDK产生动态代理了

package samples;  
/** 
 * 程序猿类 
 * @author louluan 
 */  
public class Programmer {  
  
    public void code()  
    {  
        System.out.println("I'm a Programmer,Just Coding.....");  
    }  
}  


package samples;  
import java.lang.reflect.Method;  
import net.sf.cglib.proxy.MethodInterceptor;  
import net.sf.cglib.proxy.MethodProxy;  
/* 
 * 实现了方法拦截器接口 
 */  
public class Hacker implements MethodInterceptor {  
    @Override  
    public Object intercept(Object obj, Method method, Object[] args,  
            MethodProxy proxy) throws Throwable {  
        System.out.println("**** I am a hacker,Let's see what the poor programmer is doing Now...");  
        proxy.invokeSuper(obj, args);  
        System.out.println("****  Oh,what a poor programmer.....");  
        return null;  
    }  
  
}  


package samples;  
  
import net.sf.cglib.proxy.Enhancer;  
  
public class Test {  
  
    public static void main(String[] args) {  
        Programmer progammer = new Programmer();  
          
        Hacker hacker = new Hacker();  
        //cglib 中加强器,用来创建动态代理  
        Enhancer enhancer = new Enhancer();    
                 //设置要创建动态代理的类  
        enhancer.setSuperclass(progammer.getClass());    
               // 设置回调,这里相当于是对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实行intercept()方法进行拦截  
                enhancer.setCallback(hacker);  
                Programmer proxy =(Programmer)enhancer.create();  
                proxy.code();  
          
    }  
}  

程序执行结果:



参考:
http://blog.csdn.net/luanlouis/article/details/24589193
https://www.zhihu.com/question/20794107
http://wiki.jikexueyuan.com/project/java-reflection/java-dynamic.html
http://www.jianshu.com/p/6f6bb2f0ece9

上一篇下一篇

猜你喜欢

热点阅读