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

摘要: 原创出处 juejin.cn/post/7342279484487270450 「李白的手机」欢迎转载,保留摘要,谢谢!


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

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

0、背景

一般我们系统同一个功能可能会对接多个服务商,防止某个服务商的服务不可用快速切换或者收费不同需要切换,那我们一般做快速切换逻辑传统无非就是先将每个服务商实现,然后在配置点(数据库或者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 @Autowired注解的扩展,能够自定义用户自己的Autowired注入逻辑,目前实现了两个功能分别是 @SmartAutowired 和 @AutowiredProxySPI 注解,我们这里要使用的便是AutowiredProxySPI 去实现我们的动态切换逻辑。

假设对接了多个短信服务商,下面以一个快速开始案例去看看如何使用 AutowiredProxySPI 如何实现动态切换

2、快速开始

引入依赖

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

然后在Spring配置类上标记@EnableSmartDI 注解 启用功能

2.1 @EnvironmentProxySPI注解

@EnvironmentProxySPI代表的是一个配置点,用来配置怎么获取具体实现类的逻辑。

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

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

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

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

2.2 配置当前使用的服务商

比如在配置文件中配置。这里的值可以是@BeanAliasName的值,也可以是@Component的值,也可以具体的是全路径类名。

sms:
impl: 某移短信服务

2.3 @AutowiredProxySPI注入使用

然后我们只需要像使用@Autowired注解一样去使用 @AutowiredProxySPI注解即可。

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

这样我们就完成了一个动态切换需求,只要我们改变配置配置属性${sms.impl}的值就会实时生效而无需重启服务,因为他注入的是一个代理对象每次执行时会先去实时获取当前使用的实现类然后才去执行调用, 并且在使用上与原先直接使用@Autowired基本毫无区别

2.4 定义不同的配置点

@EnvironmentProxySPI是用来配置环境变量相关的配置点,如果想要自定义配置比如在数据库中可实现自己的ProxySPI注解。

比如自定义DBProxySPI注解,并标记上@ProxySPI实现并指定具体配置获取逻辑实现类AnnotationProxyFactory即可。

然后DBProxySPI就可以像@EnvironmentProxySPI一样去使用了,下面是实现的伪代码

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

String value();

}

@Component
public class DbProxyFactory implements AnnotationProxyFactory<DBProxySPI> {

@Autowired
private SysConfigMapper sysConfigDao;

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


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

其他

GitHub项目地址

https://github.com/burukeYou/spring-smart-di

Maven仓库地址

https://central.sonatype.com/artifact/io.github.burukeyou/spring-smart-di-all

还在迭代中,第一个版本主要专注于动态切换, 还需要什么好玩的功能和特征请留下你的意见感谢。觉得有用或者能学到点什么可以star下哈哈。

文章目录
  1. 1. 0、背景
  2. 2. 1、spring-smart-di
  3. 3. 2、快速开始
    1. 3.1. 2.1 @EnvironmentProxySPI注解
    2. 3.2. 2.2 配置当前使用的服务商
    3. 3.3. 2.3 @AutowiredProxySPI注入使用
    4. 3.4. 2.4 定义不同的配置点
  4. 4. 其他