Spring框架中用到的设计模式

装饰者模式

通过将对象封装在一个装饰者对象中,动态地给对象添加新的行为,增强该对象的功能,无需修改其原始代码。比如FileInputStream类和装饰者BufferedInputStream类。

FileInputStream类和装饰者BufferedInputStream类的简单实现如下:

// InputStream 接口定义
public interface InputStream {
    int read();
}

// 具体的 InputStream 实现 - FileInputStream
public class FileInputStream implements InputStream {
    // 实现读取文件的逻辑...
    @Override
    public int read() {
        // 读取文件的具体逻辑
        System.out.println("Reading from a file.");
        return 0;
    }
}

// 装饰者抽象类
public abstract class InputStreamDecorator implements InputStream {
    protected InputStream inputStream;

    public InputStreamDecorator(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() {
        return inputStream.read();
    }
}

// 具体的装饰者 - BufferedInputStream
public class BufferedInputStream extends InputStreamDecorator {
    public BufferedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public int read() {
        // 添加缓存的逻辑
        System.out.println("Adding buffering to InputStream.");
        return super.read();
    }
}
public class Client {
    public static void main(String[] args) {
        // 使用装饰者给 FileInputStream 添加缓存功能
        InputStream fileInputStream = new FileInputStream();
        InputStream bufferedInputStream = new BufferedInputStream(fileInputStream);

        // 通过装饰者链调用
        bufferedInputStream.read();
    }
}

这种设计模式在Java中的InputStream家族就有广泛应用,允许通过组合方式来扩展InputStream的功能,而无需修改基础的输入流实现。

参考:

  1. Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南) 的“装饰者模式”部分
  2. ChatGPT

适配器模式

Spring AOP 中的适配器模式

针对Spring AOP的各种增强或者通知(Advice),使用不同的适配器生成封装了相应Advice的不同Interceptor对象,供容器使用?。

详细直接看:

  1. Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南)的“ Spring AOP 中的适配器模式”部分

  2. 设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn)的“spring AOP中的适配器模式”部分

Spring MVC 中的适配器模式

Spring MVC中,DispatcherServlet 根据请求信息调用 HandlerMapping,解析请求对应的 Handler。再根据Handler找到对应的适配器,再由适配器执行目标Controller中的请求处理方法。

适配器模式的一个简单应用例子:每定义一个(单方法的)Controller类,都需要定义一个实现HandlerAdapter的适配器,该适配器的supports方法可进行Handler是否属于xxController的判断,handle方法可执行Controller类中的请求处理方法。(该例子的具体解释往下看)

为什么要在Spring MVC中用适配器模式?

Spring MVC 中的 Controller 种类众多,不同类型的 Controller 通过不同的方法来对请求进行处理。如果不利用适配器模式的话,DispatcherServlet 直接获取对应类型的 Controller,需要的自行来判断,像下面这段代码一样:

if(mappedHandler.getHandler() instanceof MultiActionController){
   ((MultiActionController)mappedHandler.getHandler()).xxx
}else if(mappedHandler.getHandler() instanceof XXX){
    ...
}else if(...){
   ...
}

假设如果我们增加一个 HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController),这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。

有了适配器模式后,Spring启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet 会通过 handler 的类型找对应适配器(用到DispatcherServlet.getHandlerAdapter()方法), 在找适配器的过程中,会遍历List集合,针对每个适配器,都会调用supports方法进行handler instanceOf xxxController的判断,并将该适配器对象返回给用户,然后就可以统一通过适配器的 handle() 方法来调用 Controller 中的用于处理请求的方法。

每次新定义一个(单方法的)Controller类,就新定义一个相应的适配器,适配器中的supports方法中会添加if(mappedHandler.getHandler() instanceof HardController)的类似代码,handle方法会调用相应Controller的请求处理方法。Spring启动时,只不过是多将一个适配器装入List集合中,不需要改动相应代码。这样,避免了在原有代码上添加if-else语句,只需要新定义Controller类和适配器类这两类即可,满足了修改关闭,扩展开放的开闭原则。

详细参考:

  1. Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南) 的“ Spring MVC 中的适配器模式”部分
  2. 设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn) 的“spring MVC中的适配器模式”部分
  3. Spring MVC适配器的使用例子可见 SpringMVC中的适配器模式 - 写的代码很烂 - 博客园 (cnblogs.com) 的“二、模拟适配器的应用”部分

总结

通过装饰者模式的使用,可以做到不改动原代码的情况下增强对象的功能。

Spring MVC中使用适配器模式,通过适配器使用相应Controller的请求处理方法,避免了在原有代码上添加if-else语句,只需要新定义Controller类和适配器类这两个类即可,满足了修改关闭,扩展开放的开闭原则

Spring AOP中使用适配器模式,通过适配器将一个Advice对象封装转换为相应的拦截器对象,再返回给容器。其中适配器的用法跟Spring MVC中的适配器类似,获取拦截器集合时(使用DefaultAdvisorAdapterRegistry.getInterceptors()方法),也是会遍历适配器集合,然后找到对应的适配器,生成相应的拦截器对象。通过Advice类和相应适配器类,来避免Spring MVC中不使用适配器模式会面临的在原有代码上添加if-else语句的情况,同样满足了修改关闭,扩展开放的开闭原则

总的来讲,适配器模式的使用,减少了if-else这样大量的重复判断,让代码更加易于维护和拓展

参考

Spring 中的设计模式详解 | JavaGuide(Java面试 + 学习指南)

ChatGPT (openai.com)

设计模式 | 适配器模式及典型应用 - 掘金 (juejin.cn)

SpringMVC中的适配器模式 - 写的代码很烂 - 博客园 (cnblogs.com)

重学 Java 设计模式:实战适配器模式「从多个MQ消息体中,抽取指定字段值场景」 | 小傅哥 bugstack 虫洞栈

  • 21
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值