Spring通过入参选择不同的Service
在有多个实现类的情况,想要根据入参来判断具体执行的实现类。
比如根据入参code不同,采用不同的实现类去执行。
可以使用Spring
中的FactoryBean
来实现。
简单实现
@Component
public class QueryServiceFactory {
@Resource
List<QueryService> beanList;
private static Map<StatusEnum, QuerySerStatusice> beanMap;
@PostConstruct
public void afterPropertiesSet() {
beanMap = new HashMap<>(16);
beanList.forEach((bean) -> beanMap.put(bean.getType(), bean));
}
public static <T extends QueryService> T getService(StatusEnum status) {
return (T) beanMap.get(code);
}
}
这种写法比较简单易懂,但是每次调用之前都需要获取一下实现类。
基于FactoryBean实现
@Primary
@Component
public class QueryServiceFactory implements FactoryBean<QueryService> {
@Resource
List<QueryService> beanList;
@Override
public QueryService getObject() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InvocationHandler invocationHandler = new QueryServiceInvocationHandler();
return (QueryService)Proxy.newProxyInstance(loader, new Class[] {QueryService.class}, invocationHandler);
}
class QueryServiceInvocationHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String firstParam = this.fetchFirstParam(args, method);
QueryService bean = this.fetchBean(firstParam);
try {
return method.invoke(bean, args);
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
/**
* 获取用来判断具体执行bean的入参,这里举例获取第一个参数
*/
private String fetchFirstParam(Object[] args, Method method) {
Object firstArg = args[0];
if (firstArg instanceof String) {
return (String)firstArg;
} else {
throw new RuntimeException("ERROR FIRST_ARGUMENT_TYPE:" + firstArg.getClass().getName()
+ " FOR METHOD: " + method.getName());
}
}
/**
* 使用拿到的参数来判断具体执行bean,这里只是简单用bean的名称来区分了,其实可以做任意扩展
*/
private QueryService fetchBean(String firstParam) {
for (QueryService eachBean : beanList) {
if (eachBean.getClass().getName().equals(firstParam)) {
return eachBean;
}
}
throw new RuntimeException("NO SERVICE BEAN FOR firstParam: " + firstParam);
}
}
@Override
public Class<QueryService> getObjectType() {
return QueryService.class;
}
}
具体的实现类:
public interface QueryService {
public void print(String beanClassName);
}
@Service
public class QueryServiceA implements QueryService {
public void print(String beanClassName){
System.out.println("运行QueryServiceA");
}
}
@Service
public class QueryServiceB implements QueryService {
public void print(String beanClassName){
System.out.println("运行QueryServiceB");
}
}
测试:
@Service
public class QueryApplication{
@Resource
private QueryService queryService;
public void print(String beanClassName){
queryService.print("QueryServiceB");
}
}
此时会打印运行QueryServiceB
注意点:
1、QueryService的实现类需要加@Service
2、QueryServiceFactory上面加@Primary
因为FactoryBean
也会返回一个QueryService
的SpringBean
,此时有多个QueryService
的Bean
,Spring
就不知道你注入哪个,加上@Primary
注解后,所有地方实际注入的都是我们写的QueryServiceFactory
,具体执行的时候再通过入参判断具体调用哪个实现类。