【Betty】action的处理 —— 分发机制
在之前的博文中,本人讲过:
使用 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