您现在的位置是:亿华云 > 域名

30个类手写Spring核心原理之MVC映射功能

亿华云2025-10-03 16:08:45【域名】1人已围观

简介接下来我们来完成MVC模块的功能,应该不需要再做说明。Spring MVC的入口就是从DispatcherServlet开始的,而前面的章节中已完成了web.xml的基础配置。下面就从Dispatch

接下来我们来完成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.json?name=Tom ,就会映射到MyAction中的@GPRequestMapping(“query.json”)对应的query()方法,得到如下图所示结果。

在浏览器中输入 http://localhost/web/addTom.json?name=tom&addr=HunanChangsha ,就会映射到MyAction中的@GPRequestMapping(“add*.json”)对应的add()方法,得到如下图所示结果。

在浏览器中输入 http://localhost/web/remove.json?id=66 ,就会映射到MyAction中的@GPRequestMapping(“remove.json”)对应的remove()方法,并将id自动转换为int类型,得到如下图所示结果。

在浏览器中输入 http://localhost/web/edit.json?id=666&name=Tom ,就会映射到MyAction中的@GPRequestMapping(“edit.json”)对应的edit()方法,并将id自动转换为int类型,得到如下图所示结果。

在浏览器中输入 http://localhost/first.html?teacher=Tom ,就会映射到PageAction中的@GPRequestMapping(“first.html”)对应的query()方法,得到如下图所示结果。

到这里,已经实现了Spring从IoC、ID到MVC的完整功能。虽然忽略了一些细节,但是我们已经了解到,Spring的核心设计思想其实并没有我们想象得那么神秘。我们已经巧妙地用到了工厂模式、静态代理模式、适配器模式、模板模式、策略模式、委派模式等,使得代码变得非常优雅。

很赞哦!(711)