Action的处理-分发机制

  • 在服务器客户机模式中,当客户机发送一个请求客户端应当如何处理呢?比如客户端发送一个登录的请求,服务器端应当去调用处理登录的方法,即服务器就要根据请求做出对应的响应。
  •     实际上其过程就是客户机上执行某一个方法,将方法返回值,通过网络中传输的流的方式传输给服务器,服务器找到该请求对应的响应方法,并执行,将结果再次通过字节流的方式传输给客户机!以此来进行通信。

但是客户端的请求是多种多样的,我们需要判断到低是哪种请求,比如下面这样写的代码不利于维护(且满眼的if else也尽显低级)。且也有类似在客户端需要执行响应的方法但方法会在不同的模块中,亦不能用if else去处理。

  public class Action{
    public void dealRequest(String stream) {
        if (请求是登录){
            判断ID和password是否正确   
        } else if (请求是退出) {
            判断当前能否退出
        } else if (请求是..) {
        判断...
        } else if (请求是..) {
            判断...
        } else if (请求是..) {
            判断...
        } else if (请求是..) {
            判断...
        } else if (请求是..) {
            判断...
        }
    }
}

于是我们可以在服务器端做一个可以根据请求自动去调用相应的方法的DealRequest分发器在客户端做一个DealResponse分发器(我们可以用注解标识的方法去解决)

可以用三个注解:此类可命名为ActionClass,拥有这个注解的类才可进行扫描,其表明此类中有要调用的方法

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(RUNTIME)
@Target(TYPE)
public @interface ActionClass {

}

此类可命名为Action,拥有这个注解的方法才可放入之后的Map中去,其中name可以当做map中的键值

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(RUNTIME)
@Target(METHOD)
public @interface Action {
	String name();
}

因为调用方法需要用到反射机制所以得获取参数的类型,则下面这个类可命名Para,用来标记方法的参数的,其中的name的赋值是为了以后找到对应的参数。

import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Retention(RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Para {
	String name();
}

此时当在需要调用的方法上写上这些注解,例子如下:

import com.htt.Annotation.Action;
import com.htt.Annotation.ActionClass;
import com.htt.Annotation.Para;
import com.htt.stu_mis.student.model.StudentModel;

@ActionClass
public class StudentAction {
	public StudentAction() {
	}
	
	@Action(name = "login")
	public StudentModel studentLogin( @Para(name = "id") String id,
	                                  @Para(name = "password") String password) {
		// 这里应该从数据库表中检测id和password的正确性
		// TODO 如果id和password都正确,则,返回除了password之外的所有有效信息
		// 否则,返回的StudentMode对象中的id值为"ERROR",其它信息无效。
		return student;
	}
}

此时若要将其放在map中,其键为方法上Action注解中name的值,值应该为此action的一个方法的抽象体。

则方法应该包装为一个类。此类名为ActionDefination 。

package com.htt.CSFramework.action;
import java.lang.reflect.Method;

public class ActionDefination {
	private Method method; // 此方法
	private Object object; // 执行此方法的对象
	
	public ActionDefination() {
	}
	
	 Object getObject() {
		return object;
	}

	public void setObject(Object object) {
		this.object = object;
	}
	
	 Method getMethod() {
		return method;
	}
	
	public void setMethod(Method method) throws NoSuchMethodException,  SecurityException {
		this.method = method;
	}
}

当以上的准备的工作做好之后,就应该去形成一个map了,map的形成可通过包扫描,扫描带有ActionClass注释的类,然后找到以类中有Action注解的方法中name的值为键,以ActionDefination 的实例为值。(其中包扫描的知识可在我的博客中查找,附上链接包解析博客点此查看。


 

package com.htt.CSFramework.action;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.htt.Annotation.Action;
import com.htt.Annotation.ActionClass;

public class ActionBeanFactory {
	private static final Map<String, ActionDefination> actionMap = new HashMap<>();

	public ActionBeanFactory() {
	}
	
//此方法是将执行方法的对象传进来,以防class.newinstance出来的对象空指针现象(比如一个view页面中控件失效)
	public static void addAction(Object object) {
//得到该类的class类型
		Class<?> klass = object.getClass();
//得到该类的所有方法
		Method[] methods = klass.getDeclaredMethods();
//遍历所有方法
		for (Method method : methods) {
			if (!method.isAnnotationPresent(Action.class)) {
				continue;
			}
//若有Action注解则形成方法抽象实例
			Action action = method.getAnnotation(Action.class);
			String name = action.name();
			
			ActionDefination defination = new ActionDefination();
			try {
				defination.setMethod(method);
				defination.setObject(object);
			} catch (NoSuchMethodException | SecurityException e) {
				e.printStackTrace();
			}
//以Action注解中name为键,以方法实例为值放入map中
			actionMap.put(name, defination);
		}
	}
	//包扫描(可查看上面附上的链接查看包扫描的博客)
	public static void scanPackage(String packageName) {
		new PackageScanner() {	                        
                    @Override
			public void dealClass(Class<?> klass) {
				if (klass.isAnnotation() || klass.isInterface()
				  || klass.isPrimitive() || klass.isEnum()
				  || !klass.isAnnotationPresent(ActionClass.class)) {
                                    //若此类不是含有ActionClass的注解则不予处理
					return;
				}
				try {
//用反射的方法的到执行方法的对象
					Object object = klass.newInstance();
					Method[] methods = klass.getDeclaredMethods();
					for (Method method : methods) {
						if (!method.isAnnotationPresent(Action.class)) {
							continue;
						}
						Action action = method.getAnnotation(Action.class);
						String name = action.name();
						
						ActionDefination defination = new ActionDefination();
						try {
							defination.setMethod(method);
							
						} catch (NoSuchMethodException | SecurityException e) {
							e.printStackTrace();
						}
						defination.setObject(object);
						actionMap.put(name, defination);
					}
				} catch (InstantiationException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
			}
		    }
		}.packageScanner(packageName);;
	}
//提供外部以方法查找
	static ActionDefination  getAction(String action) {
		return actionMap.get(action);
	}

	public static Set<String> getSet() {
		return actionMap.keySet();
	}
}

上面的ActionBeanFactory可以扫描到所需包下所有符合要求的方法,接下来就是通过传递参数执行这些方法。


但是在C/S中传递的信息是流所以传递的参数就只能是字符串或者字节流的形式,所以,我们应该对传递的参数进行处理,将其生成能供我们识别的形式,这里我们将参数转换为字符串的形式,会用到gson,将对象转换为gson字符串:

则此类可称为:ArgumentsMaker

package com.htt.CSFramework.action;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class ArgumentsMaker {
	//gson使用时可保证多线程安全
        //得到Gson对象
	private static final Gson gson = new GsonBuilder().create();
        //通过此Type可以将字符串转化为Map<String, String>的类型
        //如果不用
	private static final Type TYPE = new TypeToken<Map<String, String>>(){}.getType();	
	//用来存储参数的键,键和要发往对端后调用方法的Para注释中name的值对应,值为实际参数。
	private Map<String, String> argus;
	
	public ArgumentsMaker() {
		this.argus = new HashMap<>();
	}
        //通过gson将其构造成map形式
	public ArgumentsMaker(String para) {
		argus = gson.fromJson(para, TYPE);
	}
	
	public static Gson getGson() {
		return gson;
	}
        //通过Type和value得到实际的类型的值
	public <T> T getValue(String name, Type type) {
		String valueString = argus.get(name);
		if (valueString == null) {
			return null;
		}
		return gson.fromJson(valueString, type);
	}
        //往map中加入要传入的参数
	public ArgumentsMaker addArgument(String name, Object value) {
		argus.put(name, gson.toJson(value));
		return this;
	}
        //通过此法将map转化为gson字符串
	@Override
	public String toString() {
		return gson.toJson(argus);
	}
}

接下来就可以做一个分发器的类,其功能有二:

  1. 得到参数数组,通过上面的ArgumentsMaker类来得到参数数组;
  2. 通过反射机制调用要执行的方法。(这个分发器包括DealRequest 和DealResponse)

用到的工具有json-2.7.jar 可搜索 若找不到可 邮件 联系我发你([email protected])

此类名字可称为:Action

package com.htt.CSFramework.action;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Map;

import com.google.gson.reflect.TypeToken;
import com.htt.Annotation.Para;

public class Action implements IAction {
        //准备好type解开对端发来的gson流的字符串
	private static final Type TYPE = new TypeToken<Map<String, String>>(){}.getType();
	
	//得到要调用函数的参数
	private Object[] getParaValues(String para, Method method) throws Exception {
		Map<String, String> paraMap;
		paraMap = ArgumentsMaker.getGson().fromJson(para, TYPE);
		
                //若参数map为空则默认为无参
		if (paraMap.isEmpty()) {
			return new Object[] {};
		}
		//得到方参数的数组
		Object[] values = new Object[paraMap.size()];
		//通过method和para注解的到参数数组
		Parameter[] parameters = method.getParameters();
		int i = 0;
		for (Parameter parameter : parameters) {
			if (!parameter.isAnnotationPresent(Para.class)) {
				throw new Exception("参数" +  parameter.getName()  + "没有para注解");
			}
			Para paraAnno = parameter.getAnnotation(Para.class);
			得到将要在map中查找的键值
			String paraName = paraAnno.name();
                        //得到参数的类型
                        //其中getParameterizedType()可得到参数的泛型参数类型
			Type paraType = parameter.getParameterizedType();
			String paraValue = paraMap.get(paraName);
                        //将参数一一对应放入参数数组
			values[i++] = ArgumentsMaker.getGson().fromJson(paraValue, paraType);
		}
		return values;
	}
	
	@Override
	public Object dealRequest(String action, String para) throws Exception {
                //通过action得到此方法的抽象实例,action与Action注释类中的name对应
		ActionDefination defination = ActionBeanFactory.getAction(action);
		if (defination == null) {
			throw new Exception("没有对应的实体");
		}
		Object object = defination.getObject();
		Method method = defination.getMethod();
		Object result = null;
		//通过反射机制执行此方法
		result = method.invoke(object, getParaValues(para, method));
		return result;
	}
        //因为response执行的必然是客户端的方法,而要执行的是服务器发来的参数,只有一个
	@Override
	public void dealReseponse(String action, String para) throws Exception {
		ActionDefination defination = ActionBeanFactory.getAction(action);
		if (defination == null) {
			throw new Exception("没有对应的实体");
		}
		Object object = defination.getObject();
		Method method = defination.getMethod();
		Parameter[] parameters = method.getParameters();
		int paraCount = parameters.length;
		if (paraCount > 1) {
			throw new Exception("method" + method.getName() + "参数大于1个");
		}
		Object[] values = null;
		if (paraCount == 0) {
			values = new Object[] {};
		} else {
			values = new Object[1];
			Type paraType = parameters[0].getParameterizedType();
			values[0] = ArgumentsMaker.getGson().fromJson(para, paraType);
		}
		method.invoke(object, values);
	}
}

以下为演示的

此为demoAction

package com.htt.CSFramework.action;

import com.htt.Annotation.Action;
import com.htt.Annotation.ActionClass;
import com.htt.Annotation.Para;

@ActionClass
public class DemoAction {
	
	@Action(name = "one")
	public void FunOne() {
		System.out.println("FunOne : 无参");
		System.out.println();
	}
	
	@Action(name = "two")
	public void FunTwo(
			@Para(name = "arg1") String a) {
		System.out.println("FunTwo : 一参");
		System.out.println("a : " + a);
		System.out.println();
	}
	
	@Action(name = "three")
	public void FunThree(
			@Para(name = "我是1") int a,
			@Para(name = "我是2") int b) {
		System.out.println("FunThree : 双参");
		System.out.println("a : " + a + " b : " + b);
		System.out.println();
	}
}

此为Deom

package com.htt.CSFramework.action;

public class Demo {

	public static void main(String[] args) {
		ActionBeanFactory.scanPackage("com.htt.CSFramework.action");
		Action ac = new Action();
		try {
			ac.dealRequest("one", new ArgumentsMaker().toString());
			
			ac.dealResponse("two", new ArgumentsMaker()
					.addArgument("arg1", "我是two里的a").toString());
			
			ac.dealRequest("three", new ArgumentsMaker().addArgument("我是1", 3)
					.addArgument("我是2", 4).toString());
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

最后的结果为此:

这个可以作为一个小框架,以后要使用可直接用注释的方法来标记即可,达到了不去管其的方法是如何写的在哪写的,执行即可,达到了解耦的效果,其也可用xml文档实现,但xml代码实现过于多,没有此简洁。

猜你喜欢

转载自blog.csdn.net/baidu_41922630/article/details/101453692
今日推荐