Dynamic Service Provider Switching with spring-smart-di and AutowiredProxySPI
This article explains how to dynamically switch between multiple service providers in a Spring backend by configuring identifiers, using spring-smart-di's AutowiredProxySPI and related annotations, and provides step‑by‑step code examples for seamless, runtime‑configurable dependency injection.
In real‑world backend development, a single feature often needs to integrate with multiple service providers to avoid downtime and to optimize costs; switching providers quickly is essential for stability and cost control.
The traditional method creates a separate implementation class for each provider and stores the active provider name in a configuration point (e.g., a database or Nacos). Every business call reads this configuration and manually loads the corresponding class.
For example, when integrating several SMS providers, the system can dynamically choose which provider to use based on a configuration value.
Step 1: Define the current provider in a configuration location, such as Nacos or a database. sms.impl = "某腾短信"
Step 2: In the code that sends an SMS, retrieve the provider name and obtain the corresponding implementation class. Example pseudocode:
void sendSmsTouser(Req req) {
// 1、获取当前使用的服务商
String name = get("sms.impl");
// 2、获取对应的实现类
SmsService smsService = springContext.getBean(name);
// 3、使用 smsService 执行具体业务逻辑
smsService.sendMsg(req);
}This manual approach is cumbersome because each call must read the configuration and load the class. A more elegant solution is to let Spring’s @Autowired automatically inject the implementation based on the configuration, updating automatically when the configuration changes. The spring-smart-di library provides AutowiredProxySPI to achieve this.
1. spring-smart-di Overview
spring-smart-di extends Spring’s @Autowired with custom injection logic, offering two key annotations: @SmartAutowired and @AutowiredProxySPI . This article focuses on using AutowiredProxySPI for dynamic provider switching.
2. Quick Start
2.1 Add Dependency
Include the library in your Maven pom.xml :
<dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>spring-smart-di-all</artifactId>
<version>0.2.0</version>
</dependency>2.2 Enable Feature
Annotate a Spring configuration class with @EnableSmartDI to activate the library.
2.3 Use @EnvironmentProxySPI
This annotation marks a configuration point that determines how to obtain the concrete implementation. For the SMS example, annotate the service interface with @EnvironmentProxySPI("${sms.impl}") and store the provider name in a property.
@EnvironmentProxySPI("${sms.impl}")
public interface SmsService {}
@BeanAliasName("某腾短信服务")
@Component
public class ASmsService implements SmsService {}
@BeanAliasName("某移短信服务")
@Component
public class BSmsService implements SmsService {}2.4 Configure Current Provider
Set the active provider in your configuration file (YAML, properties, etc.). The value can be the alias defined by @BeanAliasName , a component name, or a fully‑qualified class name.
sms:
impl: 某移短信服务2.5 Inject with @AutowiredProxySPI
Inject the service just like a normal @Autowired field:
// 依赖注入
@AutowiredProxySPI
private SmsService smsService;The proxy fetches the current implementation at each call, so changing ${sms.impl} takes effect immediately without restarting the application.
2.6 Define Custom Configuration Points
If you need a non‑environment source (e.g., a database), create a custom annotation such as @DBProxySPI and implement a ProxySPI factory:
@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
{
@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}")
public interface SmsService {}With these steps, you can flexibly switch service providers at runtime, and spring-smart-di keeps the code clean, maintainable, and responsive to configuration changes.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.