只要肯花時間,一氣呵成手寫SpringMVC框架不是問題!

一、Git項目地址:

 

地址:https://github.com/kobeyk/handwriting-springmvc.git

 

 

網上可以搜到很多,大致思路和手寫步驟如下:

1、仿Spring註解,自定義一套屬於自己的註解,如仿寫常用的@Controller、@Service、@AutoWired等

2、自定義DispatcherServlet類,繼承HttpServlet類,重寫幾個方法,如servlet的初始化方法init()

 

 

3、在init方法中,做一些Servlet容器初始化的工作:

   3.1 掃描package下面的所有class,將class的完全限定名保存在List集合中

   3.2 實例化第一步集合中的Bean對應的實例,實例的過程採用的是Java的反射機制,且哪些bean是需要實例化的是有過濾條件 的,如加了@Controller註解的bean類纔可以newInstance(),實例化的結果,放在beanMap中進行存儲,map中的key就是    bean的name,value就是bean實例。

   3.3 實例化單個bean後,接着要處理單個bean中依賴注入的問題,也就是要處理@AutoWired註解的類字段了

   3.4 bean依賴注入的問題解決後,剩下的就是解決前端請求url和後端method之間的映射(Mapping)了,也就是說,前端發送url請求後,後端,也就是Servlet容器是如何知道要去找哪個Controller中的哪個對應的method去執行呢?這就需要我們再次通過反射技術,去找尋url和method之間的關係了,找到後,存放在mapping容器中,以便於doGet和doPost方法中取出調用。

4、最後,配置項目的web.xml

 

 


 

二、項目結構

 

 


 

三、核心代碼(部分)

 

public class MyDispatcherServlet extends HttpServlet {

    // 重寫三個方法,一個是父類GenericServlet的初始化方法init(),兩個分別是父類HttpServlet的doGet和doPost請求處理方法

    /**存放指定的掃描包下面的所有的class*/
    List<String> classNames = new ArrayList<>();

    /**存放符合條件的實例化後的bean(類似於簡版的ioc容器),如加了xxx註解的bean要放進來*/
    Map<String,Object> beanMap = new HashMap<>(16);

    /**url和method映射關係 (請求api地址和處理請求的方法的鍵值對)*/
    Map<String, Method> urlMethodMap = new HashMap<>(16);

    @Override
    public void init() throws ServletException {
        // 1、第一步,掃描指定的包(主要是掃描xxx.class文件)
        scanPackage("com.appleyk");
        System.out.println("classNames = " + classNames);
        // 2、利用反射,實例化bean(將實例化的bean,放入bean容器map中)
        beanInstance();
        System.out.println("beanMap = " + beanMap);
        //3、處理@XXAutoWired註解,bean實例中的字段如果有這個註解(依賴其他bean),則將該註解對應的實例從beanMap中取出,並賦予該字段
        doAutoWired();
        // 4、處理請求的url與控制器類中方法的mapping(映射)關係
        handleMapping();
        System.out.println("urlMethodMap = " + urlMethodMap);
    }



    /**
     * 獲取指定掃描包下面的所有class的名稱(限定類名)
     * @param packageStr 要掃描的包名
     */
    public void scanPackage(String packageStr){

        // 1、將xx.xx結構的包名,轉換爲實際意義上的xx/xx/路徑
        String pageckageDir = packageStr.replaceAll("\\.", "/");
        // 2、根據包路徑,拿到當前類路徑(classPath)
        URL resource = this.getClass().getClassLoader().getResource(pageckageDir);
        // 3、拿到資源的文件(全)路徑
        String fileStr = resource.getFile();
        // 4、根據路徑創建文件
        File file = new File(fileStr);
        // 5、判斷文件是否存在,不存在直接返回
        if(!file.exists()){
            return;
        }

        // 6、獲取classes文件下面的所有文件集合(可能是文件,也有可能是目錄)
        File[] files = file.listFiles();
        for (File filePath : files) {
            String className = packageStr + "." + filePath.getName().replace(".class","");
            if(filePath.isDirectory()){
                // 如果是文件夾的話,遞歸繼續獲取類限定名
                scanPackage(className);
            }else{
                // 如果是文件的話,直接包名+“.”+文件拼接成類限定名,然後加入到集合中
                classNames.add(className);
            }
        }

    }


    /**
     * 首字母小寫
     */
    private String lowerFirstCase(String str){
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

}

 


 

 

四、如何更改MVC項目的contextPath

 

 


 

 

五、啓動Tomcat,來一波測試,驗證手寫300多行代碼的實際效果

 

(1)run

 

 


 

(2)瀏覽器輸入請求url

 

 


 

http://localhost:8080/springmvc/user/query

 

A: 默認不給參數的調用情況

 


 

 

 


 

B: 帶參數的調用情況

 

http://localhost:8080/springmvc/user/query?name=appleyk&sex=%E7%94%B7

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章