Skip to content

代理模式对服务进行扩展

概述

代理模式的核心思想是通过引入一个代理对象来间接访问另一个对象,从而可以在访问这个对象时添加一些额外的控制逻辑,比如权限验证、缓存、延迟加载等。代理模式可以帮助我们在不改变原始对象的情况下,对其进行控制和扩展。从描述上看代理模式跟适配器模式有点像, 但是他们实际上是不一样的,装饰模式主要的目的是在原有功能的基础上扩展新的功能。而代理模式则不同,代理模式主要是控制对原有功能的访问。一句话说明白区别就是:装饰模式最后一定会调用原有功能接口,而代理模式可能不会调用原有功能接口。在 spring 中,AOP 就是利用了动态带来来实现的,这篇我们主要来讲静态代理在业务场景的应用。

案例

在大健康康养系统初期,用户经常会点击反复查看睡眠报告,开始用户体量并不大,我们都是直接从数据库中查询的,后面测试压测希望提这个接口的吞吐量,那么就可以采用代理模式引入缓存实现该功能。

定义服务接口

java
public interface SleepReportService {
    String report(String userId);
}

实现默认的服务

java
@Service
@Slf4j
public class DefaultSleepService implements SleepReportService{
    @Override
    public String report(String userId) {
        log.info("从数据库中查询");
        return String.format("userId=%s的睡眠报告",userId);
    }

}

代理类

java
@Service
@Slf4j
public class SleepReportServiceProxy implements SleepReportService{

    private final SleepReportService defaultSleepService;

    private final RedisTemplate redisTemplate;

    private boolean enableCache = true; // 这里也可以配置到配置中心去,进行动态控制,当前就写死了

    public SleepReportServiceProxy(SleepReportService defaultSleepService, RedisTemplate redisTemplate) {
        this.defaultSleepService = defaultSleepService;
        this.redisTemplate = redisTemplate;
    }

    @Override
    public String report(String userId) {
        if (enableCache) {
            String key = "SLEEP_REPORT:" + userId;
            String report = (String)redisTemplate.opsForValue().get(key);
            if (!StringUtils.hasText(report)) {
                // 缓存没有那么就通过原服务类去获取
                String dbReport = defaultSleepService.report(userId);
                redisTemplate.opsForValue().set(key,dbReport,10, TimeUnit.SECONDS);
                return dbReport;
            }
            log.info("从缓存中返回");
            return report;
        }
        return defaultSleepService.report(userId);
    }
}

测试类

java
    @Autowired
    private SleepReportService defaultSleepService;

    @Autowired
    private SleepReportService sleepReportServiceProxy;
    @Test
    public void testProxy() throws InterruptedException {
        String report = defaultSleepService.report("1");
        System.out.println(report);

        String report1 = sleepReportServiceProxy.report("1");
        System.out.println(report1);

        String report2 = sleepReportServiceProxy.report("1");
        System.out.println(report2);

        Thread.sleep(11 * 1000);

        String report3 = defaultSleepService.report("1");
        System.out.println(report3);
    }

输出

text
2024-08-29 18:49:54.805  INFO 7616 --- [           main] c.s.d.p.s.proxy.DefaultSleepService      : 从数据库中查询
userId=1的睡眠报告
2024-08-29 18:49:55.077  INFO 7616 --- [           main] c.s.d.p.s.proxy.DefaultSleepService      : 从数据库中查询
userId=1的睡眠报告
2024-08-29 18:49:55.091  INFO 7616 --- [           main] c.s.d.p.s.proxy.SleepReportServiceProxy  : 从缓存中返回
userId=1的睡眠报告
2024-08-29 18:50:06.093  INFO 7616 --- [           main] c.s.d.p.s.proxy.DefaultSleepService      : 从数据库中查询
userId=1的睡眠报告

Released under the MIT License.