Dynamic Service Provider Switching with Spring Smart DI
This article explains how to implement runtime switching of multiple service providers in a Spring‑based backend by configuring the active implementation in a central store and using the spring‑smart‑di library's AutowiredProxySPI to inject the appropriate bean automatically.
In many systems a single feature may need to integrate with multiple service providers, and the ability to switch between them quickly—whether for availability or cost reasons—is essential. Traditionally this is done by storing the current provider name in a configuration source (such as a database or Nacos) and retrieving it at runtime before invoking the provider's logic.
The article first outlines the manual approach: store a key like sms.impl = "某腾短信" in the configuration, read it in code, obtain the corresponding implementation bean from the Spring context, and then call its method.
It then introduces spring‑smart‑di , an extension of Spring's @Autowired that can automatically resolve the implementation based on a configurable proxy. The core annotation used is @AutowiredProxySPI , which works together with @EnvironmentProxySPI to define a configuration point.
1. spring‑smart‑di provides two custom annotations: @SmartAutowired and @AutowiredProxySPI . The latter is used to achieve dynamic switching without changing the injection code.
2. Quick Start
Add the Maven dependency:
<dependency>
<groupId>io.github.burukeyou</groupId>
<artifactId>spring-smart-di-all</artifactId>
<version>0.2.0</version>
</dependency>Enable the feature on a Spring configuration class with @EnableSmartDI .
2.1 @EnvironmentProxySPI marks a configuration point that determines how to obtain the concrete implementation. For example, define the service interface:
@EnvironmentProxySPI("${sms.impl}")
public interface SmsService {}
@BeanAliasName("某腾短信服务")
@Component
public class ASmsService implements SmsService {}
@BeanAliasName("某移短信服务")
@Component
public class BSmsService implements SmsService {}The current provider is set in a YAML file:
sms:
impl: 某移短信服务2.3 @AutowiredProxySPI Injection Use it just like @Autowired :
@AutowiredProxySPI
private SmsService smsService;When the ${sms.impl} value changes, the proxy automatically resolves the new bean without restarting the application.
2.4 Custom Configuration Points If you need a different source (e.g., a database), you can create a custom annotation such as @DBProxySPI backed by a DbProxyFactory that implements AnnotationProxyFactory . The factory reads the configuration from the database and returns the appropriate bean.
@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 {}The article concludes with links to the GitHub repository and Maven Central, noting that the library is still evolving and inviting feedback.
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.