zoukankan      html  css  js  c++  java
  • Java中的JDK动态代理

    所谓代理,其实就是相当于一个中间人,当客户端需要服务端的服务时,不是客户直接去找服务,而是客户先去找代理,告诉代理需要什么服务,然后代理再去服务端找服务,最后将结果返回给客户。
    在日常生活中,就拿买火车票来比喻。以前买票,都是需要到火车站去买的,但是有些人距离火车站太远不方便去,来来回回总是很麻烦。于是铁道部就在城市的各个地方增设一个火车票代售点,在这个代售点就可以买票而不用去火车站了。这里,这个代售点就相当于一个代理,可以提供火车站所提供的服务。
    下面说说JAVA中的代理
    代理的实现又分为静态代理和动态代理,

    一.静态代理

    静态代理意思就是静态的,不能改变的,客户,服务,代理这三者是已经决定好了的。它的实现需要一个接口和这个接口的实现类。
    这个接口就是服务端所提供的服务。
    比如下面的代码:

    //服务
    interface Station{
        void sellTickit();
    }
    
    //服务提供类
    class SubStation implements Station{
        public void sellTickit(){
            System.out.println("卖火车票总站");
        }
    }
    
    //服务代理类
    class ProxyStation implements Station{
    
        private Station station = null;
    
        public ProxyStation(Station station){
            this.station = station;
        }
        
        public void sellTickit(){
            System.out.println("这里是代售点,需要去总站拿票");
            station.sellTickit();
            System.out.println("已经从总站拿到票");
            
        }
    }
    
    public class ProxyTest{
        //此处调用就相当于客户端
        public void static main(String[] args){
            Station station = new ProxyStation(new SubStation());
            station.sellTickit();
        }
    }
    
    //输出结果:
    //这里是代售点,需要去总站拿票
    //卖火车票总站
    //已经从总站拿到票
    
    

    从上面代码可以看到,当用户调用sellTickit服务时,并不是直接调用服务类的sellTickit,而是调用代理类的sellTickit,代理类中sellTickit再去调用服务类的方sellTickit来提供服务的。这样,代理类就可以在实际调用服务类的服务时做更多的事情,也可以向客户端屏蔽服务类提供服务的细节。
    这就是静态代理,虽然说实现起来比较简单,但是从简单的问题上更容易看清楚本质。代理的本质其实就是代理向客户端屏蔽服务端,并且向客户端提供服务端的部分服务
    还是拿上面卖票的例子来说,目前服务提供类只提供了卖票服务,所以代理中也只提供了卖票服务。如果现在,服务提供类增加了退票、改签等服务,那么在代理中,就应该也增加这些服务(当然也可以不增加,这样客户就直接去总站退、改,这里假设代理会提供所有的服务)。理解了代理的本质,再来看看动态代理。

    二.动态代理

    动态代理的实现又分为JDK动态代理和CGLIB动态代理,这里先只说JDK动态代理。动态就是相对于静态的,代理类并不是一开始就有的,而是动态生成的。
    此动态代理同样需要一个接口和此接口的实现类,看如下代码

    //服务
    interface Station{
        void sellTickit();
    }
    
    //服务提供类
    class SubStation implements Station{
        public void sellTickit(){
            System.out.println("卖火车票总站");
        }
    }
    
    class MyInvocationHandler implements InvocationHandler{
    	
    	Object target = null;
    	public MyInvocationHandler(Object obj){
    		this.target = obj;
    	}
    
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("这里是代售点,需要去总站拿票");
    		Object result = method.invoke(target, args);
    		System.out.println("已经从总站拿到票");
    		return result;
    	}
    }
    
    public class ProxyTest {
    	public static void main(String[] args){
    		SubStation subStation = new SubStation();
    		Station proxyStation = (Subject) Proxy.newProxyInstance(subStation .getClass().getClassLoader(),
                                                                            subStation .getClass().getInterfaces(), 
                                                                            new MyInvocationHandler(subStation ));
    		proxyStation.sellTickit();
    	}
    }
    

    可以看到,原来静态定义的代理类已经没有,产生代理类是使用了Proxy.newProxyInstance方法,此方法接收的三个参数分别为服务提供类的类加载器,服务器提供类的接口和一个代理方法调用处理类
    这样看着并不能很好的理解到底发生了什么事情,那么我就在main中的proxyStation.sellTickit()处增加断点来调试。

    可以看到,此时proxyStation的值为$Proxy0,这就是动态生成的代理类。其中还是成员变量h,它就是MyInvocationHandler的实例,h.target就是SubStation的实例。
    那么如何看到$Proxy0代理类的内容呢?可以参考这边文章Java代理模式,通过反编译出Java代码,我们可以看到动态代理所产生的代理类也很类似静态代理中的代理类,只不过是动态生成的。可以看到$Proxy0中实现了服务接口中的所有方法,在它的方法中,是通过调用MyInvocationHandler.invoke来调用服务提供类的服务的。
    MyInvocationHandler.invoke方法中的三个参数: proxy, method, args分别代表了$Proxy0的实例,调用的方法和方法的参数。

  • 相关阅读:
    Asp.net routing vs Url rewriting
    How to combine WCF Route and MVC Route to work together.
    Servlets beat CGI
    What if you encounter a problem when consume your WCF service ? How to Diagnostic it ?
    uva 4965 Sum the Square
    zoj 3633 Alice's present
    4966 Normalized Form
    ZOJ 3015 Collision Ball Game
    二分图 最小路径覆盖
    uva 2696 Air Raid
  • 原文地址:https://www.cnblogs.com/bencakes/p/6238162.html
Copyright © 2011-2022 走看看