- 完成ControllerRequestProcessor类的编写: Controller请求处理器
- 完成JspRequestProcessor类的编写:jsp资源请求处理
- 完成PreRequestProcessor类的编写: 请求预处理,包括编码以及路径处理
- 完成StaticResourceRequestProcessor类的编写: 静态资源请求处理,包括但不限于图片,css,以及js文件等, 转发到DefaultServlet
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; } }
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); } }
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); } }
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; } }
相关代码如下:
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...