动态指定 Spring 容器内接口实现的设计思路
编辑假设你在一个业务类中注入了这样一个 Bean:
@Autowired
private TestService testService;
TestService
是一个接口,假设它有两个实现类:TestServiceImpl1
和 TestServiceImpl2
。现在的问题是,如何根据不同的环境或请求参数,动态选择调用哪个实现类呢?
今天我们就来聊聊如何通过反射和 Spring 注解来实现这个需求。
第一步:设计一个 TestApiSwitcher
我们可以设计一个 TestApiSwitcher
来动态选择实现类:
public TestService getTestService() {
SwitcherInvocationHandler switcherInvocationHandler = new SwitcherInvocationHandler();
return Reflection.newProxy(TestService.class, switcherInvocationHandler);
}
这里用到了 @Bean
注解,它是 Spring 中的一个常用注解,通常用于在配置类中声明一个方法,并将其返回值注册为一个 Bean。而 @Primary
注解则用于指定一个 Bean 作为默认的首选实现。
当有多个同类型的 Bean 需要注入时,Spring 会根据类型匹配进行自动装配。但如果存在多个匹配的 Bean,Spring 会优先选择被 @Primary
注解标记的 Bean。
第二步:实现 SwitcherInvocationHandler
接下来,我们需要实现 SwitcherInvocationHandler
。这个类的作用是根据请求参数动态选择实现类。
@Component
public class TestServiceProxyConfig {
@Autowired
private TestServiceImpl1 testServiceImpl1;
@Autowired
private TestServiceImpl2 testServiceImpl2;
@Bean(name = "testService")
@Primary
public TestService getTestService() {
SwitcherInvocationHandler switcherInvocationHandler = new SwitcherInvocationHandler();
return (TestService) Proxy.newProxyInstance(TestService.class.getClassLoader(),
new Class[] { TestService.class }, switcherInvocationHandler);
}
private class SwitcherInvocationHandler implements InvocationHandler {
private final Set<String> METHODS_ON_OBJECT_CLASS = (Set) Arrays.stream(Object.class.getMethods())
.map(Method::getName).collect(Collectors.toSet());
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (METHODS_ON_OBJECT_CLASS.contains(method.getName())) {
return method.invoke(this, args);
} else {
try {
// 根据 args 进行路由
if (getArgInfo(args) == 1)
return method.invoke(testServiceImpl1, args);
if (getArgInfo(args) == 2)
return method.invoke(testServiceImpl2, args);
if (getArgInfo(args) == 3)
return method.invoke(testServiceImpl1, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
return null;
}
}
private int getArgInfo(Object[] args) {
// 按需实现具体逻辑
int count = 0;
if (args.length > 0 && args[0] instanceof Usr) {
Usr usr = (Usr) args[0];
if (usr.getAge() != null) {
count++;
}
if (usr.getName() != null) {
count++;
}
}
return count;
}
}
在这个实现中,METHODS_ON_OBJECT_CLASS
是一个记录了 Object
类基本方法的集合。SwitcherInvocationHandler
是一个代理类,实现了 InvocationHandler
接口,通过 JDK 动态代理进行实现。在每个方法调用前,都会先调用 invoke
方法。
总结
通过反射和 Spring 注解,我们可以轻松实现根据请求参数动态选择接口实现类的需求。首先,我们需要指定一个 @Primary
的 Bean 作为默认实现,然后在实现中对该 Bean 进行反射,代理接口方法。在具体方法被调用前,根据参数路由到具体的实现类中。
反射就像一把瑞士军刀,配合 Spring 注解,可以实现很多强大的功能。希望这篇文章能帮助你更好地理解如何动态选择 Spring 容器中的接口实现。如果你有任何问题或想法,欢迎在评论区留言讨论!
- 0
- 0
-
分享