设计模式之代理模式

1、定义

代理模式(Proxy Pattern)为其他对象提供一种代理以控制这个对象的访问。

2、形式

代理模式有好几种不同的形式,下面我们针对不同的形式分别进行讲解。

1)静态代理

通用类图如下所示:

在这里插入图片描述
其中

Subject为抽象主题角色,定义了一个普通的业务类型,既可以是抽象类也可以是接口;

RealSubject为具体主题角色,是被代理的角色,也是业务逻辑的具体执行者;

Proxy为代理主题角色,负责代理真实角色,该类把所有抽象主题定义的方法限制委托给真实主题角色实现,并在真实主题角色处理完毕前后做预处理和善后处理工作(添加了新的行为);

通用源码为:

示例

Subject

public interface Subject {
    public void request();
}

RealSubject

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

Proxy

public class Proxy implements Subject {
    //要代理的实现类
    public RealSubject realSubject;
    //默认被代理者
    public Proxy(){
        this.subject = new RealSubject();
    }
    //实现接口中定义的方法
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    public void before(){
        //do something
    }
    //善后处理
    public void after(){
        //do something
    }
}

示例租房子,本来需要租客(省略)直接找到租户 (Host) 租房子 (Rent),现在租户为了方便将出租交给了中介 (Proxy) 处理。

房屋出租

public interface Rent{
	public void rent();
}

租户角色

public class Host implements Rent{
	public void rent(){
		system.out.println("租房子");
	}
}

中介角色

public class Proxy implements Rent{
	private Host host;
	public Proxy(){}
	public Proxy(Host host){
		this.host = host;
	}
	public void rent(){
		seeHouse();
		host.rent();
		fare();
	}
}
//预处理:带租客看房
public void seeHouse(){
}
//善后处理:获得中介费
public void fare(){
}

客户角色

public class Client{
	public static void main(String[] args){}
		//创建真实角色
		Host host = new Host();
		//将真实角色进行代理
		Proxy proxy = new Proxy(host);
		//通过代理角色进行业务操作
		proxy.rent();
	}

2)普通代理

用户必须知道代理角色才能访问真实角色,也即客户端只能访问代理角色,无法访问真实角色。

示例代码如下:

Subject

public interface Subject {
    public void request();
}

RealSubject

public class RealSubject implements Subject {
    String name = "";
   	//限制谁能创建对象
    public RealSubject(Subject realSubject, String name) throws Exception{
        if(realSubject == null){
            throw new Exception("不能创建真实角色!");
        }else {
            this.name = name;
        }
    }
    public void request() {
        //业务处理逻辑
        System.out.println(this.name + "submits request");
    }
}

Proxy

public class Proxy implements Subject {
    //要代理的实现类
    public Subject subject = null;
    //默认被代理者
    public Proxy(String name){
        try {
            this.subject = new RealSubject(this, name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //实现接口中定义的方法
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    public void before(){
        //do something
        System.out.println("preHandleMethod");
    }
    //善后处理
    public void after(){
        //do something
        System.out.println("postHandleMethod");
    }
}

Client 场景类

public class Client {
    public static void main(String[] args) {
        Proxy proxy = new Proxy("lee");
        proxy.request();
    }
}

在该模式下,调用者只知道代理而不知道真实角色是谁,屏蔽了真实角色的变更对高层模块的影响,因此该模式适用于对扩展性较高的场合。

3)强制代理

只能通过真实角色查找到指定的代理才能访问真实角色,也即无法直接访问真实角色,也无法通过new一个代理访问,只能通过真实对象指定的代理进行访问。

示例代码如下:

Subject

public interface Subject {
    public void request();
}

RealSubject

public class RealSubject implements Subject {
    private String name = "";
    public Subject proxy = null;
    public RealSubject(String name) {
        this.name = name;
    }
    public void request() {
        //业务处理逻辑
        if(this.isProxy())
            System.out.println(this.name + " submits request");
        else
            System.out.println("请使用指定的代理访问!");
    }
    public Subject getProxy(){
        this.proxy = new Proxy(this);
        return this.proxy;
    }
    private boolean isProxy(){
        if(this.proxy == null){
            return false;
        }else {
            return true;
        }
    }
}

Proxy

public class Proxy implements Subject {
    //要代理的实现类
    public Subject subject = null;
    //默认被代理者
    public Proxy(Subject subject){
        this.subject = subject;
    }
    //实现接口中定义的方法
    public void request() {
        this.before();
        this.subject.request();
        this.after();
    }
    //预处理
    public void before(){
        //do something
        System.out.println("preHandleMethod");
    }
    //善后处理
    public void after(){
        //do something
        System.out.println("postHandleMethod");
    }
}

Client

public class Client {
    public static void main(String[] args) {
        //下面两种方式均无法访问真实对象的方法
        // Proxy proxy = new Proxy(new RealSubject("lee"));
        //Subject subject = new RealSubject("lee");
        Subject proxy = new RealSubject("lee").getProxy();
        proxy.request();
    }
}

强制代理的核心就是要从真实角色查到代理角色进行访问,高层模块必须通过真实角色提供的getProxy()方法获得一个代理对象来进行访问。

4)动态代理

动态代理在实现阶段不用关心代理谁,而是在运行阶段才指定代理哪一个对象。简单来说,动态代理就是静态代理的升级版:静态代理需要为每一个真实角色指定一个代理,而动态代理的是接口,而真实的代理对象是在运行时确定的

从上面的解释可以看出,动态代理的被代理类必须实现一个接口,但还有一些技术(如CGLIB)不需要接口也可以实现动态代理。

示例代码:以租房为例

抽象角色

public interface Rent {
    void rent();
}

真实角色

public class Host implements Rent{
    public void rent() {
        System.out.println("房屋出租");
    }
}

代理角色

public class ProxyInvocationHandler implements InvocationHandler {
    //代理的是接口
    private  Rent rent;

    public ProxyInvocationHandler (Rent rent){
	    this.rent = rent;
    }

    //生成代理类
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
    }

    //proxy:代理类
    //method:代理类调用处理程序对象的方法
    //调用方法并返回结果
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        seeHouse();
        Object result = method.invoke(rent, args);
        fare();
        return result;
    }

    //代理类特有方法
    public void seeHouse(){
        System.out.println("带租客看房");
    }

    public void fare(){
        System.out.println("收中介费");
    }
}

客户角色

public class Client {
    public static void main(String[] args) {
        //真实对象
        Host host = new Host();
        //生成代理实例
        ProxyInvocationHandler pih = new ProxyInvocationHandler(host);
        Rent proxy = (Rent) pih.getProxy();
        proxy.rent();
    }
}

这里的动态代理使用了位于java.lang.reflect包下提供的Proxy类和InvocationHandler接口,通过这两个可以生成JDK动态代理类或动态代理对象。代理对象代理的是接口,这也是动态代理的核心。从本质上来说,动态代理是通过反射实现的

动态代理也是一个非常流行的技术—AOP的核心,AOP(面向横切面编程)对系统的设计和编码有非常大的影响,对于日志、事务、权限等一系列与业务逻辑分离的操作都推迟到系统设计阶段之后用AOP的方式切入进去。

接下来我们看看动态代理的通用类图:

在这里插入图片描述
动态代理类实现InvocationHandler接口代理真实角色,通知接口抽象出通知方法,通过动态代理切入业务逻辑。

代码框架如下:

Subject

public interface Subject {
    public void doSomething(String str);
}

RealSubject

public class RealSubject implements Subject {
    public void doSomething(String str) {
        System.out.println("do something " + str);
    }
}

MyInvocationHandler 动态代理的Handler类

public class MyInvocationHandler implements InvocationHandler {
    public Object target = null;
    public MyInvocationHandler(Object obj){
        this.target = obj;
    }
    //代理方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.target, args);
    }
}

IAdvice 前置通知接口

public interface IAdvice {
    public void exec();
}

BeforeAdvice

public class BeforeAdvice implements IAdvice{
    public void exec() {
        System.out.println("前置通知操作!");
    }
}

DynamicProxy 动态代理类

public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
        //寻找JoinPoint连接点
        if(true){
            (new BeforeAdvice()).exec();
        }
        return (T) Proxy.newProxyInstance(loader, interfaces, h);
    }
}

Client 场景类

public class Client {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        InvocationHandler handler = new MyInvocationHandler(subject);
        //代理该类的所有接口
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), handler);
        proxy.doSomething("Finish!");
    }
}

上面的动态代理类是一个通用类,可以继承该类获得一个具体的动态代理对象,以简化代码。代码修改如下:

SubjectDynamicProxy 动态代理类

public class SubjectDynamicProxy extends DynamicProxy{
    public static <T> T newProxyInstance(Subject subject){
        ClassLoader load = subject.getClass().getClassLoader();
        Class<?>[] classes = subject.getClass().getInterfaces();
        InvocationHandler handler = new MyInvocationHandler(subject);
        return (T) Proxy.newProxyInstance(loader, classes, handler);
    }
}

Client 场景类

public class Client {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Subject proxy = SubjectDynamicProxy.newProxyInstance(subject);
        proxy.doSomething("Finish!");
    }
}

这样我们便得到了一个简单的AOP框架。

动态代理弥补了静态代理的缺点,由于代理的是接口,因此动态代理可以代理多个类,并且可以轻松完成公共业务的扩展。

3、优缺点

  • 职责清晰:真实的角色只需要关注实际的业务逻辑,不同关系其他非本职责的事务(例如输出日志等),而是通过后期的代理来完成。
  • 高扩展性:真实主题角色的变化不会影响代理类的使用;可以通过切面轻松完成业务逻辑的扩展。
  • 智能化:动态代理可以实现 ‘智能’ 的接口代理。

4、使用场景

代理模式使用的场景非常多,如果在业务中存在很多的中间过程(例如输出日志、事务管理、执行验证等),就可以用代理模式来实现。类比到现实世界,打官司只管答辩,请律师帮忙搞定中间的复杂步骤;或是租房子只管交房收钱,把中间事宜交给中介进行管理等等,都体现了代理模式的思想。Spring框架中的AOP思想就是基于动态代理模式,详情可参考Spring框架

5、小结

代理模式为控制对象的访问提供了一个有效的方式,在许多框架中都有应用,可以结合框架去仔细体会它的精妙之处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leo木

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值