【JavaSE】csFrameWork——分发器

分发器的应用场合

在csFrameWork中,最主要的就是彼此的交互问题。客户端与客户端之间的“交流”,事实上,是通过服务器这个中间媒介,也就是,客户端会向服务器发出各种请求,而服务器就要根据请求做出对应的响应。其中用到的就是分发器。

分发器的实现

在csFrameWork里面,我们实现的是 request 和 response 分发器。客户端发出 request 动作请求,在服务器端回应 response 动作请求。
由于,客户端会向服务器端所发出请求,我们把这个请求作为request,传递给服务器端,服务器进行解析,再去执行对应的方法,将执行方法的返回值作为response,传送给客户端,在客户端进行解析,再处理相关的结果。
由此可见,分发器的作用是,客户端需要执行什么操作,服务器就回应什么操作。比如:客户端要进行app的登录,他想验证账号和密码是否正确,就需要把账号、密码和dealUserLogin作为一个request,发送给服务器,服务器检测到了并进行解析,然后找到对应的dealUserLogin的方法,反射机制执行,将结果的返回值和dealUserLogin作为一个request返回到客户端,客户端进行解析,再找到对应的dealUserLogin方法反射机制执行。

下面,将用两种方法实现上述讲的csFrameWork里面的分发器。
方法一:用注解+包扫描+反射机制实现:
我们先编写的是ActionBeanDefinition类

public class ActionBeanDefinition {
	//	需要执行的方法
	private Method method;
	//	该方法所在的类
	private Class<?> klass;
	//	该类的对象
	private Object object;
	
	public ActionBeanDefinition() {
	}

	Method getMethod() {
		return method;
	}

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

	Class<?> getKlass() {
		return klass;
	}

	void setKlass(Class<?> klass) {
		this.klass = klass;
	}

	Object getObject() {
		return object;
	}

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

这个类是存放的是一个方法,这个方法在以后,如果发送的某个请求需要执行的是该方法的话,那么它将会被执行,否则,不执行该方法。这样的方法是由使用我们这套工具的用户编写的,方法不止一个,于是,我们需要一个ActionBeanFactory类,用这个类来存储这些方法。
懒汉vs饿汉
包扫描技术

public class ActionBeanFactory {
	//	使用单例工厂,目的是为了不管怎么掉用,这个actionPool只有一份,
	//	防止因出现多份而找不到所需要执行的方法
	//	以传递过来的线管操作的名字作为键,该操作对应的方法、方法所在的类及类的对象作为值
	private static final Map<String, ActionBeanDefinition> actionPool;
	
	//	饿汉模式
	static {
		actionPool = new HashMap<String, ActionBeanDefinition>();
	}
	
	public ActionBeanFactory() {
	}
	//	包扫描技术
	//	通过扫描指定的包,将满足要求的方法扫描到antionPool里面
	public static void actionPackageScan(String packageName) {
		new PackageScanner() {
			@Override
			public void dealClass(Class<?> klass) {
				if (klass.isPrimitive() || klass.isEnum()
						|| klass.isInterface()
						|| klass.isAnnotation()
						|| klass.isArray()
						|| !klass.isAnnotationPresent(ActionClass.class)) {
					return ;
				}
				//	执行到这里,证明满足了类含有ActionClass注解
				try {
					Object object = klass.newInstance();
					
					Method[] methods = klass.getMethods();
					for (Method method : methods) {
						if (! method.isAnnotationPresent(Action.class)) {
							continue ;
						}
						//	执行到这里,证明满足了方法含有Action注解
						ActionBeanDefinition abd = new ActionBeanDefinition();
						abd.setObject(object);
						abd.setKlass(klass);
						abd.setMethod(method);
						
						Action action = method.getAnnotation(Action.class);
						String actionName = action.name();
						actionPool.put(actionName, abd);
					}
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}.packageScanner(packageName);
	}
	//	方便外面调用,取得该BeanMethodDefinition
	public static ActionBeanDefinition getAction(String actionName) {
		return actionPool.get(actionName);
	}
}

由于上述实现了包扫描,那么需要把满足要求的类和方法加上对应的注解,确保能将他们扫描到actionPool里面。

//	类的注解
@Retention(RUNTIME)
@Target(TYPE)
public @interface ActionClass {
	String name() default "";
}
//	方法的注解
@Retention(RUNTIME)
@Target(METHOD)
public @interface Action {
	String name() default "";
}
//	参数的注解
@Retention(RUNTIME)
@Target(PARAMETER)
public @interface ActionParameter {
	String value() default "";
}

代码写到这里,我们已经可以将满足要求的方法,通过包扫描,放到一个actionMap里面,现在就需要客户端发送请求,我们根据请求,在actionMap里面找到对应的方法,并执行,并将结果返回到客户端,在客户端解析请求,执行与之对应的方法。
下面将要编写我们的执行过程
编写一个IActionExecuter接口

public interface IActionExecuter {
	String doRequest(String action,String parameter) throws Exception;
	void doResponse(String action,String parameter) throws Exception;
}

实现该接口的类必须实现上面两个方法,通过方法的名字可以看出,一个是处理来自客户端的request请求的,一个是处理来自服务器的response请求的。
我们可以再编写一个ActionExecuter类来实现刚才的接口,把他作为一个默认的实现类。因为大多数的csFrameWork的app,都需要的是实现服务器和客户端之间的“沟通”,所以可以默认实现。因为我们写的是接口,用户如果想完善其他的功能,(比如从不同的数据库里面获取数据),可以通过实现接口写自己的类。

这里运用Gson类,Gson的巧妙使用将Gson化的Map解析成对应的Map

public class ActionExecuter implements IActionExecuter {
	private static final Gson gson = new GsonBuilder().create();

	public ActionExecuter() {
	}

	private Object[] getParameterObject(Method method,String parameter) throws Exception {
		//	ArgumentMaker类存在的意义,请看上述的[将Gson化的Map解析成对应的Map]博文
		ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
		
		Parameter[] parameters = method.getParameters();
		Object[] objects = new Object[parameters.length];
		for (int index = 0;index < parameters.length;index++) {
			if (!parameters[index].isAnnotationPresent(ActionParameter.class)) {
				throw new Exception("参数" + parameters[index].getName() + "没有注解");
			}
			ActionParameter actionParameter = parameters[index]
												.getAnnotation(ActionParameter.class);
			String parameterName = actionParameter.value();
			//	这里.getParameterizedType()方法可以解决泛型问题
			Type parameterKlass = parameters[index].getParameterizedType();
			
			Object object = argumentMaker.getObject(parameterName, parameterKlass);
			objects[index] = object;
 		}
		
		return objects;
	}
	
	@Override
	public String doRequest(String action, String parameter) throws Exception {
		//	在actionPool中找到对应的类
		ActionBeanDefinition abd = ActionBeanFactory.getAction(action);
		if (abd == null) {
			throw new Exception("服务器端 未找到action对应的类");
		}
		
		Object object = abd.getObject();
		Method method = abd.getMethod();
		//	反射机制执行该方法
		Object result = method.invoke(object, getParameterObject(method,parameter));
		//	将该方法的执行结果过 Gson 巧妙转化成字符串返回到客户端
		return gson.toJson(result);
	}

	@Override
	public void doResponse(String action, String parameter) throws Exception {
		ActionBeanDefinition abd = ActionBeanFactory.getAction(action);
		System.out.println("-------------------");
		if (abd == null) {
			throw new Exception("客户端 未找到action对应的类");
		}
		
		Method method = abd.getMethod();
		Object object = abd.getObject();
		if (parameter.equalsIgnoreCase("null")) {
			method.invoke(object, new Object[] {});
			return ;
		}
		ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
		
		Parameter para = method.getParameters()[0];
		if (!para.isAnnotationPresent(ActionParameter.class)) {
			throw new Exception("参数" + para.getName() + "没有注解");
		}
		
		Type paraType = para.getParameterizedType();
		Object paraObject = argumentMaker.getObject(action, paraType);
		method.invoke(object, paraObject);
	}
}

到此为止,我们可以将上述的几个类打成一个.jar包,下面我来测试一下。
先写一个Action类

@ActionClass
public class StudentAction {

	public StudentAction() {
	}
	
	@Action(name="login")
	public StudentInfo getUserById(@ActionParameter("id") String id
			,@ActionParameter("password") String password) {
		
		System.out.println("id : " + id );
		System.out.println("password : " + password);
		StudentInfo studentInfo = new StudentInfo();
		studentInfo.setId(id);
		studentInfo.setPassword(password);
		return studentInfo;
	}
}

写一个Model类

public class StudentInfo {
	private String id;
	private String password;
	
	public StudentInfo() {
	}

	public synchronized String getId() {
		return id;
	}

	public synchronized void setId(String id) {
		this.id = id;
	}

	public synchronized String getPassword() {
		return password;
	}

	public synchronized void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "id=" + id + ", password=" + password;
	}
}

进行测试:

public class Test {

	public static void main(String[] args) {
		Gson gson = new GsonBuilder().create();
		ActionBeanFactory.actionPackageScan("com.csframework.annotation.action");
		ActionExecuter actionExecuter = new ActionExecuter();
		try {
			String result = actionExecuter.doRequest("login",new ArgumentMaker()
					.addAction("id", "123123")
					.addAction("password", "dadasd") 
					.toString());
			
			 StudentInfo student = gson.fromJson(result, StudentInfo.class);
			 System.out.println(student);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

执行结果如下:
在这里插入图片描述
方法二:用xml文件配置+反射机制实现:
与方法一相比,方法二的扫描不是通过注解工具,而是通过xml文件解析工具XML文件的解析(工具思想)。将上述需要加注解的类,编写成xml文件,再扫描到actionPool里面,需要执行的时候,和方法一一样,找出来并执行。
来看具体代码:

public class ActionBeanFactory {
	private static final Map<String, ActionBeanDefinition> actionPool;
	
	static {
		actionPool = new HashMap<String, ActionBeanDefinition>();
	}
	//	将一个类设置进来,表明这个类里面存在着在外面需要反射机制执行的方法
	public static void setObject(String action, Object object) {
		ActionBeanDefinition actionBeanDefinition = actionPool.get(action);
		if (actionBeanDefinition != null && actionBeanDefinition.getObject() == null) {
			actionBeanDefinition.setObject(object);
		}
	}
	
	private static void getMethod(Element element
									,ActionBeanDefinition actionBeanDefinition) {
		String methodName = element.getAttribute("method");
		List<String> parameterTypes = new ArrayList<String>();

		new XmlParser() {
			@Override
			public void dealElement(Element element, int index) {
				String name = element.getAttribute("name");
				String typeName = element.getAttribute("type");
				actionBeanDefinition.add(name);
				parameterTypes.add(typeName);
			}
		}.parseTag(element, "parameter");;
		
		Class<?>[] parameters = new Class<?>[parameterTypes.size()];
		for (int index = 0;index < parameters.length;index++) {
			String type = parameterTypes.get(index);
			parameters[index] = MecType.toType(type);
		}
		Class<?> klass = actionBeanDefinition.getKlass();
		try {
			Method method = klass.getMethod(methodName, parameters);
			actionBeanDefinition.setMethod(method);
			
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
	}
	//xml文件解析工具
	public static void scanXml(String path) {
		new XmlParser() {
			@SuppressWarnings("unused")
			@Override
			public void dealElement(Element element, int index) {
				String actionName = element.getAttribute("name");
				String klassName = element.getAttribute("class");
				
				try {
					Class<?> klass = Class.forName(klassName);
					Object object = klass.newInstance();

					ActionBeanDefinition actionBeanDefinition = new ActionBeanDefinition();
					actionBeanDefinition.setKlass(klass);
					getMethod(element,actionBeanDefinition);
					
					actionPool.put(actionName, actionBeanDefinition); 
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}.parseTag(XmlParser.getDocument(path), "action");;
	}
	
	public static ActionBeanDefinition getAction(String action) {
		return actionPool.get(action);
	}
}

.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<actions>
	<action name="userLogin" class="com.csframework.model.UserLogin" method="getUserById">
		<parameter name="netId" type="string"></parameter>
		<parameter name="id" type="string"></parameter>
		<parameter name="password" type="string"></parameter>
	</action>
</actions>

IActionProcesser接口与方法一的IActionExecuter接口类似

public class DefaultActionProcesser implements IActionProcesser {

	public DefaultActionProcesser() {
	}

	@Override
	public String dealRequest(String action, String parameter) throws Exception{
		//需要进行扫描
		ActionBeanDefinition actionBeanDefinition = ActionBeanFactory.getAction(action);
		if (actionBeanDefinition == null) {
			throw new ActionDoNotFondException("actionBeanDefinition 未定义");
		}
		
		Object object = actionBeanDefinition.getObject();
		if (object == null) {
			object = actionBeanDefinition.getKlass().newInstance();
		}
		Method method = actionBeanDefinition.getMethod();
		Object[] parametersValue = getParameters(parameter,actionBeanDefinition,method);
		
		Object result = (String) method.invoke(object, parametersValue);
		//	返回Gson字符串
		return ArgumentMaker.gson.toJson(result);
	}

	private Object[] getParameters(String parameter,ActionBeanDefinition actionBeanDefinition,Method method) {
		Parameter[] parameters = method.getParameters();
		ArgumentMaker argumentMaker = new ArgumentMaker(parameter);
		List<String> parameterNames = actionBeanDefinition.getParameterList(); 
		
		Object[] objects = new Object[parameters.length];
		for (int index = 0;index < parameterNames.size();index++) {
			Object object = argumentMaker.getValue(parameterNames.get(index),
					parameters[index].getParameterizedType());
			objects[index] = object;
		}
		
		return objects;
	}
	
	@Override
	public void dealResponse(String action, String parameter) throws Exception{
		ActionBeanDefinition actionBeanDefinition = ActionBeanFactory.getAction(action);
		if (actionBeanDefinition == null) {
			throw new ActionDoNotFondException(action + "未定义");
		}
		
		Object object = actionBeanDefinition.getObject();
		Method method = actionBeanDefinition.getMethod();
		Parameter[] parameters = method.getParameters();
		Object[] parameterObject = new Object[1];
 		parameterObject[0] = ArgumentMaker.gson.fromJson(parameter, parameters[0].getParameterizedType());
		method.invoke(object, parameterObject);
	}
}

Model类

public class UserLogin {

	public UserLogin() {
	}
	//	为了简单化,这个方法的返回值为String类型
	public String getUserById(String netId,String id,String password) {
		System.out.println("执行了");
		return netId + id + password;
	}
}

Test类:

public class Test {

	public static void main(String[] args) {
		Gson gson = new GsonBuilder().create();
		ActionBeanFactory.scanXml("/chat_room_action.mapping.xml");
		DefaultActionProcesser actionProcessor = new DefaultActionProcesser();
		try {
			String result = actionProcessor.dealRequest("userLogin"
					,new ArgumentMaker().add("netId", "SSSSS")
					.add("id", "23123")
					.add("password", "ferd321").toString());
			System.out.println("result : " + result);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

执行结果:
在这里插入图片描述

总结

到这里,我们的这两套工具就可以完全独立出来。对比发现,第二套采用xml解析工具的分发器没有第一套灵活。现在我们只需要用户向我们的框架中注入规定的方法,而不去关心其具体实现,达到了解耦的目的。我们以后可以在服务器和客户端分别写出请求和响应的方法,通过网络间字符串的传递,在服务器和客户端分别执行我们注入的方法!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值