- 完成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; } }
相關代碼以下:css
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...java