Spring MVC 框架入门学习

144
作者 丹青笔
2016.08.07 00:52* 字数 7657

1.Spring web mvc介绍

Spring web mvc和Struts2都属于表现层的框架,它是Spring框架的一部分,我们可以从Spring的整体结构中看得出来:


2.Web mvc

  1. 用户发起request请求至控制器(Controller)
    控制接收用户请求的数据,委托给模型进行处理

  2. 控制器通过模型(Model)处理数据并得到处理结果
    模型通常是指业务逻辑

  3. 控制器将模型数据在视图(View)中展示
    web中模型无法将数据直接在视图上显示,需要通过控制器完成。如果在C/S应用中模型是可以将数据在视图中展示的。
  4. 控制器将视图response响应给用户
    通过视图展示给用户要的数据或处理结果。

3.Spring web mvc 架构

架构图


流程

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

组件说明:

++以下组件通常使用框架提供实现:++

DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。

HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

HandlAdapter:通过扩展处理器适配器,支持更多类型的处理器。

ViewResolver:通过扩展视图解析器,支持更多类型的视图解析,例如:jsp、freemarker、pdf、excel等。

++下边两个组件通常情况下需要开发++:

Handler:处理器,即后端控制器用controller表示。

View:视图,即展示给用户的界面,视图中通常需要标签语言展示模型数据。

4.开发环境准备

本教程使用Eclipse+tomcat7开发

详细参考“Eclipse开发环境配置-indigo.docx”文档

5.第一个springmvc工程

第一步:建立一个Web项目

在eclipse下创建动态web工程springmvc_01。

第二步:导入spring3.1.4的jar包


第三步:前端控制器配置

在WEB-INF\web.xml中配置前端控制器,

<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    // 表示servlet随服务启动
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    // 以.action结尾的请求交给DispatcherServlet处理
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

load-on-startup:表示servlet随服务启动;

url-pattern:*.action的请交给DispatcherServlet处理。

contextConfigLocation:指定springmvc配置的加载位置,如果不指定则默认加载WEB-INF/[DispatcherServlet 的Servlet 名字]-servlet.xml。

第四步:springmvc配置文件

Springmvc默认加载WEB-INF/[前端控制器的名字]-servlet.xml,也可以在前端控制器定义处指定加载的配置文件,如下:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>

如上代码,通过contextConfigLocation加载classpath下的springmvc-servlet.xml配置文件,配置文件名称可以不限定[前端控制器的名字]-servlet.xml。

第五步:配置处理器映射器

在springmvc-servlet.xml文件配置如下:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.1.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">

    <!-- 处理器映射器 -->
    <!-- 根据bean的name进行查找Handler 将action的url配置在bean的name中 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />

BeanNameUrlHandlerMapping:表示将定义的Bean名字作为请求的url,需要将编写的controller在spring容器中进行配置,且指定bean的name为请求的url,且必须以.action结尾。

第六步:配置处理器适配器

在springmvc-servlet.xml文件配置如下:

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

SimpleControllerHandlerAdapter:即简单控制器处理适配器,所有实现了org.springframework.web.servlet.mvc.Controller 接口的Bean作为Springmvc的后端控制器。

第七步:配置视图解析器

在springmvc-servlet.xml文件配置如下:

<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

InternalResourceViewResolver:支持JSP视图解析.

viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,所以classpath必须包含jstl的相关jar包;

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

第八步:后端控制器开发

后端控制器即controller,也有称为action

importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
importorg.springframework.web.servlet.ModelAndView;
importorg.springframework.web.servlet.mvc.Controller;

public class HelloWorldController implements Controller {
    @Override
    Public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        ModelAndView mv = new ModelAndView();
        // 添加模型数据
        mv.addObject("message", "Hello World!");
        // 设置逻辑视图名,最终视图地址=前缀+逻辑视图名+后缀
        mv.setViewName("hello");
        return mv;
      }
}

org.springframework.web.servlet.mvc.Controller:处理器必须实现Controller 接口。

ModelAndView:包含了模型数据及逻辑视图名

第九步:后端控制器配置

在springmvc-servlet.xml文件配置如下:

<!--处理器-->

<bean name="/hello.action" class="springmvc.action.HelloWorldController"/>

name="/hello.action" :前边配置的BeanNameUrlHandlerMapping,表示如过请求的URL为“上下文/hello.action”,则将会交给该Bean进行处理。

第十步:视图开发

创建/WEB-INF/jsp/hello.jsp视图页面:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>第一个程序</title>
    </head>
    <body>
        <!-- 显示一行信息hello world!!!! -->
        ${message}
    </body>
</html>

${message}:表示显示由HelloWorldController处理器传过来的模型数据。

第十一步:部署在tomcat测试

通过请求:http://localhost:8080/springmvc_01/hello.action,如果页面输出“Hello World! ”就表明我们成功了。

总结:

主要进行如下操作:

  1. 前端控制器DispatcherServlet配置
    加载springmvc的配置文件
  2. HandlerMapping配置
  3. HandlerAdapter配置
  4. ViewResolver配置
    前缀和后缀
  5. 后端控制器编写
  6. 后端控制器配置
  7. 视图编写

从上边的步骤可以看出,通常情况下我们只需要编写后端控制器和视图。

6.HandlerMapping处理器映射器

HandlerMapping 给前端控制器返回一个HandlerExecutionChain 对象(包含一个Handler (后端控制器)对象、多个HandlerInterceptor 拦截器)对象。

BeanNameUrlHandlerMapping

beanName Url映射器

<!—beanName Url映射器 -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

将后端控制器的bean name作为请求的url。

SimpleUrlHandlerMapping

<!—简单url映射 -->
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/hello1.action">hello_controller</prop>
                <prop key="/hello2.action">hello_controller</prop>
            </props>
        </property>
    </bean>

可定义多个url映射至一个后端控制器,hello_controller为后端控制器bean的id

7.HandlerAdapter处理器适配器

HandlerAdapter会把后端控制器包装为一个适配器,支持多种类型的控制器开发,这里使用了适配器设计模式。

SimpleControllerHandlerAdapter

简单控制器处理器适配器.

所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean作为Springmvc的后端控制器。

适配器配置如下:

<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />

HttpRequestHandlerAdapter

HTTP请求处理器适配器.

HTTP请求处理器适配器将http请求封装成HttpServletResquestHttpServletResponse对象,和servlet接口类似。

适配器配置如下:

<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>

Controller实现如下:

publicclass HelloWorldController2 implements HttpRequestHandler {

    @Override
    publicvoid handleRequest(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("message", "HelloWorld!");
        request.getRequestDispatcher("/WEB-INF/jsp/hello.jsp").forward(request, response);
            // 也可以自定义响应内容
            //response.setCharacterEncoding("utf-8");
            //response.getWriter().write("HelloWorld!");
    }
}

从上边可以看出此适配器器的controller方法没有返回ModelAndView,可通过response修改定义响应内容。

8.Controller控制器

AbstractCommandController(命令控制器)

该控制器能把请求参数封装到一个命令对象(模型对象)中。

public class MyCommandController extends AbstractCommandController {

    /**
     * 通过构造函数设置命令对象
     */
    public MyCommandController(){
        this.setCommandClass(Student.class);
        this.setCommandName("student");//不是必须设置
    }

    @Override
    protected ModelAndView handle(HttpServletRequest request,
            HttpServletResponse response, Object command, BindException errors)
            throws Exception {

        Student student = (Student)command;
        System.out.println(student);
        Return null;
    }

    /**
     * 字符串转换为日期属性编辑器
     */
    @Override
    Protected void initBinder(HttpServletRequest request,
            ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
    }

}
publicclass Student {

    public Student() {
    }
    public Student(String name) {
        this.name = name;
    }
    private String name;//姓名
    private Integer age;//年龄
    private Date birthday;//生日
    private String address;//地址

….get/set方法省略

Controller配置;

<bean id="command_controller" name="/command.action" class="springmvc.action.MyCommandController"/>

使用命令控制器完成查询列表及表单提交

代码略

9.问题解决

日期格式化

在controller注册属性编辑器

/**
     * 注册属性编辑器(字符串转换为日期)
     */
    @InitBinder
    public void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
    }

Post时中文乱码

web.xml中加入:

<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

以上可以解决post请求乱码问题。

对于get请求中文参数出现乱码解决方法有两个

修改tomcat配置文件添加编码与工程编码一致,如下:

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

另外一种方法对参数进行重新编码:

String userName new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")

ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码

10.注解开发

第一个例子

创建工程的步骤同第一个springmvc工程,注解开发需要修改handlermapperhandlMapperAdapter,如下:

springmvc-servlet.xml中配置:

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

注解映射器和注解适配器可以使用 <mvc:annotation-driven /> 代替。
<mvc:annotation-driven /> 默认注册了注解映射器和注解适配器等bean。

HelloWorldController编写:

@Controller
publicclass HelloWorldController {

    @RequestMapping(value="/hello")
    public String hello(Model model)throws Exception{
        model.addAttribute("message", "HelloWorld!");
        return"hello";
    }
}

注解描述

@Controller:用于标识是处理器类

@RequestMapping:请求到处理器功能方法的映射规则

Controller配置

在springmvc-servlet.xml中配置定义的controller:

<!-- controller -->
<bean class="springmvc.action.HelloWorldController"/>

组件扫描

<context:component-scan base-package="springmvc.action" />

扫描 @component@controller@service@repository 的注解

注意:如果使用组件扫描则controller不需要在springmvc-servlet.xml中配置

11.与struts2不同

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

注意:

不要在action类中定义方法所用到的变量,变量要定义到方法体中。因为方法是在栈内存中,不会导致线程问题。

12.@Controller

标识该类为控制器类,@controller@service@repository 分别对应了web应用三层架构的组件即控制器、服务接口、数据访问接口。

13.@RequestMapping

URL路径映射

@RequestMapping(value="/user")或@RequestMapping("/user")

根路径+子路径

根路径:

@RequestMapping 放在类名上边,如下:

@Controller
@RequestMapping("/user")

子路径:

@RequestMapping 放在方法名上边,如下:

@RequestMapping("/useradd")
public String useradd(….

URI 模板模式映射

@RequestMapping(value="/useredit/{userId}"):{×××}占位符,请求的URL可以是“/useredit/001”或“/useredit/abc”,通过在方法中使用@PathVariable 获取{×××}中的×××变量。

@RequestMapping("/useredit/{userid}")
    public String useredit(@PathVariable String userid,Model model) throws Exception{
        //方法中使用@PathVariable获取useried的值,使用model传回页面
        model.addAttribute("userid", userid);
        return"/user/useredit";
    }

实现restFul,所有的url都是一个资源的链接,有利于搜索引擎对网址收录。

多个占位符:

@RequestMapping("/useredit/{groupid}/{userid}")
public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
        //方法中使用@PathVariable获取useried的值,使用model传回页面
        model.addAttribute("groupid", groupid);
        model.addAttribute("userid", userid);
        return"/user/useredit";
    }

页面

<td>
    <a href="${pageContext.request.contextPath}/user/editStudent/${student.id}/${student.groupId}/${student.sex}.action">修改</a>
</td>

请求方法限定

限定GET方法

@RequestMapping(method = RequestMethod.GET)

如果通过Post访问则报错:

HTTP Status 405 - Request method 'POST' not supported

例如

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

限定POST方法

@RequestMapping(method = RequestMethod.POST)

如果通过Post访问则报错:

HTTP Status 405 - Request method 'GET' not supported

GET和POST都可以

@RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

14.请求数据绑定(重点)

Controller方法通过形参接收页面传递的参数。

如下:

@RequestMapping("/userlist")
public String userlist(
HttpServletRequest request,
            HttpServletResponse response,
            HttpSession session,
            Model model
)

默认支持的参数类型

HttpServletRequest

通过request对象获取请求信息

HttpServletResponse

通过response处理响应信息

HttpSession

通过session对象得到session中存放的对象

session.setAttribute("userinfo", student);
session.invalidate(); // 使session失效

Model

通过model向页面传递数据,如下:

model.addAttribute("user", new User("李四"));

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

命令/表单对象

自动将请求参数绑定到功能处理方法的命令/表单对象上。

Controller方法通过形参接收命令/表单对象。

Java基本数据类型

布尔型:

页面如下:

<tr>
    <td>用户状态:</td>
    <td>
        <inputtype="radio"name="userstate"value="true"/>
        <inputtype="radio"name="userstate"value="false"/>
    </td>
</tr>

整型
例子略

单精度/双精度
例子略

日期型需要添加属性编辑器:

@InitBinder
    Public void initBinder(HttpServletRequest request,ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true));

例子略

Pojo对象

页面上以pojo对象中属性名称命名:

将pojo对象中的属性名于传递进来的属性名对应,如果传进来的参数名称和对象中的属性名称一致则将参数值设置在pojo对象中

页面定义如下;

<input type="text" name="age"/>
<input type="text" name="birthday"/>

Contrller方法定义如下:

public String useraddsubmit(Model model,User user)throws Exception{
    System.out.println(user);
}

页面上以pojo对象名点属性名命名:

如果采用类似struts中对象.属性的方式命名,需要将pojo对象作为一个包装对象的属性,action中以该包装对象作为形参。

包装对象定义如下:

Public class UserVo {
    private User user;

    public User getUser() {
        return user;
    }

    Public void setUser(User user) {
        this.user = user;
    }
}

页面定义:

<input type="text" name="user.age" />
<input type="text" name="user.birthday" />

Controller方法定义如下:

public String useraddsubmit(Model model,UserVo userVo)throws Exception{
    System.out.println(userVo.getUser());
}

字符串数组

使用情景:页面提交批量数据以数组接受

页面定义如下:

页面选中多个checkbox向controller方法传递

<c:forEach items="${studentList}" var="student">
    <tr>
        <td><input type="checkbox" name="deleteids" value="${student.id }"></td>
        <td>${student.id}</td>
        <td>${student.name}</td>

传递到controller方法中的格式是:001,002,003

Controller方法中可以用String[]接收,定义如下:

@RequestMapping("/deleteStudent")
    public String deleteStudent(String[] deleteids)throws Exception {
        return "editStudentSubmit";
    }

List

List中存放对象,并将定义的List放在包装类中,action使用包装对象接收。

List中对象:

成绩对象

public class StudentScore {
    private String coursename;//课程名称
    private Float score;//成绩
    public String getCoursename() {
        returncoursename;
    }
    Public void setCoursename(String coursename) {
        this.coursename = coursename;
    }
    public Float getScore() {
        returnscore;
    }
    Public void setScore(Float score) {
        this.score = score;
    }
}

Public class UserVo {
Private List<StudentScore> scores;//成绩

  //get/set方法..
}

包装类中定义List对象,并添加get/set方法如下:

private List<StudentScore> scores;//成绩

页面:

<tr>
    <td>课程成绩:</td>
    <td>
        课程名:<input type="text"name="scores[0].coursename"/>成绩:<input type="text"name="scores[0].score"/><br/>
        课程名:<input type="text"name="scores[1].coursename"/>成绩:<input type="text"name="scores[1].score"/><br/>
        课程名:<input type="text"name="scores[2].coursename"/>成绩:<input type="text"name="scores[2].score"/><br/>
    </td>
</tr>

Contrller方法定义如下:

public String useraddsubmit(Model model,UserVo userVo)throws Exception{
System.out.println(userVo.getScores ());
}

Map

在包装类中定义Map对象,并添加get/set方法,action使用包装对象接收。

包装类中定义Map对象如下:

Public class UserVo {
    private Map<String, Object>studentinfo = new HashMap<String, Object>();
  //get/set方法..
}

页面定义如下:

<tr>
    <td>学生信息:</td>
    <td>
    姓名:<inputtype="text"name="studentinfo['name']"/>
    年龄:<inputtype="text"name="studentinfo['age']"/>
.. .. ..

<!-- studentinfo是包装对象中的属性名

[]里边name是存储map中的key

action中将map定义在包装对象中,以包装对象接收数据。--!>

    </td>
</tr>

Contrller方法定义如下:

public String useraddsubmit(Model model,UserVo userVo)throws Exception{
    System.out.println(userVo.getStudentinfo());
}

注意:包含日期的要进行格式转换(放到action中

// 使用注解进行日期格式数据转换
    @InitBinder
    private void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
        // 指定日期类型及日期数据的格式
        // 日期类型要和student类的birthday一致
        binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
    }

@RequestParam绑定单个请求参数

value:参数名字,即入参的请求参数名字,如value=“studentid”表示请求的参数区中的名字为studentid的参数的值将传入;

required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报400错误码;
defaultValue:默认值,表示如果请求中没有同名参数时的默认值

定义如下:

public String userlist(@RequestParam(defaultValue="2",value="group",required=true) String groupid) {

}

形参名称为groupid,但是这里使用value="group"限定参数名为group,所以页面传递参数的名必须为group

这里通过required=true限定groupid参数为必需传递,如果不传递则报400错误,由于使用了defaultvalue=”2”默认值即使不传group参数它的值为”2”,所以页面不传递group也不会报错,如果去掉defaultvalue=”2”且定义required=true则如果页面不传递group则会报错。

@PathVariable 绑定URI 模板变量值

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上

@RequestMapping(value="/useredit/{groupid}/{userid}",method={RequestMethod.GET,RequestMethod.POST})
    public String useredit(@PathVariable String groupid,@PathVariable String userid,Model model) throws Exception{
        //方法中使用@PathVariable获取useried的值,使用model传回页面
        model.addAttribute("groupid", groupid);
        model.addAttribute("userid", userid);
        return"/user/useredit";
    }

如请求的URL 为“控制器URL/useredit/1/admin.action”,则自动将URL中模板变量{groupid}和{userid}绑定到@PathVariable注解的同名参数上,即入参后groupid=“1”userid=“admin”

结果转发

Redirect

Contrller方法返回结果重定向到一个url地址,如果方式:

return "redirect:/user/userlist.action";

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

/user/userlist.action?groupid=2&…..

forward

controller方法执行后继续执行另一个controller方法。

return "forward:/user/userlist.action";

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

如下例子:

@RequestMapping("/c")
    public String c(String groupid,UserVo userVo)throws Exception{

        System.out.println("...c...."+groupid+"...user..."+userVo.getUser());
        return "forward:/to/d.action";
    }

    @RequestMapping("/d")
    public String d(String groupid,UserVo userVo)throws Exception{

        System.out.println("...d...."+groupid+"...user..."+userVo.getUser());
        return "success";
    }

@RequestBody @ResponseBody实现json数据交互

@RequestBody

作用:

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

本例子应用:

@RequestBody注解实现接收http请求的json数据,将json数据转换为java对象.

@ResponseBody

作用:

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

本例子应用:

@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端

请求json,响应json实现:

页面上请求的是一个json串,页面上实现时需要拼接一个json串提交到action。

Action将请求的json串转为java对象。SpringMVC利用@ResquesBody注解实现.

Action将java对象转为json,输出到页面。SpringMVC利用@ResponseBody注解实现.

环境准备

Springmvc默认用MappingJacksonHttpMessageConverterjson数据进行转换,需要加入jackson的包,如下:

配置:

在注解适配器中加入messageConverters

<!--注解适配器 -->

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
        </list>
    </property>
</bean>

controller编写

/**
     * 请求json单个对象,返回json单个对象
     * @param user
     * @return
     * @throws Exception
     */
    @RequestMapping("/requestjson")
    //@RequestBody接收json串自动转为user对象,@ResponseBody将user转为json数据响应给客户端
    Public @ResponseBody User requestjson(@RequestBody User user)throws Exception{
        System.out.println(user);
        return user;
    }

页面js方法编写:

function request_json(){
         //将json对象传成字符串
    var user = JSON.stringify({name: "张三", age: 3});
    alert(user);
      $.ajax(
        {
            type:'post',
            url:'${pageContext.request.contextPath}/requestjson.action',
            contentType:'application/json;charset=utf-8',//请求内容为json
            data:user,
            success: function(data){
                alert(data.name);
            }
        }    
    )
  }

测试结果:


从上图可以看出请求的数据是json格式


从上图可以看出响应的数据也是json格式,json数据的内容是从User对象转换得来。

Form提交,响应json实现

采用form提交是最常用的作法,通常有post和get两种方法,响应json数据是为了方便客户端处理,实现如下:

环境准备

同第一个例子

controller编写

/

 * 客户端提交表单,服务端返回json
 * @param user
 * @return
 * @throws Exception
 */
@RequestMapping("/formsubmit")
Public @ResponseBody User formsubmit(User user)throws Exception{
    System.out.println(user);
    return user;
}**

页面js方法编写:

function formsubmit(){
    var user = "name=张三&age=3";
    alert(user);
      $.ajax(
        {
            type:'post',//这里改为get也可以正常执行
            url:'${pageContext.request.contextPath}/formsubmit.action',
            //ContentType没指定将默认为:application/x-www-form-urlencoded
            data:user,
            success:function(data){
            alert(data.name);
        }

        }    
    )
}

从上边的js代码看出,已去掉ContentType的定义,ContentType默认为:application/x-www-form-urlencoded格式。

测试结果:


从上图可以看出请求的数据是标准的key/value格式。


从上图可以看出响应的数据也是json格式,json数据的内容是从User对象转换得来。

<mvc:annotation-driven />配置:

注解映射器和注解适配器可以使用 <mvc:annotation-driven /> 代替。

<mvc:annotation-driven /> 默认注册了注解映射器和注解适配器等bean。

如下:

以下配置可用 <mvc:annotation-driven /> 代替:

<!--注解映射器 -->    
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
    <!--注解适配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
        <list>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
        </list>
        </property>
    </bean>

15.拦截器

定义

Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。
SpringMVC拦截器是针对mapping配置的拦截器

拦截器定义

实现HandlerInterceptor接口,如下:

Public class HandlerInterceptor1 implements HandlerInterceptor{

    /**
     * controller执行前调用此方法
     * 返回true表示继续执行,返回false中止执行
     * 这里可以加入登录校验、权限拦截等
     */
    @Override
    Public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        Return false;
    }
    /**
     * controller执行后但未返回视图前调用此方法
     * 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
     */
    @Override
    Public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
    }
    /**
     * controller执行后且视图返回后调用此方法
     * 这里可得到执行controller时的异常信息
     * 这里可记录操作日志,资源清理等
     */
    @Override
    Public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {        
    }
}

拦截器配置

针对某种mapping配置拦截器

<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="springmvc.intercapter.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>

针对所有mapping配置全局拦截器

<!--拦截器 -->
<mvc:interceptors>
    <!--多个拦截器,顺序执行 -->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>
    </mvc:interceptor>
</mvc:interceptors>

正常流程测试

代码:


定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2,每个拦截器的preHandler方法都返回true

运行流程

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor2..postHandle..

HandlerInterceptor1..postHandle..

HandlerInterceptor2..afterCompletion..

HandlerInterceptor1..afterCompletion..

中断流程测试

代码:
定义两个拦截器分别为:HandlerInterceptor1HandlerInteptor2

运行流程

HandlerInterceptor1preHandler方法返回false,HandlerInterceptor2返回true,运行流程如下:

HandlerInterceptor1..preHandle..

从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且controller也不执行了。

HandlerInterceptor1preHandler方法返回trueHandlerInterceptor2返回false,运行流程如下:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的ostHandler没有执行,第二个拦截器的postHandlerafterCompletion没有执行,且controller也不执行了。

总结:

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用

拦截器应用

用户身份认证

Public class LoginInterceptor implements HandlerInterceptor{

    @Override
    Public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        //如果是登录页面则放行
        if(request.getRequestURI().indexOf("login.action")>=0){
            return true;
        }
        HttpSession session = request.getSession();
        //如果用户已登录也放行
        if(session.getAttribute("user")!=null){
            return true;
        }
        //用户没有登录挑战到登录页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);

        return false;
    }
}
编程技术