Spring的MVC框架主要由DispatcherServlet、處理器映射、處理器(控制器)、視圖解析器、視圖組成。html
完整的Spring MVC處理 流程以下:前端
DispatcherServlet接口:java
Spring提供的前端控制器,全部的請求都有通過它來統一分發。在DispatcherServlet將請求分發給Spring Controller以前,須要藉助於Spring提供的HandlerMapping定位到具體的Controller。node
HandlerMapping接口:git
可以完成客戶請求到Controller映射。web
Controller接口:spring
須要爲併發用戶處理上述請求,所以實現Controller接口時,必須保證線程安全而且可重用。數組
Controller將處理用戶請求,這和Struts Action扮演的角色是一致的。一旦Controller處理完用戶請求,則返回ModelAndView對象給DispatcherServlet前端控制器,ModelAndView中包含了模型(Model)和視圖(View)。spring-mvc
從宏觀角度考慮,DispatcherServlet是整個Web應用的控制器;從微觀考慮,Controller是單個Http請求處理過程當中的控制器,而ModelAndView是Http請求過程當中返回的模型(Model)和視圖(View)。安全
ViewResolver接口:
Spring提供的視圖解析器(ViewResolver)在Web應用中查找View對象,從而將相應結果渲染給客戶。
本次實現沒有視圖解析內容。主要包括,自動掃描class類、經過解析註解實現bean的實例化、bean之間的依賴注入、經過註解映射路徑返回正確的處理方法。
Spring MVC框架主要依賴於Java的反射機制實現。實現原理與上面描述一致。
工程名MySpringMVC
代碼存放servlet包。
DispatcherServlet
package zqq.servlet; import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import zqq.annotations.EnjoyAuthowired; import zqq.annotations.EnjoyController; import zqq.annotations.EnjoyRequestMapping; import zqq.annotations.EnjoyRequestParam; import zqq.annotations.EnjoyService; import zqq.controller.ZqqController; /** * Servlet implementation class DispatcherServlet */ public class DispatcherServlet extends HttpServlet { private static final long serialVersionUID = 1L; // 掃描獲得的類名集合 List<String> classNames = new ArrayList<String>(); // 存放全部Spring實例的Map Map<String, Object> beans = new HashMap<String, Object>(); // 存放全部路徑映射 Map<String, Object> handlerMap = new HashMap<String, Object>(); /** * @see HttpServlet#HttpServlet() */ public DispatcherServlet() { } /** * @see Servlet#init(ServletConfig) */ public void init(ServletConfig config) throws ServletException { // 一、掃描工程有多少class doScanPackage("zqq"); // 打印全部class for (String name : classNames) { System.out.println(name); } // 二、實例化 doInstance(); for (Map.Entry<String, Object> entry : beans.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } // 三、注入 doIoC(); // 四、請求映射 buildMapping(); for (Map.Entry<String, Object> entry : handlerMap.entrySet()) { System.out.println(entry.getKey() + ":" + entry.getValue()); } } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // springmvc /zqq/query String uri = request.getRequestURI(); String context = request.getContextPath(); // springmvc String path = uri.replace(context, ""); // /zqq/query // 獲取映射對應的method Method method = (Method) handlerMap.get(path); ZqqController instance = (ZqqController) beans.get("/" + path.split("/")[1]); Object args[] = this.hand(request, response, method); try { method.invoke(instance, args); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } /** * @author qqz * @date 2018年7月12日 上午1:02:44 掃描當前路徑下有多少個class類 * @param string */ private void doScanPackage(String basePackage) { // URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")); String filepath = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")).getFile(); try { filepath= java.net.URLDecoder.decode(filepath,"utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } // String fileStr = url.getFile(); // 目錄對象 File file = new File(filepath); String[] filesStr = file.list(); // 遞歸處理路徑basepackage下的類文件 for (String path : filesStr) { File filePath = new File(filepath + path); if (filePath.isDirectory()) { doScanPackage(basePackage + "." + path); } else { // 獲得class 全類名路徑 zqq.controller.ZqqController classNames.add(basePackage + "." + filePath.getName()); } } } /** * @author qqz * @date 2018年7月12日 上午1:11:04 TODO */ private void doInstance() { if (classNames.size() <= 0) { System.out.println("scan classes failed!"); } for (String className : classNames) { // 去掉.class後綴 String cn = className.replace(".class", ""); try { Class<?> clazz = Class.forName(cn); // 處理帶有EnjoyController註解的類 if (clazz.isAnnotationPresent(EnjoyController.class)) { // 實例化對象 Object instance = clazz.newInstance(); EnjoyRequestMapping reqMapping = clazz.getAnnotation(EnjoyRequestMapping.class); String key = reqMapping.value(); beans.put(key, instance); } else if (clazz.isAnnotationPresent(EnjoyService.class)) { // 實例化對象 Object instance = clazz.newInstance(); EnjoyService service = clazz.getAnnotation(EnjoyService.class); String key = service.value(); beans.put(key, instance); } else { continue; } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { e.printStackTrace(); } } } /** * @author qqz 屬性注入 * @date 2018年7月12日 上午1:21:10 TODO */ private void doIoC() { if (beans.entrySet().size() <= 0) { System.out.println("instance bean failed."); return; } for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); if (clazz.isAnnotationPresent(EnjoyController.class)) { // 獲取類中全部屬性 Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 獲取聲明注入的屬性 if (field.isAnnotationPresent(EnjoyAuthowired.class)) { EnjoyAuthowired authowired = field.getAnnotation(EnjoyAuthowired.class); // 獲取註解EnjoyAutowired中命名的值 String value = authowired.value(); // 放開權限設置屬性值 field.setAccessible(true); try { field.set(instance, beans.get(value)); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } else { continue; } } } } } /** * @author qqz * @date 2018年7月12日 上午1:32:25 TODO */ private void buildMapping() { if (beans.entrySet().size() <= 0) { System.out.println("instance bean failed."); return; } for (Map.Entry<String, Object> entry : beans.entrySet()) { Object instance = entry.getValue(); Class<?> clazz = instance.getClass(); // 映射是在Controller層 if (clazz.isAnnotationPresent(EnjoyController.class)) { // 獲取類映射 EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class); String classPath = requestMapping.value(); // 獲取方法上的映射 Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(EnjoyRequestMapping.class)) { EnjoyRequestMapping requestMapping1 = method.getAnnotation(EnjoyRequestMapping.class); String methodPath = requestMapping1.value(); // 構建方法路徑與方法的映射 handlerMap.put(classPath + methodPath, method); } else { continue; } } } } } /** * @author qqz * @date 2018年7月12日 上午1:59:48 * 方法參數註解解析 * @param request * @param response * @param method * @return */ private static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method) { // 拿到當前執行的方法有哪些參數 Class<?>[] paramClazzs = method.getParameterTypes(); // 根據參數的個數,new 一個參數的數組, 將方法裏全部參數賦值到args來 Object[] args = new Object[paramClazzs.length]; int arg_i = 0; int index = 0; for (Class<?> paramClazz : paramClazzs) { if (ServletRequest.class.isAssignableFrom(paramClazz)) { args[arg_i++] = request; } if (ServletResponse.class.isAssignableFrom(paramClazz)) { args[arg_i++] = response; } // 從0-3判斷有沒有RequestParam註解,很明顯paramClazz爲0和1時,不是,當爲2和3時爲@RequestParam,須要 // 解析[@zqq.annotation.EnjoyRequestParam(value=name)] Annotation[] paramAns = method.getParameterAnnotations()[index]; if (paramAns.length > 0) { for (Annotation paramAn : paramAns) { if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass())) { EnjoyRequestParam rp = (EnjoyRequestParam)paramAn; //找到註解裏的name和age args[arg_i++] = request.getParameter(rp.value()); } } } index ++; } return args; } }
代碼存放的包annotations中。包括以下幾個
EnjoyAuthowired.java
EnjoyController.java
EnjoyRequestMapping.java
EnjoyRequestParam.java
EnjoyService.java
屬性註解EnjoyAuthowired.java
package zqq.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface EnjoyAuthowired { String value() default ""; }
Controller註解EnjoyController.java
package zqq.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface EnjoyController { String value() default ""; }
映射註解EnjoyRequestMapping.java
package zqq.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface EnjoyRequestMapping { String value() default ""; }
參數註解EnjoyRequestParam.java
package zqq.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface EnjoyRequestParam { String value() default ""; }
Service註解EnjoyService.java
package zqq.annotations; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface EnjoyService { String value() default ""; }
代碼存放controller包。
/** * ZqqController.java */ package zqq.controller; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import zqq.annotations.EnjoyAuthowired; import zqq.annotations.EnjoyController; import zqq.annotations.EnjoyRequestMapping; import zqq.annotations.EnjoyRequestParam; import zqq.service.ZqqService; @EnjoyController @EnjoyRequestMapping("/zqq") public class ZqqController { @EnjoyAuthowired("ZqqServiceImpl") private ZqqService zqqService; @EnjoyRequestMapping("/query") public void query(HttpServletRequest req, HttpServletResponse resp, @EnjoyRequestParam("name") String name, @EnjoyRequestParam("age") String age) { PrintWriter pw; try { pw = resp.getWriter(); String result = zqqService.query(name, age); pw.write(result); } catch (IOException e) { e.printStackTrace(); } } }
代碼存放service包
/** * ZqqService.java */ package zqq.service; public interface ZqqService { String query(String name,String age); }
Service實現類存放service/impl
/** * ZqqServiceImpl.java */ package zqq.service.impl; import zqq.annotations.EnjoyService; import zqq.service.ZqqService; @EnjoyService("ZqqServiceImpl") public class ZqqServiceImpl implements ZqqService { /* * (non-Javadoc) * * @see zqq.service.ZqqService#query(java.lang.String, java.lang.String) */ @Override public String query(String name, String age) { return "{name:" + name + ",age:" + age + "}"; } }
src/main/webapp/WEB-INF/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>DispatcherServlet</servlet-name> <display-name>DispatcherServlet</display-name> <description></description> <servlet-class>zqq.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
部署後訪問localhost:8080/MySpringMVC/zqq/query?name=zqq&age=18
能夠在頁面上看到請求中的name和age。
參考資料:
SpringMVC框架介紹