手写springmvc
手寫springmvc
既然已經手寫了spring的IOC,那springmvc肯定也要嘗試寫寫了。手寫spring博客:https://www.cnblogs.com/xiaojiesir/p/11139203.html
SpringMVC的運行流程:
?
(1)首先瀏覽器發送請求——>DispatcherServlet,前端控制器收到請求后自己不進行處理,而是委托給其他的解析器進行處理,作為統一訪問點,進行全局的流程控制;
(2)DispatcherServlet——>HandlerMapping,處理器映射器將會把請求映射為HandlerExecutionChain對象(包含一個Handler處理器對象、多個HandlerInterceptor攔截器)對象;
(3)DispatcherServlet——>HandlerAdapter,處理器適配器將會把處理器包裝為適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持很多類型的處理器;
(4)HandlerAdapter——>調用處理器相應功能處理方法,并返回一個ModelAndView對象(包含模型數據、邏輯視圖名);
(5)ModelAndView對象(Model部分是業務對象返回的模型數據,View部分為邏輯視圖名)——> ViewResolver, 視圖解析器將把邏輯視圖名解析為具體的View;
(6)View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構;
(7)返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。
?
梳理SpringMVC的設計思路
在沒有使用框架前,我們是使用servlet來實現前后端交互的,所以springmvc說到底還是在servlet基礎上展開的。如果對servlet不是很熟悉的話,需要先學習下servlet。
框架只是方便開發,底層知識才是最重要的。在掌握底層知識的基礎上,再學習框架的設計理念來成長自己。
Servlet?生命周期
Servlet 生命周期可被定義為從創建直到毀滅的整個過程。以下是 Servlet 遵循的過程:
- Servlet 通過調用?init ()?方法進行初始化。
- Servlet 調用?service()?方法來處理客戶端的請求。
- Servlet 通過調用?destroy()?方法終止(結束)。
- 最后,Servlet 是由 JVM 的垃圾回收器進行垃圾回收的。
?
根據servlet生命周期來看,我們可以在init()方法中初始化基本的beans,并實例化controller層中的定義的service的變量,同時實現映射URL請求的Path和方法。
而service方法我們用doget方法和dopost方法。doPost()主要是實現參數的解析,并通過反射的機制實現方法的調用。
?
這里只粘貼了部分代碼,具體代碼可以看下:https://github.com/xiaojiesir/handwritingspringmvc
項目結構
配置web.xml
使用servlet前,我們需要再web.xml中配置以下代碼
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><servlet><servlet-name>mvc</servlet-name><servlet-class>com.springframework.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>application.properties</param-value></init-param><!-- Servlet 就會在服務器啟動 時執行了。(注意:如果設置為負整數或者不配置,則不會在啟動 服務器時執行,而要等此Servlet 被調用時才會被執行。 )--><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>mvc</servlet-name><url-pattern>/*</url-pattern></servlet-mapping></web-app><url-pattern>/*</url-pattern> 這種攔截匹配規則其實是一種錯誤方式,因為只是簡易springmvc框架,不需要jsp,所以沒問題。具體可以百度了解這方面知識。
實現init方法
@Overridepublic void init(ServletConfig config) throws ServletException {System.out.println("init方法");//1.加載配置文件 doLoadCongig(config.getInitParameter(LOCATION));//2.掃描所有相關的類doScanner(p.getProperty("scanPackage"));//3.初始化所有的相關類的實例,并保存到IOC容器中 doInstance();//4.依賴注入 doAutowired();//5.構造HandlerMapping initHandlerMapping();//6.等待請求,匹配URL,定位方法,反射調用執行//調用doGet或者doPost方法//提示信息System.out.println("my springmvc is success");}init方法中的1.2.3.4可以參考之前手寫spring中的代碼,我詳細講解下5
第五步遍歷容器中的bean,利用@MyRequestMapping注解將url與方法做映射
private void initHandlerMapping() {if (ioc.isEmpty()) {return;}try {for (Entry<String, Object> entry : ioc.entrySet()) {Class<? extends Object> clazz = entry.getValue().getClass();if (!clazz.isAnnotationPresent(MyController.class)) {continue;}// 拼url時,是controller頭的url拼上方法上的urlString baseUrl = "";if (clazz.isAnnotationPresent(MyRequestMapping.class)) {MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);baseUrl = annotation.value();}Method[] methods = clazz.getMethods();for (Method method : methods) {if (!method.isAnnotationPresent(MyRequestMapping.class)) {continue;}MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);String url = annotation.value();url = (baseUrl + "/" + url).replaceAll("/+", "/");handlerMapping.put(url, method);System.out.println(url + "," + method);}}} catch (Exception e) {e.printStackTrace();}}實現dopost方法
根據請求url,獲取method,根據@MyRequestParam注解獲取方法參數并賦值,利用反射執行方法
@Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// TODO Auto-generated method stubtry {System.out.println("doPost");doDispatch(req,resp);//未采用策略模式//doDispatchDesignPatterns(req,resp);//采用策略模式} catch (Exception e) {// TODO Auto-generated catch block e.printStackTrace();} }未采用策略模式,參數類型使用if else if判斷類型,
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {// TODO Auto-generated method stubif(this.handlerMapping.isEmpty()){return;}String url =req.getRequestURI();// /myspringmvc/demo/userString contextPath = req.getContextPath();// /myspringmvcurl = url.replace(contextPath, "").replaceAll("/+", "/");if(!this.handlerMapping.containsKey(url)){resp.getWriter().write("404 Not Found!!");return;}Map<String,String[]> params = req.getParameterMap();Method method =this.handlerMapping.get(url);//獲取方法的參數列表Class<?>[] paramerterTypes = method.getParameterTypes();//獲取請求的參數Map<String,String[]> parameterMap = req.getParameterMap();//保留參數值Object[] paramValues = new Object[paramerterTypes.length];//方法的參數列表for (int i = 0; i < paramerterTypes.length; i++) {//根據參數名稱,做某些處理Class parameterType =paramerterTypes[i];if(parameterType == HttpServletRequest.class){//參數類型已明確,這邊強轉類型paramValues[i] =req;continue;}else if(parameterType == HttpServletResponse.class){paramValues[i] = resp;continue;}else if(parameterType == String.class){//獲取當前方法的參數Annotation[][] an = method.getParameterAnnotations();//個數和paramerterTypes.length一樣Annotation[] paramAns = an[i];for (Annotation paramAn : paramAns) {//判斷傳進的paramAn.getClass()是不是 MyRequestParam 類型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {MyRequestParam cr = (MyRequestParam) paramAn;String value = cr.value();paramValues[i] = req.getParameter(value);}}}}try {//String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());//獲取源代碼中給出的‘底層類’簡稱String beanName = "/" + url.split("/")[1];method.invoke(this.ioc.get(beanName), paramValues);} catch (Exception e) {// TODO: handle exception e.printStackTrace();}}采用策略模式
private void doDispatchDesignPatterns(HttpServletRequest req, HttpServletResponse resp) {// 通過req獲取請求的url /myspringmvc/demo/userString url = req.getRequestURI();// /myspringmvcString context = req.getContextPath();// /demo/userString path = url.replaceAll(context, "");// 通過當前的path獲取handlerMap的方法名Method method = this.handlerMapping.get(path);// 獲取beans容器中的beanObject instance = this.ioc.get("/" + path.split("/")[1]);// 處理參數HandlerAdapterService ha = (HandlerAdapterService) this.ioc.get("myHandlerAdapter"); Object[] args = ha.handle(req, resp, method, ioc);// 通過反射來實現方法的調用try {method.invoke(instance, args);} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}處理參數接口
package com.xiaojiesir.demo.handlerAdapter;import java.lang.reflect.Method; import java.util.Map;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public interface HandlerAdapterService {public Object[] handle(HttpServletRequest req, HttpServletResponse resp,Method method, Map<String, Object> beans); }處理參數的實現類
import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService; import com.xiaojiesir.demo.argumentResolver.ArgumentResolver;@MyService("myHandlerAdapter") public class MyHandlerAdapter implements HandlerAdapterService {@Overridepublic Object[] handle(HttpServletRequest req, HttpServletResponse resp, Method method, Map<String, Object> beans) {// TODO Auto-generated method stub//獲取方法中含義的參數Class<?>[] paramClazzs = method.getParameterTypes();System.out.println("======當前需要解析的參數對應的類=========");for(Class<?> clazz: paramClazzs) {System.out.println(clazz);}// 定義一個返回參數的結果集Object[] args = new Object[paramClazzs.length]; // Object[] args = {req, resp, "name", "xiaojiesir"};// 定義一個ArgumentResolver實現類的MapMap<String, Object> argumentResolvers = getBeansOfType(beans, ArgumentResolver.class);System.out.println("======當前需要解析的參數對應的類實例化=========");for(Map.Entry<String, Object> map: argumentResolvers.entrySet()) {System.out.println("key:" + map.getKey() + "; value:" + map.getValue());}//定義參數索引int paramIndex = 0;//定義數組下標索引int i = 0; // 開始處理參數for(Class<?> paramClazz: paramClazzs) {//哪個參數對應了哪個參數解析類,用策略模式來找for (Map.Entry<String, Object> entry : argumentResolvers.entrySet()) {ArgumentResolver ar = (ArgumentResolver)entry.getValue();if (ar.support(paramClazz, paramIndex, method)) {args[i++] = ar.argumentResolver(req,resp,paramClazz,paramIndex,method);}}paramIndex++;}return args;}/*** @param beans IOC容器中全部的bean* @param intfType 定義的ArgumentResolver類* @return*/private Map<String, Object> getBeansOfType(Map<String, Object> beans,Class<ArgumentResolver> intfType) {Map<String, Object> resultBeans = new HashMap<>();for(Map.Entry<String, Object> map: beans.entrySet()) {// 獲取滿足ArgumentResolver接口的beanClass<?>[] intfs = map.getValue().getClass().getInterfaces();if(intfs != null && intfs.length >0) {for(Class<?> intf: intfs) {// 將滿足的bean存儲在resultBeans中if(intf.isAssignableFrom(intfType)) {resultBeans.put(map.getKey(), map.getValue());}}}}return resultBeans;}}參數接口
import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;public interface ArgumentResolver {/*** 判斷當前的類是繼承于ArgumentResolver* @param type 當前參數注解的類對象* @param paramIndex 參數下標* @param method 當前的方法* @return*/public boolean support(Class<?> type, int paramIndex, Method method);/*** * @param request* @param response* @param type* @param paramIndex* @param method* @return*/public Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method); }處理Request請求參數
import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService;/** 處理Request請求參數*/ @MyService("httpServletRequestArgumentResolver") public class HttpServletRequestArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {return ServletRequest.class.isAssignableFrom(type);}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {return request;}}處理Response參數
import java.lang.reflect.Method;import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyService; /** 處理Response參數*/ @MyService("httpServletResponseArgumentResolver") public class HttpServletResponseArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {return ServletResponse.class.isAssignableFrom(type);}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {return response;}}處理自定義的參數
import java.lang.annotation.Annotation; import java.lang.reflect.Method;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import com.springframework.annotation.MyRequestParam; import com.springframework.annotation.MyService;@MyService("requestParamArgumentResolver") public class RequestParamArgumentResolver implements ArgumentResolver {@Overridepublic boolean support(Class<?> type, int paramIndex, Method method) {// type = class java.lang.String// @MyRequestParam("name")String name//獲取當前方法的參數Annotation[][] an = method.getParameterAnnotations();Annotation[] paramAns = an[paramIndex];for (Annotation paramAn : paramAns) {//判斷傳進的paramAn.getClass()是不是 MyRequestParam 類型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {return true;}}return false;}@Overridepublic Object argumentResolver(HttpServletRequest request,HttpServletResponse response, Class<?> type, int paramIndex,Method method) {//獲取當前方法的參數Annotation[][] an = method.getParameterAnnotations();Annotation[] paramAns = an[paramIndex];for (Annotation paramAn : paramAns) {//判斷傳進的paramAn.getClass()是不是 MyRequestParam 類型if (MyRequestParam.class.isAssignableFrom(paramAn.getClass())) {MyRequestParam cr = (MyRequestParam) paramAn;String value = cr.value();return request.getParameter(value);}}return null;}}啟動服務,在URL輸入請求地址:
http://localhost:8080/myspringmvc/demo/user?name=xiaojiesir123&id=1
?
總結:
手寫的spring和springmvc主要用了反射、注解。正式的spring中還有很多設計模式,值得我們學習。所以我們在掌握基礎知識后,多讀框架的源碼,獲取框架中的設計模式,設計理念更助于提升自己。
?
轉載于:https://www.cnblogs.com/xiaojiesir/p/11157235.html
總結
以上是生活随笔為你收集整理的手写springmvc的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库课程设计报告——员工工资管理系统
- 下一篇: 查看docker容器日志