DispatcherServlet:分发、调度
根据上一节,已经实现了将controller的方法添加到容器中,而DispatcherServlet的作用就是接收来自客户端的请求,然后通过URI的组合,来找到对应的@RequestMapping注解的方法,调用对应的方法,最后返回响应
第一步:获取URI,根据URI来匹配对应的BeanDefinition
String requestURI = req.getRequestURI();Map<String, BeanDefinition<?>> maps = BeanContainer.getMaps();//通过匹配URI来找到对应的BeanDefinition
BeanDefinition<?> beanDefinition = maps.get(requestURI);
if (beanDefinition == null) {throw new FrameWorkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(), ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());
}
第二步:获取容器中的BeanDefinition的MethodDefinition和ParameterDefinition
//获取对应的controller类对象
Object t = beanDefinition.getT();
MethodDefinition methodDefinition = beanDefinition.getMethodDefinition();
//获取方法对象
Method method = methodDefinition.getMethod();
method.setAccessible(true);
//获取参数列表
List<ParameterDefinition> parameterDefinitions = methodDefinition.getParameters();
第三步:调用对应的方法
Object[] args;
Model model = new Model();try {args = handlerParameterArgs(parameterDefinitions, req, resp, model);//调用Controller层里某个方法Object returnVal = method.invoke(t, args);if (returnVal != null) {//处理返回值handlerReturnVal(methodDefinition, returnVal, req, resp, model);}
} catch (Exception e) {System.out.println(Arrays.toString(e.getStackTrace()));;
}
handlerParameterArgs:将调用的方法的参数列表与请求数据适配
/*** 集中处理参数的函数,通过判断参数的类型,对不同类型的参数进行处理,包括* 1. 常见参数类型:八大基本数据类型及其包装类 + String* 2. 数组类型* 3. HttpServletRequest 类型* 4. httpServletResponse 类型* 5. List<?> 类型* 6. 自定义类型** @param parameterDefinitions 参数描述对象列表(从controller的方法下抽取出来的)* @param req 请求对象* @param resp 响应对象* @param model 数据体(应该是,里面是Map,key为数据名,value为数据体,最后通过装载到request对象转发出去)* @return 参数列表 Object[] args*/
public Object[] handlerParameterArgs(List<ParameterDefinition> parameterDefinitions, HttpServletRequest req, HttpServletResponse resp, Model model) throws ClassNotFoundException {if (parameterDefinitions == null) {return null;}//实际参数的列表Object[] args = new Object[parameterDefinitions.size()];//将请求中的参数添加到args中for (ParameterDefinition parameterDefinition : parameterDefinitions) {String name = parameterDefinition.getParameterName();//参数名Class<?> type = parameterDefinition.getType();//参数类型int index = parameterDefinition.getIndex();//参数下标if (judgeTypeIsJavaOrNot(type)) {//常见数据类型handlerJavaType(req, name, type, args, index);} else if (type == HttpServletRequest.class) {//请求类型args[index] = req;} else if (type == HttpServletResponse.class) {//相应类型args[index] = resp;} else if (type == String[].class) {//数组类型String[] parameterValues = req.getParameterValues(name);args[index] = parameterValues;} else if (type == List.class) {//集合类型handlerListType(parameterDefinition, req, args, index);} else if (type == Model.class) {//Model类型args[index] = model;} else {//自定义类型handlerOtherType(parameterDefinition, req, args, index);}}return args;
}
judgeTypeIsJavaOrNot:判断方法参数是否是常见数据类型
常见参数类型:八大基本数据类型及其包装类 + String
private static final Class<?>[] COMMON_CLASSES = new Class[]{byte.class, short.class, int.class, long.class, float.class, double.class, char.class,Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Character.class,String.class};
/*** 判断参数的类型是不是常见类型 [COMMON_CLASSES]** @param type 参数类型* @return 是否为常见类型*/
public boolean judgeTypeIsJavaOrNot(Class<?> type) {for (Class<?> clazz : COMMON_CLASSES) {if (type == clazz) {return true;}}return false;
}
handlerJavaType:处理常见参数
/*** 处理不同的常见数据类型,将其转化为对应的数据并添加到参数列表对应的位置** @param req 请求对象* @param name 字段名* @param type 字段类型* @param args 参数列表* @param index 参数在方法中的下标*/
public void handlerJavaType(HttpServletRequest req, String name, Class<?> type, Object[] args, int index) {String parameter = req.getParameter(name);if (type == byte.class || type == Byte.class) {args[index] = Byte.parseByte(parameter);} else if (type == short.class || type == Short.class) {args[index] = Short.parseShort(parameter);} else if (type == int.class || type == Integer.class) {args[index] = Integer.parseInt(parameter);} else if (type == long.class || type == Long.class) {args[index] = Long.parseLong(parameter);} else if (type == float.class || type == Float.class) {args[index] = Float.parseFloat(parameter);} else if (type == double.class || type == Double.class) {args[index] = Double.parseDouble(parameter);} else if (type == char.class || type == Character.class) {args[index] = parameter.toCharArray()[0];} else if (type == boolean.class || type == Boolean.class) {args[index] = Boolean.parseBoolean(parameter);}if (type == String.class) {args[index] = parameter;}
}
handlerListType:处理List参数
/*** 处理方法的参数是集合类型的方法,如果参数是List集合,那么要将List中的泛型取出来并设置对应的属性* 最后将泛型对应的对象添加到List中,再将List添加到参数列表中** @param parameterDefinition 参数描述对象(包含泛型的类型)* @param req 请求对象* @param args 参数列表* @param index 参数对应的下标*/
public void handlerListType(ParameterDefinition parameterDefinition, HttpServletRequest req, Object[] args, int index) throws ClassNotFoundException {Type[] types = parameterDefinition.getTypes();//参数的泛型列表Type genericType = types[0];//泛型列表String typeName = genericType.getTypeName();//泛型的名称 cn.cnmd.pojo.UserList<Object> list = new ArrayList<>();Map<String, String[]> parameterMap = req.getParameterMap();Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();for (Map.Entry<String, String[]> entry : entries) {String key = entry.getKey();String fieldValue = entry.getValue()[0];int i = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")));String fieldName = key.substring(key.indexOf(".") + 1);Class<?> aClass = Class.forName(typeName);Object o = null;try {o = list.get(i);} catch (IndexOutOfBoundsException e) {//该集合下标上没有元素try {o = aClass.newInstance();//创建对象list.add(o);} catch (InstantiationException | IllegalAccessException ex) {throw new RuntimeException(ex);}}try {BeanUtils.setProperty(o, fieldName, fieldValue);} catch (IllegalAccessException | InvocationTargetException e) {throw new RuntimeException(e);}}args[index] = list;
}
handlerOtherType:处理自定义参数
/*** 处理自定义类型** @param parameterDefinition 参数描述对象* @param req 请求对象* @param args 参数列表* @param index 参数下标*/
public void handlerOtherType(ParameterDefinition parameterDefinition, HttpServletRequest req, Object[] args, int index) {try {Object obj;//如果参数上带有@RequestBody注解则会将JSON字符串转化为对象if (parameterDefinition.isRequestBodyHasOrNot()) {BufferedReader reader = req.getReader();StringBuffer sb = new StringBuffer();char[] cs = new char[1024];int len;while ((len = reader.read(cs)) != -1) {sb.append(cs, 0, len);}//String --> Objectobj = objectMapper.readValue(sb.toString(), parameterDefinition.getType());} else { //如果不带@RequestBody注解,则正常当作自定义对象处理obj = parameterDefinition.getType().newInstance();Map<String, String[]> parameterMap = req.getParameterMap();BeanUtils.populate(obj, parameterMap);}args[index] = obj;} catch (InstantiationException | IllegalAccessException | InvocationTargetException | IOException e) {throw new RuntimeException(e);}
}
handlerRequestVal:将Model中的k-v添加到request请求体中
/*** 将model数据对象装载到request对象中** @param request 请求体对象* @param map model数据对象的map(k-v存储了需要传递给前端的数据)*/
public void handlerRequestVal(HttpServletRequest request, Map<String, Object> map) {Set<Map.Entry<String, Object>> entries = map.entrySet();for (Map.Entry<String, Object> entry : entries) {String key = entry.getKey();Object value = entry.getValue();request.setAttribute(key, value);}
}
handlerReturnVal:返回响应的总调度(按照方法的返回类型)
- 如果是String类型,说明返回值是一个URI,ps:‘/user/index.jsp’,就将数据加载到request对象中,跳转到该页面
- 如果是ModelAndView类型,说明返回值是一个ModelAndView对象[ Model数据体对象 + URI],再将数据加载到request对象中,通过ModelANdView.getViewName()跳转
- 如果是JSON数据[方法上带有@ResponseBody注解],就直接将数据发送到前端
/*** 处理返回值的函数,通过判断URI调用对应的方法的返回值类型,选择不同返回逻辑* 1.如果是String类型,说明返回值是一个URI,ps:'/user/index.jsp',就将数据加载到request对象中,跳转到该页面* 2.如果是ModelAndView类型,说明返回值是一个ModelAndView对象[ Model数据体对象 + URI],再将数据加载到request对象中,通过ModelANdView.getViewName()跳转* 3.如果是JSON数据[方法上带有@ResponseBody注解],就直接将数据发送到前端** @param methodDefinition 方法描述对象* @param returnVal 调用方法的返回值对象* @param request 请求体对象* @param response 响应体对象* @param model 数据体对象*/
public void handlerReturnVal(MethodDefinition methodDefinition, Object returnVal, HttpServletRequest request, HttpServletResponse response, Model model) throws ServletException, IOException {//如果返回的是一个String, 代表直接跳转,ps:'user/login2.action'if (returnVal.getClass() == String.class) {handlerRequestVal(request, model.getMap());jumpPage(returnVal, request, response);//如果返回的是ModelAndView对象,那么代表跳转的地址作为属性被注入到ModelAndView对象中,通过modelAndView.getViewName()跳转} else if (returnVal.getClass() == ModelAndView.class) {ModelAndView modelAndView = (ModelAndView) returnVal;handlerRequestVal(request, modelAndView.getMap());jumpPage(modelAndView.getViewName(), request, response);//如果这个方法上有@REsponseBody注解,则直接将returnVal转化为JSON字符串传出} else if (methodDefinition.isResponseBodyHasOrNot()) {String jsonStr = objectMapper.writeValueAsString(returnVal);sendResponse(jsonStr, response);}}
jumpPage:跳转页面
/*** 页面跳转函数** @param uri 需要跳转的URI* @param request 请求体对象(里面携带了从model装载的数据)* @param response 相应体对象*/
public void jumpPage(Object uri, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {String view = (String) uri;request.getRequestDispatcher(view).forward(request, response);
}
sendResponse:直接返回响应
/*** 向前端发送JSON数据* @param jsonStr JSON字符串* @param response 相应提对象*/
public void sendResponse( String jsonStr,HttpServletResponse response) throws IOException {response.getWriter().write(jsonStr);
}