代理设计模式

Posted hymKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代理设计模式相关的知识,希望对你有一定的参考价值。

今天在看Spring Aop,Aop在设计上使用的动态代理的设计模式,因此来对代理设计模式做一个系统性的复习总结,思考代理设计模式在实际架构工作中,以及业务代码的设计中所可能的实际应用场景。

首先,代理设计模式是一种结构型设计模式。代理设计模式是一种使用率非常高的设计模式,其定义是为其它对象提供一种代理以控制这个对象的访问。这个“控制”指对原有对象中的方法控制,既可以是增强,也可以是限制。

联想一下,我们在实际生活中的一个代理场景,租房经纪人,租房经纪人是对实际房东的代理,经纪人操作着房东房子的租用权利;对于房东,房东是委托租房经纪人去向外租出自己的房子。其实代理模式也叫做委托模式。很显然,在这个实际场景中,存在核心的两方角色:代理方、委托方,故也被叫做委托模式,是从委托方角色定义的。另外实现委托的方式,除了代理还有别的方式。所以,通常情况下,我们仍然更多称这种“通过为其它对象提供一种代理对象来控制某个对象的访问”的这种代码结构设计方式,为代理设计模式。

一、代理设计模式的通用模型

通过类图看一下代理设计模式中的三个角色的定义:

**Subject抽象主题角色(接口):**抽象主题角色可以是抽象类,也可以是接口,是一个普通的业务类型的定义,无特殊要求。

**RealSubject具体主题角色(实现类):**被委托角色、被代理角色,是业务逻辑的具体执行者。

**Proxy代理主题角色:**代理类,负责对真实角色的应用,把所有抽象主题类定义的方法(request())限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理工作和善后处理工作。

下面看一下具体的通用模型的代码实现:

Subject.java

package com.design.proxymode.common;

/**
 * 抽象主题角色
 */
public interface Subject {
    public void request();
}

RealSubject.java

package com.design.proxymode.common;

public class RealSubject implements Subject {
    @Override
    public void request() {
        //业务逻辑处理
    }
}

Proxy.java

package com.design.proxymode.common;

public class Proxy implements Subject {
    //要代理的那个实现类
    private Subject subject=null;

    public Proxy(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void request() {
        before();
        subject.request();
        after();
    }
    //预处理
    private void before() {
    }
    //善后处理
    private void after() {
    }
}

小结:

1、在上述代码中,可以看到Proxy具体代理谁,即被代理对象是通过构造函数传入的,在实际业务中,就由实际的场景模块来决策要创建谁的代理。这样的形式,一个代理类可以代理多个委托者或被代理者。

2、在Proxy的类中,有两个方法,分别为预处理before()和后处理after(),在真实角色subject.request()的执行前后,我们可以加入原有真实角色不具备的处理逻辑。Spring Aop框架也正是在解决before和after的优雅实现。

二、代理设计模式的应用

代理设计模式优缺点

代理设计模式优点:

  • 职责清晰

    真实角色只处理他核心关心的业务逻辑的处理

  • 高扩展性

    具体真实角色无论如何变化,只要实现了相同接口,代理类可以不需要做任何修改的情况下使用。

  • 智能化

    主要表现在动态代理设计上【后补】

关于代理模式的缺点,在后文中的会综合分析。

代理设计模式的分类

静态代理:

我们需要手动去创建代理对象的这种方式,被称为静态代理。在静态代理模式中,又可以细分为一下两种情况,分别为普通代理、强制代理。

普通代理:普通代理要求客户端只能访问代理角色。类图如下:

强制代理:一般思维是通过代理找到真实角色,但是强制代理,却是要通过真实角色找到代理角色,而且是强制性的必须的。类图如下:

普通代理的应用场景很容易理解,强制代理的应用场景,举一个现实世界中的例子,企业内部的审批系统,一般领导可以在特定情况下,指定代理审批人,而且只要领导自己有权限指定。是不是实现这样的一个审批功能,就可以应用我们的强制代理模式。

关于静态代理和普通代理的代码,就不贴出来了,核心是对这两种代理应用场景的思考和理解。

三、动态代理

动态代理是通过java jdk的Proxy类创建指定接口的代理对象,而这个对象的创建过程是在运行期间,动态代理实际上是JVM在运行期动态创建class字节码并加载的过程。

很显然这样一套可实现动态代理技术,会给我们的编码工作带来很大的便捷。先看一下动态代理的基本实现:

代码如下:

Subject.java

package com.design.proxymode.dynamic;

/**
 * 抽象主题
 */
public interface Subject {
    //业务操作
    public void  doSomething();
}

RealSubject.java

package com.design.proxymode.dynamic;

/**
 * 真实主题
 */
public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("do something...");
    }
}

MyInvocationHandler.java

package com.design.proxymode.dynamic;

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

/**
 * 动态代理的代用处理器
 */
public class MyInvocationHandler implements InvocationHandler {
    //被代理的对象
    private Object target=null;

    //通过构造函数传入一个对象
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //代理方法
        return method.invoke(this.target,args);
    }
}

DynamicProxy.java

package com.design.proxymode.dynamic;

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

public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler){
        //正常返回代理对象
        return (T) Proxy.newProxyInstance(loader, interfaces, handler);
    }
}

Client.java

package com.design.proxymode.dynamic;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        //定义一个主题角色
        Subject realSubject = new RealSubject();
        //定义一个处理者handler,其实就是方法的触发器
        MyInvocationHandler handler = new MyInvocationHandler(realSubject);
        //定义一个代理主题角色,主题角色、代理角色都是用上转型的方式定义
        Subject proxy= DynamicProxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),handler);
        //代理执行行为
        proxy.doSomething();
    }
}

通过类图和代码实现可以发现,代理角色不用再和真实角色实现相同的接口了,而是通过DynamicProxy传入的参数动态的生成相应接口实现类的代理类,这样的技术是不是很便捷。

InvocationHandler是jdk提供的动态代理接口,对被代理类的方法进行代理。

至此,关于代理的基础技术点就完事了,接下来会思考:动态代理能给我我们带来怎样的应用的好处呢,回顾文章开始所说的Aop技术,正是对动态代理的典型应用。

以上是关于代理设计模式的主要内容,如果未能解决你的问题,请参考以下文章

Java RMI地址解析问题

用于从 cloudkit 检索单列的代码模式/片段

是否有在单个活动中处理多个片段的 Android 设计模式?

java代码实现设计模式之代理模式

Java设计模式-代理模式之动态代理(附源代码分析)

十条实用的jQuery代码片段