基于JMS消息中间件的分布式系统初探究(二) - 服务端反射调用组件方法

来源:互联网 发布:中兴对讲机软件 编辑:程序博客网 时间:2024/06/02 06:09

在上一篇文章中,我们解决了子系统之间的通讯问题,并跑起来了一个模型项目。这里我们要详细实现服务端程序。

我们在服务端运行Spring,利用Spring的IoC容器来管理所有的Service组件,然后根据接收到的JMS消息通过反射动态调用Service方法

首先要先设计一下协议:

public class MessageProtocol implements Serializable {    /**     * 要调用的接口全限定名     */    private String interfaceName;    /**     * 要调用的方法名     */    private String methodName;    /**     * 方法参数列表     */    private List<Object> argList;    // setter and setters ...}

由上协议类可以看出,我们是通过接口名、方法名和参数列表来定位服务端组件的。假如在服务端我们有一个AccountService接口的实现类DefaultAccountService

public interface AccountService {    void registerMember(MemberDto dto);    void sayHello(String name);}

我们想要在客户端调用其registerMember()方法,那么就应该这样封装MessageProtocol类:

    interfaceName = "com.fh.common.service.AccountService";    methodName = "registerMember";    argList = Arrays.asList(new MemberDto());

然后将这个对象以ObjectMessagepayload的格式通过JMS发送给服务端,服务端要做的工作是:

  1. 通过反射得到interfaceName对应的Class对象
  2. 通过反射得到该Class对像中的methodName方法
  3. 调用method.invoke()方法,将argList传递进去
  4. 将返回值通过JMS返回给客户端

由于Java的反射机制中调用Class#getDeclaredMethods()方法开销比较大,所以我们应该缓存得到的Method对象。思想是,当客户端第一次请求该方法时,先调用getDeclaredMethods()方法,将返回的结果保存到一个Map<String, List<Method> >数据结构中,即:

    /**     * K: 接口的全限定名     * <p> V: 这个接口实现类的所有{@code Method}对象     */    public static Map<String, List<Method>> methodCache = new HashMap<>();

然后,当下一次客户端要请求这个组件的方法时,直接从methodCache中查找而不是调用getDeclaredMethods()方法,这样能极大提高运行效率。
消息处理方法如下:

public Object onMessage(MessageProtocol protocol) {        System.out.println("message received");        // 取出请求信息        // 接口名        String interfaceName = protocol.getInterfaceName();        // 参数列表        List<Object> argList = protocol.getArgList();        // 方法名        String methodName = protocol.getMethodName();        Object returnVal = null;        try {            Class clazz = Class.forName(interfaceName);            Object service = BeanUtil.getBeanByInterface(clazz);            // 反射调用            returnVal = BeanUtil.invokeMethod(clazz, service, methodName, argList.toArray());        } catch (// ... ...) {           // ... ...        }        return returnVal;    }

BeanUtil工具类的完整实现如下:

public class BeanUtil {    private BeanUtil() {    }    /**     * 通过反射调用业务逻辑方法     * @param clazz     * @param methodName     * @param <T>     */    public static <T> Object invokeMethod(Class<T> clazz, Object component, String methodName, Object[] args) throws NoSuchMethodException, NoSuchClassException, InvocationFailedException {        Method method = getMethodInCache(clazz.getName(), methodName);        Object returnValue = null;        try {            returnValue = method.invoke(component, args);        } catch (IllegalAccessException e) {            throw new InvocationFailedException("invoke " + methodName + " failed");        } catch (InvocationTargetException e) {            throw new InvocationFailedException("invoke " + methodName + " failed");        }        return returnValue;    }    /**     * 通过接口Class对象,从Spring容器中得到对应的实现组件     */    public static <T> T getBeanByInterface(Class<T> clazz) {        T result = null;        Map<String, T> map = ContextHolder.ctx.getBeansOfType(clazz);        // 返回第一个bean        Set<Map.Entry<String, T>> set = map.entrySet();        for (Map.Entry<String, T> entry : set) {            result = entry.getValue();            break;        }        cacheMethod(clazz);        return result;    }    /**     * 如果是第一次调用,缓存Class的Method对象     * @param clazz     * @param <T>     */    private static <T> void cacheMethod(Class<T> clazz) {        if (false == isAlreadyCached(clazz.getName())) {            Method[] methods = clazz.getDeclaredMethods();            ContextHolder.methodCache.put(clazz.getName(), Arrays.asList(methods));        }    }    private static boolean isAlreadyCached(String className) {        return ContextHolder.methodCache.containsKey(className);    }    /**     * 从Cache中得到Method对象     */    private static Method getMethodInCache(String className, String methodName) throws NoSuchClassException, NoSuchMethodException {        List<Method> methodList = ContextHolder.methodCache.get(className);        if (null == methodList) {            throw new NoSuchClassException(className);        }        Optional<Method> optMethod = methodList.stream().filter( (meth) -> {            return meth.getName().equals(methodName);        }).findAny();        if (false == optMethod.isPresent()) {            throw new NoSuchMethodException(methodName);        }        return optMethod.get();    }}

至此,一个完整可用的分布式网站后端就能够运行起来了。当然这只是一个粗犷的实现,还有很多可以进行性能优化的地方。

0 0
原创粉丝点击