spring-smart-di 动态切换接口源

本文介绍了如何使用spring-smart-di的AutowiredProxySPI注解实现在Spring应用中,根据配置动态切换短信服务商的功能。通过配置环境变量或自定义配置点,实现服务的自动切换,无需重启服务。
摘要由CSDN通过智能技术生成

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.1 配置当前使用的服务商

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

sms:
  impl: 某移短信服务

2.3 @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项目地址

maven仓库地址

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

可以使用Druid数据Spring Boot的多数据支持来实现动态切换MySQL数据。具体步骤如下: 1. 添加Druid和Spring Boot多数据依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.21</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> ``` 2. 配置Druid数据 在application.properties文件中添加以下配置: ```properties # 数据1 spring.datasource.druid.datasource-1.url=jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.druid.datasource-1.username=root spring.datasource.druid.datasource-1.password=123456 spring.datasource.druid.datasource-1.driver-class-name=com.mysql.cj.jdbc.Driver # 数据2 spring.datasource.druid.datasource-2.url=jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false spring.datasource.druid.datasource-2.username=root spring.datasource.druid.datasource-2.password=123456 spring.datasource.druid.datasource-2.driver-class-name=com.mysql.cj.jdbc.Driver ``` 3. 配置多数据 在application.properties文件中添加以下配置: ```properties # 数据1对应的JPA配置 spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect spring.datasource.druid.datasource-1.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.datasource-1.initial-size=5 spring.datasource.druid.datasource-1.min-idle=5 spring.datasource.druid.datasource-1.max-active=20 spring.datasource.druid.datasource-1.filters=stat,log4j # 数据2对应的JPA配置 datasource2.jpa.hibernate.ddl-auto=update datasource2.jpa.show-sql=true datasource2.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect spring.datasource.druid.datasource-2.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.datasource-2.initial-size=5 spring.datasource.druid.datasource-2.min-idle=5 spring.datasource.druid.datasource-2.max-active=20 spring.datasource.druid.datasource-2.filters=stat,log4j # 多数据配置 spring.datasource.dynamic.primary=datasource-1 spring.datasource.dynamic.datasource-1=druid.datasource-1 spring.datasource.dynamic.datasource-2=druid.datasource-2 ``` 4. 编写动态数据切换代码 在需要使用多数据的地方,通过以下代码来动态切换数据: ```java // 获取数据 DynamicDataSourceContextHolder.setDataSourceKey("datasource-1"); DataSource dataSource = DynamicDataSource.getInstance().getDataSource(); // 使用数据 JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); List<Map<String, Object>> result = jdbcTemplate.queryForList("select * from user"); // 切换数据 DynamicDataSourceContextHolder.setDataSourceKey("datasource-2"); dataSource = DynamicDataSource.getInstance().getDataSource(); jdbcTemplate = new JdbcTemplate(dataSource); result = jdbcTemplate.queryForList("select * from user"); ``` 以上就是使用Druid和Spring Boot多数据支持来实现动态切换MySQL数据的步骤。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值