How to Dynamically Switch Service Providers in Spring with spring-smart-di
This article explains how to use spring-smart-di to dynamically switch between multiple service providers in a Spring application, reducing manual configuration, improving fault tolerance, and enabling runtime updates without restarting the service.
In real-world system development, a common requirement is to integrate multiple service providers for the same functionality, to avoid single points of failure and to optimize costs.
Traditional approach: implement a class for each provider and configure the active provider via a config (database, Nacos). Each call retrieves the config and invokes the corresponding implementation.
Example with multiple SMS providers: configure a property like sms.impl = "某腾短信", then in code retrieve the property and get the bean.
void sendSmsTouser(Req req) {
// 1. Get current provider name
String name = get("sms.impl");
// 2. Get corresponding implementation bean
SmsService smsService = springContext.getBean(name);
// 3. Use smsService to send message
smsService.sendMsg(req);
}This manual method is cumbersome. spring-smart-di offers a more elegant solution where @Autowired can automatically inject the implementation based on configuration and update when the config changes, using AutowiredProxySPI.
1. Introduction to spring-smart-di
spring-smart-di extends Spring's @Autowired with custom injection logic, providing annotations @SmartAutowired and @AutowiredProxySPI. This article focuses on using AutowiredProxySPI for dynamic provider switching.
2. Quick Start
2.1 Add Dependency
Add the spring-smart-di dependency to 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 spring-smart-di.
2.3 Use @EnvironmentProxySPI
@EnvironmentProxySPI marks a configuration point that determines how to obtain the concrete implementation, e.g., from an environment variable ${sms.impl}.
@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 application.yml:
sms:
impl: 某移短信服务2.5 Inject with @AutowiredProxySPI
Inject the service just like @Autowired:
// Dependency injection
@AutowiredProxySPI
private SmsService smsService;Changing the ${sms.impl} value updates the injected implementation at runtime without restarting the service.
2.6 Define Custom Configuration Points
@EnvironmentProxySPI is for environment variables. To fetch configuration from a database, define a custom annotation like @DBProxySPI using @ProxySPI and implement a factory that retrieves the bean name from the database.
@Inherited
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ProxySPI(DbProxyFactory.class) // specify logic
public @interface DBProxySPI {
String value();
}
@Component
public class 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}")
public interface SmsService {
}spring-smart-di thus provides a concise and efficient way to handle dynamic switching scenarios, making code more flexible and maintainable.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
