CSFramework---分发器(action的处理)

C/S开发框架之action的处理

客户端向服务器请求“资源”, 服务器需要将“资源”准备好,并回送给客户端。
比如说,针对用户登录这一操作,客户端将用户输入的账号,密码等信息发送给服务器,请求服务器确认用户合法性;那么,服务器需要在数据库中进行确认用户的合法性,并将确认结果回送给客户端。
这里,对于客户端请求的系列操作代码,不是我们C/S工具可以做的,而应该是使用这个工具的开发者应该去实现的。但是,我们希望我们的C/S工具能够“自动识别,自动执行”开发者相关操作的代码,并将结果自动回送给客户端。

关于NetMessage中的action

这个问题的开始要从用户登录说起,

	private void dealUserLogin() {
		String id = jtxtUserName.getText();
		String password = new String(jpswPassword.getPassword());
		password = String.valueOf(password.hashCode());
		
		ActionBeanFactory.setObject("userLogin", this);
		client.sendRequest("userLogin", new ArgumentMaker()
						.add("id", id)
						.add("password", password)
						.toString());
	}

从上述代码可以看出,客户端将向服务器发送一个请求,其中action是userLogin,那么,用户的请求不仅仅只是登录,还可能是,比如查询课表等其他相应的对“资源”的请求,因此,NetMessage中的action的作用就在于此,不同的action应该对应着不同的“操作代码”。所以,我们以action为键,以其对应的“操作”为值,形成一个Map。

也就是说,我们C/S工具的工作是:
1.读取(扫描)相应的配置文件或者注解方式;
2.把action与对应的方法装到一个Map中;
3.在处理action时,找到Map中对应的类和方法;
4.根据用户提供的参数的值,填充方法的参数;
5.用反射机制执行这个方法。

首先,先来实现action对应的“操作”,它应该包括,object,method以及参数。

package com.mec.csFramework.action;

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

public class ActionBeanDefinition {
	private Object object;
	private Method method;
	private List<ActionParameter> parameterList;
	
	ActionBeanDefinition() {
	}

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

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

	Object getObject() {
		return object;
	}

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

	Method getMethod() {
		return method;
	}

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

}

这个类的实现很简单,三个成员及Setters和Getters,并且都是包内权限。关于三个成员,object和method很好理解,至于参数,我们等会儿再做解释。

接下来,就是准备一个以action为键,对应的“操作”为值(即ActionBeanDefinition)的一个Map,来负责通过包扫描找到对应“操作”的类及方法。这步操作有不同的方法可以实现,可以通过扫描XML文件反射机制获取方法和参数,

<?xml version="1.0" encoding="UTF-8"?>
<actions>
	<action name = "userLogin" class = "com.mec.chatRoom.server.user.action.UserAction" method = "getUserById">
		<parameter name = "id" type = "string"></parameter>
		<parameter name = "password" type = "string"></parameter>
	</action>
</actions>

也可以通过注解,将action与对应方法进行描述,这里需要准备三个注解,分别对类,方法和参数。

@Retention(RUNTIME)
@Target(TYPE)
public @interface Actioner {
}
@Retention(RUNTIME)
@Target(METHOD)
public @interface Mapping {
	String value();
}
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface Argument {
	String value();
}

相应地,将对应注解加到action对应的具体的类(APP开发者写的)中相应位置。这里,我们编写了userLogin对应的”操作“类,及UserAction.

package com.mec.chat_room.server.user.action;

import com.mec.chat_room.server.user.model.UserInfo;
import com.mec.chat_room.server.user.service.UserService;
import com.mec.csframework.action.Actioner;
import com.mec.csframework.action.Argument;
import com.mec.csframework.action.Mapping;

@Actioner
public class UserAction {
	private UserService userService;

	public UserAction() {
		this.userService = new UserService();
	}
	
	@Mapping("userLogin")
	public UserInfo getUserById(
			@Argument("id") String id,
			@Argument("password") String password) {
		UserInfo user = userService.getUserById(id, password);
		
		if (user == null) {
			user = new UserInfo();
			user.setId("ERROR");
		} else {
			user.setPassword(null);
		}
		
		return user;
	}

}

注解准备好后,就可以编写这个Map了。

package com.mec.csFramework.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 com.mec.application.dao.Annotationer;
import com.mec.application.dao.Argument;
import com.mec.application.dao.Mapping;
import com.mec.util.PackageScanner;

public class ActionBeanFactory {
	private static final Map<String, ActionBeanDefinition> actionPool;
	
	static {
		actionPool = new HashMap<String, ActionBeanDefinition>();
	}
	
	public ActionBeanFactory() {
	}
	
	ActionBeanDefinition getActionBeanDefinition(String action) {
		return actionPool.get(action);
	}
	
	//包扫描,找到action对应的类
	public static void scanPackage(String packageName) {
		new PackageScanner() {		
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isAnnotation()
						|| klass.isArray()
						|| klass.isEnum()
						|| klass.isInterface()
						|| klass.isPrimitive()
						|| !klass.isAnnotationPresent(Annotationer.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);
						
						dealParameter(abd, method);
						
						actionPool.put(action, abd);
					}
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}.scanPackage(packageName);
	}
	
	//独立出来专门处理参数
	private static void dealParameter(ActionBeanDefinition abd, Method method) {
		List<ActionParameter> parameterList = new ArrayList<ActionParameter>();
		
		Parameter[] parameters = method.getParameters();
		for (Parameter parameter : parameters) {
			if (!parameter.isAnnotationPresent(Argument.class)) {
				continue;
			}
			Argument argument = parameter.getAnnotation(Argument.class);
			String parameterName = argument.value();
			ActionParameter actionParameter = new ActionParameter(parameterName, parameter);
			parameterList.add(actionParameter);
		}
		abd.setParameterList(parameterList);
	}
}

现在,我们来说一下对参数的处理。之所以给参数加注解,是因为为了获取参数的名字,即"id" 和"password",因为hashMap中的键值对是无序的,就是说我们往Map里”写“的时候是先id后password的顺序,当”读“的时候有可能就是先password后id的顺序,直接输出也是没办法获取参数名字的,只能得到arg0,arg1这样的参数名。而用户输入的参数的值,实质在一开始就已经通过ArgumentMaker打包成了json字符串,而我们是通过ArgumentMaker中的getValue方法获取的参数的值,而这个方法需要的参数就是参数的名字和类型(这里可能说的比较绕,结合代码就可以看得更清楚了)。所以,基于这样的考虑,对参数做如下处理:

package com.mec.csframework.action;

import java.lang.reflect.Parameter;

public class ActionParameter {
	private String name;
	private Parameter parameter;
	
	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;
	}
	
}

反射机制自动执行方法

以上有关注解的读取和Map的构建,是在服务器启动前就应该完成的。
我们做的C/S工具是在将来会被包括我们在内的编程者用来编写APP等基于C/S模式的系统;APP开发者要用到我们C/S工具中提供的REQUEST网络命令,确定action的值,发送客户端资源请求;同时APP开发者需要自己编写与action对应的类和方法,比如上述的UserAction;我们的C/S工具能自动根据action的值,找到并执行这些方法,以完成相关资源请求。

package com.mec.csframework.action;

public interface IActionProcessor {
	String dealRequest(String action, String parameter) throws Exception;
	void dealResponse(String action, String parameter) throws Exception;
}

给一个默认的接口适配器,完成对请求和响应的具体处理:

package com.mec.csframework.action;

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

import com.mec.util.ArgumentMaker;

public class DefaultActionProcessor implements IActionProcessor {

	public DefaultActionProcessor() {
	}
	
	@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);
	}
	
	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;
	}

	@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});
		}
	}

}

dealRequest()方法应该是在ServerConversation中处理REQUEST网络命令的时候执行的。即,客户端向服务器端发送REQUEST请求,并指定action和参数,服务器根据action在Map中找到对应的ActionBeanDefinition,并利用反射机制执行相关的方法,得到该方法的返回值,并将其字符串化;客户端接收RESPONSE命令,并根据action和字符串化的返回值,找到处理的方法,即dealResponse()方法,同样利用反射机制自动执行。

小结

这样的一个C/S框架,其实是非常粗糙简陋的,非常稚嫩的,一些细节问题并没有细化完整的解决,有些功能没有完全的实现,只是完成了一些基本的功能,程序有很大的改进余地。

至此,关于CSFramework的主要核心内容就介绍完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值