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

我们在服务端运行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();
    }
}

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

你可能感兴趣的内容
Spring 之 JMS 监听JMS消息 收藏,4414 浏览
0条评论

dexcoder

这家伙太懒了 <( ̄ ﹌  ̄)>
Owner