关于java:30个类手写Spring核心原理之MVC映射功能4

8次阅读

共计 20632 个字符,预计需要花费 52 分钟才能阅读完成。

本文节选自《Spring 5 外围原理》

接下来咱们来实现 MVC 模块的性能,应该不须要再做阐明。Spring MVC 的入口就是从 DispatcherServlet 开始的,而后面的章节中已实现了 web.xml 的根底配置。上面就从 DispatcherServlet 开始添砖加瓦。

1 MVC 顶层设计

1.1 GPDispatcherServlet

咱们曾经理解到 Servlet 的生命周期由 init() 到 service() 再到 destory() 组成,destory() 办法咱们不做实现。后面咱们讲过,这是 J2EE 中模板模式的典型利用。上面先定义好全局变量:


package com.tom.spring.formework.webmvc.servlet;

import com.tom.spring.formework.annotation.GPController;
import com.tom.spring.formework.annotation.GPRequestMapping;
import com.tom.spring.formework.context.GPApplicationContext;
import com.tom.spring.formework.webmvc.*;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
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.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

//Servlet 只是作为一个 MVC 的启动入口
@Slf4j
public class GPDispatcherServlet extends HttpServlet {

    private  final String LOCATION = "contextConfigLocation";

    // 读者能够思考一下这样设计的经典之处
    //GPHandlerMapping 最外围的设计,也是最经典的
    // 它间接干掉了 Struts、Webwork 等 MVC 框架
    private List<GPHandlerMapping> handlerMappings = new ArrayList<GPHandlerMapping>();

    private Map<GPHandlerMapping,GPHandlerAdapter> handlerAdapters = new HashMap<GPHandlerMapping, GPHandlerAdapter>();

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

    private GPApplicationContext context;

}

上面实现 init() 办法,咱们次要实现 IoC 容器的初始化和 Spring MVC 九大组件的初始化。@Override
    public void init(ServletConfig config) throws ServletException {
        // 相当于把 IoC 容器初始化了
        context = new GPApplicationContext(config.getInitParameter(LOCATION));
        initStrategies(context);
    }

    protected void initStrategies(GPApplicationContext context) {

        // 有九种策略
        // 针对每个用户申请,都会通过一些解决策略解决,最终能力有后果输入
        // 每种策略能够自定义干涉,然而最终的后果都统一

        // =============  这里说的就是传说中的九大组件 ================
        initMultipartResolver(context);// 文件上传解析,如果申请类型是 multipart,将通过 MultipartResolver 进行文件上传解析
        initLocaleResolver(context);// 本地化解析
        initThemeResolver(context);// 主题解析

        /** 咱们本人会实现 */
        //GPHandlerMapping 用来保留 Controller 中配置的 RequestMapping 和 Method 的对应关系
        initHandlerMappings(context);// 通过 HandlerMapping 将申请映射到处理器
        /** 咱们本人会实现 */
        //HandlerAdapters 用来动静匹配 Method 参数,包含类转换、动静赋值
        initHandlerAdapters(context);// 通过 HandlerAdapter 进行多类型的参数动静匹配

        initHandlerExceptionResolvers(context);// 如果执行过程中遇到异样,将交给 HandlerExceptionResolver 来解析
        initRequestToViewNameTranslator(context);// 间接将申请解析到视图名

        /** 咱们本人会实现 */
        // 通过 ViewResolvers 实现动静模板的解析
        // 本人解析一套模板语言
        initViewResolvers(context);// 通过 viewResolver 将逻辑视图解析到具体视图实现

        initFlashMapManager(context);//Flash 映射管理器
    }

    private void initFlashMapManager(GPApplicationContext context) {}
    private void initRequestToViewNameTranslator(GPApplicationContext context) {}
    private void initHandlerExceptionResolvers(GPApplicationContext context) {}
    private void initThemeResolver(GPApplicationContext context) {}
    private void initLocaleResolver(GPApplicationContext context) {}
    private void initMultipartResolver(GPApplicationContext context) {}

    // 将 Controller 中配置的 RequestMapping 和 Method 进行一一对应
    private void initHandlerMappings(GPApplicationContext context) {
        // 依照咱们通常的了解应该是一个 Map
        //Map<String,Method> map;
        //map.put(url,Method)

        // 首先从容器中获取所有的实例
        String [] beanNames = context.getBeanDefinitionNames();
        try {for (String beanName : beanNames) {// 到了 MVC 层,对外提供的办法只有一个 getBean() 办法
                // 返回的对象不是 BeanWrapper,怎么办?Object controller = context.getBean(beanName);
                //Object controller = GPAopUtils.getTargetObject(proxy);
                Class<?> clazz = controller.getClass();

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

                String baseUrl = "";

                if (clazz.isAnnotationPresent(GPRequestMapping.class)) {GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
                    baseUrl = requestMapping.value();}

                // 扫描所有的 public 类型的办法
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {if (!method.isAnnotationPresent(GPRequestMapping.class)) {continue;}

                    GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
                    this.handlerMappings.add(new GPHandlerMapping(pattern, controller, method));
                    log.info("Mapping:" + regex + "," + method);

                }

            }
        }catch (Exception e){e.printStackTrace();
        }

    }

    private void initHandlerAdapters(GPApplicationContext context) {
        // 在初始化阶段,咱们能做的就是,将这些参数的名字或者类型按肯定的程序保留下来
        // 因为前面用反射调用的时候,传的形参是一个数组
        // 能够通过记录这些参数的地位 index,一一从数组中取值,这样就和参数的程序无关了
        for (GPHandlerMapping handlerMapping : this.handlerMappings){
            // 每个办法有一个参数列表,这里保留的是形参列表
            this.handlerAdapters.put(handlerMapping,new GPHandlerAdapter());
        }

    }

    private void initViewResolvers(GPApplicationContext context) {
        // 在页面中输出 http://localhost/first.html
        // 解决页面名字和模板文件关联的问题
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource (templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);

        for (File template : templateRootDir.listFiles()) {this.viewResolvers.add(new GPViewResolver(templateRoot));
        }

    }

在下面的代码中,咱们只实现了九大组件中的三大外围组件的基本功能,别离是 HandlerMapping、HandlerAdapter、ViewResolver,实现 MVC 最外围的调度性能。其中 HandlerMapping 就是策略模式的利用,用输出 URL 间接调用不同的 Method 已达到获取后果的目标。顾名思义,HandlerAdapter 利用的是适配器模式,将 Request 的字符型参数主动适配为 Method 的 Java 实参,次要实现参数列表主动适配和类型转换性能。ViewResolver 也算一种策略,依据不同的申请抉择不同的模板引擎来进行页面的渲染。
接下来看 service() 办法,它次要负责接管申请,失去 Request 和 Response 对象。在 Servlet 子类中 service() 办法被拆分成 doGet() 办法和 doPost() 办法。咱们在 doGet() 办法中间接调用 doPost() 办法,在 doPost() 办法中调用 doDispatch() 办法,真正的调用逻辑由 doDispatch() 来执行。


@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 {
        try {doDispatch(req, resp);
        }catch (Exception e){resp.getWriter().write("<font size='25'color='blue'>500 Exception</font><br/>Details: <br/>" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]","")
                    .replaceAll("\\s","\r\n") +  "<font color='green'><i>Copyright@GupaoEDU </i></font>");
            e.printStackTrace();}
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception{

        // 依据用户申请的 URL 来取得一个 Handler
        GPHandlerMapping handler = getHandler(req);
        if(handler == null){processDispatchResult(req,resp,new GPModelAndView("404"));
            return;
        }

        GPHandlerAdapter ha = getHandlerAdapter(handler);

        // 这一步只是调用办法,失去返回值
        GPModelAndView mv = ha.handle(req, resp, handler);

        // 这一步才是真的输入
        processDispatchResult(req,resp, mv);

    }

    private void processDispatchResult(HttpServletRequest request,HttpServletResponse response, GPModelAndView mv) throws Exception {// 调用 viewResolver 的 resolveViewName() 办法
        if(null == mv){return;}

        if(this.viewResolvers.isEmpty()){return;}

        if (this.viewResolvers != null) {for (GPViewResolver viewResolver : this.viewResolvers) {GPView view = viewResolver.resolveViewName(mv.getViewName(), null);
                if (view != null) {view.render(mv.getModel(),request,response);
                    return;
                }
            }
        }

    }

    private GPHandlerAdapter getHandlerAdapter(GPHandlerMapping handler) {if(this.handlerAdapters.isEmpty()){return  null;}
        GPHandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)) {return ha;}
        return null;
    }

    private GPHandlerMapping getHandler(HttpServletRequest req) {if(this.handlerMappings.isEmpty()){return  null;}

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath,"").replaceAll("/+","/");

        for (GPHandlerMapping handler : this.handlerMappings) {Matcher matcher = handler.getPattern().matcher(url);
            if(!matcher.matches()){continue;}
            return handler;
        }

        return null;
}

GPDisptcherServlet 的残缺代码请关注微信公众号回复“Spring”。上面补充实现下面的代码中缺失的依赖类。

1.2 GPHandlerMapping

咱们曾经晓得 HandlerMapping 次要用来保留 URL 和 Method 的对应关系,这里其实应用的是策略模式。


package com.tom.spring.formework.webmvc;

import java.lang.reflect.Method;
import java.util.regex.Pattern;

public class GPHandlerMapping {
    private Object controller; // 指标办法所在的 contrller 对象
    private Method method; //URL 对应的指标办法
    private Pattern pattern;  //URL 的封装

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

    public Object getController() {return controller;}

    public void setController(Object controller) {this.controller = controller;}

    public Method getMethod() {return method;}

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

    public Pattern getPattern() {return pattern;}

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

1.3 GPHandlerAdapter

原生 Spring 的 HandlerAdapter 次要实现申请传递到服务端的参数列表与 Method 实参列表的对应关系,实现参数值的类型转换工作。外围办法是 handle(),在 handle() 办法中用反射来调用被适配的指标办法,并将转换包装好的参数列表传递过来。


package com.tom.spring.formework.webmvc;

import com.tom.spring.formework.annotation.GPRequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

// 专人干专事
public class GPHandlerAdapter {public boolean supports(Object handler){return (handler instanceof GPHandlerMapping);
    }

    public GPModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception{GPHandlerMapping handlerMapping = (GPHandlerMapping)handler;

        // 每个办法有一个参数列表,这里保留的是形参列表
        Map<String,Integer> paramMapping = new HashMap<String, Integer>();

        // 这里只是给出命名参数
        Annotation[][] pa = handlerMapping.getMethod().getParameterAnnotations();
        for (int i = 0; i < pa.length ; i ++) {for (Annotation a : pa[i]) {if(a instanceof GPRequestParam){String paramName = ((GPRequestParam) a).value();
                    if(!"".equals(paramName.trim())){paramMapping.put(paramName,i);
                    }
                }
            }
        }

        // 依据用户申请的参数信息,跟 Method 中的参数信息进行动静匹配
        //resp 传进来的目标只有一个:将其赋值给办法参数,仅此而已

        // 只有当用户传过来的 ModelAndView 为空的时候,才会新建一个默认的

        //1. 要筹备好这个办法的形参列表
        // 办法重载时形参的决定因素:参数的个数、参数的类型、参数程序、办法的名字
        // 只解决 Request 和 Response
        Class<?>[] paramTypes = handlerMapping.getMethod().getParameterTypes();
        for (int i = 0;i < paramTypes.length; i ++) {Class<?> type = paramTypes[i];
            if(type == HttpServletRequest.class ||
                    type == HttpServletResponse.class){paramMapping.put(type.getName(),i);
            }
        }



        //2. 失去自定义命名参数所在的地位
        // 用户通过 URL 传过来的参数列表
        Map<String,String[]> reqParameterMap = req.getParameterMap();

        //3. 结构实参列表
        Object [] paramValues = new Object[paramTypes.length];

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

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

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

            // 因为页面传过来的值都是 String 类型的,而在办法中定义的类型是变幻无穷的
            // 所以要针对咱们传过来的参数进行类型转换
            paramValues[index] = caseStringValue(value,paramTypes[index]);
        }

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

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

        //4. 从 handler 中取出 Controller、Method,而后利用反射机制进行调用

        Object result = handlerMapping.getMethod().invoke(handlerMapping.getController(), paramValues);

        if(result == null){return  null;}

        boolean isModelAndView = handlerMapping.getMethod().getReturnType() == GPModelAndView.class;
        if(isModelAndView){return (GPModelAndView)result;
        }else{return null;}
    }

    private Object caseStringValue(String value,Class<?> clazz){if(clazz == String.class){return value;}else if(clazz == Integer.class){return  Integer.valueOf(value);
        }else if(clazz == int.class){return Integer.valueOf(value).intValue();}else {return null;}
    }

}

1.4 GPModelAndView

原生 Spring 中 ModelAndView 类次要用于封装页面模板和要往页面传送的参数的对应关系。


package com.tom.spring.formework.webmvc;

import java.util.Map;

public class GPModelAndView {

    private String viewName; // 页面模板的名称
    private Map<String,?> model; // 往页面传送的参数

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

    public String getViewName() {return viewName;}

    public void setViewName(String viewName) {this.viewName = viewName;}

    public Map<String, ?> getModel() {return model;}

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

1.5 GPViewResolver

原生 Spring 中的 ViewResolver 次要实现模板名称和模板解析引擎的匹配。通过在 Serlvet 中调用 resolveViewName() 办法来取得模板所对应的 View。在这个 Mini 版本中简化了实现,只实现了一套默认的模板引擎,语法也是齐全自定义的。


package com.tom.spring.formework.webmvc;

import java.io.File;
import java.util.Locale;

// 设计这个类的次要目标是://1. 将一个动态文件变为一个动静文件
//2. 依据用户传送不同的参数,产生不同的后果
// 最终输入字符串,交给 Response 输入
public class GPViewResolver {
    private final String DEFAULT_TEMPLATE_SUFFIX = ".html";

    private File templateRootDir;
    private String viewName;

    public GPViewResolver(String templateRoot){String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot). getFile();
        this.templateRootDir = new File(templateRootPath);
    }

    public GPView resolveViewName(String viewName, Locale locale) throws Exception {
        this.viewName = viewName;
        if(null == viewName || "".equals(viewName.trim())){return null;}
        viewName = viewName.endsWith(DEFAULT_TEMPLATE_SUFFIX) ? viewName : (viewName + DEFAULT_TEMPLATE_SUFFIX);
        File templateFile = new File((templateRootDir.getPath() + "/" + viewName).replaceAll ("/+", "/"));
        return new GPView(templateFile);
    }

    public String getViewName() {return viewName;}
}

1.6 GPView

这里的 GPView 就是后面所说的自定义模板解析引擎,其外围办法是 render()。在 render() 办法中实现对模板的渲染,最终返回浏览器能辨认的字符串,通过 Response 输入。


package com.tom.spring.formework.webmvc;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.RandomAccessFile;
import java.util.Map;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GPView {

    public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=utf-8";

    private File viewFile;

    public GPView(File viewFile){this.viewFile = viewFile;}

    public String getContentType(){return DEFAULT_CONTENT_TYPE;}

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


        try {
            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);
                    if (null == paramValue) {continue;}
                    // 要把¥{} 两头的这个字符串取出来
                    line = matcher.replaceFirst(makeStringForRegExp(paramValue.toString()));
                    matcher = pattern.matcher(line);

                }

                sb.append(line);
            }
        }finally {ra.close();
        }
        response.setCharacterEncoding("utf-8");
        //response.setContentType(DEFAULT_CONTENT_TYPE);
        response.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("&", "\\&");
    }

}

从下面的代码能够看出,GPView 是基于 HTML 文件来对页面进行渲染的。然而退出了一些自定义语法,例如在模板页面中扫描到¥{name} 这样的表达式,就会从 ModelAndView 的 Model 中找到 name 所对应的值,并且用正则表达式将其替换(外国人喜爱用美元符号 $,咱们的模板引擎就用人民币符号¥)。

2 业务代码实现

2.1 IQueryService

定义一个负责查问业务的顶层接口 IQueryService,提供一个 query() 办法:


package com.tom.spring.demo.service;

/**
 * 查问业务
 *
 */
public interface IQueryService  {

   /**
    * 查问
    */
   public String query(String name);
     
}

2.2 QueryService

查问业务的实现 QueryService 也非常简单,就是打印一下调用工夫和传入的参数,并封装为 JSON 格局返回:


package com.tom.spring.demo.service.impl;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.tom.spring.demo.service.IQueryService;
import com.tom.spring.formework.annotation.GPService;
import lombok.extern.slf4j.Slf4j;

/**
 * 查问业务
 *
 */
@GPService
@Slf4j
public class QueryService implements IQueryService {

   /**
    * 查问
    */
   public String query(String name) {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
      String time = sdf.format(new Date());
      String json = "{name:\"" + name + "\",time:\""+ time +"\"}";
      log.info("这是在业务办法中打印的:" + json);
      return json;
   }

}

2.3 IModifyService

定义一个增、删、改业务的顶层接口 IModifyService:



package com.tom.spring.demo.service;
/**
 * 增、删、改业务
 */
public interface IModifyService {
   /**
    * 减少
    */
   public String add(String name, String addr) ;
   /**
    * 批改
    */
   public String edit(Integer id, String name);
   /**
    * 删除
    */
   public String remove(Integer id);
     
}

2.4 ModifyService

增、删、改业务的实现 ModifyService 也非常简单,次要是打印传过来的参数:


package com.tom.spring.demo.service.impl;
import com.tom.spring.demo.service.IModifyService;
import com.tom.spring.formework.annotation.GPService;

/**
 * 增、删、改业务
 */
@GPService
public class ModifyService implements IModifyService {
   /**
    * 减少
    */
   public String add(String name,String addr) {return "modifyService add,name=" + name + ",addr=" + addr;}
   /**
    * 批改
    */
   public String edit(Integer id,String name) {return "modifyService edit,id=" + id + ",name=" + name;}
   /**
    * 删除
    */
   public String remove(Integer id) {return "modifyService id=" + id;}
}

2.5 MyAction

Controller 的次要性能是负责调度,不做业务实现。业务实现办法全副在 Service 层,个别咱们会将 Service 实例注入 Controller。MyAction 中次要实现对 IQueryService 和 IModifyService 的调度,对立返回后果:



package com.tom.spring.demo.action;

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.tom.spring.demo.service.IModifyService;
import com.tom.spring.demo.service.IQueryService;
import com.tom.spring.formework.annotation.GPAutowired;
import com.tom.spring.formework.annotation.GPController;
import com.tom.spring.formework.annotation.GPRequestMapping;
import com.tom.spring.formework.annotation.GPRequestParam;
import com.tom.spring.formework.webmvc.GPModelAndView;

/**
 * 颁布接口 URL
 */
@GPController
@GPRequestMapping("/web")
public class MyAction {

   @GPAutowired IQueryService queryService;
   @GPAutowired IModifyService modifyService;

   @GPRequestMapping("/query.json")
   public GPModelAndView query(HttpServletRequest request, HttpServletResponse response,
                        @GPRequestParam("name") String name){String result = queryService.query(name);
      return out(response,result);
   }
   @GPRequestMapping("/add*.json")
   public GPModelAndView add(HttpServletRequest request,HttpServletResponse response,
            @GPRequestParam("name") String name,@GPRequestParam("addr") String addr){String result = modifyService.add(name,addr);
      return out(response,result);
   }
   @GPRequestMapping("/remove.json")
   public GPModelAndView remove(HttpServletRequest request,HttpServletResponse response,
         @GPRequestParam("id") Integer id){String result = modifyService.remove(id);
      return out(response,result);
   }
   @GPRequestMapping("/edit.json")
   public GPModelAndView edit(HttpServletRequest request,HttpServletResponse response,
         @GPRequestParam("id") Integer id,
         @GPRequestParam("name") String name){String result = modifyService.edit(id,name);
      return out(response,result);
   }
   
   private GPModelAndView out(HttpServletResponse resp,String str){
      try {resp.getWriter().write(str);
      } catch (IOException e) {e.printStackTrace();
      }
      return null;
   }
}

2.6 PageAction

专门设计 PageAction 是为了演示 Mini 版 Spring 对模板引擎的反对,实现从 Controller 层到 View 层的传参,以及对模板的渲染进行最终输入:


package com.tom.spring.demo.action;

import java.util.HashMap;
import java.util.Map;
import com.tom.spring.demo.service.IQueryService;
import com.tom.spring.formework.annotation.GPAutowired;
import com.tom.spring.formework.annotation.GPController;
import com.tom.spring.formework.annotation.GPRequestMapping;
import com.tom.spring.formework.annotation.GPRequestParam;
import com.tom.spring.formework.webmvc.GPModelAndView;

/**
 * 颁布接口 URL
 */
@GPController
@GPRequestMapping("/")
public class PageAction {

   @GPAutowired IQueryService queryService;

   @GPRequestMapping("/first.html")
   public GPModelAndView query(@GPRequestParam("teacher") String teacher){String result = queryService.query(teacher);
      Map<String,Object> model = new HashMap<String,Object>();
      model.put("teacher", teacher);
      model.put("data", result);
      model.put("token", "123456");
      return new GPModelAndView("first.html",model);
   }

}

3 定制模板页面

为了更全面地演示页面渲染成果,别离定义了 first.html 对应 PageAction 中的 first.html 申请、404.html 默认页和 500.html 异样默认页。

3.1 first.html

first.html 定义如下:


<!DOCTYPE html>
<html lang="zh-cn">
<head>
   <meta charset="utf-8">
   <title>SpringMVC 模板引擎演示 </title>
</head>
<center>
   <h1> 大家好,我是¥{teacher} 老师 <br/> 欢送大家一起来摸索 Spring 的世界 </h1>
   <h3>Hello,My name is ¥{teacher}</h3>
   <div>¥{data}</div>
   Token 值:¥{token}
</center>
</html>

3.2 404.html

404.html 定义如下:


<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title> 页面去火星了 </title>
</head>
<body>
    <font size='25' color='red'>404 Not Found</font><br/><font color='green'><i>Copyright @GupaoEDU</i></font>
</body>
</html>

3.3 500.html

500.html 定义如下:


<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title> 服务器如同累了 </title>
</head>
<body>
    <font size='25' color='blue'>500 服务器如同有点累了,须要劳动一下 </font><br/>
    <b>Message:¥{detail}</b><br/>
    <b>StackTrace:¥{stackTrace}</b><br/>
    <font color='green'><i>Copyright@GupaoEDU</i></font>
</body>
</html>

4 运行成果演示

在浏览器中输出 http://localhost/web/query.js…,就会映射到 MyAction 中的 @GPRequestMapping(“query.json”)对应的 query() 办法,失去如下图所示后果。

在浏览器中输出 http://localhost/web/addTom.j…,就会映射到 MyAction 中的 @GPRequestMapping(“add*.json”)对应的 add() 办法,失去如下图所示后果。

在浏览器中输出 http://localhost/web/remove.j…,就会映射到 MyAction 中的 @GPRequestMapping(“remove.json”)对应的 remove() 办法,并将 id 主动转换为 int 类型,失去如下图所示后果。

在浏览器中输出 http://localhost/web/edit.jso…,就会映射到 MyAction 中的 @GPRequestMapping(“edit.json”)对应的 edit() 办法,并将 id 主动转换为 int 类型,失去如下图所示后果。

在浏览器中输出 http://localhost/first.html?t…,就会映射到 PageAction 中的 @GPRequestMapping(“first.html”)对应的 query() 办法,失去如下图所示后果。

到这里,曾经实现了 Spring 从 IoC、ID 到 MVC 的残缺性能。尽管疏忽了一些细节,然而咱们曾经理解到,Spring 的外围设计思维其实并没有咱们设想得那么神秘。咱们曾经奇妙地用到了工厂模式、动态代理模式、适配器模式、模板模式、策略模式、委派模式等,使得代码变得十分优雅。

关注微信公众号『Tom 弹架构』回复“Spring”可获取残缺源码。

本文为“Tom 弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『Tom 弹架构』可获取更多技术干货!

原创不易,保持很酷,都看到这里了,小伙伴记得点赞、珍藏、在看,一键三连加关注!如果你感觉内容太干,能够分享转发给敌人滋润滋润!

正文完
 0