一、背景
在我们之前的文章产品SDK化转型:标准化与机构个性化定制解决方案中,我们探讨了一种基于SDK的灵活架构设计,旨在协调产品迭代与定制化功能之间的矛盾,并且具备良好的可维护性和可扩展性。
然而,在实际开发中,我们面临一个亟待解决的关键问题:即在机构定制化过程中,必须涉及对SDK内部进行改造的情况。举例来说,假设SDK中提供了用户密码加密的 /user/encryption
接口,默认的加密算法是MD5,但南京银行需要使用HASH算法进行加密。在这种情况下,我们既不能更改 /user/encryption
接口的默认实现,也不能直接修改SDK中的代码,因为这样做会影响其他合作机构的使用。因此,我们需要一种成本低、实现优雅的解决方案。
本文的目的在于提供针对这类问题的解决方案。
以下示例均可在 GitHub#inject-sdk 仓库上找到。
二、解决方案
针对这种常见情况,我们提供了两种解决方案:
- JDK中的SPI机制:通过Service Provider Interface(SPI)机制,实现动态加载。这种方法允许我们在运行时动态地发现和加载服务实现,为定制化提供了灵活的解决方案。
- Spring 的@Conditional条件注解:通过Spring自身提供的
@Conditional
条件注解,根据配置文件将实现注入。这种方式使得我们可以根据特定的配置条件选择性地加载和使用不同的实现,以满足各种定制化需求。
为了便于阅读后续内容,建议读者先阅读以下两篇文章先了解这两种方案涉及到的基本概念:
三、实现前提
基于以上两种解决方案,无论选择哪种都有一个实现的前提:需要将SDK模块中的具体操作层抽象成接口,即全面面向接口编程。
- 以上面的举例来说,SDK的
/user/encryption
接口,在其控制层(controller)中的代码如下所示:
java
复制代码
@Slf4j @RestController @RequestMapping(value = "/user") public class UserController { @Autowired private UserEncryptionService userEncryptionService; @GetMapping("/encryption") public void encryption(String password) { log.info("userEncryptionService: {}", userEncryptionService); userEncryptionService.encryptPassword(password); } }
- 在上述代码中,可以看到,控制层调用了
userEncryptionService
接口,该接口的内容如下:
java
复制代码
/** * 用户加密服务接口 */ public interface UserEncryptionService { /** * 将用户输入的密码进行加密处理 * @param password 用户输入的密码 * @return 加密后的密码 */ String encryptPassword(String password); }
- SDK 提供了一个默认的 MD5 加密实现类
DefaultUserEncryptionServiceImpl
,代码如下:
java
复制代码
import org.springframework.stereotype.Service; /** * 默认的用户加密服务实现类,使用 MD5 加密 */ @Service public class DefaultUserEncryptionServiceImpl implements UserEncryptionService { @Override public String encryptPassword(String password) { try { MessageDigest md = MessageDigest.getInstance("MD5"); md.update(password.get