在app层,会有许多由开发者书写的新方法,这些方法是无法从底层预支的,但是在底层代码书写请求、回复等操作时,应该对应不同action,调用其处理方法。
相当于对action的处理,基于反射机制调用未来配置的方法。
而这些方法的获取此次通过解析xml实现,如:
<actions>
<action name="login" class="StudentAction" method="login">
<parameter name="id" type="string"></parameter>
<parameter name="password" type="string"></parameter>
</action>
<action name="registry" class="StudentAction" method="registry">
<parameter name="id" type="string"></parameter>
<parameter name="nick" type="string"></parameter>
<parameter name="password" type="string"></parameter>
</action>
</actions>
xml需包括所有新添加的方法,标注action名称及其对应的类,方法名称,各个参数的名称及数据类型。(尤其参数,因为java的方法重载性,需要精准参数才能取出对应方法)
那么首先的问题是从xml中提取出所有action对应方法并存到action池中,避免之后每次调用都要现解析xml寻找方法。
解析XML文件并生成action池:
public static void scanActionMapping(String xmlPath)
throws XMLNotFoundException, SAXException, IOException, Throwable {
new XMLParser() {
@Override
public void dealElement(Element element, int index) throws Throwable {
String action = element.getAttribute("name");
String className = element.getAttribute("class");
Class<?> klass = Class.forName(className);
@SuppressWarnings("deprecation")
Object object = klass.newInstance();
String methodName = element.getAttribute("method");
ActionBeanDefinition abd = new ActionBeanDefinition();
abd.setKlass(klass);
abd.setObject(object);
new XMLParser() {
@Override
public void dealElement(Element element, int index) throws Throwable {
String parameterName = element.getAttribute("name");
String strParameterType = element.getAttribute("type");
ParameterDefinition pd = new ParameterDefinition();
pd.setName(parameterName);
pd.setType(TypeParser.toType(strParameterType));
abd.addParameter(pd);
}
}.parseTag(element, "parameter");
Class<?>[] parameterTypes = new Class<?>[abd.getParameterCount()];
int i = 0;
while (abd.hasNext()) {
ParameterDefinition pd = abd.nextParameter();
parameterTypes[i++] = pd.getType();
}
Method method = klass.getDeclaredMethod(methodName, parameterTypes);
abd.setMethod(method);
actionPool.put(action, abd);
}
}.parseTag(XMLParser.newDocument(xmlPath), "action");
}
首先按action词条解析,获取action的名称;获取其对应的类的名称,由类的名称反射出该类,再将该类实例化;获取方法名称(因为重载机制,没有参数无法获取去具体方法)。
初始化一个ABD(一个ABD保存一个类、对象、方法),将该类与对象加入一个ABD。
接着该解析parameter参数词条了:
首先提取出参数名称,类型(转换:int->Integer),并都加入一个PD(一个PD代表一个)参数,再将该PD加入ABD的参数列表。
依据ABD参数列表数量新建一个类数组存放参数数据类型,按ABD参数列表的顺序将参数数据类型加入到该类数组。
按方法名称、参数数据类型由类获取对应方法,将方法加入到ABD中,最终将该ABD(存储着类、对象、方法)加入到action池中,以便后续调用。
在说明具体用法之前,先说明一下java的泛型擦除机制:类似List<泛型>,当我们想要获取具体泛型表示的类型时,利用getClass只是返回List类型,因此在需要精确获取参数数据类型的情形下,明显无法完成该需求。
Gson提供了一个方法:private static Type mapType = new TypeToken<Map<String, String>>() {}.getType();
上述方法得到的mapType为Map<java.lang.String, java.lang.String>,这样就能获取MAP及其内部所存数据的数据类型。
而在请求传来时,所发送的参数是Gson字符串,要想获取其包含的数据类型及数据值,需要将Gson字符串反解析出来。AM:
this.argMap = gson.fromJson(parameterString, mapType);
该图存储着参数名及一个Json字符串(包含参数值及参数数据类型)的键值对。
具体对Request做出处理:
public Object dealRequest(String action, String parameter) throws Exception {
// 根据action获取对应的ActionBeanDefinition对象;
ActionBeanDefinition abd = ActionBeanFactory.getActionBean(action);
// 获取action对应的方法
Method method = abd.getMethod();
// 获取action对应的方法在反射执行时,所需的对象;
Object object = abd.getObject();
// 根据字符串化的参数,反向构建Map<String, String>
// 这个Map的键是参数名称,值是参数对象值的gson字符串
// 但是,Map的无序性,使得不能保证参数顺序正常;
ArgumentMaker argument = new ArgumentMaker(parameter);
// ActionBeanDefinition中的getParameterCount()能得到参数个数
Object[] parameterValues = new Object[abd.getParameterCount()];
int index = 0;
// 根据method得到这个方法的所有参数Parameter;用的是反射机制手段
Parameter[] parameters = method.getParameters();
// 按照ActionBeanDefinition中的参数列表中参数的顺序访问参数:
while (abd.hasNext()) {
// 获取参数信息
ParameterDefinition pd = abd.nextParameter();
// 获取参数名称
String parameterName = pd.getName();
// 按照顺序获取参数Type,这是能保证范型对象正常处理的关键!
Type parameterType = parameters[index].getParameterizedType();
// 得到对应参数名称的参数值,并保证范型得到处理
parameterValues[index] = argument.getParameter(parameterName, parameterType);
index++;
}
Object result = null;
result = method.invoke(object, parameterValues);
return result;
}
首先根据传来的action参数,在之前生成的action池中找到该action对应的ABD,取出其方法、对象。
用反射机制得到方法的全部参数Parameter并生成一个数组。
我们现在得到了一个参数数据类型数组,ABD中有一个参数列表,其元素是PD,取出PD后可得到参数名称,在Parameter数组中可以获取参数的数据类型,当获取到参数名称及参数数据类型之后,就可以通过:
public Object getParameter(String name, Type type) {
String strValue = this.argMap.get(name);
return gson.fromJson(strValue, type);
}
parameterValues[index] = argument.getParameter(parameterName, parameterType);
获取该参数值并加入数组。
当获取完全部参数数值,我们就可以通过方法的反射机制
Object result = method.invoke(object, parameterValues);
即调用了对应方法。
反射:
Class<?> klass = Class.forName(String className);
Object object = klass.getDeclaredConstructor().newInstance();
Method method = klass.getDeclaredMethod(String methodName, Class<?> parameterTypes);
Field[] fileds = klass.getDeclaredFields();
result = method.invoke(object, parameterValues);
作用:
1.按照类名称获取类
2.实例化该类获得对象
3.按照方法名称及参数数据类型获取方法
4.获取该类的成员
5.用对象及方法所需的数据值调用方法