设计模式Java实现-代理模式

147 阅读5分钟

✨这里是第七人格的博客✨小七,欢迎您的到来~✨

🍅系列专栏:设计模式🍅

✈️本篇内容: 代理模式✈️

🍱本篇收录完整代码地址:gitee.com/diqirenge/d… 🍱

楔子

如果你是JavaWeb开发者,那代理模式对你一定不陌生。为什么这么说呢?因为Spring框架里大量使用了代理模式,而JavaWeb开发,很大程度上就是面向Spring编程,但是虽然用到了代理模式,但是我们却不自知,甚至让我们自己实现一个简单的代理,我们也完成不了。

摸屁股.gif

需求背景

我们有一个网关系统,在接收到外部请求的时候,需要对http请求进行鉴权解密,并且转为dubbo协议,然后调用下层业务系统,现在让你来设计一下这个系统。

分析设计

我们简单拆分一下需求,其实大的功能就2点:

1、在调用真实的方法前,我们需要先鉴权解密

2、http协议转dubbo协议

针对鉴权解密,你可能第一个想到的就是AOP,使用切面来完成,可以当然可以。但是为了加深我们对代理模式的理解,这里我们不使用第三方包,直接用Java来编写一层代理。

针对协议转换,像dubbo这种rpc框架,一般都会提供泛化调用的扩展点,我们只需要调用对应的方法即可,但是可以多考虑一点,比如后期要接入其他的rpc框架怎么办?这里我们可以做简单一点,结合上面的代理类,做一个代理工厂,方便外部调用。

泛化调用是一种不依赖服务提供方客户端JAR包的RPC调用方式,它允许中间节点在没有接口信息的情况下传递调用信息。

UML图

根据分析设计,我们可以先画一个简单的UML图,后面通过UML图编码

GenericProxy.png

模块名称

flyweight

模块地址

gitee.com/diqirenge/d…

模块描述

代理模式代码示例

代码实现

1、抽象一个泛化调用接口

/**
 * 代理模式 - 抽象出来的接口
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/12/21
 */
public interface GenericService {
    Object genericMethod(String methodName, Object[] args);
}

2、编写被代理的类

实现我们的泛化接口

/**
 * 代理模式 - 被代理的类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/12/21
 */
public class DubboServiceImpl implements GenericService {
    @Override
    public Object genericMethod(String methodName, Object[] args) {
        // 在这里实现具体的业务逻辑
        System.out.println("[" + methodName + "]调用 Dubbo 服务成功");
        return null;
    }
}

3、编写代理类

这里需要实现我们的代理逻辑

/**
 * 代理模式 - 代理类
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/12/21
 */
public class GenericProxy implements GenericService {
    private GenericService target;

    public GenericProxy(GenericService target) {
        this.target = target;
    }

    @Override
    public Object genericMethod(String methodName, Object[] args) {
        // 在这里实现代理逻辑,例如日志记录、性能监控、鉴权解密等
        System.out.println("[" + methodName + "]进入到代理类中,开始执行代理逻辑...");
        System.out.println("[" + methodName + "]代理逻辑:鉴权");
        System.out.println("[" + methodName + "]代理逻辑:解密");
        System.out.println("[" + methodName + "]代理逻辑执行完毕,开始执行目标方法...");
        return target.genericMethod(methodName, args);
    }
}

4、编写代理工厂

这个代理工厂只是一个示例,真正生产当中,一般都是通过url来的

/**
 * 代理模式 - 代理工厂
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/12/21
 */
public class GenericProxyFactory {

    GenericService genericService;

    public GenericProxyFactory(String methodName) {
        this.genericService = new DubboServiceImpl();
    }

    private final Map<String, GenericService> genericReferenceCache = new ConcurrentHashMap<>();

    public GenericService newInstance(String methodName) {
        System.out.println("[" + methodName + "]通过代理工厂创建代理");
        return genericReferenceCache.computeIfAbsent(methodName, k -> new GenericProxy(genericService));
    }

}

5、编写测试类

/**
 * 代理模式
 * 关注公众号【奔跑的码畜】,一起进步不迷路
 *
 * @author 第七人格
 * @date 2023/12/21
 */
public class ProxyTest {
    @Test
    public void test_proxy() {
        String methodName = "querySomething";
        new GenericProxyFactory(methodName)
                .newInstance(methodName)
                .genericMethod(methodName, new Object[]{});
    }
}

4、测试结果

[querySomething]通过代理工厂创建代理

[querySomething]进入到代理类中,开始执行代理逻辑...

[querySomething]代理逻辑:鉴权

[querySomething]代理逻辑:解密

[querySomething]代理逻辑执行完毕,开始执行目标方法...

[querySomething]调用 Dubbo 服务成功

实现要点

代理模式的结构相对简单,它主要包含以下角色:

1、抽象主题(Subject)类,例子中为GenericService

2、真实主题(Real Subject)类,例子中为DubboServiceImpl

3、代理主题(Proxy)类,例子中为GenericProxy

根据创建代理的方式,代理模式可以被分为静态代理和动态代理。其中,静态代理是由程序员在编译期间就已经完成代理类的创建,而动态代理则是在程序运行期间通过Java反射机制动态生成代理对象。动态代理又可以细分为JDK提供的动态代理和CGLIB动态代理,JDK实现的代理和被代理者都需要实现接口,本质是面向接口编程,而CGLib则可以不需要接口。

总结

本文从网关需求出发,使用静态代理的方式,为读者实现了需求,并总结了实现要点。读者可以尝试使用动态代理改写代码,多写多练,才能把知识变成自己的。