本文节选自《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的启动入口@Slf4jpublic 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@Slf4jpublic 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;/** * 增、删、改业务 */@GPServicepublic 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弹架构 』可获取更多技术干货!

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