设计模式之 - 代理模式(Proxy Pattern)

代理模式:代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。很多可以框架中都有用到,比如: spring的AOP的实现主要就是动态代理, mybatis的Mapper代理等。

如下来看下代理模式的UML图(来自百度图片):

  

代理类和被代理类实现共同的接口, 其中代理类中包含一个被代理类的实例引用。代理模式可以分为静态代理和动态代理,这里主要学习下动态代理。动态代理作用可以实现业务代理和通用逻辑代码解耦,在不改变业务逻辑的同时,动态的给原逻辑代码添加一些通用功能,比如打印调用日志,权限判定,事务处理等等。

下面用代码实现动态代理:

1. 定义一个人的动作行为接口

package cn.aries.pattern.ProxyPattern;
/**
 * 人的行为接口
 * @author aries
 */
public interface PersonAction {

    /**
     * 说话
     */
    public void personSay();
    /**
     * 跑步
     */
    public void personRunning();
    /**
     * 吃东西
     */
    public void personEating();

}

2. 创建人行为的的实现类

package cn.aries.pattern.ProxyPattern;
public class PersonActionImpl implements PersonAction{
    @Override
    public void personSay() {
        System.out.println("人在说话...");
    }
    @Override
    public void personRunning() {
        System.out.println("人在跑步...");
    }
    @Override
    public void personEating() {
        System.out.println("人在吃东西...");
    }
}

3. 动态代理需要一个实现了InvoketionHandler接口的类

package cn.aries.pattern.ProxyPattern;

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

public class ProxyPerson implements InvocationHandler{
    //被代理的实例对象
    PersonAction obj;
    private ProxyPerson(PersonAction obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //执行方法之前打印动作开始。
        System.out.println(method.getName() + "ation start ...");
        //使用反射执行目标方法
        method.invoke(obj, args);
        //在方法执行结束时打印动作结束。
        System.out.println(method.getName() + "ation end ...");
        return null;
    }
   //定义一个静态方法生成代理对象
    public static Object getProxyPersonAction(PersonAction obj){
        PersonAction proxy = (PersonAction) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new ProxyPerson(obj));
        return proxy;
    }
}

4. 客户端代码

package cn.aries.pattern.ProxyPattern;

public class App {
    public static void main(String[] args) throws Exception {
        //设置系统参数,将生成的代理类的class文件保存到本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        PersonAction pa = new PersonActionImpl();
        //调用生成代理类的方法
        PersonAction proxyPa = (PersonAction) ProxyPerson.getProxyPersonAction(pa);       //用代理对象调用目标方法
        proxyPa.personSay();
        proxyPa.personRunning();
        proxyPa.personEating();
        //打印代理对象的父类
        System.out.println(proxyPa.getClass().getSuperclass());
    }
}

执行结果:

personSayation start ...
人在说话...
personSayation end ...
personRunningation start ...
人在跑步...
personRunningation end ...
personEatingation start ...
人在吃东西...
personEatingation end ...
class java.lang.reflect.Proxy

当方法在中的是分别执行我们在目标方法执行前后添加的代码。

5. 代理对象是通过Proxy.newProxyInstance(...)这个方法生成的,我们进入源代码查看下

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
        if (h == null) {
            throw new NullPointerException();
        }
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        /*
         * Look up or generate the designated proxy class.        这里生成代理类的字节码文件
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * Invoke its constructor with the designated invocation handler.         */
        try {      //在这里获取代理类的构造函数,从前面的运行结果中可以得知,代理类是Proxy类的子类      //而constructorParams在Proxy类中是一个静态的常量: private static final Class<?>[] constructorParams = { InvocationHandler.class };       //所以这里获取的带InvocationHandler对象为入参的构造函数,也就是其父类Proxy的构造函数:protected Proxy(InvocationHandler h){...}
          final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;        //这里调用newInstance()方法创建代理对象,其内部实现是:return cons.newInstance(new Object[] {h} );使用反射通过含参(hanlder)生成代理对象。       //其中h赋值给了其父类Proxy类的成员变量: protected InvocationHandler h;       //最终在这里生成代理对象并返回
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

6. 到此我了解了代理对象的生产过程,但是代理对象和handler是什么关系呢,又是如何调用其invoke(...)方法呢,这暂时是个谜团让我们来看下生成的代理类的源码,这些就都清楚了。

注:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 这个是设置系统参数,将生产的代理类自己码文件保存在本地,然后我们通过反编译就可以获得其Java代码。

package com.sun.proxy;

import cn.aries.pattern.ProxyPattern.PersonAction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements PersonAction {
    //这五个静态变量前三个m0,m1,m2分别是代理类继承的Object类的hashcode(),equals(),toString()方法
    //其他从m3开始是继承的们定义的接口类的方法根据方法的多少m后面的数字递增
    private static Method m1;
    private static Method m3;
    private static Method m5;
    private static Method m0;
    private static Method m4;
    private static Method m2;

    static {
        try {
            //这里使用静态代码块对通过反射对代理对象中的方法进行实例化
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
            m3 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personEating", new Class[0]);
            m5 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personRunning", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m4 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personSay", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }

    public $Proxy0(InvocationHandler paramInvocationHandler)throws{
        super(paramInvocationHandler);
    }

    //这里是对我们定义的personEating方法进行实现
    //根据类文件我们可以看到,代理类继承了Proxy类,所以其成员变量中包含一个Handler实例对象的引用
    //在创建代理实例对象的时候,我们使用的protected Proxy(InvocationHandler h) {this.h = h;}这个构造函数
    //所以下面的h就是我们传进去的handler对象
    //这里使用handler对象调用自己的invoke()方法,m3就是我们要执行的方法,
    //后面的方法的参数,如果有参数就传对应的参数,没有就传null
    //此时我们明白了代理对象和handler的关系,以及如何调用到invoke()方法有了明确的认识了。
    public final void personEating()throws{
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
    //这里原理同上,为了节省空间这里就不贴出来了
    public final void personSay(){...}
    public final void personRunning(){...}

    public final int hashCode()throws{
        try{
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final String toString()throws {
        try{
          return (String)this.h.invoke(this, m2, null);
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final boolean equals(Object paramObject)throws{
        try{
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
}

写完后浏览了一下,好像没有发现被代理对象的引用在代理类中出现;然后想了下,代理类继承了Proxy类,其中Proxy类中有我们写的InvoketionHandler对象的是实例,而这个handler实例中就存有我们创建的被代理对象的实例引用,在invoke方法中,传入的实例对象就是我们穿件的这个被代理对象;这样就间接的持有了被代理对象的实例引用。

到此动态代理的生成过程,以及是如何调用invoke()方法的原理已经搞清楚,到此本文完结。

时间: 2024-10-03 16:08:53

设计模式之 - 代理模式(Proxy Pattern)的相关文章

设计模式之代理模式---Proxy Pattern

模式的定义 代理模式(Proxy Pattern)也叫做委托模式,定义如下: Provide a surrogate or placeholder for another object to control access to is. 为其他对象提供一种代理以控制对这个对象的访问. 类型 结构类 模式的使用场景 想想现实世界中,打官司为什么要找个律师?因为你不想参与中间过程的是是非非,只要完成自己的工作就可以,其它的事情比如事前调查,事后追查都可以由律师来负责,这就可以减少你的负担.代理模式使用

二十三种设计模式[12] - 代理模式(Proxy Pattern)

前言 代理模式,属于对象结构型模式.在<设计模式 - 可复用的面向对象软件>一书中将之描述为" 为其它对象提供一种代理以控制对这个对象的访问 ". 在代理模式中,通常使用一个类来代表另一个类的功能,并由这个代理对象去控制原对象的引用. 结构 Subjuet(公共接口):代理类和被代理类的公共接口,保证任何使用目标的地方都可以被代理类替换: RealSubject(被代理类):代理类所代表的目标类: Proxy(代理类):包含对目标类的引用,目标类的封装: 场景 在日常生活中

设计模式 - 代理模式(proxy pattern) 未使用代理模式 详解

代理模式(proxy pattern) 未使用代理模式 详解 本文地址: http://blog.csdn.net/caroline_wendy 部分代码参考: http://blog.csdn.net/caroline_wendy/article/details/37698747 如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供

设计模式三: 代理模式(Proxy) -- JDK的实现方式

设计模式三: 代理模式(Proxy) -- JDK的实现方式 简介 代理模式属于行为型模式的一种, 控制对其他对象的访问, 起到中介作用. 代理模式核心角色: 真实角色,代理角色; 按实现方式不同分为静态代理和动态代理两种; 意图 控制对其它对象的访问. 类图 实现 JDK自带了Proxy的实现, 下面我们先使用JDK的API来演示代理如何使用, 随后再探究Proxy的实现原理,并自己来实现Proxy. JDK代理类的使用: (InvocationHandler,Proxy) 使用JDK实现的代

设计模式之代理模式(Proxy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

Android设计模式之代理模式 Proxy

一.概述 代理模式也是平时比较常用的设计模式之一,代理模式其实就是提供了一个新的对象,实现了对真实对象的操作,或成为真实对象的替身.在日常生活中也是很常见的.例如A要租房,为了省麻烦A会去找中介,中介会替代A去筛选房子,A坐享中介筛选的结果,并且交房租也是交给中介,这就是一个典型的日常生活中代理模式的应用.平时打开网页,最先开到的一般都是文字,而图片等一些大的资源都会延迟加载,这里也是使用了代理模式. 代理模式的组成: Abstract Subject:抽象主题-声明真实主题和代理主题共同的接口

大熊君说说JS与设计模式之------代理模式Proxy

一,总体概要 1,笔者浅谈 当我们浏览网页时,网页中的图片有时不会立即展示出来,这就是通过虚拟代理来替代了真实的图片,而代理存储了真实图片的路径和尺寸,这就是代理方式的一种. 代理模式是比较有用途的一种模式,而且变种较多(虚代理.远程代理.copy-on-write代理.保护代理.Cache代理.防火墙代理.同步代理.智能指引),应用场合覆盖从小结构到整个系统的大结构, 我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理. 代理对象角色内部含有对真实对象的

设计模式(结构型)之代理模式(Proxy Pattern)

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(结构型)之享元模式(Flyweight Pattern)>http://blog.csdn.net/yanbober/article/details/45477551 概述 代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个

设计模式之(二)---代理模式Proxy Pattern

什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被 代理的人能干活呀. 比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的: 先定义一种类型的女人(接口): package com.fc.Proxy; public interface KindWoman { public void makeEyesWithMan(); //抛媚

代理模式(Proxy Pattern)

一.概述在软件开发中,有些对象由于创建成本高.访问时需要与其它进程交互等原因,直接访问会造成系统速度慢.复杂度增大等问题.这时可以使用代理模式,给系统增加一层间接层,通过间接层访问对象,从而达到隐藏系统复杂性.提高系统性能的目的.二.代理模式代理模式为其他对象提供一种代理以控制对这个对象的访问.其结构图如下: Subject定义了RealSubject和Proxy共用的接口,使得在任何使用RealSubject的地方都可以使用Proxy abstract class Subject { publ