[102]技术-设计模式[代理模式]

代理模式

对于client -> server 访问模式,如果我想在client 访问 server时候多做一些工作,那么可以是用代码,交互模式就变为client-> serverProxy -> server.
其具体的java 伪代码如下:

public interface iA
{
    public void do();
}
public class RealA implement iA
{
    public void do()
   {
     //dosomething....
   }
}

public  ProxyA implement iA  //运用代理后客户端访问接口不变所以需要继承相同的接口。
{
    RealA a;
    public void do()
   {
        //create A if a is not create
       //dosomething  before .....
       a.do();
       //dosomething  after .....
   }
}

main()
{
     iA a = new ProxyA();
}

动态代理模式

现在我们知道了简单的代理模式,如果A有多个实现类,比如RealA,RealA1,RealA2,难道我要对每个RealA? 类写一个代理类嘛?且就算你写了 ProxyRealA,ProxyRealB,我们也不太好统一调用。
代理有一个目的就是client -> proxy -> server这种模式中的server 封装成一种统一调用,不用管其内部实现的复杂性。
那么我们有没有办法在程序运行时候自动生成ProxyReal呢?
其大概伪代码如下:

InvocationHanderAimpl implements  InvacationHandler
{
    /**
    * 这个方法实现就是类似ProxyA.do()方法类似的功能,在调用具体方法前后做一些处理。<br>
    */
    public Object invoke(Object proxy, Method method, Object[] args)
    {
           ... do something before invoke.
           method.invoke(proxy,args);
           .. do something after invoke.
    }
} 
main()
{
     // getProxyInstance() 为iA a = new ProxyA();动态代理版本。
     // InvocationHandler 代表了该代理类具体行为,即执行方法前后如何处理。
     iA dynamicProxyObj = getProxyInstance(A.class,Aimpl.class,InvocationHandler);
     dynamicProxyObj .do();
}

动态代理的两种具体实现

  • jdk 的版本
public class ProxyHandler implements InvocationHandler   
{   
  private Object proxied;   
     
  public ProxyHandler( Object proxied )   
  {   
    this.proxied = proxied;   
  }   
     
  public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable   
  {   
    //在转调具体目标对象之前,可以执行一些功能处理
    //转调具体目标对象的方法
    return method.invoke( proxied, args);  
    //在转调具体目标对象之后,可以执行一些功能处理
  }    
} 

main()
{
    RealA  realA= new RealA  ();   
    iA  proxiedA= (A)Proxy.newProxyInstance(A.class.getClassLoader(), 
     new Class[]{iA.class}, 
     new ProxyHandler(realA));
     iA.do();
}

这里重点注意:
1.invocationHandler的实现。
2.newProxyInstance 的方法参数,interfaces只接收接口类型,即iA.class ,不接收RealA.class

//proxy.java
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h){
}
  • cglib版实现
    关于cglib,从网上找到英文版的介绍:

Cglib is an open source library that capable creating and loading class files in memory during Java run time. To do that it uses Java byte-code generation library ‘asm’, which is a very low level byte code creation tool,其开源类库为:net.sf.cglib.proxy.*。

我们先看看源码如何创建Cglib代理类的

package com.thoughtworks.proxy.factory;

.......
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.DefaultNamingPolicy;
import net.sf.cglib.core.Predicate;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.InvocationHandler;
import net.sf.cglib.proxy.Proxy;
.......
public class CglibProxyFactory extends AbstractProxyFactory {
{
public <T> T createProxy(Invoker invoker, Class... types) {
        Class type = this.getSingleClass(types); //type =null ,说明types对象全部是接口,没有一个类
        if(type == null) {
            //全部是接口使用标准jdk的代理方式创建代理类,即proxy.newProxyInstance();
            return standardProxyFactory.createProxy(invoker, types);
        } else if(type.isPrimitive()) {
            throw new IllegalArgumentException("Cannot subclass primitive type");
        } else {
            Class[] interfaces = this.getInterfaces(types);
            Enhancer enhancer = new Enhancer();

            while(true) {
                enhancer.setSuperclass(type);
                enhancer.setInterfaces(interfaces);
                enhancer.setCallback(new CglibProxyFactory.CGLIBInvocationHandlerAdapter(invoker));

                Object proxy;
                try {
                    proxy = enhancer.create();
                    return proxy;
                } catch (CodeGenerationException var8) {
                    Throwable wrapper = var8.getCause();
                    if(wrapper != null && wrapper.getCause() instanceof SecurityException && enhancer.getNamingPolicy() != this.namingPolicy) {
                        enhancer.setNamingPolicy(this.namingPolicy);
                        continue;
                    }
                } catch (IllegalArgumentException var9) {
                    ;
                } catch (NoSuchMethodError var10) {
                    ;
                }

                proxy = this.createWithConstructor(type, enhancer);
                return proxy;
            }
        }
    }
}

其具体如何创建和使用CglibProxy可以参考博客:http://blog.csdn.net/ljphhj/article/details/20483171

两种方式的优缺点:

CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理!

说在后面的话

这篇文章只是简单的介绍proxy干了一件什么事情。
从具体的技术层面还有以下疑问:
1.ClassLoader 装载的是哪个类?如果是一个接口的话类是如何加载进来的。
2.Cglib与jdk Proxy类的区别。
这两个问题以后待分析。

参考

https://stackoverflow.com/questions/10664182/what-is-the-difference-between-jdk-dynamic-proxy-and-cglib

关于cglib:
https://www.programcreek.com/java-api-examples/index.php?api=net.sf.cglib.proxy.MethodInterceptor
https://github.com/cglib/cglib/wiki/Tutorial

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341