编程语言
首页 > 编程语言> > javaSE & CSFramework(?)& 请求与响应

javaSE & CSFramework(?)& 请求与响应

作者:互联网

在前面的章节中,我们实现了一个 服务器 / 客户端的基本通信框架。实现了客户端上线,下线。服务器开机,宕机等基本操作。当然我们也实现了从一台客户端发消息给另一台客户机(一对一),一台客户端给其他所有客户端发消息(一对多),且在这过程中,服务器成为一个中转站,是信息传递的重要选择节点。

假设我们还是在做聊天室,以上的功能让我们有能力与其他人聊天,但是,似乎少了些什么?对,就是登陆。

聊天用昵称来区分人,而一个昵称就对应着一个账号。这个账号是需要注册,登录的。而且,仔细想来,登陆,注册,这些操作只是客户端和服务器的之间的交互。

以登录为例:

简单来说,就是请求/响应过程。

最重要的是,请求和响应不是一个专属动作,它可以是一类动作。
例如,想特别关注一个用户,又想取消。还有类似于浏览器对用户的一些响应。这就相当于在实现App的不同请求功能与服务器对应的响应功能。

而且,是将这功能和我们的框架结合起来。

一、Request 和 Response

怎么加入到已有框架中?
再以登录为例
将登录的请求信息(含录入信息)发送给服务器,如果不是框架,我们可以直接Server上增加实现该响应的方法。但是不改变框架,怎么融入?

这里使用到了两大武器。XML文件和反射机制。
XML文件将请求的某个动作(action)和某个类的某个方法形成映射关系。
我们要使用反射机制扫描这个XML文件来调用后面App功能开发的模板类的映射方法。完成响应。

request基本逻辑如下:

  1. 根据action和XML的映射关系,找到相关类对象(obj)和方法(method)
  2. 根据XML方法描述的,将参数(parameter)转化成(method)在反射执行时需要的值(对象);
  3. 反射执行method,并得到method执行结果result;
  4. 将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