美文网首页
Java动态代理

Java动态代理

作者: 米刀灵 | 来源:发表于2016-11-02 14:56 被阅读20次

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



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

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

    • Subject角色负责定义RealSubject和Proxy角色应该实现的接口。
    • RealSubject角色用来真正完成业务服务功能。
    • Proxy角色负责将自身的Request请求,调用realsubject 对应的request功能来实现业务功能,自己不真正做业务。
        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角色。

    • 在静态代理中,代理Proxy中的方法,都指定了调用了特定的realSubject中的对应的方法,Proxy所做的事情,无非是调用在不同的request时,调用触发realSubject对应的方法。
    • 动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。


    动态代理分为两种:

    • JDK动态代理:就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。
    • CGLIB:就是通过继承。**因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。

    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

    相关文章

      网友评论

          本文标题:Java动态代理

          本文链接:https://www.haomeiwen.com/subject/qjcsuttx.html