Spring Boot中适配器模式的实现方式

在 Spring Boot 项目开发中,经常会遇到现有接口与业务所需接口不兼容、第三方组件 API 无法直接对接业务逻辑的场景,适配器模式能完美解决这类接口适配问题。同时 Spring Boot 的依赖注入和 Bean 管理机制,可便捷地实现适配器的注册、管理与调用,让接口适配的代码更优雅、易维护。

当你的业务满足以下至少 2 条时,就可以考虑使用适配器模式:

  1. 现有接口与目标接口定义不一致,无法直接调用;
  2. 需要对接多个第三方组件,各组件 API 风格不同但业务逻辑相似;
  3. 希望在不修改原有代码的前提下,复用已有接口实现;
  4. 后续可能新增更多外部组件 / 接口,需要统一对接规范;
  5. 需要对原有接口进行包装,屏蔽底层实现细节。

1. 适配器模式定义

  • 适配器模式(Adapter Pattern)是一种结构型设计模式,它的核心是将一个类的接口转换成客户希望的另一个接口,使原本由于接口不兼容而无法一起工作的那些类可以协同工作。
  • 核心思想:通过引入适配器类,作为两个不兼容接口之间的桥梁,实现接口的转换和兼容,让原有代码无需修改即可适配新的业务需求。
  • 适配器模式主要分为类适配器(通过继承实现)和对象适配器(通过组合实现),在 Java 中因单继承特性,对象适配器使用更为广泛,本文也将以对象适配器为核心实现。

2. 定义适配器接口

  • 为了方便切换任何一个oss,将公共方法抽取为接口,由某个oss的实现类去编写具体逻辑
java 复制代码
public interface StorageAdapter {
    /**
     * 创建bucket
     */
    void createBucket(String bucket);

    /**
     * 上传文件
     */
    void uploadFile(MultipartFile multipartFile, String bucket, String objectName);

    /**
     * 获取文件在oss中的url
     */
    String getUrl(String bucket, String objectName);
}

3. 实现适配器类

  • Minio适配器类:通过继承或者组合方式,将被适配者类(minioUtils)的接口与适配器接口转换起来,使得客户端可以按照适配器接口进行操作。
java 复制代码
@Slf4j
public class MinioStorageAdapter implements StorageAdapter {

    @Resource
    private MinioUtil minioUtil;

    @Value("${minio.url}")
    private String url;

    @Override
    @SneakyThrows
    public void createBucket(String bucket) {
        minioUtil.createBucket(bucket);
    }

    /**
     * 上传文件
     */
    @Override
    @SneakyThrows
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        minioUtil.createBucket(bucket);
        if(objectName != null) {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, objectName + "/" + multipartFile.getOriginalFilename());
        } else {
            minioUtil.uploadFile(multipartFile.getInputStream(), bucket, multipartFile.getOriginalFilename());
        }
    }

    /**
     * 获取文件在oss中的url
     */
    @Override
    public String getUrl(String bucket, String objectName) {
        return url + "/" + bucket + "/" + objectName;
    }
}
  • Aliyun适配器类
java 复制代码
/**
 * 阿里云oss 具体实现逻辑
 */
public class OssStorageAdapter implements StorageAdapter {

    @Override
    public void createBucket(String bucket) {
        System.out.println("oss");
    }

    @Override
    public void uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
    }

    @Override
    public String getUrl(String bucket, String objectName) {
        return"oss";
    }
}

4. 定义StorageConfig类

  • 如果想再加入一个新的OSS对象,只需新增一个xxadapter适配器类且在@Bean注解的方法中加一个else即可。

  • 注意:这里直接使用new的方式创建实现类(实现类也不需要使用@Service注解),而不是先把所有的实现类通过注解定义出来,再直接返回对象,这样如果新增一个OSS的话,不光要加else,还需再把实现类通过直接定义出来。

java 复制代码
@Configuration
public class StorageConfig {

    @Value("${storage.service.type}")
    private String storageType;

    @Bean
    public StorageAdapter storageAdapter() {
        if ("minIo".equals(storageType)) {
            return new MinioStorageAdapter();
        } else if("oss".equals(storageType)) {
            return new OssStorageAdapter();
        } else {
            throw new IllegalArgumentException("为找到对应的文件存储处理器");
        }
    }
}

5. 使用适配器工厂处理请求

java 复制代码
@Service
public class FileService {

    @Autowired
    private StorageAdapter storageAdapter;

    /**
     * 创建bucket
     */
    public void createBucket(String bucket) {
        storageAdapter.createBucket(bucket);
    }

    /**
     * 上传图片、返回图片在MinIo的地址
     */
    public String uploadFile(MultipartFile multipartFile, String bucket, String objectName) {
        storageAdapter.uploadFile(multipartFile, bucket, objectName);
        objectName = (StringUtils.isEmpty(objectName) ? "" : objectName + "/") + multipartFile.getOriginalFilename();
        return storageAdapter.getUrl(bucket, objectName);
    }
}
java 复制代码
@RestController
@Slf4j
public class FilesController {
    @Resource
    private FileService fileService;

    /**
     * 上传文件, 返回文件在oss中的地址
     * @param uploadFile:文件,getOriginalFilename获取原始文件名
     * @param bucket:桶名称
     * @param objectName:上传后的文件在存储桶中的存储路径(存储目录)
     */
    @PostMapping("/upload")
    public ResponseEntity<String> upload(MultipartFile uploadFile, String bucket, String objectName) {
        try {
            Preconditions.checkArgument(!ObjectUtils.isEmpty(uploadFile), "文件不能为空");
            Preconditions.checkArgument(!StringUtils.isEmpty(bucket), "bucket桶名称不能为空");
            if (log.isInfoEnabled()) {
                log.info("FileController.upload.uploadFile:{}, bucket:{}, objectName:{}", uploadFile.getOriginalFilename(), bucket, objectName);
            }
            String url = fileService.uploadFile(uploadFile, bucket, objectName);
            return ResponseEntity.ok(url);
        } catch (Exception e) {
            log.info("FileController.upload.error:{}", e.getMessage(), e);
            return ResponseEntity.ok("上传文件失败");
        }
    }
}

6. 适配器模式优势

  • 接口兼容:解决了现有接口与目标接口不兼容的问题,让原本无法协同工作的类可以一起工作;
  • 开闭原则:新增适配者(如新增银联支付回调)时,只需新增对应的适配器类,无需修改原有业务代码;
  • 代码复用:无需修改原有适配者代码,直接复用现有接口实现,符合迪米特法则和开闭原则;
  • 解耦业务与实现:客户端只需调用统一的目标接口,屏蔽了底层不同适配者的实现细节;
  • 扩展性强:可灵活对适配器进行包装,增加日志、缓存、异常处理等通用逻辑,不影响原有代码。

7. 典型应用场景

  • 第三方组件 / SDK 接口对接(如支付、短信、物流接口的统一适配);
  • 老旧系统接口改造,在不修改原有代码的前提下适配新的业务接口;
  • 多个异构系统集成,统一对外提供标准化接口;
  • 框架之间的整合,适配不同框架的 API 规范;
  • 自定义接口包装原生 JDK / 框架接口,简化业务调用。
相关推荐
baizhigangqw2 小时前
SpringBoot中整合ONLYOFFICE在线编辑
java·spring boot·后端
pangares2 小时前
Spring Boot文件上传
java·spring boot·后端
波波七2 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
book123_0_992 小时前
Spring boot创建时常用的依赖
java·spring boot·后端
014-code2 小时前
Kafka + Spring Boot 实战入门
java·spring boot·kafka·消息队列
qwert10372 小时前
Spring Boot从0到1 -day02
java·spring boot·后端
灰阳阳2 小时前
Spring Boot+Nginx+MySQL容器化实战
spring boot·mysql·nginx
召田最帅boy3 小时前
为博客每日一句添加音频播放功能
spring boot·html·音视频
小箌3 小时前
springboot_02
java·spring boot·后端