【Betty】action的处理 —— 分发机制

shadowLogo

在之前的博文中,本人讲过:

使用 Betty 来进行开发的开发者,在什么场景,处理什么样的请求和响应,都是 不可预料 的!

所以,我们在设计应用层的时候,就要为使用者留有扩展的可能
在我们学习 Java Web 的过程中,有一款框架也是专门来处理 浏览器请求处理 的框架 —— Spring MVC
因此,本篇博文所讲解的 action 的处理,正是模拟 Spring MVC 的原理 —— 分发机制 来实现的

那么,有些没有基础的同学可能就会有这样的疑问:

什么是 分发机制

定义:

用本人的话来讲,就是:

  • 首先,向 某个注册中心(或者 工厂)注册/记录 当前对象的 处理能力(即:能够处理什么请求/响应)
  • 接着,当 对端请求 发送过来后,请求分发器 可以将 当前请求,根据 注册/记录 过的 处理器 中 进行筛选,将 当前请求 交由 能够处理的处理器 去处理

那么,我们该如何在通过 代码 实现 分发机制 呢?

实现思路:

请求描述符对应的处理器信息 都存入一个Map中,
在要处理请求时,只需要根据 请求描述符 就可以得到 相应的处理器信息

那么,在本篇博文中,本人的具体 实现步骤 为:

客户端向服务器发出“请求”时,必须使用命令:REQUEST
另外,客户端还需要填充action,以明确请求的内容
每一个action的字符串由APP开发者定义的
每一个action应该与 某一个操作 对应;
那么,以action为键,以 “操作”为值,可以形成Map
“操作” 可以是 类 及其 方法


在实现 分发机制 前,要先有一些类的基础供我们使用,
否则是无法单凭一个类或者接口就能实现 分发机制

因此,本人先来给出几个 必备的 基础代码

基础代码:

首先,本人来定义一个类,来封装 请求参数 的信息:

请求参数 的封装 —— ActionParameter:

package edu.youzg.betty.action;

import java.lang.reflect.Parameter;

public class ActionParameter {
    private String name;    // 请求参数 名称字符串
    private Parameter parameter;    // 处理 该action的 请求参数

    ActionParameter() {
    }

    ActionParameter(String name, Parameter parameter) {
        this.name = name;
        this.parameter = parameter;
    }

    String getName() {
        return name;
    }

    void setName(String name) {
        this.name = name;
    }

    Parameter getParameter() {
        return parameter;
    }

    void setParameter(Parameter parameter) {
        this.parameter = parameter;
    }

}

由于使用本框架的开发者,对于 自定义action处理逻辑未知
所以,我们想要在接收到请求后,调用 app开发者 自定义的处理逻辑,只能通过 反射机制
那么,本人来给出一个 封装了 反射机制 调用 处理方法 的全部信息 的 类:

处理方法信息 的封装 —— ActionBeanDefinition:

package edu.youzg.betty.action;

import java.lang.reflect.Method;
import java.util.List;

/**
 * 本类所提供的内容,其主旨目的是为“反射机制”调用提供便利<br>
 * 为反射机制调用,需要如下基本内容:<br>
 * object 对象,与class有关 <br>
 * method 方法<br>
 * 参数<br>
 */
public class ActionBeanDefinition {
    private Object object;	// 反射机制 的 执行对象
    private Method method;	// 反射机制 要调用的方法
    private List<ActionParameter> parameterList;	// 调用目标方法 所需的 参数列表

    ActionBeanDefinition() {
    }

    Object getObject() {
        return object;
    }

    List<ActionParameter> getParameterList() {
        return parameterList;
    }

    void setParameterList(List<ActionParameter> parameterList) {
        this.parameterList = parameterList;
    }

    void setObject(Object object) {
        this.object = object;
    }

    Method getMethod() {
        return method;
    }

    void setMethod(Method method) {
        this.method = method;
    }

}

在我们使用框架进行开发的过程中,
一般会发现,注解的使用会大大 提升开发效率,并且 可读性也很强

那么,本人来给出 三个注解,来优化使用者的体验:

首先,是用于标记 app开发者的自定义请求处理类@Actioner注解

处理类的标记 —— @Actioner注解:

package edu.youzg.betty.action;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(TYPE)
public @interface Actioner {
}

接下来,是用于 标记 当前方法能处理什么请求 中的@Mapping注解

处理方法的标记 —— @Mapping注解:

package edu.youzg.betty.action;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface Mapping {
    String value(); // 请求描述符
}

最后一个,是 用于 标记参数映射关系 的注解 —— @Argument注解

请求参数的标记 —— @Argument注解:

package edu.youzg.betty.action;

import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Argument {
    String value(); // 被标记的参数,对应的参数名
}

接下来,本人来编写一个 扫描并存储ActionBean映射关系的工厂类 —— ActionBeanFactory类

处理器映射工厂 —— ActionBeanFactory类:

在编写此类的过程中,也是用到了本人所编写的一个 Java小工具 —— 《【小工具】包扫描器 ——PackageScanner》

package edu.youzg.betty.action;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.youzg.util.PackageScanner;

/**
 * 存储ActionBean映射关系 的 工厂类
 */
public class ActionBeanFactory {
    private static final Map<String, ActionBeanDefinition> actionPool;
    static {
        actionPool = new HashMap<>();
    }

    public ActionBeanFactory() {
    }
    
    /**
     * 通过扫描指定路径下的所有文件,
     * 将其中的action与对应的处理逻辑信息,记录并保存在actionPool中
     * @param packageName 指定要扫描的路径
     */
    public static void scanPackage(String packageName) {
        new PackageScanner() {
            @Override
            public void dealClass(Class<?> klass) {
                if (klass.isPrimitive()
                        || klass.isAnnotation()
                        || klass.isEnum()
                        || klass.isArray()
                        || klass.isInterface()
                        || !klass.isAnnotationPresent(Actioner.class)) {
                    return;
                }

                try {
                    Object object = klass.newInstance();

                    Method[] methods = klass.getDeclaredMethods();
                    for (Method method : methods) {
                        if (!method.isAnnotationPresent(Mapping.class)) {
                            continue;
                        }
                        Mapping mapping = method.getAnnotation(Mapping.class);
                        String action = mapping.value();

                        ActionBeanDefinition abd = new ActionBeanDefinition();
                        abd.setMethod(method);
                        abd.setObject(object);
                        dealMethodParameter(abd, method);

                        actionPool.put(action, abd);
                    }
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }.scanPackage(packageName);
    }

    private static void dealMethodParameter(ActionBeanDefinition actionBean, Method method) {	//处理方法的参数设置进相应的ActionBeanDefinition中
        Parameter[] parameters = method.getParameters();
        List<ActionParameter> parameterList = new ArrayList<>();

        for (Parameter parameter : parameters) {
            if (!parameter.isAnnotationPresent(Argument.class)) {
                continue;
            }
            Argument argument = parameter.getAnnotation(Argument.class);
            String argName = argument.value();
            ActionParameter actionParameter = new ActionParameter(argName, parameter);
            parameterList.add(actionParameter);
        }

        actionBean.setParameterList(parameterList);
    }

    /**
     * 设置 执行该方法的对象
     * @param action
     * @param object
     */
    public static void setObject(String action, Object object) {
        ActionBeanDefinition abd = getActionBean(action);
        if (abd == null) {
            return;
        }
        abd.setObject(object);
    }
    
    static ActionBeanDefinition getActionBean(String action) {
        return actionPool.get(action);
    }

}

接下来,就是本篇博文的核心 —— 分发机制 的实现:

核心代码:

由于我们对于 action的分发处理,有很多实现方式
比如:负载均衡、集群与反向代理 等功能的开发
因此,本人在这里,先来给出一个 用于规范和扩展的 action处理接口

action处理接口—— IActionProcessor接口:

package edu.youzg.betty.action;

public interface IActionProcessor {
    /**
     * 处理请求,必然要做出相应的响应
     * @param action 具体操作描述
     * @param parameter 请求参数
     * @return 请求处理结果
     * @throws Exception
     */
    String dealRequest(String action, String parameter) throws Exception;

    /**
     * 处理响应,是在我们发出请求之后的,
     * 所以,不用再对响应做出响应,因此无返回值
     * @param action 具体操作描述
     * @param parameter 响应参数
     * @throws Exception
     */
    void dealResponse(String action, String parameter) throws Exception;
}

现在,本人来给出一个 仅仅实现了action处理的基本功能默认实现类

默认action处理分发器—— DefaultActionProcessor类:

在编写此类的过程中,也是用到了本人所编写的一个 Java小工具 —— 《【小工具】JSON化/反JSON化器 —— ArgumentMaker》

package edu.youzg.betty.action;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;

import edu.youzg.util.ArgumentMaker;

public class DefaultActionProcessor implements IActionProcessor {

    public DefaultActionProcessor() {
    }

    /**
     * 处理请求
     * @param action 请求描述符
     * @param parameter 请求参数
     * @return
     * @throws Exception
     */
    @Override
    public String dealRequest(String action, String parameter) throws Exception {
        ActionBeanDefinition actionBean = ActionBeanFactory.getActionBean(action);
        if (actionBean == null) {
            throw new Exception("action:[" + action + "]未定义");
        }

        Object object = actionBean.getObject();
        Method method = actionBean.getMethod();
        List<ActionParameter> parameterList = actionBean.getParameterList();

        Object[] args = prepareParameters(parameterList, parameter);
        Object result = method.invoke(object, args);

        return ArgumentMaker.gson.toJson(result);
    }

    /**
     * 根据 请求参数描述信息列表 和 实际请求参数的json化字符串,解析出 参数数组,供反射机制调用
     * @param parameterList 请求参数列表
     * @param parameter 实际请求参数 的 json化字符串
     * @return
     */
    private Object[] prepareParameters(List<ActionParameter> parameterList, String parameter) {
        ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
        if (parameterList.isEmpty()) {
            return new Object[] {};
        }

        Object[] result = new Object[parameterList.size()];
        int index = 0;
        for (ActionParameter actionParameter : parameterList) {
            String parameterName = actionParameter.getName();
            Type type = actionParameter.getParameter().getParameterizedType();
            result[index++] = argumentMaker.getValue(parameterName, type);
        }

        return result;
    }

    /**
     * 处理响应
     * @param action 响应描述符
     * @param parameter 响应参数
     * @throws Exception
     */
    @Override
    public void dealResponse(String action, String parameter) throws Exception {
        // 处理RESPONSE
        ActionBeanDefinition abd = ActionBeanFactory.getActionBean(action);
        Object object = abd.getObject();
        Method method = abd.getMethod();
        if (method.getParameterCount() <= 0) {
            method.invoke(object, new Object[] {});
        } else {
            Type type = method.getParameters()[0].getParameterizedType();
            Object para = ArgumentMaker.gson.fromJson(parameter, type);
            method.invoke(object, new Object[] {para});
        }
    }

}

那么,本人在最后,再来对 action分发 做一点总结:

总结:

其实我们也能看得出:
分发逻辑,是通过使用Map这样的 双列集合,进行 映射存储映射查询 实现的


至此,action的分发处理,就讲解完毕了!

相信很多同学看到这篇博文的时候,已经发现本人设计的 Betty 的巧妙之处了

那么,在之后的博文中,本人将 通过使用本篇博文和之前博文中 所设计的代码,来实现 供使用者开发使用的 应用层API

posted @ 2020-04-22 00:29  在下右转,有何贵干  阅读(122)  评论(0编辑  收藏  举报