zoukankan      html  css  js  c++  java
  • Java代理:静态代理、动态代理

    要理解动态代理,需要先理解反射(http://www.cnblogs.com/Donnnnnn/p/7729443.html)

    通俗理解:

    在很多底层框架中都会用得到,比如struts,Spring等都用到了动态代理,它的作用很简单:
    利用反射将你要使用的类,重新生成一个子类或本类,这样框架就可以利用这个新生成的类做一些事情,比如在该类的方法前后加一些代码。
    这样的话,你想像一下,你是不是不用修改任何已经编写好的代码,只要使用代理就可以灵活的加入任何东西,将来不喜欢了,不用也不会影响原来的代码。


    例子:

    我们大家都知道微商代理,简单地说就是代替厂家卖商品,厂家“委托”代理为其销售商品。关于微商代理,首先我们从他们那里买东西时通常不知道背后的厂家究竟是谁,也就是说,“委托者”对我们来说是不可见的;

     

    静态代理:

    代理类在程序运行前就已经存在(通常都是我们在Java代码中定义的)

     

    代码实例:

     

    厂家: 委托类(被代理类)
    微商代理: 代理类

     

    通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。

    下面我们用Vendor类代表生产厂家,BusinessAgent类代表微商代理,来介绍下静态代理的简单实现

    委托类和代理类都实现了Sell接口

    1、Sell接口的定义如下:

    package First;
    
    public interface Sell {
    
        void sell();
        
        void ad();
        
    }

    2、Vendor类的定义如下:

    package First;
    /*
     * 委托类:厂家
     */
    public class Vendor  implements Sell{
    
        @Override
        public void sell() {
            System.out.println("sell method"); 
        }
    
        @Override
        public void ad() {
            System.out.println("ad method");
        }
    
        
        
    }

     

    3、代理类BusinessAgent的定义如下:

    package First;
    /*
     * 代理类:微商
     */
    public class BusinessAgent implements Sell {
    
        private Vendor vendor;
        
        public BusinessAgent(Vendor vendor) {
            this.vendor = vendor;
        }
        
        @Override
        public void sell() {
            vendor.sell();
        }
    
        @Override
        public void ad() {
            vendor.ad();
        }
        
    
    }

     

    从BusinessAgent类的定义我们可以了解到,静态代理可以通过聚合来实现,让代理类持有一个委托类的引用即可。

     

    下面我们考虑一下这个需求:

    给Vendor类增加一个过滤功能,只卖货给大学生。

     

    通过静态代理,我们无需修改Vendor类的代码就可以实现,只需在BusinessAgent类中的sell方法中添加一个判断方法即可,如下所示:

     

    4、增加功能版BusinessAgent:

    package First;
    /*
     * 代理类:微商
     */
    public class BusinessAgent implements Sell {
    
        private Vendor vendor;
        
        public BusinessAgent(Vendor vendor) {
            this.vendor = vendor;
        }
        
        @Override
        public void sell() {
            if(isCollegeStudent()){
                vendor.sell();
            }
            
        }
    
        @Override
        public void ad() {
            vendor.ad();
        }
        
        public boolean isCollegeStudent(){
            //为了好理解,这里直接返回True
            return true;
        }
    
    }

     

     

    添加一个判断是否是大学生的方法:isCollegeStudent(),只需在代理类BusinessAgent类中判断,无需修改委托类Vendor

     

    • 优点一:可以隐藏委托类的实现;
    • 优点二:可以实现客户与委托类间的解耦,在不修改委托类代码的情况下能够做一些额外的处理。


    静态代理的局限在于运行前必须编写好代理类

     

    动态代理:

    代理类在程序运行时创建的代理方式被成为动态代理。(是不是想到了反射)

    也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。

    例子:

    假设我们要实现这样一个需求:

    在执行委托类中的方法之前输出“before”,在执行完毕后输出“after”。

    我们还是以上面例子中的Vendor类作为委托类,BusinessAgent类作为代理类来进行介绍。

    首先我们来使用静态代理来实现这一需求,相关代码如下:

    package First;
    /*
     * 代理类:微商
     */
    public class BusinessAgent implements Sell {
    
        //引用委托类
        private Vendor vendor;
        
        public BusinessAgent(Vendor vendor) {
            this.vendor = vendor;
        }
        
        @Override
        public void sell() {
            System.out.println("before");
            vendor.sell();
            System.out.println("after");
        }
    
        @Override
        public void ad() {
            System.out.println("before");
            vendor.ad();
            System.out.println("after");
        }
    
    }

    从以上代码中我们可以了解到,通过静态代理实现我们的需求需要我们在每个方法中都添加相应的逻辑,

    这里只存在两个方法所以工作量还不算大,假如Sell接口中包含上百个方法呢?这时候使用静态代理就会编写许多冗余代码。

    通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。

    下面我们来具体介绍下如何使用动态代理方式实现我们的需求:

    一、使用动态代理

    (1)委托类的定义

    package First;
    /*
     * 委托类:厂家
     */
    public class Vendor  implements Sell{
    
        @Override
        public void sell() {
            System.out.println("sell method"); 
        }
    
        @Override
        public void ad() {
            System.out.println("ad method");
        }
    
        
        
    }

    (2)中介类(调用处理器)

    中介类必须实现InvocationHandler接口,作为调用处理器”拦截“对代理类方法的调用。

    InvocationHandler接口(java.lang.reflect包下)

     

    这个接口的定义如下:

    public interface InvocationHandler { 
      Object invoke(Object proxy, Method method, Object[] args);
    }
    

      

      从InvocationHandler这个名称我们就可以知道,实现了这个接口的中介类用做“调用处理器”。

      当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,

      这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。因此我们只需在中介类的invoke方法实现中输出“before”,然后调用委托类的invoke方法,再输出“after”。下面我们来一步一步具体实现它。

    中介类的定义如下:

    package First;
    /*
     * 中介类:实现类
     */
    import java.lang.reflect.Method;
    
    public class DynamicProxy implements InvocationHandler{
    
        private Object obj;//obj为委托类对象
        
        public DynamicProxy(Object obj){
            this.obj = obj;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before");
            Object result = method.invoke(obj, args);
            
            System.out.println("before");
            return result;
        }
    
        
        
    }

    (3)动态生成代理类

    package First;
    /*
     * 动态生成代理类
     */
    import java.lang.reflect.Proxy;
    
    public class Main {
    
        public static void main(String[] args) {
            
            DynamicProxy inter = new DynamicProxy(new Vendor());
            /*
             * 1. 获取代理类实例sell (它的实现类是Vendor)
             * 2. newProxyInstance方法来获取一个代理类实例
             * 3. newProxyInstance(代理类的ClassLoader , 接口类Sell , 动态代理的实现类DynamicProxy)
             */
            Sell sell = (Sell)(Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[] {Sell.class}, inter));
            sell.sell(); 
            sell.ad(); 
        }
        
    }

    我们运行一下,看看我们的动态代理是否能正常工作。我这里运行后的输出为:

    before
    sell method
    after
    before
    ad method
    after

    AOP的通知也是这样实现的吧

    在以上代码中,我们调用Proxy类的newProxyInstance方法来获取一个代理类实例。这个代理类实现了我们指定的接口并且会把方法调用分发到指定的调用处理器。

    这个方法的声明如下:

    public static Object newProxyInstance(ClassLoader loader, 
        Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    

      

    方法的三个参数含义分别如下:

    • loader:定义了代理类的ClassLoder;
    • interfaces:代理类实现的接口列表
    • h:调用处理器,也就是我们上面定义的实现了InvocationHandler接口的类实例 ( 即DynamicProxy类 )

      

    参考:

    http://www.jianshu.com/p/cbd58642fc08

    目前还没有理解动态代理,以上只是做个笔记加深我自己的印象

     

  • 相关阅读:
    JavaScript的兼容小坑和调试小技巧
    前端jQuery实现瀑布流
    angular常用属性大全
    Eclipse易卡死
    工作反思
    半年回忆
    努力做到
    产品经理如何应对技术的「做不了」这样的问题(转)
    优秀的产品经理我还有多远
    简历技巧
  • 原文地址:https://www.cnblogs.com/Donnnnnn/p/7735493.html
Copyright © 2011-2022 走看看