1. 本章须要实现的内容:
- 实现 ControllerRequestProcessor 类的编写:Controller 申请处理器
- 实现 JspRequestProcessor 类的编写:jsp 资源申请解决
- 实现 PreRequestProcessor 类的编写:申请预处理, 包含编码以及门路解决
- 实现 StaticResourceRequestProcessor 类的编写:动态资源申请解决,包含但不限于图片,css,以及 js 文件等, 转发到 DefaultServlet
2. PreRequestProcessor 类的编写
2.1 PreRequestProcessor 类须要实现的代码
package org.myframework.mvc.processor.impl;
/**
* @author wuyiccc
* @date 2020/6/16 18:54
* 岂曰无衣,与子同袍~
*/
import lombok.extern.slf4j.Slf4j;
import org.myframework.mvc.RequestProcessorChain;
import org.myframework.mvc.processor.RequestProcessor;
/**
* 申请预处理, 包含编码以及门路解决
*/
@Slf4j
public class PreRequestProcessor implements RequestProcessor {
@Override
public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
// 1. 设置申请编码,将其对立设置成 UTF-8
requestProcessorChain.getRequest().setCharacterEncoding("UTF-8");
// 2. 将申请门路开端的 / 剔除,为后续匹配 Controller 申请门路做筹备
//(个别 Controller 的解决门路是 /aaa/bbb,所以如果传入的门路结尾是 /aaa/bbb/,// 就须要解决成 /aaa/bbb)String requestPath = requestProcessorChain.getRequestPath();
//http://localhost:8080/myframework requestPath="/"
if (requestPath.length() > 1 && requestPath.endsWith("/")) {requestProcessorChain.setRequestPath(requestPath.substring(0, requestPath.length() - 1));
}
log.info("preprocess request {} {}", requestProcessorChain.getRequestMethod(), requestProcessorChain.getRequestPath());
return true;
}
}
2.2 PreRequestProcessor 类相干代码解说:
3. StaticResourceRequestProcessor 类的编写
3.1 StaticResourceRequestProcessor 类须要实现的代码:
package com.wuyiccc.helloframework.mvc.processor.impl;
import com.wuyiccc.helloframework.mvc.RequestProcessorChain;
import com.wuyiccc.helloframework.mvc.processor.RequestProcessor;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
/**
* @author wuyiccc
* @date 2020/7/14 22:40
* 岂曰无衣,与子同袍~
*/
@Slf4j
public class StaticResourceRequestProcessor implements RequestProcessor {
public static final String DEFAULT_TOMCAT_SERVLET = "default";
public static final String STATIC_RESOURCE_PREFIX = "/static/";
//tomcat 默认申请派发器 RequestDispatcher 的名称
RequestDispatcher defaultDispatcher;
public StaticResourceRequestProcessor(ServletContext servletContext) {this.defaultDispatcher = servletContext.getNamedDispatcher(DEFAULT_TOMCAT_SERVLET);
if (this.defaultDispatcher == null) {throw new RuntimeException("There is no default tomcat servlet");
}
log.info("The default servlet for static resource is {}", DEFAULT_TOMCAT_SERVLET);
}
@Override
public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
//1. 通过申请门路判断是否是申请的动态资源 webapp/static
if (isStaticResource(requestProcessorChain.getRequestPath())) {
//2. 如果是动态资源,则将申请转发给 default servlet 解决
defaultDispatcher.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse());
return false;
}
return true;
}
// 通过申请门路前缀(目录)是否为动态资源 /static/
private boolean isStaticResource(String path) {return path.startsWith(STATIC_RESOURCE_PREFIX);
}
}
3.2 StaticResourceRequestProcessor 类相干代码解说:
4. JspRequestProcessor
4.1 JspRequestProcessor 须要实现的代码:
package org.myframework.mvc.processor.impl;
import org.myframework.mvc.RequestProcessorChain;
import org.myframework.mvc.processor.RequestProcessor;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
/**
* @author wuyiccc
* @date 2020/6/16 18:59
* 岂曰无衣,与子同袍~
*/
/**
* jsp 资源申请解决
*/
public class JspRequestProcessor implements RequestProcessor {
//jsp 申请的 RequestDispatcher 的名称
private static final String JSP_SERVLET = "jsp";
//Jsp 申请资源门路前缀
private static final String JSP_RESOURCE_PREFIX = "/templates/";
/**
* jsp 的 RequestDispatcher, 解决 jsp 资源
*/
private RequestDispatcher jspServlet;
public JspRequestProcessor(ServletContext servletContext) {jspServlet = servletContext.getNamedDispatcher(JSP_SERVLET);
if (null == jspServlet) {throw new RuntimeException("there is no jsp servlet");
}
}
@Override
public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {if (isJspResource(requestProcessorChain.getRequestPath())) {jspServlet.forward(requestProcessorChain.getRequest(), requestProcessorChain.getResponse());
return false;
}
return true;
}
/**
* 是否申请的是 jsp 资源
*/
private boolean isJspResource(String url) {return url.startsWith(JSP_RESOURCE_PREFIX);
}
}
4.2 JspRequestProcessor 相干代码解说:
5. ControllerRequestProcessor 类的编写
5.1 ControllerRequestProcessor 须要实现的代码:
package org.myframework.mvc.processor.impl;
import lombok.extern.slf4j.Slf4j;
import org.myframework.core.BeanContainer;
import org.myframework.mvc.RequestProcessorChain;
import org.myframework.mvc.annotation.RequestMapping;
import org.myframework.mvc.annotation.RequestParam;
import org.myframework.mvc.annotation.ResponseBody;
import org.myframework.mvc.processor.RequestProcessor;
import org.myframework.mvc.render.JsonResultRender;
import org.myframework.mvc.render.ResourceNotFoundResultRender;
import org.myframework.mvc.render.ResultRender;
import org.myframework.mvc.render.ViewResultRender;
import org.myframework.mvc.type.ControllerMethod;
import org.myframework.mvc.type.RequestPathInfo;
import org.myframework.util.ConverterUtil;
import org.myframework.util.ValidationUtil;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author wuyiccc
* @date 2020/6/16 19:14
* 岂曰无衣,与子同袍~
*/
/**
* Controller 申请处理器
*/
@Slf4j
public class ControllerRequestProcessor implements RequestProcessor {
//IOC 容器
private BeanContainer beanContainer;
// 申请和 controller 办法的映射汇合
private Map<RequestPathInfo, ControllerMethod> pathControllerMethodMap = new ConcurrentHashMap<>();
/**
* 依附容器的能力,建设起申请门路、申请办法与 Controller 办法实例的映射
*/
public ControllerRequestProcessor() {this.beanContainer = BeanContainer.getInstance();
Set<Class<?>> requestMappingSet = beanContainer.getClassesByAnnotation(RequestMapping.class);
initPathControllerMethodMap(requestMappingSet);
}
private void initPathControllerMethodMap(Set<Class<?>> requestMappingSet) {if (ValidationUtil.isEmpty(requestMappingSet)) {return;}
//1. 遍历所有被 @RequestMapping 标记的类,获取类下面该注解的属性值作为一级门路
for (Class<?> requestMappingClass : requestMappingSet) {RequestMapping requestMapping = requestMappingClass.getAnnotation(RequestMapping.class);
String basePath = requestMapping.value();
if (!basePath.startsWith("/")) {basePath = "/" + basePath;}
//2. 遍历类里所有被 @RequestMapping 标记的办法,获取办法下面该注解的属性值,作为二级门路
Method[] methods = requestMappingClass.getDeclaredMethods();
if (ValidationUtil.isEmpty(methods)) {continue;}
for (Method method : methods) {if (method.isAnnotationPresent(RequestMapping.class)) {RequestMapping methodRequest = method.getAnnotation(RequestMapping.class);
String methodPath = methodRequest.value();
if (!methodPath.startsWith("/")) {methodPath = "/" + basePath;}
String url = basePath + methodPath;
//3. 解析办法里被 @RequestParam 标记的参数,// 获取该注解的属性值,作为参数名,// 获取被标记的参数的数据类型,建设参数名和参数类型的映射
Map<String, Class<?>> methodParams = new HashMap<>();
Parameter[] parameters = method.getParameters();
if (!ValidationUtil.isEmpty(parameters)) {for (Parameter parameter : parameters) {RequestParam param = parameter.getAnnotation(RequestParam.class);
// 目前暂定为 Controller 办法外面所有的参数都须要 @RequestParam 注解
if (param == null) {throw new RuntimeException("The parameter must have @RequestParam");
}
methodParams.put(param.value(), parameter.getType());
}
}
//4. 将获取到的信息封装成 RequestPathInfo 实例和 ControllerMethod 实例,搁置到映射表里
String httpMethod = String.valueOf(methodRequest.method());
RequestPathInfo requestPathInfo = new RequestPathInfo(httpMethod, url);
if (this.pathControllerMethodMap.containsKey(requestPathInfo)) {log.warn("duplicate url:{} registration,current class {} method{} will override the former one",
requestPathInfo.getHttpPath(), requestMappingClass.getName(), method.getName());
}
ControllerMethod controllerMethod = new ControllerMethod(requestMappingClass, method, methodParams);
this.pathControllerMethodMap.put(requestPathInfo, controllerMethod);
}
}
}
}
@Override
public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
//1. 解析 HttpServletRequest 的申请办法, 申请门路,获取对应的 ControllerMethod 实例
String method = requestProcessorChain.getRequestMethod();
String path = requestProcessorChain.getRequestPath();
ControllerMethod controllerMethod = this.pathControllerMethodMap.get(new RequestPathInfo(method, path));
if (controllerMethod == null) {requestProcessorChain.setResultRender(new ResourceNotFoundResultRender(method, path));
return false;
}
//2. 解析申请参数,并传递给获取到的 ControllerMethod 实例去执行
Object result = invokeControllerMethod(controllerMethod, requestProcessorChain.getRequest());
//3. 依据解决的后果,抉择对应的 render 进行渲染
setResultRender(result, controllerMethod, requestProcessorChain);
return true;
}
/**
* 依据不同状况设置不同的渲染器
*/
private void setResultRender(Object result, ControllerMethod controllerMethod, RequestProcessorChain requestProcessorChain) {if (result == null) {return;}
ResultRender resultRender;
boolean isJson = controllerMethod.getInvokeMethod().isAnnotationPresent(ResponseBody.class);
if (isJson) {resultRender = new JsonResultRender(result);
} else {resultRender = new ViewResultRender(result);
}
requestProcessorChain.setResultRender(resultRender);
}
private Object invokeControllerMethod(ControllerMethod controllerMethod, HttpServletRequest request) {
//1. 从申请里获取 GET 或者 POST 的参数名及其对应的值
Map<String, String> requestParamMap = new HashMap<>();
//GET,POST 办法的申请参数获取形式
Map<String, String[]> parameterMap = request.getParameterMap();
for (Map.Entry<String, String[]> parameter : parameterMap.entrySet()) {if (!ValidationUtil.isEmpty(parameter.getValue())) {
// 只反对一个参数对应一个值的模式
requestParamMap.put(parameter.getKey(), parameter.getValue()[0]);
}
}
//2. 依据获取到的申请参数名及其对应的值,以及 controllerMethod 外面的参数和类型的映射关系,去实例化出办法对应的参数
List<Object> methodParams = new ArrayList<>();
Map<String, Class<?>> methodParamMap = controllerMethod.getMethodParameters();
for (String paramName : methodParamMap.keySet()) {Class<?> type = methodParamMap.get(paramName);
String requestValue = requestParamMap.get(paramName);
Object value;
// 只反对 String 以及根底类型 char,int,short,byte,double,long,float,boolean, 及它们的包装类型
if (requestValue == null) {
// 将申请里的参数值转成适配于参数类型的空值
value = ConverterUtil.primitiveNull(type);
} else {value = ConverterUtil.convert(type, requestValue);
}
methodParams.add(value);
}
//3. 执行 Controller 外面对应的办法并返回后果
Object controller = beanContainer.getBean(controllerMethod.getControllerClass());
Method invokeMethod = controllerMethod.getInvokeMethod();
invokeMethod.setAccessible(true);
Object result;
try {if (methodParams.size() == 0) {result = invokeMethod.invoke(controller);
} else {result = invokeMethod.invoke(controller, methodParams.toArray());
}
} catch (InvocationTargetException e) {// 如果是调用异样的话,须要通过 e.getTargetException()
// 去获取执行办法抛出的异样
throw new RuntimeException(e.getTargetException());
} catch (IllegalAccessException e) {throw new RuntimeException(e);
}
return result;
}
}
5.2 ControllerRequestProcessor 相干代码解说
6. ConverterUtil 值转换工具类的补充
相干代码如下:
package com.wuyiccc.helloframework.util;
/**
* @author wuyiccc
* @date 2020/7/15 8:02
* 岂曰无衣,与子同袍~
*/
public class ConverterUtil {
/**
* 返回根本数据类型的空值
* 须要非凡解决的根本类型即 int\double\short\long\byte\float\boolean
*
* @param type 参数类型
* @return 对应的空值
*/
public static Object primitiveNull(Class<?> type) {if (type == int.class || type == double.class || type == short.class || type == long.class || type == byte.class || type == float.class) {return 0;} else if (type == boolean.class) {return false;}
return null;
}
/**
* String 类型转换成对应的参数类型
*
* @param type 参数类型
* @param requestValue 值
* @return 转换后的 Object
*/
public static Object convert(Class<?> type, String requestValue) {if (isPrimitive(type)) {if (ValidationUtil.isEmpty(requestValue)) {return primitiveNull(type);
}
if (type.equals(int.class) || type.equals(Integer.class)) {return Integer.parseInt(requestValue);
} else if (type.equals(String.class)) {return requestValue;} else if (type.equals(Double.class) || type.equals(double.class)) {return Double.parseDouble(requestValue);
} else if (type.equals(Float.class) || type.equals(float.class)) {return Float.parseFloat(requestValue);
} else if (type.equals(Long.class) || type.equals(long.class)) {return Long.parseLong(requestValue);
} else if (type.equals(Boolean.class) || type.equals(boolean.class)) {return Boolean.parseBoolean(requestValue);
} else if (type.equals(Short.class) || type.equals(short.class)) {return Short.parseShort(requestValue);
} else if (type.equals(Byte.class) || type.equals(byte.class)) {return Byte.parseByte(requestValue);
}
return requestValue;
} else {throw new RuntimeException("count not support non primitive type conversion yet");
}
}
/**
* 断定是否根本数据类型 (包含包装类以及 String)
*
* @param type 参数类型
* @return 是否为根本数据类型
*/
private static boolean isPrimitive(Class<?> type) {
return type == boolean.class
|| type == Boolean.class
|| type == double.class
|| type == Double.class
|| type == float.class
|| type == Float.class
|| type == short.class
|| type == Short.class
|| type == int.class
|| type == Integer.class
|| type == long.class
|| type == Long.class
|| type == String.class
|| type == byte.class
|| type == Byte.class
|| type == char.class
|| type == Character.class;
}
}
github 地址:https://github.com/wuyiccc/he…