代理设计模式

代理设计模式

在实际的开发中,我们经常使用代理模式。在这主要讲述代理模式的两种实现方式:基于接口的实现方式和基于继承的实现方式以及动态代理。

基于接口的代理设计模式

代理模式(Proxy Design Pattern):在不改变原始类的情况下,通过引入代理类来给原始类附加不相关的其他功能。例如:下面的示例代码,MetricsCollector类用来收集接口请求的性能数据,处理时长等。在业务系统中,通过下面这种设计方式来使用MetricsCollector类。

public class UserController{
    //...省其他的属性和方法
    private MetricsCollector metricsCollector; //依赖注入

    public UserVo login(String telePhone , String password)
    {
        long startTimeStamp = System.currentTimeMillis();
        //省略login()方法逻辑
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("login",responseTime,startTimeStamp);
        metricsCollector.recordRequest(requestInfo);
        //返回UserVo 数据;
    }

    public UserVo register(String telePhone , String password)
    {
        long startTimeStamp = System.currentTimeMillis();
        //省略register()方法;
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("register" , responseTime , startTimeStamp);
        metricsCollector.recordRequest(requestInfo);
        //返回UserVo数据;
    }
}

上面的代码存在着一些问题。第一个问题:性能统计代码“侵入”业务代码,与业务代码高度耦合。如果未来要替换MetricsCollector类,那么替换成本会比较大。第二个问题:性能统计代码与业务代码无关。

为了将性能统计代码与业务代码解耦。我们定义一个代理类UserControllerProxy,它与原始UserController实现了相同的接口IUserController。Usercontroller只负责业务代码。代理类UserControllerProxy负责在业务代码执行前后,附加性能统计的代码。并通过委托的方式来调用原始类执行业务代码。大致的设计代码如下所示:

public interface IUserController{
    UserVo login(String telePhone , String password);
    UserVo register(String telePhone , String password);
}
public class UserController implements IUserController{
    //......省略其他的属性和方法
    @Override
    public UserVo login(String telePhone , String password){
    // ......省略login()函数的逻辑
    }
    @Override
    public UserVo register(String telePhone , Strin password){
    //.......省略register()函数的逻辑
    }
}

public class UserControllerProxy implements IUserController{

    private UserController userController;
    private MetricsCollector metricsCollector;
    public UserControllerProxy(UserController userController){
        this.userController = userController;
        this.metricsCollector = new MetricsCollector();
    }
    @Override
    public UserVo login(String telephone,String password){
        long startTimeStamp = System.currentTimeMillis();
        UserVo userVo = userController.login(telePhone , password);
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("login",responseTime,startTimeStamp);
        metricsCollector.recordRequest(requestInfo);

        return userVo;
    }

    @Override
    public UserVo register(String telePhone , String password){
        long startTimeStamp = System.currentTimeMillis();
        UserVo userVo = userController.register(telePhone , password);
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("register",responseTime,startTimeStamp);
        metricsCollector.recordRequest(requestInfo);

        return userVo;
    }
}

UserControllerProxy类的使用方式如下面所示。因为原始类和代理类实现相同的接口,在编写代码时,我们基于接口而非实现编程,因此将UserController类对象替换为UserControllerProxy对象,不需要改动太多代码。

基于继承实现代理模式

在基于接口实现的代理模式中,原始类和代理类实现相同的接口,但是如果原始类没有定义接口,并且原始类并不是由我们开发和维护的。例如,它来自于一个第三方的类库,那么,我们就没办法直接修改原始类,也就是无法给他重新定义一个接口。对于这种情况,我们可以采用继承的方式,对外部类进行扩展。让代理类先继承原始类,再扩展附加功能。具体的代码实现如下所示:

public class UserControllerProxy extends UserController{
    private MetricsCollector metricsCollector;
    public UserControllerProxy(){
        this.metricsCollector = new MetricsCollector();
    }
    public UserVo login(String telePhone , String password){
        long startTimeStamp = System.currentTimeMillis();
        UserVo userVo = super.login(telePhone , password);
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("login",responseTime,startTimeStamp);
        metricsCollector.recordRequest(requestInfo);
        return userVo;
    }

    public UserVo register(String telePhone , String password){
        long startTimeStamp = System.currentTimeMillis();
        UserVo userVo = userController.register(telePhone , password);
        long endTimeStamp = System.currentTimeMillis();
        long responseTime = endTimeStamp - startTimeStamp;

        RequestInfo requestInfo = new RequestInfo("register",responseTime,startTimeStamp);
        metricsCollector.recordRequest(requestInfo);
        return userVo;
    }
}

基于反射实现动态代理

不过上面代码依旧,存在着一些问题,一方面在代理类中,我们将原始类中的所有方法全都实现一遍,并且为每个方法附加相似的代码逻辑。另一方面,如果有很多的类需要添加附加功能,我们需要针对很多类创建一个代理类。并且每个代理类的代码又很相似,没必要重复的开发。对于这个问题我们可以利用动态代理来解决这个问题。动态代理是指不事先为每个原始类编写代理类,而是在代码运行时,动态地为原始类创建代理类,并用代理类替换代码中的原始类。

对于Java语言,本身就已经提供了动态代理语法(底层依赖Java反射语法)。我们使用Java的动态代理实现之前的性能统计的例子。代码实现如下所示,其中MetricsCollectorProxy是动态代理类,动态地为每个需要统计性能的类创建代理类。

public class MetricsCollectorProxy{
    private MetricsCollector metricsCollector;

    public MetricsCollectorProxy(MetricsCollector metricsCollector){
        this.metricsCollector = metricsCollector;
    }

    public Object createProxy(Object proxiedObject){
        Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamcProxyHandler(proxiedOject);
        return Proxy.newProxyInstance(proxiedObject.getClass().getClassLoader(),interfaces,handler);
    }

    private class DynamicProxyHandler implements InvocationHandler{
        private Object proxiedObject;
        
        public DynamicProxyHandler(Object proxiedObject){
            this.proxiedObject = proxiedObject;
        }
        
        @Override
        public Object invoke(Object proxy , Method method , Object[] args) throw Throwable{
            long startTimeStamp = System.currentTimeMillis();
            Object result = method.invoke(proxiedObject , args);
            long endTimeStamp = System.currentTimeMillis();
            long responseTime = endTimeStamp - startTimeStamp;
             
            String apiName = proxiedObject.getClass().getName()+":"+method.getName();   
            RequestInfo requestInfo = new RequestInfo(apiName,responseTime,startTimeStamp);
            metricsCollector.recordRequest(requestInfo);
            return result;
        }
    }
}



//使用举例
MetricsCollectorProxy proxy = new MetricsCollectorProxy();
IUserController userController = (IUserController)proxy.createProxy(new UserController());

实际上,SpringAOP底层实现原理就是基于动态代理。

代理模式的各种应用场景

代理模式的基本功能是通过创建代理类为原始类附加不相关的其他功能。代理模式的应用场景非常的多;

  1. 非业务需求开发

  2. RPC中的应用

  3. 缓存中的应用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值