SpringMVC学习笔记 - 01

SpringMVC系列文章


1. SpringMVC 概述

1.1 关于三层架构和 MVC 模型

  • 在我们常用的 B/S架构(Browser/Server,浏览器/服务器模式) 中,系统标准的三层架构为:表现层,业务层以及持久层,每一层都是各司其职,分工很明确。
    • 表现层也叫 web层 ,负责和客户端进行数据交互,也就是接收客户端的请求,向客户端响应结果。在 web层 中又可以细分为展示层和控制层,展示层负责结果的展示,控制层负责接受请求。表现层的设计一般都会采用 MVC 模型(表现层的设计模型,与其他层无关)。
    • 业务层也叫 service层,负责业务逻辑处理,一般会依赖数据层。如果需要对数据持久化,那么应该保证事务一致性。
    • 持久层也叫 dao层,负责数据持久化,简单地说就是用于和数据库进行交互,对数据库中的表进行增删查改。
  • MVC 全称为 Model View Controller,是一种用于设计表现层的模式,每个部分都是各司其职。
    • Model(模型):数据模型,一般就是 JavaBean,用于封装数据。
    • View(视图):比如 JSP 、 HTML ,用于展示数据给用户。
    • Controller(控制器):比如Servlet,用于和用户进行交互,也就是接收用户请求,对其进行处理,最终生成响应给用户。

1.2 SpringMVC 简单介绍

  • SpringMVC 是一种基于 Java 实现的 MVC 设计模型的请求驱动类型的轻量级WEB框架,属于 Spring FrameWork 的后续产品,已经融合在 Spring Web Flow 里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。
  • SpringMVC 是属于表现层的框架,见图
    在这里插入图片描述

2. SpringMVC 入门

2.1 入门案例

  • 创建 Maven 工程,导入依赖如下
<properties>
    <!-- Spring 版本 -->
    <spring.version>5.0.2.RELEASE</spring.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <!-- provided:provided依赖范围只有在当JDK或者一个容器已提供该依赖之后才使用,provided依赖在编译和测试时需要,在运行时不需要。比如servlet api被tomcat容器提供 -->
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
  • 创建 SpringMVC 的配置文件,这里我将其命名为 springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/mvc
       http://www.springframework.org/schema/mvc/spring-mvc.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 注解扫描的包 -->
    <context:component-scan base-package="cn.ykf"/>

    <!-- 配置视图解析器 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 配置 Spring 开启注解 MVC 的支持 -->
    <mvc:annotation-driven/>
</beans>
  • web.xml 中配置 DispatcherServlet(核心控制器)
<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 服务器启动就创建该控制器,并且加载 spring 配置文件,创建容器 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 对所有请求都拦截 -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • webapp 目录下创建 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>入门案例</title>
</head>
<body>
    <h3>入门案例</h3>
    <%-- 这里使用相对路径,使用绝对路径会变成了 http://localhost:8080/hello --%>
    <a href="hello">点击进入入门案例</a><hr/>
</body>
</html>
  • 编写控制器 cn.ykf.controller.HelloController,并且使用注解配置
@Controller
public class HelloController {

    @RequestMapping(path = "/hello")
    public String hello() {
        System.out.println("Hello SpringMVC");
        return "success";	// 被视图解析器解析后为 /WEB-INF/pages/success.jsp
    }
}
  • webapp/WEB-INF/pages 目录下创建 success.jsp,用于展示成功跳转页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>成功</title>
</head>
<body>
    <h3>访问成功</h3>
</body>
</html>

2.2 入门案例执行过程分析

  1. 当 Tomcat 服务器启动的时候,应用被加载到 Tomcat 容器中。首先读取 web.xml,因为 DispatcherServlet 配置了 <load-on-startup> 标签,所以会在服务器一启动就被创建。同时因为 DispatcherServlet 指定了 Spring MVC的配置文件 springmvc.xml 的位置,所以会读取springmvc.xml,创建视图解析器 InternalResourceViewResolver 和控制器 HelloController 对象并加入到 IoC 容器中。(实际上并不止创建了这两个对象)
  2. index.jsp 向服务器发送请求时,由于 DispatcherServlet 所映射的路径为 <url-pattern>/</url-patttern>,所以这个请求会先被 DispatcherServlet 所拦截到。但是该 Servlet 并不处理请求,而是根据我们请求的 URI 路径 /hello ,去匹配 @RequestMapping 绑定的路径。
  3. 根据 @RequestMapping 找到匹配的方法 hello() 后,执行并取得返回值 success 。借助视图解析器 InternalResouceViewResolver 得到最终的结果视图为 /WEB-INF/pages/success.jsp
  4. Tomcat 服务器渲染页面,对客户端浏览器做出响应。
    入门案例执行分析

2.3 涉及的 SpringMVC 组件

  • DispatcherServlet (前端控制器):用户的请求会先达到该控制器,是整个执行流程的控制中心,由它调用其他组件来处理用户的请求,其存在降低了其它组件的耦合性。
  • HandlerMapping(处理器映射器):负责根据用户的请求找到对应的 Handler 即处理器。
  • Handler(处理器):开发中需要编写的具体业务控制器,也就是 Controller ,由它来对具体的用户请求进行处理。
  • HandlerAdapter(处理器适配器):对 Handler 进行执行,是适配器模式的一种应用。
  • ViewResolver(视图解析器): 负责将处理结果生成 View 视图。首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。
  • View(视图):展示给用户的具体页面,比如说 jsp 页面
  • 其中,HandlerMapping 处理器映射器、HandlerAdapter 处理器适配器、View Resolver 视图解析器称为 SpringMVC 的三大组件。
  • 配置文件中的 <mvc:annotation-drivern>自动帮我们加载处理器映射器和处理器适配器

2.4 @RequestMapping 注解

  • @RequestMapping 用于建立请求 URL 路径和处理请求方法之间的对应关系。
  • 出现的位置可以在类上或者方法上
    • @RequestMapping 在类上时,相当于是 URL 路径的 一级目录,可以让我们的 URL 按照模块化管理。比如在入门案例的 HelloController 类上使用 @RequestMapping("/user") ,那么此时我们的请求路径就应该为 user/hello
    • @RequestMapping 在方法上时,如果搭配类上的 @RequestMapping,那么就是 URL 路径的 二级目录;如果单独在方法上时,那么就是一级目录
  • 属性如下
    • value/path :这两个属性互为别名,都是用来指定请求的 URL 路径,
    • methods :用于指定请求的方式,值为 RequestMethod.POST、RequestMethod.POST 等,出现多个值时为或的关系。
    • params : 用于指定限制请求参数的条件,支持简单的表达式,要求请求参数的 keyvalue 必须和条件一模一样。譬如:
      • params={"username=ykf","arg"} 表示请求参数中必须要有参数 arg 和值为 ykfusername 参数(此时请求参数中这两个参数必须都要存在)。
      • params={"money!=100"} 表示请求参数中不能有值为 100money 参数(此时请求参数中没有 money 也是可以的)。
    • headers:用于指定限制请求消息头的条件。
    • 以上的限制条件属性出现多个时,为与的关系
  • 测试代码如下所示
@Controller
// 定义一级目录
@RequestMapping("/user")
public class UserController{
    /**
     * 二级目录映射
     *
     * @return
     */
    @RequestMapping("/test1")
    public String test1() {
        System.out.println("二级目录...");
        return "success";
    }

    /**
     * 请求方式的限制,这里限制只能post方式访问
     *
     * @return
     */
    @RequestMapping(value = "/test2", method = {RequestMethod.POST})
    public String test2() {
        System.out.println("限制post方式访问");
        return "success";
    }

    /**
     * 请求参数的限制
     *
     * @return
     */
    // @RequestMapping(value = "/test3", params = {"money!=100"})
    // public String test3(String money) {
    @RequestMapping(value = "/test3", params = {"username=aa", "arg"})
    public String test3(String username, String arg) {
        // System.out.println("限制参数" + "----" + money);
        System.out.println("限制参数" + "----" + username + "------" + arg);
        return "success";
    }

    /**
     * 请求头的限制
     *
     * @return
     */
    @RequestMapping(value = "/test4", headers = {"Accept"})
    public String test4() {
        System.out.println("限制请求头");
        return "success";
    }
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>RequestMapping的使用</title>
</head>
<body>
    <h3>RequestMapping的使用</h3>
    <%-- 访问二级目录 --%>
    <a href="user/test1">二级目录</a><hr/>
    <%-- Controller限制请求方式为POST,所以超链接方式无法访问(GET) --%>
    <a href="user/test2">限制请求方式</a><hr/>
    <%-- 限制请求参数,此时去掉任一参数或者修改username的值都无法访问 --%>
    <a href="user/test3?username=aa&arg=bb">限制请求参数(1)</a><hr/>
    <%-- 如果有money参数,值不能为100,没有的话可以直接访问 --%>
    <a href="user/test4?money=100">限制请求参数(2)</a><hr/>
    <%-- 限制请求头中必须有 Accept --%>
    <a href="user/test5">限制请求头</a><hr/>
</body>
</html>

3. 请求参数的绑定

  • 当我们从表单提交数据到后台的时候,请求参数都是基于 key=value 的形式,SprigMVC 可以将请求参数直接绑定到对应方法的参数上。譬如:
<a href="user/deleteUser?uid=1">删除用户</a>
  • 该超链接可以向对应的方法发送一个 uid=1 的请求参数,此时我们利用 SpingMVC 就可以这样接收:
/**
* 模拟删除用户
* @return
*/
@RequestMapping("/deleteUser")
public String deleteUser(Integer uid){
	System.out.println("删除了 id 为 " + uid+ " 的用户...");
	return "success";
}
  • 参数绑定支持的数据类型有以下几种:
    • 基本数据类型和 String 类型
    • POJO 类型,其中可以包含其他 POJO 对象的引用
    • 数组和集合类型,譬如 ListMap 等集合

3.1 基本类型和 String 类型作为参数

  • 如果要绑定基本类型或 String 类型的参数,那么要求前端的请求参数名称必须与控制器方法的形参名称保持一致(严格区分大小写),见以下示例:
  • jsp代码:
<a href="param/testParam1?id=10001&username=鱼开饭">普通参数的绑定</a>
  • 控制器代码:
/**
 * 普通参数的绑定
 *
 * @return
 */
@RequestMapping("/testParam1")
public String testParam1(Integer id, String username) {
    System.out.println(id + "----" + username);
    return "success";
}
  • 运行结果
    普通参数的绑定

3.2 POJO 类型作为参数

  • 如果要绑定 POJO 类型的参数,那么要求前端的请求参数名称和 POJO 类的属性名称保持一致,同时控制器方法的参数类型为对应的 POJO 类型。特别地,如果 POJO 类中含有其他 POJO 类的引用,那么请求参数名称应该为 外部属性名.内部属性名,见以下示例:
  • jsp代码:
<form action="param/testParam2" method="post">
    用户名: <input type="text" name="username"/><br/>
    密码:<input type="text" name="password"/><br/>
    金额:<input type="text" name="money"/><br/>
    <%-- 外部属性名 user 内部属性名 name,因此请求参数为 user.name --%>
    真实姓名:<input type="text" name="user.name"/><br/>
    年龄:<input type="text" name="user.age"/><br/>
    <input type="submit" value="提交">
</form>
  • 控制器代码:
/**
 * 绑定到带有其他对象引用的 JavaBean
 *
 * @param account
 * @return
 */
@RequestMapping("/testParam2")
public String testParam3(Account account) {
    System.out.println(account);
    return "success";
}
  • 实体类 Account 类、User 类代码:
// 外部类
public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    // 其他 JavaBean 对象引用
    private User user;
    
    // getters and setters
    // ...
}

// 内部类
public class User implements Serializable {
    private String name;
    private Integer age;

    // getters and setters
    // ...
}
  • 运行结果
    pojo参数的绑定

3.3 集合类型作为参数

  • 如果要绑定集合类型的参数,那么要求集合类型的请求参数必须在 POJO 类中,同时前端的请求参数名称和 POJO 类的集合属性名称保持一致。当给 List 集合中的元素赋值时,使用下标;当给 Map 集合中的元素赋值时,使用键值对,见以下示例:
  • jsp代码:
<form action="param/testParam4" method="post">
    用户名: <input type="text" name="username"/><br/>
    密码:<input type="text" name="password"/><br/>
    金额:<input type="text" name="money"/><br/>
    <%-- 绑定到 List 集合 --%>
    持卡人1名称:<input type="text" name="userList[0].name"/><br/>
    持卡人1年龄:<input type="text" name="userList[0].age"/><br/>
    持卡人2名称:<input type="text" name="userList[1].name"/><br/>
    持卡人2年龄:<input type="text" name="userList[1].age"/><br/>
    <%-- 绑定到 Map 集合 --%>
    持卡人3名称:<input type="text" name="userMap['one'].name"/><br/>
    持卡人3年龄:<input type="text" name="userMap['one'].age"/><br/>
    持卡人4名称:<input type="text" name="userMap['two'].name"/><br/>
    持卡人4年龄:<input type="text" name="userMap['two'].age"/><br/>
    <input type="submit" value="提交">
</form>
  • 控制器代码:
/**
 * 绑定到带有集合属性的 JavaBean
 *
 * @param account
 * @return
 */
@RequestMapping("/testParam4")
public String testParam4(Account account) {
    System.out.println(account);
    return "success";
}
  • Account 类代码:
public class Account implements Serializable {
    private String username;
    private String password;
    private Double money;
    // List 集合
    private List<User> userList;
    // Map 集合
    private Map<String, User> userMap;
        
    // getters and setters
    // ...
}
  • 运行结果
    集合参数的绑定

如果直接将参数封装到一个普通 List 集合(不在 JavaBean 中),像这样:

<a href="param/testList?args=test1,test2,test3">普通集合的绑定</a>
@RequestMapping("/testList")
    public String testList(List<String> args) {
        System.out.println(args);
        return "success";
    }

那么会报 500 错误,提示 List 没有默认构造函数无法初始化,如图
在这里插入图片描述
在其他博客(SpringMVC学习01:请求路径匹配和参数绑定)看到的解决方法是:使用 @RequestParam 注解进行参数绑定(关于 @RequestParam 的使用请看下文),像这样:

public String testList(@RequestParam("args") List<String> args) {
	// ...
}

3.4 请求参数乱码问题

  • 对于 GET 方式提交的请求,如果参数中含有中文,那么是不会乱码的,因为 Tomcat 8 已经将乱码问题解决了。如果 GET 方式请求乱码,那么可以在 Tomcat 安装目录下的 conf\server.xml 中进行修改,如图
    GET方式乱码设置
  • 而对于 POST 方式提交的参数乱码问题,则应该在 web.xml 中配置一个 SpringMVC 提供的过滤器 CharacterEncodingFilter,见代码
<web-app>
    <!-- 配置编码过滤器 -->
    <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>
    
    <!-- servlet 配置写在下方 -->
</web-app>

<Filter> 位置最好出现在 <servlet> 之前

3.5 自定义类型转换器

  • 我们都知道,从表单提交的任何数据类型都是字符串类型。但是当提交到控制器方法时,对于某些参数我们可以用其它的数据类型(IntegerDouble 等)来接收。譬如:前端提交的请求参数中有 id=1,那么此时我们在控制器方法中可以直接使用 public String test(Integer id){} 来接收,这是因为 SpringMVC 有着内置的转换器,可以帮我们把一些常用的数据类型进行转换。
  • 可是有的时候会出现这样的情况,前端提交了一个日期字符串,控制器方法使用 Date 类型的变量进行接收,由于日期字符串有很多种格式,所以 SpringMVC 的内置转换器不一定能帮我们转换成 Date 类型,譬如:
  • jsp代码
<a href="param/testStringToDate?date=2020-02-02">字符串转日期</a>
  • 控制器方法代码
@RequestMapping("/testStringToDate")
public String testStringToDate(Date date) {
    System.out.println(date);
    return "success";
}
  • 运行报错
    在这里插入图片描述
  • 此时我们就可以根据自己的需求来定义自定义的类型转换器,要求实现 Converter<S,T> 接口,其中 S 为待转换的类型, T 为目标类型。
/**
 * 自定义日期转换器
 *
 * @author yukaifan
 * @ClassName StringToDateConverter
 * @date 2020-02-29 9:35
 */
public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        // 检查待转换字符串是否为空
        if (source == null || "".equals(source.trim())) {
            throw new RuntimeException("待转换日期字符串不能为空!");
        }
        // 解析字符串
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            return sdf.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new RuntimeException("日期格式有误,必须为 yyyy-MM-dd 格式!");
        }
    }
}
  • 最后我们需要在 SpringMVC 的配置文件中配置该类型转换器
<!-- 引用配置的类型转换服务 -->
<mvc:annotation-driven conversion-service="conversionServiceFactoryBean"/>

<!-- 配置类型转换器 -->
<bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <!-- 给工厂注入一个新的类型转换器 -->
    <property name="converters">
        <set>
            <!-- 配置自定义类型转换器 -->
            <bean class="cn.ykf.converter.StringToDateConverter"></bean>
        </set>
    </property>
</bean>

3.6 使用 Servlet 原生 API 作为参数

  • SpringMVC 支持使用原始 Servlet API 对象作为方法参数,也就是说,我们可以把这些参数直接写在控制器方法的参数列表上,这些对象如下表所示:

    全限定类名
    javax.servlet.http.HttpServletRequest
    javax.servlet.http.HttpServletResponse
    javax.servlet.http.HttpSession
    java.security.Principal
    java.util.Locale
    java.io.InputStream
    java.io.OutputStream
    java.io.Reader
    java.io.Writer
  • 示例代码

@RequestMapping("/testServlet")
public String testServlet(HttpServletRequest request, HttpSession session, HttpServletResponse response) {
	// ...
}

4. 常用注解

4.1 @RequestParam

  • 该注解的作用是把请求中指定名称的参数绑定到控制器方法的某个参数上。常用属性如下:
    • value / name : 这两个属性互为别名,都是指定请求参数中的名称
    • required : 请求参数中是否必须提供此参数,默认值为 true,表示必须提供,否则报错
  • jsp代码
<a href="${pageContext.request.contextPath}/anno/testRequestParam?name=鱼开饭">RequestParam</a>
  • 关于 EL 表达式 ${pageContext.request.contextPath} 失效的原因:可能是因为 web.xml 的版本为 2.4 以下,2.4 以下版本 isELIgnored 默认值为 true,可以采用以下方法:
  1. 在 jsp 页面 page 指令中开启 EL 表达式 : <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
  2. 直接修改 web.xml 版本为 2.4 以上,快捷省事 :<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  • 控制器代码
@Controller
@RequestMapping("/anno")
public class AnnotationController {
    /**
     * RequestParam 的使用
     *
     * @param username
     * @return
     */
    @RequestMapping("/testRequestParam")
    public String testRequestParam(@RequestParam("name") String username) {
        System.out.println(username);

        return "success";
    }
}

此时使用 @RequestParam("name") ,那么如果请求参数中没有 name 参数,程序就会报错。同时,由于已经使用 @RequestParam 进行参数的绑定,所以控制器方法的参数名称 username 已经无法绑定了,也就是说,即使请求参数中有参数 username,那么也不会绑定到控制器方法的 username 参数上了。

4.2 @RequestBody

  • 该注解的作用是获取请求体内容,得到的内容为 key1=value1&key2=value2...,只适用 POST 方式提交的请求,因为 GET 方式无请求体。属性如下:
    • required : 是否必须有请求体,默认值为 true,表示必须有请求体,GET 方式请求会报错;当为 false 时,GET 方式请求得到的为 null
  • jsp代码
<form action="${pageContext.request.contextPath}/anno/testRequestBody" method="post">
    用户名 : <input type="text" name="username"/><br/>
    年龄 : <input type="text" name="age"/><br/>
    <input type="submit" value="提交"/>
</form>
  • 控制器代码
@RequestMapping("/testRequestBody")
    public String testRequestBody(@RequestBody String body) throws UnsupportedEncodingException {
        System.out.println(body);
        // 中文乱码
        System.out.println(URLDecoder.decode(body, "UTF-8"));
        return "success";
    }

4.3 @PathVariable

  • 该注解的作用是将 URL 中的占位符绑定到控制器方法的参数上,例如 localhost:8080/springmvc/user/{uid},这里的 {uid} 就是一个占位符。属性如下:
    • value :指定 URL 中的占位符名称
    • required : 是否必须提供占位符
  • jsp代码
<a href="${pageContext.request.contextPath}/anno/testPathVariable/10001">PathVariable</a>
  • 控制器代码
@RequestMapping("/testPathVariable/{uid}")
public String testPathVariable(@PathVariable("uid") String id) {
    System.out.println(id);
    return "success";
}

该注解可以用于绑定 REST 风格 URL 的占位符,restful 有以下特点:

  • 资源(Resources):每一个 URI 对应着一个资源
  • 表现层(Representation):把资源具体呈现出来的形式
  • 状态转化(State Transfer):客户端使用 GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET 用来获取资源,POST 用来新建资源(也可以用于更新资源),PUT 用来更新资源,DELETE 用来删除资源,譬如:
    • /account/1 HTTP GET : 得到 id = 1 的 account
    • /account/1 HTTP DELETE: 删除 id = 1 的 account
    • /account/1 HTTP PUT: 更新 id = 1 的 account
    • /account HTTP POST: 新增 account

4.4 @RequestHeader 和 @CookieValue

  • @RequestHeader 用于获取请求消息头。属性如下:
    • value : 要获取的消息头名称
    • required : 是否必须有此消息头
  • @CookieValue 用于获取 Cookie 的值。属性如下:
    • value : 要获取的 Cookie名称
    • required : 是否必须有此 Cookie
  • jsp代码
<%-- RequestHeader --%>
<a href="${pageContext.request.contextPath}/anno/testRequestHeader">RequestHeader</a><hr />
<%-- CookieValue --%>
<a href="${pageContext.request.contextPath}/anno/testCookieValue">CookieValue</a><hr />
  • 控制器代码
@RequestMapping("/testRequestHeader")
public String testRequestHeader(@RequestHeader("Accept") String header){
    System.out.println(header);
    return "success";
}

@RequestMapping("/testCookieValue")
public String testCookieValue(@CookieValue("JSESSIONID") String cookieValue){
    System.out.println(cookieValue);
    return "success";
}

4.5 @ModelAttribute

  • 该注解用于修饰方法或者参数,如果修饰方法,那么该方法(有无返回值均可)会在控制器方法之前执行;如果修饰参数,可以获取指定的数据并赋值给参数。当表单提交的数据不是完整的实体类数据时,我们就可以借助该注解,对空属性进行预处理(譬如赋值为数据库记录中该对象原本的值)。属性如下:
    • value : 用于获取数据的 keykey 可以是 POJO 类的属性名称,也可以是 Map 集合的 key
  • jsp 代码
<%-- ModelAttribute --%>
<form action="${pageContext.request.contextPath}/anno/testModelAttribute">
    姓名 : <input type="text" name="name"/><br/>
    年龄 : <input type="text" name="age"/><br/>
    <input type="submit" value="提交"/>
</form><hr />
  • 控制器代码(@ModelAttribute 修饰的方法带返回值)
@RequestMapping("/testModelAttribute")
public String testModelAttribute(User user) {
    System.out.println(user);
    return "success";
}

/**
 * 带返回值,对提交数据进行预处理
 *
 * @param name
 * @return
 */
@ModelAttribute
public User initUser(String name) {
    // 模拟根据姓名从数据库查出对应的 User 对象
    User user = new User();
    user.setName(name);
    user.setAge(10);
    // 待会 testModelAttribute 获取到的 User 对象就带有 Date 属性
    user.setDate(new Date());

    return user;
}

控制器代码(@ModelAttribute 修饰的方法不带返回值)

@RequestMapping("/testModelAttribute")
public String testModelAttribute(@ModelAttribute("one") User user) {
    System.out.println(user);
    return "success";
}
/**
 * 不带返回值,将数据放入 Map 集合中
 *
 * @param name
 * @param map
 */
@ModelAttribute
public void initUser(String name, Map<String, User> map) {
    // 模拟根据姓名从数据库查出对应的 User 对象
    User user = new User();
    user.setName(name);
    user.setAge(10);
    user.setDate(new Date());

    map.put("one", user);
}

4.6 @SessionAttributes

  • 该注解可以将数据存入 Session 域,使得数据在多次执行控制器方法的时候共享,只能作用在类上。属性如下:
    • value : 用于指定存入的属性名称
    • type : 用于指定存入的数据类型
  • jsp代码
<%-- SessionAttributes --%>
<a href="${pageContext.request.contextPath}/anno/testPut">存入数据到 SessionAttribute</a>
<a href="${pageContext.request.contextPath}/anno/testGet">从 SessionAttribute 取出数据</a>
<a href="${pageContext.request.contextPath}/anno/testRemove">从 SessionAttribute 移除数据</a>
  • 控制器代码
@Controller
@RequestMapping("/anno")
// 指定要存入 session 中的数据
@SessionAttributes({"msg"})
public class AnnotationController {
	/**
     * 存数据到 session
     *
     * @param model
     * @return
     */
    @RequestMapping("/testPut")
    public String testPut(Model model) {
        model.addAttribute("msg", "存入的数据");
        return "success";
    }

    /**
     * 从 session 中取数据
     *
     * @param model
     * @return
     */
    @RequestMapping("/testGet")
    public String testGet(ModelMap model) {
        System.out.println(model.get("msg"));
        return "success";
    }

    /**
     * 从 session 中移除数据
     *
     * @param status
     * @return
     */
    @RequestMapping("/testRemove")
    public String testRemove(SessionStatus status) {
        status.setComplete();
        return "success";
    }
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值