Spring源碼------手寫體驗MVC(升級版)

Spring------手寫體驗MVC(升級版)

目錄

Spring------手寫體驗MVC(升級版)

1、前言

2、構建思想

2.1 Spring MVC的大致工作流程

2.2 Spring MVC的九大組件

2.3 Spring MVC的核心執行流程

3、核心代碼

4、總結


1、前言

本篇博客並非是對Spring源碼的深入研究。而是對上一篇博客《Spring源碼------手寫體驗MVC》結構的優化:進行職責對的解耦。過程中主要是體驗Spring IOC和DI的初始化過程。那麼這篇博客涉及到的知識點大致有以下幾點:

  • 如何自定義註解,如何通過反射機制去賦予註解強大的功能(說白了,就是體驗在反射機制下,註解功能是多麼的強大)
  • Spring Ioc容器的實現原理
  • Spring DI 註解注入
  • Java反射機制
  • Java I/O流(加載配置文件,讀取配置文件信息) 
  • 正則表達式
     

2、構建思想

2.1 Spring MVC的大致工作流程

img

  • 用戶發送請求至前端控制器DispatcherServlet;
  •  DispatcherServlet收到請求後,調用HandlerMapping處理器映射器,請求獲取Handle;
  • 處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一併返回給DispatcherServlet;
  • DispatcherServlet 調用 HandlerAdapter處理器適配器;
  • HandlerAdapter 經過適配調用 具體處理器(Handler,也叫後端控制器);
  • Handler執行完成返回ModelAndView;
  • HandlerAdapter將Handler執行結果ModelAndView返回給DispatcherServlet;
  • DispatcherServlet將ModelAndView傳給ViewResolver視圖解析器進行解析;
  • ViewResolver解析後返回具體View;
  • DispatcherServlet對View進行渲染視圖(即將模型數據填充至視圖中)
  • DispatcherServlet響應用戶。

2.2 Spring MVC的九大組件

SpringMVC九大組件
序號 組件名 解釋
1 MultipartResolver 多文件上傳組件
2 LocaleResolver 本地語言環境
3 ThemeResolver 主題模板處理器
4 HandlerMapping 保存Url映射關係
5 HandlerAdapter 動態參數適配器
6 HandlerExceptiomResolver 異常攔截器
7 RequestToViewNameTransltor 視圖讀取器,從request中獲取viewname
8 ViewResolvers 視圖轉換器,模板引擎
9 FlashMapManager 參數緩存器

 

 

 

 

 

 

 

 

 

2.3 Spring MVC的核心執行流程

3、核心代碼

3.1 核心類結構圖

      

3.2 核心代碼

/**
 * @description: 委派模式,職責:負責任務調度,請求分發
 * @author: zps
 * @create: 2020-05-09 8:25
 **/
public class ZPSDispatcherServlet extends HttpServlet {

    private ZPSApplicationContext applicationContext;

    private List<ZPSHandlerMapping> handlerMappings = new ArrayList<ZPSHandlerMapping>();

    private Map<ZPSHandlerMapping, ZPSHandlerAdapter> handlerAdapters = new HashMap<ZPSHandlerMapping, ZPSHandlerAdapter>();

    private List<ZPSViewResolver> viewResolvers = new ArrayList<ZPSViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        //委派,根據URL去找到一個對應的Method並通過response返回
        try {
            doDispatch(req,resp);
        } catch (Exception e) {
            try {
                processDispatchResult(req,resp,new ZPSModelAndView("500"));
            } catch (Exception e1) {
                e1.printStackTrace();
                resp.getWriter().write("500 Exception,Detail : " + Arrays.toString(e.getStackTrace()));
            }
        }

    }
    @Override
    public void init(ServletConfig config) throws ServletException {

        //初始化Spring核心IoC容器
        applicationContext = new ZPSApplicationContext(config.getInitParameter("contextConfigLocation"));

        //初始化SpringMVC的九大組件
        initStrategies(applicationContext);

        System.out.println("GP Spring framework is init.");
    }

    //核心處理
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //完成了對HandlerMapping的封裝
        //完成了對方法返回值的封裝ModelAndView

        //通過URL獲得一個HandlerMapping
        ZPSHandlerMapping handler = getHandler(req);
        if(handler == null){
            processDispatchResult(req,resp,new ZPSModelAndView("404"));
            return;
        }
        
        //根據一個HandlerMaping獲得一個HandlerAdapter
        ZPSHandlerAdapter ha = getHandlerAdapter(handler);

        //解析某一個方法的形參和返回值之後,統一封裝爲ModelAndView對象
        ZPSModelAndView mv = ha.handler(req,resp,handler);

        //就把ModelAndView變成一個ViewResolver
        processDispatchResult(req,resp,mv);

    }

    private ZPSHandlerAdapter getHandlerAdapter(ZPSHandlerMapping handler) {
        if(this.handlerAdapters.isEmpty()){return null;}
        return this.handlerAdapters.get(handler);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ZPSModelAndView mv) throws Exception {
        if(null == mv){return;}
        if(this.viewResolvers.isEmpty()){return;}

        for (ZPSViewResolver viewResolver : this.viewResolvers) {
            ZPSView view = viewResolver.resolveViewName(mv.getViewName());
            //直接往瀏覽器輸出
            view.render(mv.getModel(),req,resp);
            return;
        }
    }

    private ZPSHandlerMapping getHandler(HttpServletRequest req) {
        if(this.handlerMappings.isEmpty()){return  null;}
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replaceAll(contextPath,"").replaceAll("/+","/");

        for (ZPSHandlerMapping mapping : handlerMappings) {
            Matcher matcher = mapping.getPattern().matcher(url);
            if(!matcher.matches()){continue;}
            return mapping;
        }
        return null;
    }


    //這裏主要是進行九大主鍵的初始化
    private void initStrategies(ZPSApplicationContext context) {
//        //多文件上傳的組件
//        initMultipartResolver(context);
//        //初始化本地語言環境
//        initLocaleResolver(context);
//        //初始化模板處理器
//        initThemeResolver(context);

        //c初始化處理器映射器
        initHandlerMappings(context);

        //初始化參數適配器
        initHandlerAdapters(context);

//        //初始化異常攔截器
//        initHandlerExceptionResolvers(context);
//        //初始化視圖預處理器
//        initRequestToViewNameTranslator(context);

        //初始化視圖轉換器
        initViewResolvers(context);
//        //FlashMap管理器
//        initFlashMapManager(context);
    }

    //初始化視圖轉換器
    private void initViewResolvers(ZPSApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        for (File file : templateRootDir.listFiles()) {
            this.viewResolvers.add(new ZPSViewResolver(templateRoot));
        }
    }

    private void initHandlerAdapters(ZPSApplicationContext context) {
        for (ZPSHandlerMapping handlerMapping : handlerMappings) {
            this.handlerAdapters.put(handlerMapping,new ZPSHandlerAdapter());
        }
    }
    //初始化處理器映射器
    private void initHandlerMappings(ZPSApplicationContext context) {
        if(this.applicationContext.getBeanDefinitionCount() == 0){ return;}

        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object instance = applicationContext.getBean(beanName);
            Class<?> clazz = instance.getClass();

            if(!clazz.isAnnotationPresent(ZPSController.class)){ continue; }

            //相當於提取 class上配置的url
            String baseUrl = "";
            if(clazz.isAnnotationPresent(ZPSRequestMapping.class)){
                ZPSRequestMapping requestMapping = clazz.getAnnotation(ZPSRequestMapping.class);
                baseUrl = requestMapping.value();
            }

            //只獲取public的方法
            for (Method method : clazz.getMethods()) {
                if(!method.isAnnotationPresent(ZPSRequestMapping.class)){continue;}
                //提取每個方法上面配置的url
                ZPSRequestMapping requestMapping = method.getAnnotation(ZPSRequestMapping.class);

                // //demo//query
                String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*",".*")).replaceAll("/+","/");
                Pattern pattern = Pattern.compile(regex);
                //handlerMapping.put(url,method);
                handlerMappings.add(new ZPSHandlerMapping(pattern,instance,method));
                System.out.println("Mapped : " + regex + "," + method);
            }

        }
    }

}
/**
 * @description: 處理器映射器,由相應的url找到相應的handler
 * @author: zps
 * @create: 2020-05-09 8:30
 **/
public class ZPSHandlerMapping {
    private Pattern pattern;     //URL
    private Method method;  //對應的Method
    private Object controller;//Method對應的實例對象

    public ZPSHandlerMapping(Pattern pattern, Object controller, Method method) {
        this.pattern = pattern;
        this.method = method;
        this.controller = controller;
    }

    public Pattern getPattern() {
        return pattern;
    }

    public void setPattern(Pattern pattern) {
        this.pattern = pattern;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public Object getController() {
        return controller;
    }

    public void setController(Object controller) {
        this.controller = controller;
    }
}
/**
 * @description: 處理器適配器,這裏主要是完成請求方法與處理方法的參數轉換
 * @author: zps
 * @create: 2020-05-09 9:30
 **/
public class ZPSHandlerAdapter {

    public ZPSModelAndView handler(HttpServletRequest req, HttpServletResponse resp, ZPSHandlerMapping handler) throws Exception{

        //保存形參列表
        //將參數名稱和參數的位置,這種關係保存起來
        Map<String,Integer> paramIndexMapping = new HashMap<String, Integer>();

        //通過運行時的狀態
        Annotation[] [] pa = handler.getMethod().getParameterAnnotations();
        for (int i = 0; i < pa.length ; i ++) {
            for(Annotation a : pa[i]){
                if(a instanceof ZPSRequestParam){
                    String paramName = ((ZPSRequestParam) a).value();
                    if(!"".equals(paramName.trim())){
//                        String value = Arrays.toString(params.get(paramName))
//                                .replaceAll("\\[|\\]","")
//                                .replaceAll("\\s+",",");
//                        paramValues[i] = value;
                        paramIndexMapping.put(paramName,i);
                    }
                }
            }
        }

        //初始化一下
        Class<?> [] paramTypes = handler.getMethod().getParameterTypes();
        for (int i = 0; i < paramTypes.length; i++) {
            Class<?> paramterType = paramTypes[i];
            if(paramterType == HttpServletRequest.class || paramterType == HttpServletResponse.class){
                paramIndexMapping.put(paramterType.getName(),i);
            }
        }


        //去拼接實參列表
        Map<String,String[]> params = req.getParameterMap();

        Object [] paramValues = new Object[paramTypes.length];

        for (Map.Entry<String,String[]> param : params.entrySet()) {
            String value = Arrays.toString(params.get(param.getKey()))
                    .replaceAll("\\[|\\]","")
                    .replaceAll("\\s+",",");

            if(!paramIndexMapping.containsKey(param.getKey())){continue;}

            int index = paramIndexMapping.get(param.getKey());

            //允許自定義的類型轉換器Converter,這裏硬編碼
            paramValues[index] = castStringValue(value,paramTypes[index]);
        }

        if(paramIndexMapping.containsKey(HttpServletRequest.class.getName())){
            int index = paramIndexMapping.get(HttpServletRequest.class.getName());
            paramValues[index] = req;
        }

        if(paramIndexMapping.containsKey(HttpServletResponse.class.getName())){
            int index = paramIndexMapping.get(HttpServletResponse.class.getName());
            paramValues[index] = resp;
        }

        Object result = handler.getMethod().invoke(handler.getController(),paramValues);
        if(result == null || result instanceof Void){return null;}

        boolean isModelAndView = handler.getMethod().getReturnType() == ZPSModelAndView.class;
        if(isModelAndView){
            return (ZPSModelAndView)result;
        }
        return null;
    }

    private Object castStringValue(String value, Class<?> paramType) {
        if(String.class == paramType){
            return value;
        }else if(Integer.class == paramType){
            return Integer.valueOf(value);
        }else if(Double.class == paramType){
            return Double.valueOf(value);
        }else {
            if(value != null){
                return value;
            }
            return null;
        }

    }
}
/**
 * @description: 返回結果封裝
 * @author: zps
 * @create: 2020-05-09 11:30
 **/
public class ZPSModelAndView {
    private String viewName;   //視圖名
    private Map<String,?> model;  //返回結果

    public ZPSModelAndView(String viewName, Map<String, ?> model) {
        this.viewName = viewName;
        this.model = model;
    }

    public ZPSModelAndView(String viewName) {
        this.viewName = viewName;
    }

    public String getViewName() {
        return viewName;
    }

    public Map<String, ?> getModel() {
        return model;
    }
}
/**
 * @description: 視圖解析器
 * @author: zps
 * @create: 2020-05-09 11:40
 **/
public class ZPSViewResolver {
    private final String DEFAULT_TEMPLATE_SUFFIX = ".html";
    private File tempateRootDir;
    public ZPSViewResolver(String templateRoot) {
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
        tempateRootDir = new File(templateRootPath);
    }

    public ZPSView resolveViewName(String viewName){
        if(null == viewName || "".equals(viewName.trim())){return null;}
        viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX)? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
        File templateFile = new File((tempateRootDir.getPath() + "/" + viewName).replaceAll("/+","/"));
        return new ZPSView(templateFile);
    }

}
/**
 * @description: 視圖渲染
 * @author: zps
 * @create: 2020-05-09 12:40
 **/
public class ZPSView {

    private File viewFile;
    public ZPSView(File templateFile) {
        this.viewFile = templateFile;
    }

    //渲染
    public void render(Map<String, ?> model, HttpServletRequest req, HttpServletResponse resp) throws Exception {
        StringBuffer sb = new StringBuffer();
        RandomAccessFile ra = new RandomAccessFile(this.viewFile,"r");

        String line = null;
        while (null != (line = ra.readLine())){
            line = new String(line.getBytes("ISO-8859-1"),"utf-8");
            Pattern pattern = Pattern.compile("¥\\{[^\\}]+\\}",Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(line);
            while (matcher.find()){
                String paramName = matcher.group();
                paramName = paramName.replaceAll("¥\\{|\\}","");
                Object paramValue = model.get(paramName);
                line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
                matcher = pattern.matcher(line);
            }
            sb.append(line);
        }
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().write(sb.toString());
    }

    //處理特殊字符
    public static String makeStringForRegExp(String str) {
        return str.replace("\\", "\\\\").replace("*", "\\*")
                .replace("+", "\\+").replace("|", "\\|")
                .replace("{", "\\{").replace("}", "\\}")
                .replace("(", "\\(").replace(")", "\\)")
                .replace("^", "\\^").replace("$", "\\$")
                .replace("[", "\\[").replace("]", "\\]")
                .replace("?", "\\?").replace(",", "\\,")
                .replace(".", "\\.").replace("&", "\\&");
    }
}

4、總結

結合前面三篇博客,大致可以得出Spring框架的初始化過程和原理:

  • 首先就是初始化Spring IoC 容器  (Ioc和DI)

       1. 根據配置文件的信息,掃描相關的包,然後將類信息封裝成BeanDefinition對象

       2.  把BeanDefinition進行緩存,如果是延遲加載,則在進行getBean()時獲取相關的BeanDefinition進行實例化

       3.當調用getBean()方法時,創建Bean實例,並對創建的實例進行一層包裝,封裝成BeanWrapper對象進行緩存

       4.進行bean的一個屬性依賴注入 

  • Spring MVC啓動時調用init()方法,進行九大組件的初始化(這裏只介紹三個)

       1.初始化處理器映射器,即把處理器的url與handler(也就是我們常用的RequestMapping註解方法)進行綁定

       2.初始化處理器適配器,主要是進行handler請求與用戶請求中參數的適配

       3.初始化視圖解析器,主要是對相應視圖模板進行初始化

  • 初始化後,便可以進行請求的處理了

 

個人一點小結,若有誤,希望指出!!!!

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