欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

学会 SpringMVC,看这一篇就够了!

程序员文章站 2022-07-10 16:10:54
...

什么是 SpringMVC ?

SpringMVC 实际上是 Spring 的一部分,它是一个表现层的框架。

什么是 MVC 模型

学会 SpringMVC,看这一篇就够了!
用户的请求会到达 Servlet,然后根据请求调用相应的 Java Bean,并把所有的显示结果交给 JSP 去完成,这样的模式我们就称为 MVC 模式。

  1. M 代表 模型(Model):数据,dao,bean
  2. V 代表 视图(View):网页,JSP,用来展示模型中的数据
  3. C 代表 控制器(controller):把不同的数据(Model),显示在不同的视图(View)上,Servlet
    扮演的就是这样的角色。

SpringMVC 的架构

学会 SpringMVC,看这一篇就够了!
特点:

  1. 结构松散,几乎可以在 Spring MVC 中使用各类视图
  2. 松耦合,各个模块分离
  3. 与 Spring 无缝集成

SpringMVC 的处理流程

学会 SpringMVC,看这一篇就够了!

SpringMVC 的框架结构

学会 SpringMVC,看这一篇就够了!

  1. 用户发送请求至前端控制器DispatcherServlet。
  2. DispatcherServlet收到请求调用HandlerMapping处理器映射器。
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
  4. DispatcherServlet通过HandlerAdapter处理器适配器调用处理器。
  5. 执行处理器(Controller,也叫后端控制器)。
  6. Controller执行完成返回ModelAndView。
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。
  8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
  9. ViewReslover解析后返回具体View。
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
  11. DispatcherServlet响应用户。

上面的解释可能比较难懂,下面我对此做一个更详细的讲解

  1. 浏览器发送请求,请求具体发到谁呢?先发到前端控制器,也就是说所有的请求都给发到前端控制器,前端控制器是所有请求的入口,但前端控制器不能处理业务请求,它只是一个请求的转发。
  2. 谁来处理业务请求呢?Handler处理器来真正处理业务请求,那么问题来了,前端控制器如何来找到这个Handler处理器呢?处理器映射器记录的就是请求的url和处理的方法之间的映射关系,这个映射关系是怎么建立起来的呢?就是通过@RequestMapping这个注解来建立起来的,这个映射关系就相当于一个Map(key-value这种形式),key就是请求的url,value就是处理的Handler。现在,前端控制器拿到这个请求之后,要找到对应的Handler,怎么找呢?就要找处理器映射器,问它请求谁来处理?
  3. 处理器映射器会根据你请求的url来找对应的处理器,找不到就会报错,如果找到之后,这时,它就会返回一个处理器执行链,这个处理器执行链里面除了有Handler之外,还有拦截器(这儿我们可以开发自己的拦截器),然后返回给前端控制器。
  4. 前端控制器依然不能处理这个业务请求,它这时做的还有另外一件事情,因为返回Handler,它也不知道这个Handler是什么类型,因为在springmvc中Handler除了可以是注解形式的之外,其实还可以是非注解形式的(非注解形式我们一般不用),前端控制器并不知道这个Handler到底是什么类型的,那就没办法执行它,那总得找个东西执行,这时它就会把这个事交给另外一个组件来处理,这个组件就叫处理器适配器,这个处理器适配器就是来适配不同类型的Handler。它就会根据你不同类型的Handler来选择不同类型的适配器来执行它。
  5. 假如当前Handler是注解形式的,那么它就会选择注解形式的处理器适配器来执行这个Handler。Handler就执行了,也就是说我们Controller类中的那个方法就执行了,方法执行之后,里面的业务就处理了。
  6. 业务处理之后,最后返回一个ModelAndView。处理器适配器拿到这个结果是没有用的,它的作用就是执行这个Handler,把这个Handler执行完之后,它的事就做完了。
  7. 做完之后,拿到这个返回结果,那么它会原封不动地把这个返回结果扔给前端控制器,这时处理器适配器的事就做完了。
  8. 前端控制器拿到这个ModelAndView,它还是没有办法处理,它还是不能返回html,这时它要找到相应的jsp,因为ModelAndView即包含模型又包含视图,这个视图指定我们要用谁来渲染这个数据。我们要渲染数据,这时它就要找一个视图解析器来解析这个视图,由于这个视图也有很多种(我们最常见的视图是jsp,除了jsp,其实还有其他的,比如说还可以是报表,还可以是pdf,还可以是freemaker等),它会找不同的视图解析器来处理。因为现在我们最常用的视图是jsp,所以它就找到jsp对应的视图解析器。
  9. 找到这个视图解析器,它来把这个视图解析,解析完了之后它会返回一个View对象。
  10. 最后我们再调用这个视图解析器的渲染视图这个过程,渲染视图这个过程其实就是对于我们的jsp来说,就是把这个数据渲染成html。
  11. 最终渲染成html之后,就响应给用户。

SpringMVC 的组件说明

  • DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于mvc模式中的c,DispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。

  • HandlerMapping:处理器映射器

HandlerMapping负责根据用户请求找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

  • Handler:处理器

Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。

  • HandlAdapter:处理器适配器

通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

  • ViewResolver:视图解析器

ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。

  • View:视图

SpringMVC框架提供了很多的View视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是jsp。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。

其实,在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为springmvc的三大组件。

springmvc.xml

springmvc.xml 是 SpringMVC的核心配置文件,其实发现,就算我们的 springmvc.xml 写成下面这个样子,程序也是可以运行的。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <context:component-scan base-package="com.itheima.springmvc.controller"/>

</beans>

为啥只有这么一点东西呢?SpringMVC的三大组件哪去了?我们发现这几个组件并没配置,但却是可以的,就是因为它有一个默认配置,DispatcherServlet.properties这个默认配置文件里面默认加载了。

我们如果使用默认加载的注解方式的映射器和适配器,那么对它们的可控制性是比较小的,所以一般来说,我们都是自己配置的,因为有的时候我们需要扩展一些其他的组件。

注解映射器和适配器

配置组件扫描器

使用组件扫描器可省去在 Spring 容器中配置每个 Controller 类的繁琐。使用<context:component-scan>自动扫描标记 @controller 注解的控制器类,配置如下:

<context:component-scan base-package="com.itheima.springmvc.controller"/>

如果要扫描多个包,多个包中间使用半角逗号分隔。

配置 RequestMappingHandlerMapping

注解式处理器映射器,对类中标记@ResquestMapping注解的方法进行映射,根据@ResquestMapping注解定义的url匹配@ResquestMapping注解标记的方法,匹配成功返回HandlerMethod对象给前端控制器,HandlerMethod对象中封装了url对应的方法Method。

<!-- 配置注解式处理器映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
配置 RequestMappingHandlerAdapter

注解式处理器适配器,对标记@ResquestMapping注解的方法进行适配。

<!-- 配置注解式处理器适配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>
<mvc:annotation-driven>

使用注解要注意一个问题,就是注解适配器和映射器必须配对使用,也就是说,不能一个用注解,一个用非注解。要用一起用,要么都不用。其实在SpringMVC中还有更加简便的注解,SpringMVC使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping和RequestMappingHandlerAdapter,可在springmvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。如果配置一个注解驱动之后,那么就可以不用配置处理器映射器和处理器适配器了。

配置视图解析器

我们也可在 springmvc.xml 配置文件中自己手动配置视图解析器

<!-- 配置视图解析器(对jsp默认解析的视图解析器) -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- prefix:前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"></property>
    <!-- suffix:后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>

prefix和suffix:查找视图页面的前缀和后缀,最终视图的址为:前缀+逻辑视图名+后缀,逻辑视图名需要在Controller返回的ModelAndView中指定,比如逻辑视图名为hello,则最终返回的jsp物理视图地址就为 “WEB-INF/jsp/hello.jsp”。

Controller类的参数绑定

假设现在我们有一个需求,需要根据 id 查询数据,那么我们需要从请求的参数中把请求的 id 取出来。id 应该包含在 Request 对象中。可以从 Request 对象中取 id。故我们可以写如下代码:

public ModelAndView editItem(HttpServletRequest request) {
    // 从request中取出参数
    String strId = request.getParameter("id");
    int id = new Integer(strId);
    // 调用服务
    Items items = itemService.getItemById(id);
    // 把结果传递给页面
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("item", items);
    // 设置逻辑视图
    modelAndView.setViewName("editItem");
    return modelAndView;
}

如果想获得Request对象只需要在Controller类方法的形参中添加一个参数即可。SpringMVC框架会自动把Request对象传递给方法。这就是SpringMVC框架默认支持的参数类型。

SpringMVC框架默认支持的参数类型

处理器形参中添加如下类型的参数,处理适配器会默认识别并进行赋值。

  1. HttpServletRequest:通过request对象获取请求信息。
  2. HttpServletResponse:通过response处理响应信息。
  3. HttpSession:通过session对象得到session中存放的对象。
  4. Model/ModelMap:ModelMap是Model接口的实现类,我们可通过Model或ModelMap向页面传递数据
model.addAttribute("item", items);

页面中通过${item.XXXX}获取item对象的属性值。

实际上,使用Model和ModelMap的效果是一样的,如果直接使用Model接口,SpringMVC会实例化ModelMap。如果使用Model接口,那么editItem方法可以改造成:

@RequestMapping("/itemEdit")
public String editItem(HttpServletRequest request, 
        HttpServletResponse response, HttpSession session, Model model) {
    // 从request中取出参数
    String strId = request.getParameter("id");
    int id = new Integer(strId);
    // 调用服务
    Items items = itemService.getItemById(id);

    // 使用模型设置返回结果,model是框架给我们传递过来的对象,所以这个对象也不需要我们返回
    model.addAttribute("item", items); // 类似于:modelAndView.addObject("item", items);
    // 返回逻辑视图
    return "editItem";
}

如果使用Model接口则可以不使用ModelAndView对象,Model对象可以向页面传递数据(model是框架给我们传递过来的对象,所以这个对象不需要我们返回),View对象则可以使用String返回值替代。不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据。

简单数据类型绑定

当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。从Request取参数的方法可以进一步简化。这样一来,editItem方法可以改造成:

@RequestMapping("/itemEdit")
public String editItem(Integer id, Model model) { 
    // 调用服务
    Items items = itemService.getItemById(id);
    // 把数据传递给页面,需要用到Model接口
    model.addAttribute("item", items); 
    // 返回逻辑视图
    return "editItem";
}

注意,参数类型推荐使用包装数据类型,因为基础数据类型不可以为null。

@RequestParam

使用@RequestParam注解常用于处理简单类型的绑定。

  1. value:参数名字,即入参的请求参数名字,如value=“item_id”表示请求的参数区中的名字为item_id的参数的值将传入。
  2. required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
  3. defaultValue:默认值,表示如果请求中没有同名参数时的默认值。

使用@RequestParam注解,editItem方法可以改造成:

@RequestMapping("/itemEdit")
public String editItem(@RequestParam(value="id",defaultValue="1",required=true) Integer ids, Model model) { 
    // 调用服务
    Items items = itemService.getItemById(ids);
    // 把数据传递给页面,需要用到Model接口
    model.addAttribute("item", items); 
    // 返回逻辑视图
    return "editItem";
}

required=true限定id参数为必须传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传id参数值。

使用pojo接收表单数据

如果提交的参数很多,或者提交的表单中的内容很多的时候可以使用pojo接收数据。要求pojo对象中的属性名和表单中input的name属性一致。就像下图所示:
学会 SpringMVC,看这一篇就够了!
请求的参数名称和pojo的属性名称一致,会自动将请求参数赋值给pojo的属性。注意:提交的表单中不要有日期类型的数据,否则会报400错误。

@RequestMapping注解的使用

通过@RequestMapping注解可以定义不同的处理器映射规则。

URL路径映射

例如:

@RequestMapping("/item"
@RequestMapping(value="/item")

value的值是数组,所以可以将多个url映射到同一个方法上。

窄化请求映射

在class上添加@RequestMapping(url)指定通用请求前缀, 限制此类下的所有方法请求url必须以请求前缀开头,通过此方法对url进行分类管理。如下:@RequestMapping放在类名上边,设置请求前缀。

@Controller
@RequestMapping("/item")
public class ItemController {

}
请求方法限定

限定GET方法:

@RequestMapping(method = RequestMethod.GET)
@RequestMapping(value="/updateitem",method={RequestMethod.GET})

限定POST方法:

@RequestMapping(method = RequestMethod.POST)

GET和POST都可以:

@RequestMapping(value="/updateitem",method={RequestMethod.POST,RequestMethod.GET})

Controller类的方法返回值

返回ModelAndView

Controller类方法中定义ModelAndView对象并返回,对象中可添加model数据、指定view。

返回void

在Controller类方法形参上可以定义request和response,使用request或response指定响应结果:

使用request转向页面,如下:

request.getRequestDispatcher("页面路径").forward(request, response);

之前我们实现查询,返回的是ModelAndView,如果现在该方法的返回值是void,那么就应使用request跳转页面,如下:

@RequestMapping("/itemList2")
public void itmeList2(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 查询商品列表
    List<Items> itemList = itemService.getItemList();
    // 向页面传递参数
    request.setAttribute("itemList", itemList);
    // 如果使用原始的方式做页面跳转,必须给的是jsp的完整路径
    request.getRequestDispatcher("/WEB-INF/jsp/itemList.jsp").forward(request, response);
}

如果使用原始的方式做页面跳转,那么必须给定jsp页面的完整路径。

也可以通过response实现页面重定向:

response.sendRedirect("url")

也可以通过response指定响应结果,例如响应json数据如下:

response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");

例如,将以上itmeList2方法修改为:

@RequestMapping("/itemList2")
public void itmeList2(HttpServletRequest request, HttpServletResponse response) throws Exception {
    PrintWriter writer = response.getWriter();
    response.setCharacterEncoding("utf-8");
    response.setContentType("application/json;charset=utf-8");
    writer.write("{\"id\":\"123\"}");
}
返回字符串

Controller类方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。
学会 SpringMVC,看这一篇就够了!
Redirect重定向:
Contrller类方法返回结果重定向到一个url地址,如下商品信息修改提交后重定向到商品查询方法,参数无法直接带到商品查询方法中。

@RequestMapping(value="/updateitem",method={RequestMethod.POST,RequestMethod.GET})
public String updateItems(Items items) {
    itemService.updateItem(items);
    // '/'是不包含工程名的根目录,即http://localhost:8080/springmvc-web2/item/itemList.action
    return "redirect:/item/itemList.action";
}

redirect方式相当于“response.sendRedirect()”,转发后浏览器的地址栏变为转发后的地址,因为转发即执行了一个新的request和response。由于新发起一个request,原来的参数在转发时就不能传递到下一个url,如果要传参数可以在/item/itemList.action后边加参数,如下:

return "redirect:/item/itemList.action?id=xxx&name=xxx";

但如果你使用的是Model接口,那么SpringMVC框架会自动将Model中的数据拼装到/item/itemList.action后面。

forward转发:
Controller类方法执行后继续执行另一个Controller类方法,如下商品修改提交后转向到商品修改页面,修改商品的id参数可以直接带到商品修改方法中。

@RequestMapping(value="/updateitem",method={RequestMethod.POST,RequestMethod.GET})
public String updateItems(Items items) throws UnsupportedEncodingException {
    itemService.updateItem(items);
    return "forward:/item/itemList.action";
}

forward方式相当于“request.getRequestDispatcher().forward(request,response)”,转发后浏览器地址栏还是原来的地址。转发并没有执行新的request和response,而是和转发前的请求共用一个request和response。所以转发前请求的参数在转发后仍然可以读取到。

异常处理思路

系统的dao、service、controller出现异常都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理,SpringMVC提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
学会 SpringMVC,看这一篇就够了!

全局异常处理器案例

自定义异常类

为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

我们可在工程下编写一个自定义异常类——CustomerException.java,如下:

public class CustomerException extends Exception {

    private String expMessage;

    public CustomerException() {

    }

    public CustomerException(String msg) {
        this.expMessage = msg;
    }

    public String getExpMessage() {
        return expMessage;
    }

    public void setExpMessage(String expMessage) {
        this.expMessage = expMessage;
    }

}
自定义异常处理器

全局异常处理器处理思路:

  1. 解析出异常类型。
  2. 如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面展示。
  3. 如果该异常类型不是系统自定义的异常,那么应取出错误的堆栈信息,并记录下来,在错误页面展示。

SpringMVC提供一个HandlerExceptionResolver接口,自定义全局异常处理器必须要实现这个接口,所以我们可编写一个自定义全局异常处理器,如下:

/**
 * 全局异常处理器
 *
 */
public class GlobalExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception exception) {
        // 判断异常的种类
        String msg = null;
        if (exception instanceof CustomerException) {
            // 如果是自定义异常,就从异常里面取出错误消息
            CustomerException custExp = (CustomerException) exception;
            msg = custExp.getExpMessage();
        } else {
            // 如果是运行时异常,则取错误的堆栈信息
            exception.printStackTrace(); // 向控制台上打印堆栈信息

            StringWriter s = new StringWriter();
            PrintWriter printWriter = new PrintWriter(s);
            exception.printStackTrace(printWriter);
            msg = s.toString();
        }

        // 写日志、发短信、发邮件
        // 在此省略这一步......

        // 返回一个友好的错误页面,并显示错误消息
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg", msg);
        modelAndView.setViewName("error");
        return modelAndView;
    }

}

记得还要在/WEB-INF/jsp目录下提供一个错误页面——error.jsp

异常处理器配置

在springmvc.xml文件中配置这个自定义的异常处理器:

<!-- 配置全局异常处理器,且全局异常处理器只能有一个 -->
<bean class="com.itheima.springmvc.exception.GlobalExceptionResolver"></bean>

SpringMVC和前台交互形式

学会 SpringMVC,看这一篇就够了!
可以看出,前台传过来的方式有两种,一种是传json格式的数据过来,另一种就是在url的末尾传普通的key/value串过来,针对这两种方式,在Controller类中会有不同的解析,但是在Controller类中返回的json格式的数据都是一样的。下面来具体分析一下SpringMVC是如何与前台进行json数据的交互的。在讲之前先认识两个注解。

@RequestBody注解

@RequestBody注解用于读取http请求的内容(字符串),通过SpringMVC提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到Controller类方法的参数上。

本例子应用:@RequestBody注解实现接收http请求的json数据,将json数据转换为java对象。如下:
学会 SpringMVC,看这一篇就够了!

@ResponseBody注解

@ResponseBody注解用于将Controller类的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json、xml等,通过Response响应给客户端。

本例子应用:@ResponseBody注解实现将Controller类方法返回对象转换为json响应给客户端,如下:
学会 SpringMVC,看这一篇就够了!

Resultful服务

Restful就是一个资源定位及资源操作的风格,不是标准也不是协议,只是一种风格,是对http协议的诠释。

资源定位:互联网所有的事物都是资源,要求url中没有动词,只有名词,没有参数。url请求的风格就像这样:

http://blog.csdn.net/eson_15/article/details/51743514

资源操作:使用put、delete、post、get等不同方法对资源进行操作,分别对应添加、删除、修改、查询。一般使用时还是post和get,put和delete几乎不使用。

现在有这样一个需求:使用RESTful方式实现商品信息查询。有需求,就要解决需求。我们可将ItemController类中的editItem方法改造为:

@RequestMapping("/itemEdit/{id}")
// 如果id和方法的形参一致,@PathVariable注解中可以不写内容
public String editItem(@PathVariable("id") Integer iid, Model model) { 
    // 调用服务
    Items items = itemService.getItemById(iid);
    // 把数据传递给页面,需要用到Model接口
    model.addAttribute("item", items); 
    // 返回逻辑视图
    return "editItem";
}

@RequestMapping(value="/itemEdit/{id}"):{×××}表示占位符,请求的URL可以是“/itemEdit/1”或“/itemEdit/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。如果@RequestMapping中表示为”/viewItems/{id}”,id和形参名称一致,那么@PathVariable就不用指定名称。

除此之外,还要在前端控制器中针对REST进行配置,即将web.xml文件中的前端控制器配置改造为:

<!-- 配置前端控制器 -->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <!-- 指定springmvc配置文件的路径。如果不指定,默认为:/WEB-INF/${servlet-name}-servlet.xml -->
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/springmvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <!-- /:拦截所有请求,不包括jsp。/*:拦截所有请求,包括jsp,应该配置"/" -->
    <url-pattern>/</url-pattern>
</servlet-mapping>

但是有个问题,使用上面的配置后会拦截所有的url(虽说不包括jsp),那么对静态资源也会拦截,所以DispatcherServlet也会解析静态资源,但是这样的话就会出错,所以我们要设置一下不让它解析静态资源。SpringMVC的<mvc:resources mapping="" location="">标签可实现对静态资源进行映射访问。如下是对js文件的访问配置:

<mvc:resources location="/js/" mapping="/js/**"/>

SpringMVC中的拦截器

SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

SpringMVC中拦截器的定义

在SpringMVC中,定义拦截器要实现HandlerInterceptor接口,并实现该接口中提供的三个方法,如下:

public class Interceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("Interceptor1 preHandle........");
        // 执行的时机是在Handler执行之前执行此方法
        // 返回值:如果返回true,就放行,不拦截,正常执行Handler进行处理
        // 返回值:如果返回false,那就拦截,Handler就不能正常处理了
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        // Handler执行之后,在返回ModelAndView之前,对modelAndView做些手脚
        System.out.println("Interceptor1 postHandle........");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        // 返回ModelAndView之后
        // Handler执行过程中出现异常,可以在此处理异常
        System.out.println("Interceptor1 afterCompletion........");
    }

}

针对这三个方法,我做一下简单的分析:

  1. preHandle方法:该方法执行的时机是在Handler执行之前执行。可以用于身份认证、身份授权等。比如如果认证没有通过表示用户没有登录,需要此方法拦截不再往下执行(return
    false),否则就放行(return true)。
  2. postHandle方法:该方法执行的时机是在Handler执行之后,在返回ModelAndView之前执行,可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发,将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里统一指定视图。
  3. afterCompletion方法:返回ModelAndView之后执行。应用场景:统一异常处理(即Handler执行过程中出现异常,可以在此处理异常),统一日志处理等。
SpringMVC中拦截器的配置

针对某种HandlerMapping配置拦截器:
在SpringMVC中,拦截器是针对具体的HandlerMapping进行配置的,也就是说如果在某个HandlerMapping中配置拦截,经过该HandlerMapping映射成功的Handler最终会使用该拦截器。比如,假设我们在springmvc.xml配置文件中配置的映射器是org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,那么我们可以这样来配置拦截器:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="handlerInterceptor1"/>
            <ref bean="handlerInterceptor2"/>
        </list>
    </property>
</bean>
<bean id="handlerInterceptor1" class="com.itheima.springmvc.interceptor.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="com.itheima.springmvc.interceptor.HandlerInterceptor2"/>

针对所有HandlerMapping配置全局拦截器:

那么在SpringMVC中,如何配置类似于全局的拦截器呢?上面也说了,SpringMVC中的拦截器是针对具体的映射器而言的,为了解决这个问题,SpringMVC框架将配置的类似全局的拦截器注入到每个HandlerMapping中,这样就可以成为全局的拦截器了。配置如下:

<!-- 配置拦截器 -->
<mvc:interceptors>
    <!-- 按照配置的顺序执行拦截器 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.itheima.springmvc.interceptor.Interceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.itheima.springmvc.interceptor.Interceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

注意:path=”/**”表示拦截所有的url包括子url路径。在实际开发中,一般我们都用这种配置,<mvc:mapping>中指定要拦截的url即可。

SpringMVC与Struts2的不同

  1. SpringMVC的入口是一个servlet即前端控制器,而Struts2入口是一个filter过虑器。
  2. SpringMVC是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),Struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
  3. Struts2采用值栈存储请求和响应的数据,通过OGNL存取数据,SpringMVC通过参数解析器将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面。jsp视图解析器默认使用jstl。

转载至:MyBatis+SpringMVC