手寫SpringMVC思路
1.web.xml載入
為了讀取web.xml中的配置,我們用到ServletConfig這個類,它代表當前Servlet在web.xml中的配置資訊。通過web.xml中載入我們自己寫的MyDispatcherServlet和讀取配置檔案。
2、初始化階段
在前面我們提到DispatcherServlet的initStrategies方法會初始化9大元件,但是這裡將實現一些SpringMVC的最基本的元件而不是全部,按順序包括:
- 載入配置檔案
- 掃描使用者配置包下面所有的類
- 拿到掃描到的類,通過反射機制,例項化。並且放到ioc容器中(Map的鍵值對 beanName-bean) beanName預設是首字母小寫
- 初始化HandlerMapping,這裡其實就是把url和method對應起來放在一個k-v的Map中,在執行階段取出
3、執行階段
每一次請求將會呼叫doGet或doPost方法,所以統一執行階段都放在doDispatch方法裡處理,它會根據url請求去HandlerMapping中匹配到對應的Method,然後利用反射機制呼叫Controller中的url對應的方法,並得到結果返回。按順序包括以下功能:
- 異常的攔截
- 獲取請求傳入的引數並處理引數
- 通過初始化好的handlerMapping中拿出url對應的方法名,反射呼叫
手寫SpringMVC基本實現
/** * 手寫SpringMVC框架 作者:每特教育-餘勝軍<br> * * @QQ644064779 1.自定義DispatcherServlet<br> * 2.servlet init()方法初始化###只會執行一次<br> * ######2.1獲取當前包下所有的類<br> * ######2.2初始化當前包下所有的類,使用Java反射機制初始化物件存放在SpringMVC容器中key(beanId)- * value( 當前例項物件) <br> * ######2.3初始化HandlerMapping方法,將url和方法對應上 <br> * ########2.3.1使用Java反射技術讀取類的資訊,存放在map集合中key為url請求地址,value為對應方法 * <br> * ########2.3.2使用Java反射技術讀取類的資訊,存放在map集合中key為url請求地址,value為對應例項物件 * <br> * 3.servlet get或者post請求<br> * ######## 3.1.1獲取請求地址,使用Java反射技術找到對應的方法和例項物件進行執行 <br> */ public class ExtDispatcherServlet extends HttpServlet { // mvc bean key=beanid ,value=物件 private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>(); // mvc 請求方法 key=requestUrl,value=物件 private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<String, Object>(); // mvc 請求方法 key=requestUrl,value=方法 private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<String, String>(); /** * 初始化自定義SpringMVC容器 */ public void init() throws ServletException { try { // 1.獲取當前包下所有的類 List<Class<?>> classes = ClassUtil.getClasses("com.itmayiedu.ext.controller"); // 2.初始化當前包下所有的類,使用Java反射機制初始化物件存放在SpringMVC容器中key(beanId)-value( // 當前例項物件) findClassMVCBeans(classes); // 3.初始化HandlerMapping方法,將url和方法對應上 handlerMapping(mvcBeans); } catch (Exception e) { } } // 2.初始化當前包下所有的類,使用Java反射機制初始化物件存放在SpringMVC容器中key(beanId)-value( // 當前例項物件) public void findClassMVCBeans(List<Class<?>> classes) throws ClassNotFoundException, InstantiationException, IllegalAccessException { mvcBeans = new ConcurrentHashMap<String, Object>(); for (Class<?> classInfo : classes) { ExtController extController = classInfo.getDeclaredAnnotation(ExtController.class); if (extController != null) { // 預設類名小寫 作為bean的名稱 String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName()); mvcBeans.put(beanId, ClassUtil.newInstance(classInfo)); } } } // 3.初始化HandlerMapping方法,將url和方法對應上 public void handlerMapping(ConcurrentHashMap<String, Object> mvcBeans) { // 遍歷mvc bean物件 for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) { // springmvc 注入object物件 Object mvcObject = entry.getValue(); // 判斷類上是否有@ExtRequestMapping註解 Class<? extends Object> classInfo = mvcObject.getClass(); String requestBaseUrl = null; ExtRequestMapping classExtRequestMapping = classInfo.getAnnotation(ExtRequestMapping.class); if (classExtRequestMapping != null) { requestBaseUrl = classExtRequestMapping.value(); } // 遍歷當前類的所有方法,判斷方法上是否有註解 Method[] declaredMethods = classInfo.getDeclaredMethods(); for (Method method : declaredMethods) { ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class); if (methodExtRequestMapping != null) { String httpRequestUrl = methodExtRequestMapping.value(); mvcBeanUrl.put(requestBaseUrl + httpRequestUrl, mvcObject); mvcMethodUrl.put(requestBaseUrl + httpRequestUrl, method.getName()); } } } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { doDispatch(req, resp); } catch (Exception e) { // TODO: handle exception } } public void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // 1.獲取請求url地址 String requestUrl = req.getRequestURI(); // 2.使用請求url查詢對應mvc 控制器bean Object object = mvcBeanUrl.get(requestUrl); if (object == null) { resp.getWriter().println("http ext not found controller 404"); return; } // 3.獲取對應的請求方法 String methodName = mvcMethodUrl.get(requestUrl); if (StringUtils.isEmpty(methodName)) { resp.getWriter().println("http ext not found Method 404"); return; } // 4.使用java反射技術執行方法 Class<? extends Object> classInfo = object.getClass(); String resultPage = (String) methodInvoke(classInfo, object, methodName); // 5.檢視展示 viewdisplay(resultPage, req, resp); } // 執行方法 public Object methodInvoke(Class<? extends Object> classInfo, Object object, String methodName) { try { Method method = classInfo.getMethod(methodName); Object result = method.invoke(object); return result; } catch (Exception e) { e.printStackTrace(); return null; } } // 檢視展示 public void viewdisplay(String pageName, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { // 獲取字尾資訊 String suffix = ".jsp"; // 頁面目錄地址 String prefix = "/"; req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res); } }