Spring通过入参选择Service

Spring通过入参选择Service

马草原 1,354 2023-08-13

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也会返回一个QueryServiceSpringBean,此时有多个QueryServiceBeanSpring就不知道你注入哪个,加上@Primary注解后,所有地方实际注入的都是我们写的QueryServiceFactory,具体执行的时候再通过入参判断具体调用哪个实现类。