SpringMVC 入门、请求、响应


SpringMVC 概述

SSM 简介

SSM 三层架构

  • 表现层:负责数据展示

  • 业务层:负责业务处理

  • 数据层:负责数据操作

image


MVC 简介

MVC(Model View Controller)是一种用于设计及创建 Web 应用程序表现层的模式。

  • Model(模型):数据模型,用于封装数据

  • View(视图):页面视图,用于展示数据

    • jsp
    • html
  • Controller(控制器):处理用户交互的调度器,用于根据用户需求处理程序逻辑

    • Servlet
    • SpringMVC

image

SpringMVC 简介

SpringMVC 是一种基于 Java 实现的、MVC 模型的、轻量级的 Web 框架。

SpringMVC 优点

  1. 使用简单
  2. 性能突出(相比现有的框架技术)
  3. 灵活性强

入门案例

SpringMVC 工作流程分析:

  1. 服务器启动:
    1. 加载 web.xml 中 DispatcherServlet;
    2. 读取 spring-mvc.xml 中的配置,加载所有 com 包中所有标记为 bean 的类;
    3. 读取 bean 中方法上方标注 @RequestMapping 的内容;
  2. 处理请求:
    1. DispatcherServlet 配置拦截所有请求“/”;
    2. 使用请求路径与所有加载的 @RequestMapping 的内容进行比对;
    3. 执行对应的方法;
    4. 根据方法的返回值在 webapp 目录中查找对应的页面并展示。

实现示例

  1. 导入 SpringMVC 相关的 Maven 依赖:
<!-- servlet3.1规范的坐标 -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!--jsp坐标-->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
</dependency>
<!--spring的坐标-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>
<!--spring web的坐标-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>
<!--springmvc的坐标-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.1.9.RELEASE</version>
</dependency>
  1. 定义表现层处理器 Controller(等同于 Servlet),并配置成 Spring 的 bean:
@Controller
public class UserController {

    public void save(){
        System.out.println("user mvc controller is running ...");
    }
}
  1. 定义 SpringMVC 配置文件(格式与 Spring 配置文件一致):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       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/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!--扫描加载所有的控制类类-->
    <context:component-scan base-package="com"/>

</beans>
  1. web.xml 中配置 SpringMVC 核心控制器,用于将请求转发到对应的具体业务处理器 Controller 中(等同于 Servlet 配置):
<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*:spring-mvc.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  1. 设定具体 Controller 的访问路径与返回页面(等同于 Servlet 在 web.xml 中的配置):
// 设定当前方法的访问映射地址
@RequestMapping("/save")
// 设置当前方法返回值类型为String,用于指定请求完成后跳转的页面
public String save(){
    System.out.println("user mvc controller is running ...");
    // 设定具体跳转的页面
    return "success.jsp";
}

Spring 技术架构

image

  1. DispatcherServlet(前端控制器):是整体流程控制的中心,由其调用其它组件处理用户的请求,有效降低了组件间的耦合性。
  2. HandlerMapping(处理器映射器):负责根据用户请求找到对应具体的 Handler 处理器。
  3. Handler(处理器):业务处理的核心类,通常由开发者编写,描述具体的业务。
  4. HandlAdapter(处理器适配器):通过它对处理器进行执行。
  5. View Resolver(视图解析器):将处理结果生成 View 视图。
  6. View(视图):最终产出结果,常用视图如 jsp、html。

SpringMVC 基础配置

常规配置

Controller 加载控制

SpringMVC 的处理器对应的 bean 必须按照规范格式开发,为了避免加入无效的 bean,可通过 bean 加载过滤器进行包含设定或排除设定。

例如,表现层 bean 标注通常设定为 @Controller,因此可以通过注解名称进行过滤控制:

<context:component-scan base-package="com">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

静态资源加载

<!--放行指定类型静态资源配置方式-->
<mvc:resources mapping="/img/**" location="/img/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/css/**" location="/css/"/>

<!--SpringMVC提供的通用资源放行方式-->
<mvc:default-servlet-handler/>

中文乱码处理

web.xml

<!-- 乱码处理过滤器,与Servlet中使用的完全相同,差异之处在于处理器的类由Spring提供 -->
<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>

注解驱动

目标:删除 web.xml 和 spring-mvc.xml 。

注意
image

实现示例

  1. 使用注解形式,将 SpringMVC 核心配置文件替换为配置类
package com.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.Controller;

@Configuration
@ComponentScan(
        value="com",
        includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes={Controller.class})
)
public class SpringMvcConfig implements WebMvcConfigurer {

    // 注解配置放行指定资源格式
    // @Override
    // public void addResourceHandlers(ResourceHandlerRegistry registry) {
    //     registry.addResourceHandler("/img/**").addResourceLocations("/img/");
    //     registry.addResourceHandler("/js/**").addResourceLocations("/js/");
    //     registry.addResourceHandler("/css/**").addResourceLocations("/css/");
    // }

    // 注解配置通用放行资源的格式
    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();;
    }
}
  1. 替换 web.xml:基于 servlet3.0 规范,自定义 Servlet 容器初始化配置类,加载 SpringMVC 核心配置类
package com.config;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
import java.util.Objects;

public class ServletInitConfig extends AbstractDispatcherServletInitializer {
    /*
    创建 Servlet 容器时,使用注解的方式加载 SpringMVC 配置类中的信息,并加载成 Web 专用的 ApplicationContext 对象
    该对象放入了 ServletContext 范围,后期在整个 Web 容器中可以随时获取调用
     */
    @Override
    protected WebApplicationContext createServletApplicationContext() {
        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
        ctx.register(SpringMvcConfig.class);
        return ctx;
    }

    // 注解配置映射地址方式,服务于 SpringMVC 的核心控制器 DispatcherServlet
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    // 乱码处理作为过滤器,在 servlet 容器启动时进行配置
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        super.onStartup(Objects.<ServletContext>requireNonNull(servletContext));
        CharacterEncodingFilter cef = new CharacterEncodingFilter();
        cef.setEncoding("UTF-8");
        FilterRegistration.Dynamic registration = servletContext.addFilter("characterEncodingFilter", cef);
        registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD,
                DispatcherType.INCLUDE), false, "/*");
    }
}

请求

请求映射:@RequestMapping

@RequestMapping 使用

  • 类型:类注解;方法注解
  • 位置:处理器类定义上方;处理器类中的方法定义上方
  • 作用:为当前处理器中所有方法设定公共的访问路径前缀;绑定请求地址与对应处理方法间的关系
// 示例:方法注解
@Controller
public class UserController {
    // 访问 URI:/user/requestURL1
    @RequestMapping("/requestURL1")
    public String requestURL2() {
        return "page.jsp";
    }
}

// 示例:类注解
@Controller
@RequestMapping("/user")
public class UserController {
    // 访问 URI:/user/requestURL2
    @RequestMapping("/requestURL2")
    public String requestURL2() {
        return "page.jsp";
    }
}

常用属性

@RequestMapping(
    value="/requestURL3",  // 设定请求路径,与path属性、value属性相同
    method = RequestMethod.GET,  // 设定请求方式
    params = "name",  // 设定请求参数条件
    headers = "content-type=text/*",  // 设定请求消息头条件
    consumes = "text/*",  // 用于指定可以接收的请求正文类型(MIME类型)
    produces = "text/*"  // 用于指定可以生成的响应正文类型(MIME类型)
)
public String requestURL3() {
    return "/page.jsp";
}

普通类型传参

// URL 访问:http://localhost:8080/requestParam1?name=xiaoming&age=14
@RequestMapping("/requestParam1")
public String requestParam1(String name ,String age){
    System.out.println("name="+name+", age="+age);
    return "page.jsp";
}

@RequestParam

  • 类型:形参注解
  • 位置:处理器类中的方法形参前方
  • 作用:绑定请求参数与处理方法形参间的关系

示例

// http://localhost:8080/requestParam2?userName=Jock
@RequestMapping("/requestParam2")
public String requestParam2(@RequestParam(
                            name = "userName",
                            required = true,
                            defaultValue = "xiaohuang") String name) {
    System.out.println("name="+name);
    return "page.jsp";
}
  • 当未传参即直接访问“/requestParam2”时,方法会取默认值“xiaohuang”。
  • 当配置了“required=true”但未配置“defaultValue”时,访问时不传参则会报 400 错。
    image

对象类型传参

POJO

当使用 POJO(简单 Java 对象)时,传参名称与 POJO 类属性名保持一致即可。

  • POJO 类
package com.bean;

public class User {

    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
  • Controller
// URL 访问:http://localhost:8080/requestParam3?name=xiaodong&age=18
@RequestMapping("/requestParam3")
public String requestParam3(User user){
    System.out.println("name="+user.getName());
    return "page.jsp";
}

参数冲突

当 POJO 的属性与其他形参出现同名问题时,将被同时赋值。

// 访问 URL:http://localhost:8080/requestParam4?name=xiaoyi&age=14
@RequestMapping("/requestParam4")
public String requestParam4(User user, String age){
    System.out.println("user.age="+user.getAge()+", age="+age);  // user.age=14, age=14
    return "page.jsp";
}

建议使用 @RequestParam 注解进行区分。

复杂对象类型

当对象中出现对象属性时,则要求入参名称与对象层次结构名称保持一致。

image

对象集合

1)当复杂对象中出现用 List 保存对象数据时,要求入参名称与对象层次结构名称保持一致,并使用数组格式描述集合中对象的位置。

  • bean:
public class User {
    private String name;
    private Integer age;
    private List<Address> addresses;
}

public class Address {
    private String province;
    private String city;
    private String address;
}
  • Controller:
// 访问URL:http://localhost:8080/requestParam7?addresses[0].province=bj&addresses[1].province=tj
@RequestMapping("/requestParam7")
public String requestParam7(User user){
    System.out.println("user.addresses="+user.getAddresses());
    return "page.jsp";
}

注意The valid characters are defined in RFC 7230 and RFC 3986
问题解决

2)当复杂对象中出现用 Map 保存对象数据时,要求入参名称与对象层次结构名称保持一致,并使用映射格式描述集合中对象的位置。

  • bean:
public class User {
    private String name;
    private Integer age;
    private Map<String, Address> addressMap;
}

public class Address {
    private String province;
    private String city;
    private String address;
}
  • controller:
// 访问 URL:http://localhost:8080/requestParam8?addressMap['home'].province=bj&addressMap['job'].province=tj
@RequestMapping("/requestParam8")
public String requestParam8(User user){
    System.out.println("user.addressMap="+user.getAddressMap());
    return "page.jsp";
}

数组集合类型传参

// 访问 URL:http://localhost:8080/requestParam9?nick=xiaoming1&nick=xiaoming2
@RequestMapping("/requestParam9")
public String requestParam9(String[] nick){
    System.out.println(nick[0]+", "+nick[1]);  // xiaoming1, xiaoming2
    return "page.jsp";
}

// 访问 URL:http://localhost:8080/requestParam10?nick=xiaoming1&nick=xiaoming2
@RequestMapping("/requestParam10")
public String requestParam10(@RequestParam("nick") List<String> nick){
    System.out.println(nick);  // [xiaoming1, xiaoming2]
    return "page.jsp";
}

注意:

  • SpringMVC 默认将 List 作为对象处理,赋值前先创建对象,然后将 nick 作为对象的属性进行处理。而由于 List 是接口,无法创建对象,报无法找到构造方法异常;修复类型为可创建对象的 ArrayList 类型后,对象可以创建,但没有 nick 属性,因此数据为空。

  • 此时需要告知 SpringMVC 的处理器 nick 是一组数据,而不是一个单一数据。

  • 因此通过 @RequestParam 注解,将数量大于 1 个的 names 参数打包成参数数组后, SpringMVC 才能识别该数据格式,并判定形参类型是否为数组或集合,并按数组或集合对象的形式操作数据。


类型转换器

SpringMVC 会对接收的参数进行自动类型转换,该工作通过 Converter 接口实现。

image

image

标量转换器

  • StringToBooleanConverter String —> Boolean
  • ObjectToStringConverter Object —> String
  • StringToNumberConverterFactory String —> Number( Integer、Long 等)
  • NumberToNumberConverterFactory Number子类型之间(Integer、Long、Double 等)
  • StringToCharacterConverter String —> java.lang.Character
  • NumberToCharacterConverter Number子类型(Integer、Long、Double 等) —> java.lang.Character
  • CharacterToNumberFactory java.lang.Character —> Number 子类型(Integer、Long、Double 等)
  • StringToEnumConverterFactory String —> enum 类型
  • EnumToStringConverter enum 类型 —> String
  • StringToLocaleConverter String —> java.util.Local
  • PropertiesToStringConverter java.util.Properties —> String
  • StringToPropertiesConverter String —> java.util.Properties

集合、数组相关转换器

  • ArrayToCollectionConverter 数组 —> 集合(List、Set)
  • CollectionToArrayConverter 集合(List、Set) —> 数组
  • ArrayToArrayConverter(数组间转换)
  • CollectionToCollectionConverter 集合间(List、Set)
  • MapToMapConverter Map间
  • ArrayToStringConverter 数组 —> String 类型
  • StringToArrayConverter String —> 数组(实现方式为 trim 后使用 "," 进行 split)
  • ArrayToObjectConverter 数组 —> Object
  • ObjectToArrayConverter Object —> 单元素数组
  • CollectionToStringConverter 集合(List、Set) —> String
  • StringToCollectionConverter String —> 集合(List、Set)(实现方式为 trim 后使用 "," 进行 split)
  • CollectionToObjectConverter 集合 —> Object
  • ObjectToCollectionConverter Object —> 单元素集合

默认转换器

  • ObjectToObjectConverter(Object 间转换)
  • IdToEntityConverter Id —> Entity
  • FallbackObjectToStringConverter Object —> String

日期类型格式转换

配置版:声明自定义的转换格式并覆盖系统转换格式

<!-- 启用自定义Converter -->
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 1.设定格式类型Converter,注册为Bean,受SpringMVC管理 -->
<bean id="conversionService"
      class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <!-- 2.自定义Converter格式类型设定,该设定使用的是同类型覆盖的思想 -->
    <property name="formatters">
        <!-- 3.使用set保障相同类型的转换器仅保留一个,避免冲突 -->
        <set>
            <!-- 4.设置具体的格式类型 -->
            <bean class="org.springframework.format.datetime.DateFormatter">
                <!-- 5.类型规则 -->
                <property name="pattern" value="yyyy-MM-dd"/>
            </bean>
        </set>
    </property>
</bean>

注解版

  • 名称:@DateTimeFormat
  • 类型:形参注解、成员变量注解
  • 位置:形参前面或成员变量上方
  • 作用:为当前参数或变量指定类型转换规则
  • 注意:依赖注解驱动支持(<mvc:annotation-driven />)
  • 范例:
// 形参前
// 访问 URL:http://localhost:8080/requestParam11?date=2021-12-12
@RequestMapping("/requestParam11")
public String requestParam11(@DateTimeFormat(pattern="yyyy-MM-dd") Date date){
    System.out.println("date="+date);
    return "page.jsp";
}

// 成员变量上方
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday;

自定义类型转换器

1)实现 Converter 接口,并制定转换前与转换后的类型:

  • 配置:
  <!-- 1.将自定义Converter注册为Bean,受SpringMVC管理 -->
  <bean id="myDateConverter" class="com.itheima.converter.MyDateConverter"/>
  <!-- 2.设定自定义Converter服务bean -->
  <bean id="conversionService"
        class="org.springframework.context.support.ConversionServiceFactoryBean">
      <!-- 3.注入所有的自定义Converter,该设定使用的是同类型覆盖的思想 -->
      <property name="converters">
          <!-- 4.set保障同类型转换器仅保留一个,去重规则以Converter<S,T>的泛型为准 -->
          <set>
              <!-- 5.具体的类型转换器 -->
              <ref bean="myDateConverter"/>
          </set>
      </property>
  </bean>
  • 实现类:
// 自定义类型转换器,实现Converter接口,接口中指定的泛型即为最终作用的条件
// 本例中的泛型填写的是<String,Date>,最终出现字符串转日期时,该类型转换器生效
public class MyDateConverter implements Converter<String, Date> {
    // 重写接口的抽象方法,参数由泛型决定
    public Date convert(String source) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Date date = null;
        // 类型转换器无法预计使用过程中出现的异常,因此必须在类型转换器内部捕获,不允许抛出,框架无法预计此类异常如何处理
        try {
            date = df.parse(source);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }
}

2)通过注册自定义转换器,将该功能加入到 SpringMVC 的转换服务 ConverterService 中:

<!-- 开启注解驱动,加载自定义格式化转换器对应的类型转换服务 -->
<mvc:annotation-driven conversion-service="conversionService"/>

响应

页面跳转:转发与重定向

    // 转发
    @RequestMapping("/showPage1")
    public String showPage1() {
        System.out.println("user mvc controller is running ...");
        return "forward:page.jsp";  // 支持访问WEB-INF下的页面
    }

    // 重定向
    @RequestMapping("/showPage2")
    public String showPage2() {
        System.out.println("user mvc controller is running ...");
        return "redirect:page.jsp";  // 不支持访问WEB-INF下的页面
    }

请求转发与重定向的区别

  • 当使用请求转发时,Servlet 容器将使用一个内部的方法来调用目标页面,新的页面继续处理同一个请求,而浏览器将不会知道这个过程(即服务器行为)。与之相反,重定向的含义是第一个页面通知浏览器发送一个新的页面请求。因为当使用重定向时,浏览器中所显示的 URL 会变成新页面的 URL(浏览器行为)。而当使用转发时,该 URL 会保持不变。

  • 重定向的速度比转发慢,因为浏览器还得发出一个新的请求。

  • 同时,由于重定向产生了一个新的请求,所以经过一次重定向后,第一次请求内的对象将无法使用。

总结

  • 重定向:两次请求,浏览器行为,地址栏改变,请求域中的数据会丢失。
  • 请求转发:一次请求,服务器行为,地址栏不变,请求域中的数据不丢失。

怎么选择是重定向还是转发呢

  • 通常情况下转发更快,而且能保持请求内的对象,所以它是第一选择。但是由于在转发之后,浏览器中 URL 仍然指向开始页面,此时如果重载当前页面,开始页面将会被重新调用。如果不想看到这样的情况,则选择重定向。

  • 不要仅仅为了把变量传到下一个页面而使用 session 作用域,那会无故增大变量的作用域,转发也许可以帮助解决这个问题。

    • 重定向:以前的请求中存放的变量全部失效,并进入一个新的请求作用域。
    • 转发:以前的请求中存放的变量不会失效,就像把两个页面拼到了一起。

页面访问快捷设定(InternalResourceViewResolver)

通常,展示页面的保存位置比较固定且结构相似,因此可以设定通用的访问路径,来简化页面配置。

image

示例

<!-- 设定页面加载的前缀后缀,仅适用于默认形式,不适用于手工标注转发或重定向的方式 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/pages/"/>
    <property name="suffix" value=".jsp"/>
</bean>
public String showPage3() {
    return "page";
}

而如果未设定返回值,使用 void 类型,则默认使用访问路径来拼接前后缀:

// 最简页面配置方式:使用访问路径作为返回的页面名称,省略返回值
@RequestMapping("/showPage5")
public void showPage5() {
    System.out.println("user mvc controller is running ...");
}

带数据页面跳转

  • 实体类:
public class Book {
    private String name;
    private Double price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
  • 控制层:
import com.bean.Book;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

@Controller
public class BookController {

    // 使用原生response对象响应数据
    @RequestMapping("/showData1")
    public void showData1(HttpServletResponse response) throws IOException {
        response.getWriter().write("message");
    }
	
    // 使用原生request对象传递参数
    @RequestMapping("/showPageAndData1")
    public String showPageAndData1(HttpServletRequest request) {
        request.setAttribute("name", "xiaoming");
        return "page";
    }
    
    // 使用Model形参传递参数
    @RequestMapping("/showPageAndData2")
    public String showPageAndData2(Model model) {
        Book book  = new Book();
        book.setName("SpringMVC入门案例");
        book.setPrice(66.66d);
        // 添加数据的方式
        model.addAttribute("name", "xiaoming");
        model.addAttribute("book", book);
        return "page";
    }

    // 使用ModelAndView形参传递参数,该对象还封装了页面信息
    @RequestMapping("/showPageAndData3")
    public ModelAndView showPageAndData3(ModelAndView modelAndView) {
        // ModelAndView mav = new ModelAndView();  // 替换形参中的参数
        Book book = new Book();
        book.setName("SpringMVC入门案例");
        book.setPrice(66.66d);
        // 添加数据的方式
        modelAndView.addObject("book", book);
        modelAndView.addObject("name", "xiaoming");
        // 设置返回页面(若该方法存在多个,则以最后一个为准)
        modelAndView.setViewName("page");
        // 返回值设定成ModelAndView对象
        return modelAndView;
    }

    // ModelAndView对象支持转发的手工设定,该设定不会启用前缀后缀的页面拼接格式
    @RequestMapping("/showPageAndData4")
    public ModelAndView showPageAndData4(ModelAndView modelAndView) {
        modelAndView.setViewName("forward:/WEB-INF/pages/page.jsp");
        return modelAndView;
    }

    // ModelAndView对象支持重定向的手工设定,该设定不会启用前缀后缀的页面拼接格式
    @RequestMapping("/showPageAndData5")
    public ModelAndView showPageAndData6(ModelAndView modelAndView) {
        modelAndView.setViewName("redirect:index.jsp");
        return modelAndView;
    }

}

返回 JSON 数据

  • 导入 maven 坐标:
        <!--json相关坐标3个-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
  • Controller:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;


    // 使用@ResponseBody将返回的结果作为响应内容,而非响应的页面名称
    @RequestMapping("/showData2")
    @ResponseBody
    public String showData2(){
        return "{\"name\":\"xiaoming\"}";
    }

    // 使用jackson进行json数据格式转化(会有中文乱码问题)
    @RequestMapping("/showData3")
    @ResponseBody
    public String showData3() throws JsonProcessingException {
        Book book  = new Book();
        book.setName("SpringMVC入门案例");
        book.setPrice(66.66d);
        ObjectMapper om = new ObjectMapper();
        return om.writeValueAsString(book);
    }

    /*
    <!--开启springmvc注解驱动,对@ResponseBody的注解进行格式增强,追加其类型转换的功能,具体实现由MappingJackson2HttpMessageConverter进行-->
    <mvc:annotation-driven/>
     */
    // 使用SpringMVC注解驱动,对标注@ResponseBody注解的控制器方法进行结果转换
    // 由于返回值为引用类型,自动调用jackson提供的类型转换器进行格式转换
    @RequestMapping("/showData4")
    @ResponseBody
    public Book showData4() {
        Book book  = new Book();
        book.setName("SpringMVC入门案例");
        book.setPrice(66.66d);
        return book;
    }

    // 转换集合类型数据
    @RequestMapping("/showData5")
    @ResponseBody
    public List showData5() {
        Book book1 = new Book();
        book1.setName("SpringMVC入门案例");
        book1.setPrice(66.66d);

        Book book2 = new Book();
        book2.setName("SpringMVC入门案例");
        book2.setPrice(66.66d);

        ArrayList<Book> al = new ArrayList<>();
        al.add(book1);
        al.add(book2);
        return al;  // 返回 [{"name":"SpringMVC入门案例","price":66.66},{"name":"SpringMVC入门案例","price":66.66}]
    }

Servlet 相关接口

SpringMVC 提供访问原始 Servlet 接口 API 的功能,通过形参声明即可。

    @RequestMapping("/servletApi")
    public String servletApi(HttpServletRequest request,
            HttpServletResponse response, HttpSession session){
        System.out.println(request);  // org.apache.catalina.connector.RequestFacade@6d3a1615
        System.out.println(response);  // org.apache.catalina.connector.ResponseFacade@55405578
        System.out.println(session);  // org.apache.catalina.session.StandardSessionFacade@714a7020
        request.setAttribute("name", "xiaoming");
        System.out.println(request.getAttribute("name"));  // xiaoming
        return "page.jsp";
    }

Header 数据获取:

  • 名称:@RequestHeader
  • 类型:形参注解
  • 位置:处理器类中的方法形参前方
  • 作用:绑定请求头数据与对应处理方法形参间的关系
    // header 数据获取
    @RequestMapping("/headApi")
    public String headApi(@RequestHeader("Accept-Language") String head){
        System.out.println(head);  // zh-CN,zh;q=0.9
        return "page.jsp";
    }

Cookie 数据获取:

  • 名称:@CookieValue
  • 类型:形参注解
  • 位置:处理器类中的方法形参前方
  • 作用:绑定请求 Cookie 数据与对应处理方法形参间的关系
    // cookie 数据获取
    @RequestMapping("/cookieApi")
    public String cookieApi(@CookieValue("JSESSIONID") String jsessionid){
        System.out.println(jsessionid);
        return "page.jsp";
    }

Session 数据获取:

  • 名称:@SessionAttribute
  • 类型:形参注解
  • 位置:处理器类中的方法形参前方
  • 作用:绑定请求 Session 数据与对应处理方法形参间的关系
    // 测试用方法,为下面的试验服务,用于在session中放入数据
    @RequestMapping("/setSessionData")
    public String setSessionData(HttpSession session){
        session.setAttribute("name", "xiaoming");
        return "page";
    }

    // session 数据获取
    @RequestMapping("/sessionApi")
    public String sessionApi(@SessionAttribute("name") String name){
        System.out.println(name);  // 获取session中的name值
        return "page.jsp";
    }

Session 数据设置:

  • 名称:@SessionAttributes
  • 类型:类注解
  • 位置:处理器类上方
  • 作用:声明放入 session 范围的变量名称,适用于 Model 类型数据传参
@Controller
// 设定当前类中名称为age和gender的变量放入session范围(不常用,了解即可)
@SessionAttributes(names={"age","gender"})
public class ServletController {
	
    // 配合 @SessionAttributes(names={"age","gender"}) 使用
    // 将数据放入session存储范围,通过Model对象实现数据set,通过@SessionAttributes注解实现范围设定
    @RequestMapping("/setSessionData2")
    public String setSessionDate2(Model model) {
        model.addAttribute("age",39);
        model.addAttribute("gender","男");
        return "page.jsp";
    }
}
posted @ 2021-12-19 00:12  Juno3550  阅读(211)  评论(0编辑  收藏  举报