javaSE & CSFramework(?)& 请求与响应
作者:互联网
在前面的章节中,我们实现了一个 服务器 / 客户端的基本通信框架。实现了客户端上线,下线。服务器开机,宕机等基本操作。当然我们也实现了从一台客户端发消息给另一台客户机(一对一),一台客户端给其他所有客户端发消息(一对多),且在这过程中,服务器成为一个中转站,是信息传递的重要选择节点。
假设我们还是在做聊天室,以上的功能让我们有能力与其他人聊天,但是,似乎少了些什么?对,就是登陆。
聊天用昵称来区分人,而一个昵称就对应着一个账号。这个账号是需要注册,登录的。而且,仔细想来,登陆,注册,这些操作只是客户端和服务器的之间的交互。
以登录为例:
- 客户端将用户录的账户ID和密码作为一个信息包,发送给服务器
- 服务器在接收到这个信息包并解析之后,针对信息,和数据库里的ID与密码查验,给用户反馈一个信息包(包含了更多账户信息)。
- 客户端收到信息包,解析后,相当可以使用账户了。然后成功登录。
简单来说,就是请求/响应过程。
最重要的是,请求和响应不是一个专属动作,它可以是一类动作。
例如,想特别关注一个用户,又想取消。还有类似于浏览器对用户的一些响应。这就相当于在实现App的不同请求功能与服务器对应的响应功能。
而且,是将这功能和我们的框架结合起来。
一、Request 和 Response
怎么加入到已有框架中?
再以登录为例
将登录的请求信息(含录入信息)发送给服务器,如果不是框架,我们可以直接Server上增加实现该响应的方法。但是不改变框架,怎么融入?
这里使用到了两大武器。XML文件和反射机制。
XML文件将请求的某个动作(action)和某个类的某个方法形成映射关系。
我们要使用反射机制扫描这个XML文件来调用后面App功能开发的模板类的映射方法。完成响应。
request基本逻辑如下:
- 根据action和XML的映射关系,找到相关类对象(obj)和方法(method)
- 根据XML方法描述的,将参数(parameter)转化成(method)在反射执行时需要的值(对象);
- 反射执行method,并得到method执行结果result;
- 将result转化成String类型结果。返回给客户端。
而客户处理返回的结果也是这类逻辑,不过可以随需求而改变。
图中蓝色(就叫它蓝色吧)是基于框架可以任意配置的。而红色框架里是为完成Request做的操作。
黑色框中是为了完成Response操作。
关键步数一:生成方法池
实例如下:
模板类:
@AAction
public class UserAction {
private UserService userService;
public UserAction() {
this.userService = new UserService();
}
@AActionMethod(action = "login")//用户登录操作
public UserInfo userLogin(
@AParaName(vlaue = "id") String id,
@AParaName(vlaue = "password")String password) {
UserInfo user = new UserInfo();
user.setId(id);
user.setPassword(password);
return userService.checkUser(user);//此处是链接数据库。对用户数据进行验证。验证完成后,返回结果
}
}
XML文件
<actions>
<action name="login" class="com.mec.testserver.action.UserAction"
method="userLogin">
<parameter name="id" type="string"></parameter>
<parameter name="password" type="string"></parameter>
</action>
<action name="registry" class="com.mec.testserver.action.UserAction"
method="userRegistry">
<parameter name="id" type="string"></parameter>
<parameter name="password" type="string"></parameter>
<parameter name="nick" type="string"></parameter>
</action>
</actions>
扫描这个XML文件,借助以前XMLParser工具。
先扫描标签action
获得了是三个标签属性
接着扫描标签“parameter”
接下来,定义一个类,用来管理参数。
public class ParameterDefinition {
private Class<?> type;//参数类型
private String name;//参数名
public ParameterDefinition() {
}
Class<?> getType() {
return type;
}
void setType(Class<?> type) {
this.type = type;
}
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ParameterDefinition [type=" + type + ", name=" + name + "]";
}
}
再将Xml解析出的参数信息,加入进去。正常操作,不多说了。
于是,现在是:
接着是定义,一个方法类。由于这是需要反射机制调用的方法,需要这些:
黄色标签就是我们通过XML解析出来的
所以定义一个管理action的类。
public class ActionDefinition {
private Class<?> kClass;//元数据
private Object object;//实例
private Method method;
private String action;//操作名字
private List<ParameterDefinition> paraList;//内含参数名和参数类型
public ActionDefinition() {
}
Class<?>[] getParaTypes(){
int paraCount = paraList.size();
if (paraCount <= 0) {
return new Class<?>[]{};
}
Class<?>[] result = new Class<?>[paraCount];
for (int index = 0; index < paraCount; index++) {
Class<?> type = paraList.get(index).getType();
result[index] = type;
}
return result;
}
void addParameter(ParameterDefinition pd) {
paraList.add(pd);
}
Class<?> getkClass() {
return kClass;
}
Object getObject() {
return object;
}
Method getMethod() {
return method;
}
public List<ParameterDefinition> getParaList() {
return paraList;
}
void setkClass(Class<?> kClass) {
this.kClass = kClass;
}
void setObject(Object object) {
this.object = object;
}
void setMethod(Method method) {
this.method = method;
}
void setAction(String action) {
this.action = action;
}
}
这个类,set各个成员不难,不少可以直接从解析出标签中拿,麻烦一定的就是方法的设置。
用getDeclaredMethod就行
关键步数二:获得调整的参数值
由于反射机制调用方法,需要参数值。但是,我们的参数值,例如(id 234, password 123456)通过通信层次传输时,需要转化为一系列字符串类型。用Gson转化的。所以,我们需要反解析回来。
同时,我们希望,发送信息时,234是有解释信息(id)和之对应的。所以们形成一个ArguentMaker类
public class ArgumentMaker {
private static final Gson gson = new GsonBuilder().create();
private static final Type type = new TypeToken<Map<String , String>>(){}.getType();
private Map<String, String> argMap;
public ArgumentMaker() {
argMap = new HashMap<String, String>();
}
//将字符串数据反解析为参数名和参数值对应的键值对
public ArgumentMaker(String para) {
argMap = gson.fromJson(para, type);
}
//发送方添加参数键值对
public ArgumentMaker addArgument(String name, Object value) {
// if (argMap.containsKey(name)) {
// return ;
// }可能存在覆盖情况
argMap.put(name, gson.toJson(value));
return this;
}
//接收方是使用参数名,参数类型,获得参数值
@SuppressWarnings("unchecked")
public <T>T getArgument(String name, Class<?> type) {
if (!argMap.containsKey(name)) {
return null;
}
return (T) gson.fromJson(argMap.get(name), type);
}
//接收方是使用参数名,参数类型,获得参数值
@SuppressWarnings("unchecked")
public <T>T getArgument(String name, Type type){
String str = argMap.get(name);
if (str == null) {
return null;
}
return (T) gson.fromJson(str, type);
}
}
但是,使用fromJson时,解析是存在问题的。
Map<String, String> 和 Map<String, int> 两者的元数据类都是 Map.class.
但是,如果参数值是Map<String, String> 类型,此时gson.fromJson(str, Map.class)是无法真正解析出参数值的。
该怎么办?
Parameter[] parameters = method.getParameters();//获得某方法的参数集合
for (int index = 0; index < paraCount; index++) {
String paraName = ad.getParaList().get(index).getName();
//parameters[index].getName()得到的是arg0, arg1 ....
Type type = parameters[index].getParameterizedType();
//getParameterizedType()获得得是参数最准确类型。不同于getType()只能获得Map.class这类。
}
可能看不清楚一些字,所以我发分成两个
最后收尾:执行操作
public class DefaultActionProcessor implements IActionProcessor {
/**
* 实现对于客户端请求的响应
*/
@Override
public String dealRequestMess(String action, String para) throws Exception {
ActionDefinition ad = ActionBeenFactory.getActionDefinition(action);
ArgumentMaker aMaker = new ArgumentMaker(para);
Object object = ad.getObject();
Method method = ad.getMethod();
Object[] argValues = getParaValue(method, aMaker, ad);
Object result = method.invoke(object, argValues);
return Server.gson.toJson(result);
}
private Object[] getParaValue(Method method, ArgumentMaker aMaker, ActionDefinition ad) {
Parameter[] parameters = method.getParameters();
int paraCount = parameters.length;
if (paraCount <= 0) {
return new Object[] {};
}
Object[] result = new Object[paraCount];
// for (ParameterDefinition pd : ad.getParaList()) {
// String paraName = pd.getName();
// //Class<?> paraType = pd.getType();这里不行,是因为这里的Type解决不了泛型的
//
// result[i]
// }
for (int index = 0; index < paraCount; index++) {
String paraName = ad.getParaList().get(index).getName();
//parameters[index].getName()得到的是arg0, arg1 ....
Type type = parameters[index].getParameterizedType();
result[index] = aMaker.getArgument(paraName, type);
}
return result;
}
/**
* 实现对于服务器结果的响应
*/
@Override
public void dealReponseMess(String action, String para) throws Exception {
ActionDefinition ad = ActionBeenFactory.getActionDefinition(action);
if (ad == null) {
throw new ActionNotFoundException("action[" + action + "]不存在!");
}
Object object = ad.getObject();
Method method = ad.getMethod();
Parameter[] parameters = method.getParameters();
if (parameters.length != 1) {
throw new ResponseActionArgCountFailureException("方法["
+ method.getName() + "]参数个数不合法!");
}
Object[] value = new Object[1];
value[0] = Server.gson.fromJson(para, parameters[0].getParameterizedType());
method.invoke(object, value);
}
}
二、小结
通过XML,反射机制,再给APP功能添加者,一定的操作说明,就可以凭借此模式,实现请求与响应操作。
添加一个功能,配置一个模板类(含方法),一个XML文件。不对CsFramework框架进行修改。这样,Cs才能成为一个可用的,可持续生长,安全的工具。
当然,该工具仍然可以继续优化。
不过接下来是对此工具进行应用了。
一个是聊天室,一个是棋牌窒。
标签:index,return,CSFramework,public,响应,javaSE,type,method,String 来源: https://blog.csdn.net/Ayydead/article/details/107866969