(二)springMvc原理和手寫springMvc框架


我們從兩個方面了解springmvc執行原理,首先我們去熟悉springmvc執行的過程,然后知道原理后通過手寫springmvc去深入了解代碼中執行過程。

(一)SpringMVC流程圖

(二)SpringMVC流程

1、  用戶發送請求至前端控制器DispatcherServlet。

2、  DispatcherServlet收到請求調用HandlerMapping處理器映射器。

3、  處理器映射器找到具體的處理器(可以根據xml配置、注解進行查找),生成處理器對象及處理器攔截器(如果有則生成)一並返回給DispatcherServlet。

4、  DispatcherServlet調用HandlerAdapter處理器適配器。

5、  HandlerAdapter經過適配調用具體的處理器(Controller,也叫后端控制器)。

6、  Controller執行完成返回ModelAndView。

7、  HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。

8、  DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。

9、  ViewReslover解析后返回具體View。

10、DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。

11、 DispatcherServlet響應用戶。

(三)SpringMVC核心組件講解

1、前端控制器DispatcherServlet
作用:接收請求,響應結果,相當於轉發器,中央處理器。有了dispatcherServlet減少了其它組件之間的耦合度。
用戶請求到達前端控制器,它就相當於mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet的存在降低了組件之間的耦合性。

2、處理器映射器HandlerMapping
作用:根據請求的url查找Handler
HandlerMapping負責根據用戶請求找到Handler即處理器,springmvc提供了不同的映射器實現不同的映射方式,例如:配置文件方式,實現接口方式,注解方式等。

3、處理器適配器HandlerAdapter
作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler
通過HandlerAdapter對處理器進行執行,這是適配器模式的應用,通過擴展適配器可以對更多類型的處理器進行執行。

4、處理器Handler
Handler 是繼DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。
由於Handler涉及到具體的用戶業務請求,所以一般情況需要工程師根據業務需求開發Handler。

5、視圖解析器View resolver(不需要工程師開發),由框架提供
作用:進行視圖解析,根據邏輯視圖名解析成真正的視圖(view)
View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最后對View進行渲染將處理結果通過頁面展示給用戶。 springmvc框架提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等。
一般情況下需要通過頁面標簽或頁面模版技術將模型數據通過頁面展示給用戶,需要由工程師根據業務需求開發具體的頁面。

6、視圖View
View是一個接口,實現類支持不同的View類型(jsp、freemarker、pdf...)

(四)手寫springMvc框架思路:

1、配置web.xml,加載自定義的DispatcherServlet。

2、初始化階段,在DispatcherServlet類中,實現下面幾個步驟:

    1. 加載配置類。
    2. 掃描當前項目下的所有文件。
    3. 拿到掃描到的類,通過反射機制,實例化。並且放到ioc容器中。
    4. 初始化path與方法的映射。
    5. 獲取請求傳入的參數並處理參數通過初始化好的handlerMapping中拿出url對應的方法名,反射調用。

3、運行階段,每一次請求將會調用doGet或doPost方法,它會根據url請求去HandlerMapping中匹配到對應的Method,然后利用反射機制調用Controller中的url對應的方法,並得到結果返回。

(五)代碼階段:

1.web.xm加載

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
 3   <display-name>maven_handmvc</display-name>
 4   <servlet>
 5     <servlet-name>DispatcherServlet</servlet-name>
 6     <servlet-class>com.zzw.cn.springmvc.dispathcer.DispatcherServlet</servlet-class>
 7   </servlet>
 8   <servlet-mapping>
 9     <servlet-name>DispatcherServlet</servlet-name>
10     <url-pattern>/</url-pattern>
11   </servlet-mapping>
12 </web-app>
View Code

  2.自定義DispatcherServlet

  1 package com.zzw.cn.springmvc.dispathcer;
  2 
  3 import com.zzw.cn.springmvc.annoation.AnController;
  4 import com.zzw.cn.springmvc.annoation.AnRequestMapping;
  5 import com.zzw.cn.utils.ClassUtils;
  6 
  7 import javax.servlet.ServletConfig;
  8 import javax.servlet.ServletException;
  9 import javax.servlet.http.HttpServlet;
 10 import javax.servlet.http.HttpServletRequest;
 11 import javax.servlet.http.HttpServletResponse;
 12 import java.io.IOException;
 13 import java.lang.reflect.InvocationTargetException;
 14 import java.lang.reflect.Method;
 15 import java.util.List;
 16 import java.util.Map;
 17 import java.util.concurrent.ConcurrentHashMap;
 18 
 19 /**
 20  * @author Simple
 21  * @date 14:34 2019/8/26
 22  * @description 手寫springmvc框架流程
 23  * <p>
 24  * 思路:自定義DispatcherServlet
 25  * 1.包掃描獲取包下面所有的類
 26  * 2.初始化包下面所有的類
 27  * 3.初始化HandlerMapping方法,將url和方法對應上
 28  * 4.實現HttpServlet 重寫dopost方法
 29  */
 30 
 31 public class DispatcherServlet extends HttpServlet {
 32 
 33     //springmvc 容器存放bean
 34     private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>();
 35     private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>();
 36     private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>();
 37 
 38 
 39     @Override
 40     public void init(ServletConfig config) {
 41         String packagePath = "com.zzw.cn.springmvc";
 42         //1.進行報掃描獲取當前包下面所有的類
 43         List<Class<?>> classes = comscanPackage(packagePath);
 44         try {
 45             //2.初始化springmvcbean
 46             initSpringMvcBean(classes);
 47         } catch (Exception e) {
 48             e.printStackTrace();
 49         }
 50         //3.將請求地址和方法進行映射
 51         initHandMapping(mvcBeans);
 52     }
 53 
 54 
 55     public List<Class<?>> comscanPackage(String packagePath) {
 56         List<Class<?>> classes = ClassUtils.getClasses(packagePath);
 57         return classes;
 58     }
 59 
 60     /**
 61      * 初始化sprignbean
 62      *
 63      * @param classes
 64      * @throws Exception
 65      */
 66     public void initSpringMvcBean(List<Class<?>> classes) throws Exception {
 67         if (classes.size() == 0 || null == classes) {
 68             throw new Exception("包掃描后的classes為null");
 69         }
 70 
 71         for (Class<?> aClass : classes) {
 72             //獲取被自定義注解的controller將其初始化到自定義sprignmvc容器中
 73             AnController declaredAnnotation = aClass.getDeclaredAnnotation(AnController.class);
 74             if (declaredAnnotation != null) {
 75                 //獲取類的名字
 76                 String beanid = lowerFirstCapse(aClass.getSimpleName());
 77                 //獲取對象
 78                 Object beanObj = aClass.newInstance();
 79                 //放入sprign容器
 80                 mvcBeans.put(beanid, beanObj);
 81             }
 82         }
 83 
 84     }
 85 
 86     /**
 87      * 初始化HandlerMapping方法
 88      *
 89      * @param mvcBeans
 90      */
 91     public void initHandMapping(ConcurrentHashMap<String, Object> mvcBeans) {
 92         //遍歷springmvc 獲取注入的對象值
 93         for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
 94             Object objValue = entry.getValue();
 95             Class<?> aClass = objValue.getClass();
 96             //獲取當前類 判斷是否有自定義的requestMapping注解
 97             String mappingUrl = null;
 98             AnRequestMapping anRequestMapping = aClass.getDeclaredAnnotation(AnRequestMapping.class);
 99             if (anRequestMapping != null) {
100                 mappingUrl = anRequestMapping.value();
101             }
102             //獲取當前類所有方法,判斷方法上是否有注解
103             Method[] declaredMethods = aClass.getDeclaredMethods();
104             for (Method method : declaredMethods) {
105                 AnRequestMapping methodDeclaredAnnotation = method.getDeclaredAnnotation(AnRequestMapping.class);
106                 if (methodDeclaredAnnotation != null) {
107                     String methodUrl = methodDeclaredAnnotation.value();
108                     mvcBeanUrl.put(mappingUrl + methodUrl, objValue);
109                     mvcMethodUrl.put(mappingUrl + methodUrl, method.getName());
110                 }
111             }
112 
113         }
114 
115     }
116 
117     /**
118      * @param str
119      * @return 類名首字母小寫
120      */
121     public static String lowerFirstCapse(String str) {
122         char[] chars = str.toCharArray();
123         chars[0] += 32;
124         return String.valueOf(chars);
125 
126     }
127 
128     @Override
129     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
130         try {
131             doServelt(req, resp);
132         } catch (NoSuchMethodException e) {
133             e.printStackTrace();
134         } catch (InvocationTargetException e) {
135             e.printStackTrace();
136         } catch (IllegalAccessException e) {
137             e.printStackTrace();
138         }
139     }
140 
141     private void doServelt(HttpServletRequest req, HttpServletResponse resp) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ServletException {
142         //獲取請求地址
143         String requestUrl = req.getRequestURI();
144         //查找地址所對應bean
145         Object object = mvcBeanUrl.get(requestUrl);
146         if (object == null) {
147             resp.getWriter().println("sorry http is not exit 404");
148             return;
149         }
150         //獲取請求的方法
151         String methodName = mvcMethodUrl.get(requestUrl);
152         if (methodName == null) {
153             resp.getWriter().println("sorry method is not exit 404");
154             return;
155         }
156         //通過構反射執行方法
157         Class<?> aClass = object.getClass();
158         Method method = aClass.getMethod(methodName);
159         String invoke = (String) method.invoke(object);
160         // 獲取后綴信息
161         String suffix = ".jsp";
162         // 頁面目錄地址
163         String prefix = "/";
164         req.getRequestDispatcher(prefix + invoke + suffix).forward(req, resp);
165     }
166 
167     @Override
168     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
169         this.doPost(req, resp);
170     }
171 
172 
173 }
View Code

 3.ClassUtils工具類封裝

  1 package com.zzw.cn.utils;
  2 
  3 import java.io.File;
  4 import java.io.FileFilter;
  5 import java.io.IOException;
  6 import java.net.JarURLConnection;
  7 import java.net.URL;
  8 import java.net.URLDecoder;
  9 import java.util.ArrayList;
 10 import java.util.Enumeration;
 11 import java.util.List;
 12 import java.util.jar.JarEntry;
 13 import java.util.jar.JarFile;
 14 
 15 public class ClassUtils {
 16 
 17     /**
 18      * 從包package中獲取所有的Class
 19      *
 20      * @param pack
 21      * @return
 22      */
 23     public static List<Class<?>> getClasses(String packageName) {
 24 
 25         // 第一個class類的集合
 26         List<Class<?>> classes = new ArrayList<Class<?>>();
 27         // 是否循環迭代
 28         boolean recursive = true;
 29         // 獲取包的名字 並進行替換
 30         String packageDirName = packageName.replace('.', '/');
 31         // 定義一個枚舉的集合 並進行循環來處理這個目錄下的things
 32         Enumeration<URL> dirs;
 33         try {
 34             dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
 35             // 循環迭代下去
 36             while (dirs.hasMoreElements()) {
 37                 // 獲取下一個元素
 38                 URL url = dirs.nextElement();
 39                 // 得到協議的名稱
 40                 String protocol = url.getProtocol();
 41                 // 如果是以文件的形式保存在服務器上
 42                 if ("file".equals(protocol)) {
 43                     // 獲取包的物理路徑
 44                     String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
 45                     // 以文件的方式掃描整個包下的文件 並添加到集合中
 46                     findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
 47                 } else if ("jar".equals(protocol)) {
 48                     // 如果是jar包文件
 49                     // 定義一個JarFile
 50                     JarFile jar;
 51                     try {
 52                         // 獲取jar
 53                         jar = ((JarURLConnection) url.openConnection()).getJarFile();
 54                         // 從此jar包 得到一個枚舉類
 55                         Enumeration<JarEntry> entries = jar.entries();
 56                         // 同樣的進行循環迭代
 57                         while (entries.hasMoreElements()) {
 58                             // 獲取jar里的一個實體 可以是目錄 和一些jar包里的其他文件 如META-INF等文件
 59                             JarEntry entry = entries.nextElement();
 60                             String name = entry.getName();
 61                             // 如果是以/開頭的
 62                             if (name.charAt(0) == '/') {
 63                                 // 獲取后面的字符串
 64                                 name = name.substring(1);
 65                             }
 66                             // 如果前半部分和定義的包名相同
 67                             if (name.startsWith(packageDirName)) {
 68                                 int idx = name.lastIndexOf('/');
 69                                 // 如果以"/"結尾 是一個包
 70                                 if (idx != -1) {
 71                                     // 獲取包名 把"/"替換成"."
 72                                     packageName = name.substring(0, idx).replace('/', '.');
 73                                 }
 74                                 // 如果可以迭代下去 並且是一個包
 75                                 if ((idx != -1) || recursive) {
 76                                     // 如果是一個.class文件 而且不是目錄
 77                                     if (name.endsWith(".class") && !entry.isDirectory()) {
 78                                         // 去掉后面的".class" 獲取真正的類名
 79                                         String className = name.substring(packageName.length() + 1, name.length() - 6);
 80                                         try {
 81                                             // 添加到classes
 82                                             classes.add(Class.forName(packageName + '.' + className));
 83                                         } catch (ClassNotFoundException e) {
 84                                             e.printStackTrace();
 85                                         }
 86                                     }
 87                                 }
 88                             }
 89                         }
 90                     } catch (IOException e) {
 91                         e.printStackTrace();
 92                     }
 93                 }
 94             }
 95         } catch (IOException e) {
 96             e.printStackTrace();
 97         }
 98 
 99         return classes;
100     }
101 
102     /**
103      * 以文件的形式來獲取包下的所有Class
104      *
105      * @param packageName
106      * @param packagePath
107      * @param recursive
108      * @param classes
109      */
110     public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
111                                                         List<Class<?>> classes) {
112         // 獲取此包的目錄 建立一個File
113         File dir = new File(packagePath);
114         // 如果不存在或者 也不是目錄就直接返回
115         if (!dir.exists() || !dir.isDirectory()) {
116             return;
117         }
118         // 如果存在 就獲取包下的所有文件 包括目錄
119         File[] dirfiles = dir.listFiles(new FileFilter() {
120             // 自定義過濾規則 如果可以循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)
121             @Override
122             public boolean accept(File file) {
123                 return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
124             }
125         });
126         // 循環所有文件
127         for (File file : dirfiles) {
128             // 如果是目錄 則繼續掃描
129             if (file.isDirectory()) {
130                 findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
131                         classes);
132             } else {
133                 // 如果是java類文件 去掉后面的.class 只留下類名
134                 String className = file.getName().substring(0, file.getName().length() - 6);
135                 try {
136                     // 添加到集合中去
137                     classes.add(Class.forName(packageName + '.' + className));
138                 } catch (ClassNotFoundException e) {
139                     e.printStackTrace();
140                 }
141             }
142         }
143     }
144 }
View Code

 4.自定義注解類AnController

 1 package com.zzw.cn.springmvc.annoation;
 2 
 3 import java.lang.annotation.ElementType;
 4 import java.lang.annotation.Retention;
 5 import java.lang.annotation.RetentionPolicy;
 6 import java.lang.annotation.Target;
 7 
 8 /**
 9  * @author Simple
10  * @date 14:06 2019/8/27
11  * @description
12  */
13 @Target({ElementType.TYPE,ElementType.METHOD})
14 @Retention(RetentionPolicy.RUNTIME)
15 public @interface AnController {
16 }
View Code

 5.自定義注解類AnRequestMapping

 1 package com.zzw.cn.springmvc.annoation;
 2 
 3 import java.lang.annotation.*;
 4 
 5 /**
 6  * @author Simple
 7  * @date 14:07 2019/8/27
 8  * @description
 9  */
10 @Target({ElementType.METHOD, ElementType.TYPE})
11 @Retention(RetentionPolicy.RUNTIME)
12 @Documented
13 public @interface AnRequestMapping {
14     String value() default "";
15 }
View Code

 6.HelloWorld類

 1 package com.zzw.cn.springmvc.controller;
 2 
 3 import com.zzw.cn.springmvc.annoation.AnController;
 4 import com.zzw.cn.springmvc.annoation.AnRequestMapping;
 5 
 6 /**
 7  * @author Simple
 8  * @date 15:15 2019/8/27
 9  * @description
10  */
11 @AnController
12 @AnRequestMapping(value = "/hello")
13 public class HelloWorld {
14     @AnRequestMapping("/method")
15     public String method(){
16         return "index";
17     }
18 }
View Code

 7.index.jsp

1 <html>
2 <body>
3 <h2>Hello World!</h2>
4 </body>
5 </html>
View Code

8.訪問地址:http://localhost:8080/hello/method

9.成功結果:

10.錯誤結果

現在代碼已經完成了,也許開始你對很多東西不理解,但是沒關系可以當作了解,多敲敲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM