解析SpringMVC 前端控制器(Dispatcherservlet)

一 SpringMVC 流程圖

 

DispatcherServlet是前端控制器設計模式的實現,提供Spring Web MVC的集中訪問點,並且負責職責的分派,並且與Spring IoC容器無縫集成,從而能夠得到Spring的全部好處。前端

本身實現了一個dispatcherServlet,但願幫助你們理解DispatcherServlet的實現原理。目錄以下(後面會重構,結構會有所變化)java

構)web

二 註解 

   模仿SpringMVC ,自定義@Controller ,@Service,@AutoWired,@RequestMapping設計模式

   @Controller瀏覽器

        

    @Serviceapp

       

    @RequestMappingide

      

    @AutoWired測試

      

三 DispatcherServlet

  SpringMVC 一樣要遵循JAVA EE 規範。因此DispatcherServlet 要繼承HttpServlet。this

  實現代碼以下url

 

package servlet;

import com.dsk.annotation.Autowired;
import com.dsk.annotation.Controller;
import com.dsk.annotation.RequestMapping;
import com.dsk.annotation.Service;
import com.dsk.controller.ControllerTest;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
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;

/**
 * Created by dingshuangkun on 2018/1/29.
 */
public class Dispatcherservlet extends HttpServlet {

    private volatile List<String> classNameList = new ArrayList<>();
    private volatile Map<String, Object> instanceMap = new HashMap<>();

    /**
     * @throws ServletException 1 掃描基包
     */
    @Override
    public void init() throws ServletException {
        String baseScan = "com.dsk.controller,com.dsk.service";
        // 掃描基包 獲取包名加類名
        Map<String, List<String>> packageMap = scanFile(baseScan);
        try {
            for(Map.Entry<String,List<String>> entry : packageMap.entrySet()){
                String packageName = entry.getKey();
                List<String> classNames = entry.getValue();
                // 4 實例化(包名+類名) Class.forName()
                newInstance(packageName,classNames);
            }

            // 實現IOC 注入
            Ioc(instanceMap);
        }catch (Exception e){
            throw  new RuntimeException(e.getMessage());
        }

    }


    private Map<String, List<String>> scanFile(String baseScan) {
        Map<String, List<String>> packageMap = new HashMap<>();
        // 分割  com.dsk.controller  和 com.dsk.service
        String[] paths = baseScan.split(",");
        // 替換爲 com/dsk/controller
        for (String path : paths) {
            String filePath = "/" + path.replaceAll("\\.", "\\/");
            // 1 加載資源
            URL url = this.getClass().getClassLoader().getResource(filePath);
            // 2 獲取全路徑名
            String urlPath = url.getFile();
            // 3 處理路徑 獲得包下面的類
            List<String> classNames = handleFile(urlPath);
            // 4 分組 根據包名分組
            for(String className : classNames){
                packageMap.putIfAbsent(path,new ArrayList<>());
                packageMap.get(path).add(className);
            }
            classNameList.clear();
        }

        return packageMap;
    }

    /**
     * 處理路徑
     *
     * @param urlPath
     */
    private List<String> handleFile(String urlPath) {

        File file = new File(urlPath);
        if (file.isDirectory()) {
            String[] paths = file.list();
            for (String path : paths) {
                // 多是文件也多是文件夾 遞歸處理
                handleFile(urlPath + path);
            }
        } else {
            classNameList.add(file.getName().replace(".class", ""));
        }
        return classNameList;
    }

    /**
     * 實例化
     * 1 經過包名+類名 獲取Class 對象
     * 2 獲取註解信息@Controller 和 @Service
     * 3 實例化 放入容器Map中
     */
    private void newInstance(String packageName, List<String> classNames) throws Exception {
        if (classNames != null && classNames.size() != 0) {
            for (String className : classNames) {
                String name = packageName + "." + className;
                Class cc = Class.forName(packageName + "." + className);
                //  判斷類上是否有@Controller
                if (cc.isAnnotationPresent(Controller.class)) {
                    Controller controller = (Controller) cc.getAnnotation(Controller.class);
                    // 獲取註解的值
                    String value = controller.value();
                    if (value != null && value.length() > 0) {
                        // 把實例存放到容器中
                        instanceMap.put(value, cc.newInstance());
                    } else {
                        // 第一個字母轉小寫(還沒實現)
                        instanceMap.put(className, cc.newInstance());
                    }
                } else if (cc.isAnnotationPresent(Service.class)) {
                    Service service = (Service) cc.getAnnotation(Service.class);
                    String value = service.value();
                    if (value != null && value.length() > 0) {
                        // 把實例存放到容器中
                        instanceMap.put(value, cc.newInstance());
                    } else {
                        // 第一個字母轉小寫(還沒實現)
                        instanceMap.put(className, cc.newInstance());
                    }
                }
            }
        }
    }

    /**
     * IOC 注入
     * 1 遍歷 Map 獲取對象
     * 2 反射 獲取字段
     * 3 給帶有@Autowired 註解的字段實例化
     */
    private void Ioc(Map<String, Object> instanceMap) throws IllegalAccessException {
        if (instanceMap == null || instanceMap.size() == 0) {
            return;
        }
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {

            Object obj = entry.getValue();
            // 獲取Class 對象
            Class cc = obj.getClass();
            // 獲取聲明的字段
            Field[] fields = cc.getDeclaredFields();
            if (fields != null && fields.length > 0) {
                for (Field field : fields) {
                    // 判讀是否帶有註解 Autowired
                    if (field.isAnnotationPresent(Autowired.class)) {
                        field.setAccessible(true);
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        // 從容器中獲取對象
                        Object instance = instanceMap.get(autowired.value());
                        field.set(obj, instance);
                    }
                }
            }
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getServletPath();
        try {
            parseUrl(url);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----doPost------");
    }

    private void parseUrl(String url) throws Exception {
        String path = url.subSequence(1, url.length()).toString();
        String[] params = path.split("/");
        if (params.length != 2) {
            return;
        }
        for (Map.Entry<String, Object> entry : instanceMap.entrySet()) {
            Class cc = entry.getValue().getClass();
            // 判讀類上是否有@Controller 和 @RequestMapping 註解
            if (cc.isAnnotationPresent(Controller.class) && cc.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = (RequestMapping) cc.getAnnotation(RequestMapping.class);
                // 獲取@RequestMapping 的值
                String paramValue = requestMapping.value();
                if (paramValue != null && paramValue.length() > 0) {
                    // localhost:8080/test/test  params=test,test
                    // 判讀 類上 @RequestMapping的值是否和 params[0] 相等
                    if (paramValue.equals(params[0])) {
                        // 獲取該類的方法
                        Method[] methods = cc.getMethods();
                        if (methods != null && methods.length > 0) {
                            for (Method method : methods) {
                                // 判讀方法上是否有@RequestMapping 註解
                                if (method.isAnnotationPresent(RequestMapping.class)) {
                                    RequestMapping rem = method.getAnnotation(RequestMapping.class);
                                    // 獲取方法上@RequestMapping 的值
                                    String methodParam = rem.value();
                                    if (methodParam != null && methodParam.length() > 0) {
                                        // 方法上@RequestMapping 的值methodParam 和 params[1] 是否相等
                                        if (methodParam.equals(params[1])) {
                                            method.invoke(entry.getValue(), null);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

 

測試類

package com.dsk.controller;

import com.dsk.annotation.Autowired;
import com.dsk.annotation.Controller;
import com.dsk.annotation.RequestMapping;
import com.dsk.service.MyService;

/**
 * Created by dingshuangkun on 2018/1/29.
 */
@Controller("controllerTest")
@RequestMapping("test")
public class ControllerTest {

    @Autowired("myService")
    private MyService myService;

    @RequestMapping("test")
    public void test(){
        System.out.println("--------------");
        System.out.println("---- ******  ---");
        System.out.println("---*********----");
        System.out.println("-----------------");
    }

}
package com.dsk.service;

import com.dsk.annotation.Service;

/**
 * Created by dingshuangkun on 2018/1/29.
 */
@Service("myService")
public class MyService {

    public void  test(){
        System.out.println("----serviceTest----");
    }
}

 

配置文件

<?xml version="1.0" encoding="UTF-8"?>
<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_3_0.xsd"
         id="WebApp_ID" version="3.0">

  <servlet>
    <servlet-name>myservlet</servlet-name>
    <servlet-class>servlet.Dispatcherservlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>myservlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
  
</web-app>

 

瀏覽器輸入

http://localhost:8080/test/test

輸出

-------------- ---- ******  --- ---*********---- -----------------

相關文章
相關標籤/搜索