【设计模式】代理模式:神奇的代理模式,节省了我80%开发时间

什么是代理模式?

代理模式(英语:Proxy Pattern)是程序设计中的一种设计模式。

所谓的代理者是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。

简单点来说,就是在不改变原类的情况下新增新的功能,比如日志、流量监控、请求耗时等等操作都可以通过代理模式来解决。

概念说的比较抽象,举个例子大家可能就会明白了,spring的aop肯定都使用过吧,他是处理日志记录的好手,它就是通过代理模式实现的​。

登录操作

请看下面这份代码

package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: UserService
 * @Author: 流星007
 * @Description: user login or register
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:10
 * @Version: 1.0
 */

public class UserService {


    /**
     * login in
     * @param username
     * @param password
     */
    public Boolean  login(String username,String password){
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return true;
    }
}

这是一个简单的登录功能,参数为:username、password,这样一个登录操作就完成了,那如果我想记录登录操作耗费多长时间呢?该如何处理?直接在login方法中加入计时代码即可,没错,确实可以解决,如果这个UserService是第三方提供的类,以jar包的形式加载在我们项目中,你还能这么修改吗?
下面我就用代理模式来解决一下记录登录耗时这个功能。

静态代理

继承模式的静态代理

代码改造

package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: UserService
 * @Author: 流星007
 * @Description: user login or register
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:10
 * @Version: 1.0
 */

public class UserService {


    /**
     * login in
     * @param username
     * @param password
     */
    public Boolean  login(String username,String password){
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return true;
    }
}



package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: UserServiceProxy
 * @Author: 流星007
 * @Description: user proxy
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:29
 * @Version: 1.0
 */
public class UserServiceProxy extends UserService{


    @Override
    public Boolean login(String username, String password) {
        long startTime = System.currentTimeMillis();
        Boolean login = super.login(username, password);
        long endTime = System.currentTimeMillis();
        System.out.println("login time : " + (endTime - startTime) +" ms" );
        return login;
    }
}



package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: Test
 * @Author: 流星007
 * @Description: test
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:15
 * @Version: 1.0
 */
public class Test {

    public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy();
        Boolean login = userServiceProxy.login("zhgnsan", "123456");
        System.out.println("login result : " +login);
    }
}


username:zhgnsan
password:123456
login time : 0 ms
login result : true

Process finished with exit code 0

UserService:被代理类,UserServiceProxy:代理类,请求先经过代理类,然后由代理类去操作被代理类,最终记录时间,这样就可以在不改变原来代码的情况下新增功能,是不是很方便。

组合模式的静态代理

刚刚讲解了怎么利用继承实现一个静态代理模式,那现在再来讲解另外一种基于组合模式实现静态代理,改造代码开始

package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: IUserService
 * @Author: 流星007
 * @Description: user interface
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:26
 * @Version: 1.0
 */
public interface IUserService {

     Boolean login(String username,String password);
}


package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: UserService
 * @Author: 流星007
 * @Description: user login or register
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:10
 * @Version: 1.0
 */

public class UserService implements IUserService{

    /**
     * login in
     * @param username
     * @param password
     */
    @Override
    public Boolean  login(String username,String password){
        System.out.println("username:"+username);
        System.out.println("password:"+password);
        return true;
    }
}




package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: UserServiceProxy
 * @Author: 流星007
 * @Description: user proxy
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:29
 * @Version: 1.0
 */
public class UserServiceProxy implements  IUserService{

    private UserService userService;

    public UserServiceProxy(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Boolean login(String username, String password) {
        long startTime = System.currentTimeMillis();
        Boolean login = userService.login(username, password);
        long endTime = System.currentTimeMillis();
        System.out.println("login time : " + (endTime - startTime) +" ms" );
        return login;
    }
}



package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: Test
 * @Author: 流星007
 * @Description: test
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:15
 * @Version: 1.0
 */
public class Test {

    public static void main(String[] args) {
        UserServiceProxy userServiceProxy = new UserServiceProxy(new UserService());
        Boolean login = userServiceProxy.login("zhgnsan", "123456");
        System.out.println("login result : " +login);
    }
}

这就是基于组合模式实现的静态代理,代理类和被代理类同时实现一个IUserService接口,代理类中的login方法去调用被代理类中的login方法,这样需要稍微改动一下被代理类,让他们去实现同一个接口。

组合与继承如何选择

静态代理的实现方式相信大家也应该知道怎么实现了,那什么时候实现组合,什么时候使用继承呢?不知道大家有没有听过这么一句话:组合优于继承;能使用组合就少使用继承,原因是继承的层数过大时维护和扩展会非常难受,但是也不绝对,相对于代理模式而言,如果被代理的类来源于第三方,你无法改动原类,那么继承将是一个不错的原则,一般情况下,我还是推荐组合的这种方式。
在这里插入图片描述

动态代理

静态代理是说完了,还有一种代理模式不知道大家听过没有,那就是动态代理模式,如果熟悉java aop的都应该对java的动态代理不会陌生,那我们现在来讲讲什么是动态代理。

不知道大家思考过一个问题没有,你们觉得静态代理有什么缺点吗?
在这里插入图片描述
可能大家想到了,如果被代理的类过多,成千上百的话,代理类也要成千上百,大大增加了程序员的工作量,他们都是差不多的一个模板,难道就不能只写一次,符合所有的被代理类吗?

当然可以,这个时候动态代理就出现了,动态代理:就是我们不需要在程序运行前为被代理类创建代理类,而是在程序运行中,动态的创建代理类,然后使用这些动态生成的代理类替代原始类,是不是就省去了大量的编码时间?那如何实现呢?

下面请看我表演

package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: IUserService
 * @Author: 流星007
 * @Description: user interface
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/29 15:26
 * @Version: 1.0
 */
public interface IUserService {

     Boolean login(String username,String password);
}



package com.ymy.proxy;

import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;

import java.lang.reflect.Method;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: dynamicUserUserProxy
 * @Author: 流星007
 * @Description: dynamic User proxy
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/30 13:33
 * @Version: 1.0
 */
public class DynamicUserUserProxy {

    public Object createProxy(Object object) {
        Class[] interfaces = object.getClass().getInterfaces();
        DynamicProxyHandler handler = new DynamicProxyHandler(object);
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), interfaces, handler);
    }


    private class DynamicProxyHandler implements InvocationHandler {

        private Object object;

        public DynamicProxyHandler(Object object) {
            this.object = object;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            long startTime = System.currentTimeMillis();
            Object result = method.invoke(object, args);
            long endTime = System.currentTimeMillis();
            System.out.println("login time : " + (endTime - startTime) +" ms" );
            return result;
        }
    }

}



package com.ymy.proxy;

/**
 * @ProjectName: demo
 * @Package: com.ymy.proxy
 * @ClassName: DynamicTest
 * @Author: 流星007
 * @Description: Dynamic Test
 * csdn:https://blog.csdn.net/qq_33220089
 * 今日头条:https://www.toutiao.com/c/user/5372182357/#mid=1637641735275523
 * @Date: 2020/8/30 13:36
 * @Version: 1.0
 */
public class DynamicTest {

    public static void main(String[] args) {

        DynamicUserUserProxy proxy = new DynamicUserUserProxy();

        IUserService userService = (IUserService) proxy.createProxy(new UserService());
        Boolean login = userService.login("zhangsan", "123456");
        System.out.println("login result :" +login);
    }
}


username:zhangsan
password:123456
login time : 0 ms
login result :true

Process finished with exit code 0

我这里实现动态代理是基于cglib,由静态代理改成动态代理之后是不是觉得干净多了,如果后续还有类需要代理,就不需要在创建代理类了,让程序在运行中自己创建,简直不要太爽。

代理模式应用场景

1.日志、鉴权、限流、统计等物业功能的开发。
2.rpc远程调用。
3.一个类需要做扩展功能的时候。

总结

什么是代理模式?,就是在不改变原类的情况下新增新的功能,比如日志、流量监控、请求耗时等等操作都可以通过代理模式来解决。

代理模式分为静态代理和动态代理,静态代理可以通过继承和组合实现,动态代理可以通过cglib和jdk自带的动态代理实现,如果在日常开发过程中,你想要做一些扩展功能,比如:日志、鉴权等,那么就可以考虑一下代理模式哦。
在这里插入图片描述
推荐文章:
【设计模式】单例模式

【设计模式】工厂模式:你还在使用一堆的if/else创建对象吗?

【设计模式】建造者模式:你创建对象的方式有它丝滑吗?

【设计模式】原型模式:如何快速的克隆出一个对象?

【设计模式】策略模式:我是一个有谋略的类

【设计模式】观察者模式:一个注册功能也能使用到设计模式?

【设计模式】门面模式:接口就像门面,一眼就能看出你的代码水平

【设计模式】职责链模式:如果第三方短信平台挂了怎么办?

  • 11
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值