⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 网络 「网络」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

在系统开发的实际场景中,我们常常会碰到这样一类需求:同一个功能需要对接多个服务提供商。这么做主要基于两个重要原因。其一,为了规避某个服务商的服务出现不可用的风险,以便在出现问题时能够迅速切换到其他服务商,确保系统的稳定性和业务的连续性;其二,不同服务商的收费标准存在差异,从成本控制的角度出发,需要根据实际情况进行灵活切换。

传统的快速切换逻辑实现方法是,先为每个服务商编写对应的实现类,然后在配置点(这个配置点可以是数据库,也可以是像 Nacos 这样的配置中心)配置当前正在使用的服务商。在每次执行相关业务逻辑时,都要从配置点获取当前使用的服务商信息,再去执行该服务商对应的业务逻辑。

以系统接入多个短信服务商为例,用户可以根据自身需求动态地在不同服务商之间进行切换。下面我们详细看看如果手动实现这个功能,具体步骤是怎样的。

第一步,在某个配置位置(例如 Nacos 或者数据库)配置当前使用的服务商对应的标识值。比如,我们设置 sms.impl = "某腾短信"。

第二步,在代码中执行发短信操作时,手动获取 sms.impl 对应的服务商实现类。以下是相应的伪代码示例:

void sendSmsTouser(Req req) {
// 1、获取当前使用的服务商
String name = get("sms.impl");
// 2、获取对应的实现类
SmsService smsService = springContext.getBean(name);
// 3、使用 smsService 执行具体业务逻辑
smsService.sendMsg(req);
}

不过,这种实现方式存在明显的弊端,它比较繁琐,每次执行都需要手动去获取配置并加载对应的实现类。那么,有没有一种更优雅的方式,让 Spring 的 @Autowired 注解在注入时能够自动根据配置点的配置去注入对应的实现类,并且当配置发生变化时,注入的实现类也能自动更新呢?spring-smart-di 的 AutowiredProxySPI 就是为解决这个问题而精心设计的。

1. spring-smart-di 简介

spring-smart-di 是对 Spring @Autowired 注解的一次创新性扩展,它为用户提供了自定义 Autowired 注入逻辑的能力。目前,它实现了两个非常重要的注解:@SmartAutowired 和 @AutowiredProxySPI。在本文中,我们将重点聚焦于如何使用 AutowiredProxySPI 来实现动态切换服务提供商的功能。

假设我们的系统对接了多个短信服务商,下面我们通过一个快速上手的案例,详细了解如何使用 AutowiredProxySPI 来实现动态切换。

2、快速开始

2.1 引入依赖

首先,我们需要在项目中引入 spring-smart-di 的依赖。在 Maven 项目的 pom.xml 文件中添加以下依赖代码:

<dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>spring-smart-di-all</artifactId>
<version>0.2.0</version>
</dependency>

2.2 启用功能

在 Spring 配置类上标记 @EnableSmartDI 注解,以此来启用 spring-smart-di 的强大功能。

2.3 @EnvironmentProxySPI 注解的使用

@EnvironmentProxySPI 注解代表着一个配置点,其主要作用是配置如何获取具体实现类的逻辑。

假设我们的系统中有两个短信服务商,需要实现动态切换。我们需要在接口上配置 @EnvironmentProxySPI 注解,表示从环境变量配置点中获取当前使用的服务商。这里我们将配置信息存储在属性 ${sms.impl} 中。

@EnvironmentProxySPI("${sms.impl}")
publicinterface SmsService {
}

// 给实现类定义别名
@BeanAliasName("某腾短信服务")
@Component
publicclass ASmsService implements SmsService {
}

@BeanAliasName("某移短信服务")
@Component
publicclass BSmsService implements SmsService {
}

2.4 配置当前使用的服务商

我们可以在配置文件中配置当前使用的服务商。配置的值可以是 @BeanAliasName 注解指定的值,也可以是 @Component 注解指定的值,还可以是具体的全路径类名。

sms:
impl: 某移短信服务

2.5 @AutowiredProxySPI 注入使用

接下来,我们只需要像使用 @Autowired 注解一样使用 @AutowiredProxySPI 注解即可。

// 依赖注入
@AutowiredProxySPI
private SmsService smsService;

通过以上步骤,我们就成功完成了动态切换服务提供商的需求。只要我们改变配置属性 ${sms.impl} 的值,系统就会实时生效,而无需重启服务。这是因为 @AutowiredProxySPI 注入的是一个代理对象,每次执行时会先实时获取当前使用的实现类,然后再执行调用操作。并且,在使用上与直接使用 @Autowired 注解基本没有区别。

2.6 定义不同的配置点

@EnvironmentProxySPI 注解主要用于配置环境变量相关的配置点。如果我们想要自定义配置,例如从数据库中获取配置信息,可以实现自己的 ProxySPI 注解。

下面是一个自定义 DBProxySPI 注解的示例,我们需要标记上 @ProxySPI 注解,并指定具体的配置获取逻辑实现类 AnnotationProxyFactory。

@Inherited
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ProxySPI(DbProxyFactory.class) // 指定配置获取逻辑
public @interface DBProxySPI {
String value();
}

@Component
publicclass DbProxyFactory implements AnnotationProxyFactory<DBProxySPI> {
@Autowired
private SysConfigMapper sysConfigDao;

@Override
public Object getProxy(Class<?> targetClass, DBProxySPI spi) {
// 根据注解从数据库获取要注入的实现类
String configName = sysConfigDao.getConfig(spi.value());
return springContext.getBean(configName);
}
}

@DBProxySPI("${sms.impl}")
publicinterface SmsService {
}

通过以上的步骤,我们就可以灵活地实现动态切换服务提供商的功能,并且可以根据不同的需求自定义配置获取逻辑。spring-smart-di 为我们提供了一种简洁、高效的方式来处理这种动态切换的场景,让我们的代码更加灵活和易于维护。

文章目录
  1. 1. 1. spring-smart-di 简介
  2. 2. 2、快速开始
    1. 2.1. 2.1 引入依赖
    2. 2.2. 2.2 启用功能
    3. 2.3. 2.3 @EnvironmentProxySPI 注解的使用
    4. 2.4. 2.4 配置当前使用的服务商
    5. 2.5. 2.5 @AutowiredProxySPI 注入使用
    6. 2.6. 2.6 定义不同的配置点