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.User
List<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 --> Object
obj = 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);
}