代理模式proxy

一,定义

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,
其特征是代理类与委托类有同样的接口:表现形式如下
这里写图片描述

二,功能特点

代理类不仅仅是一个隔离客户端和委托类的中介。我们还可以借助代理来在增加一些功能,而不需要修改原有代码,严重的复合开闭原则哦。
代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
就是这样,真正的业务功能还是有委托类来实现,但是在实现业务类之前的一些公共服务。例如在项目开发中我们没有加入缓冲,日志这些功能,后期想加入,我们就可以使用代理来实现,而没有必要打开已经封装好的委托类

三,分类

对于代理的具体实现,我们有不同的方式,如果按照代理的创建时期,代理类可以分为两种。:静态代理、动态代理。

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 

动态代理:在程序运行时,运用反射机制动态创建而程。

静态代理:
首先,我们需要定义业务接口,业务接口实现类,然后定义代理类,且实现业务接口;最后写一个Client来调用。
以打官司为例
1,定义业务接口:打官司申请

package com.example.proxy;

public interface Laysuit {
    void submit();//提交申请
    void burden();//举证

}

2,业务接口实现类:投诉者

package com.example.proxy;

public class Customer implements Laysuit{

    @Override
    public void submit() {
        System.out.println("申请");
    }

    @Override
    public void burden() {
        System.out.println("举证");
    }

}

3,代理类:律师

package com.example.proxy;

public class Lawyer implements Laysuit{

    public Laysuit laysuit;
    public Lawyer(Laysuit laysuit) {
        this.laysuit=laysuit;
    }
    @Override
    public void submit() {
        laysuit.submit();
    }

    @Override
    public void burden() {
        laysuit.burden();
    }

}

4,client调用

package com.example.proxy;

import java.lang.reflect.Proxy;
/**
 * 代理模式
 * @author car
 *
 */
public class ProxyPattern {

    public static void main(String[] args) {

        Laysuit customer=new Customer();

        /*
         * 静态代理
         */
        System.out.println("静态代理");
        Laysuit laysuit=new Lawyer(customer);
        laysuit.submit();
        laysuit.burden();

    }

}

源码举例
context的组成就是典型的代理模式
这里写图片描述

 Context是一个抽象类,定义了所有的实现
 ContextImpl实现了Context的所有方法,具体的执行都在这里
 ContextWrapper相当于ContextImpl的代理,如下面代码所示,内部持有mBase,其实就是一个ContextImpl
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    /**
     * Set the base context for this ContextWrapper.  All calls will then be
     * delegated to the base context.  Throws
     * IllegalStateException if a base context has already been set.
     * 
     * @param base The new base context for this wrapper.
     */
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    ...........
}

至此我们的静态代理实现了,但是问题也跟着来了,观察代码可以发现每一个代理类只能为一个接口服务,一个Lawyer类实现了一个Laysuit接口,那么我要是有多个接口,是不是要写多个Proxy类与之对应。这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那就引入了我们的动态代理了。

动态代理
仍然以上面打官司的举例
动态代理类:

package com.example.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 动态代理
 * @author car
 *
 */
public class DynamicProxy implements InvocationHandler{

    Object object=null;
    public DynamicProxy(Object object) {
        this.object=object;
    } 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("调用    "+method.getName()+    "方法");
        return method.invoke(object, args);
    }

}

client调用:

package com.example.proxy;

import java.lang.reflect.Proxy;
/**
 * 代理模式
 * @author car
 *
 */
public class ProxyPattern {

    public static void main(String[] args) {

        Laysuit customer=new Customer();

        /*
         * 动态代理 代理instance动态生成
         */
        System.out.println("动态代理");
        DynamicProxy dynamicProxy = new DynamicProxy(customer);

        ClassLoader classLoader = customer.getClass().getClassLoader();

        Laysuit proxy = (Laysuit) Proxy.newProxyInstance(classLoader, new Class[]{Laysuit.class}, dynamicProxy);

        proxy.submit();
        proxy.burden();
    }

}

源码分析
关键代码:

生成一个代理类:

Laysuit proxy = (Laysuit) Proxy.newProxyInstance(classLoader, new Class[]{Laysuit.class}, dynamicProxy);
    public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
    throws IllegalArgumentException
    {
    if (h == null) {
        throw new NullPointerException();
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class cl = getProxyClass(loader, interfaces);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
    }
    }
 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)做了以下几件事. 
    (1)根据参数loader和interfaces调用方法 getProxyClass(loader, interfaces)创建代理类$Proxy0.$Proxy0类 实现了interfaces的接口,并继承了Proxy类. 
    (2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值 

Proxy类的构造函数如下:

public class Proxy implements java.io.Serializable {

  ...

    /** parameter types of a proxy class constructor */
    private final static Class[] constructorParams =
{ InvocationHandler.class };

     /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new <code>Proxy</code> instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param   h the invocation handler for this proxy instance
     */
    protected Proxy(InvocationHandler h) {
    this.h = h;
    }

}  

最后是动态类$Proxy0的源码:

public final class $Proxy0 extends Proxy implements Laysuit{  
    private static Method m1;  
    private static Method m0;  
    private static Method m3;  
    private static Method m2;  

    static {  
        try {  
            ...

            m3 = Class.forName("***.Customer").getMethod("submit",  
                    new Class[0]);  

              ...

        } catch (NoSuchMethodException nosuchmethodexception) {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        } catch (ClassNotFoundException classnotfoundexception) {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    } //static  

    public $Proxy0(InvocationHandler invocationhandler) {  
        super(invocationhandler);  
    }  


    public final void submit() {  
        try {  
            super.h.invoke(this, m3, null);  
            return;  
        } catch (Error e) {  
        } catch (Throwable throwable) {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

}  

总结
一个典型的动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类
Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入
Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance方法封装了2~4,只需两步即可完成代理对象的创建。
生成的ProxySubject继承Proxy类实现Subject接口,实现的Subject的方法实际调用处理器的invoke方法,而invoke方法利用反射调用的是被代理对象的的方法(Object result=method.invoke(proxied,args))
备注:
动态代理:始终无法摆脱仅支持interface代理的桎梏,因为它的设计注定了这个遗憾。回想一下那些动态生成的代理类的继承关系图,它们已经注定有一个共同的父类叫Proxy。Java的继承机制注定了这些动态代理类们无法实现对class的动态代理,原因是多继承在Java中本质上就行不通。有很多条理由,人们可以否定对 class代理的必要性,但是同样有一些理由,相信支持class动态代理会更美好。接口和类的划分,本就不是很明显,只是到了Java中才变得如此的细化。如果只从方法的声明及是否被定义来考量,有一种两者的混合体,它的名字叫抽象类。实现对抽象类的动态代理,相信也有其内在的价值。此外,还有一些历史遗留的类,它们将因为没有实现任何接口而从此与动态代理永世无缘

参考:
从java 反射看延迟加载(代理模式)(五)——理解Proxy、InvocationHandler
彻底理解JAVA动态代理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值