SpringMVC Note [vaynexiao]

入门

什么是SpringMVC

Spring MVC是一个基于MVC架构的用来简化web应用程序开发的应用开发框架,它是Spring的一个模块,无需中间整合层来整合 ,它和Struts2一样都属于表现层的框架。在web模型中,MVC是一种很流行的框架,通过把Model,View,Controller分离,把较为复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

Spring MVC 就是对这套流程的封装,屏蔽了很多底层代码,开放出接口,让开发者可以更加轻松、便捷地完成基于 MVC 模式的 Web 开发。

  • 轻量级,简单易学
  • 高效 , 基于请求响应的MVC框架
  • 与Spring兼容性好,无缝结合
  • 约定优于配置
  • 功能强大:RESTful、数据验证、格式化、本地化、主题等
  • 简洁灵活

核心组件

  • DispatcherServlet:前置控制器,是整个流程控制的核心,控制其他组件的执行,进行统一调度,降低组件之间的耦合性,相当于总指挥。
  • Handler:处理器(需要程序员开发),完成具体的业务逻辑,相当于 Servlet 或 Action。
  • HandlerMapping:DispatcherServlet 接收到请求之后,通过 HandlerMapping 将不同的请求映射到不同的 Handler。
  • HandlerInterceptor:处理器拦截器,是一个接口,如果需要完成一些拦截处理,可以实现该接口。
  • HandlerExecutionChain:处理器执行链,包括两部分内容:Handler 和 HandlerInterceptor(系统会有一个默认的 HandlerInterceptor,如果需要额外设置拦截,可以添加拦截器)。
  • HandlerAdapter:处理器适配器,Handler 执行业务方法之前,需要进行一系列的操作,包括表单数据的验证、数据类型的转换、将表单数据封装到 JavaBean 等,这些操作都是由 HandlerApater 来完成,开发者只需将注意力集中业务逻辑的处理上,DispatcherServlet 通过 HandlerAdapter 执行不同的 Handler。
  • ModelAndView:(需要程序员开发jsp)装载了模型数据和视图信息,作为 Handler 的处理结果,返回给 DispatcherServlet。
  • ViewResolver:视图解析器,DispatcheServlet 通过它将逻辑视图解析为物理视图,最终将渲染结果响应给客户端。

Spring MVC 的工作流程

中心控制器DispatcherServlet是核心作用
在这里插入图片描述
图中只有虚线部分需要手动实现,实现部分全部由框架封装完成。

  1. DispatcherServlet 表示前置控制器,是整个SpringMVC的控制中心。前端发出请求,DispatcherServlet接收请求并拦截请求。
  2. HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
  3. HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
  4. HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
  5. HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
  6. Handler让具体的Controller执行。
  7. Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
  8. HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
  9. DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
  10. 视图解析器将解析的逻辑视图名传给DispatcherServlet。
  11. DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
  12. 最终视图呈现给用户。

SpringMVC xml版

1,新建一个Moudle , 添加web的支持!建立包结构 (蓝色java+resources目录)
2,确定导入了SpringMVC 的依赖!
3,配置web.xml , 注册DispatcherServlet

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.0.RELEASE</version>
        </dependency>

4,web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0">
    <!--注册DispatcherServlet-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--关联一个springmvc的配置文件:[servlet-name]-servlet.xml-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:xxx-servlet.xml</param-value>
        </init-param>
        <!--启动级别-1-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--/ 匹配所有的请求;(不包括.jsp)-->
    <!--/* 匹配所有的请求;(包括.jsp)-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4,xxx-servlet.xml 即就是SpringMVC 的配置文件!名称:: [servletname]-servlet.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 可以显示配置映射器和处理器,不写也会有默认配置 -->
    
	<!-- 配置自动扫描的范围,也可以单独指定一个Controller类 -->
    <context:component-scan base-package="com.xxx.xxx"></context:component-scan>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp"></property><!-- 后缀 -->
    </bean>
</beans>

8,编写Controller,这里用第一种方式实现Controller接口,返回一个ModelAndView装数据,封装视图

public class HelloController implements Controller {
    
    
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) 
    	throws Exception {
    
    
        //ModelAndView 模型和视图
        ModelAndView mv = new ModelAndView();
        //封装对象,放在ModelAndView中。Model
        mv.addObject("msg","HelloSpringMVC!");
        //封装要跳转的视图,放在ModelAndView中
        mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
        return mv;
    }
}

SpringMVC 注解版

1:新建一个Moudle , 添加web支持!建立包结构 (蓝色java+resources目录)
2:由于Maven可能存在资源过滤的问题,我们将配置完善

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

3:在pom.xml文件引入相关的依赖:
主要有Spring框架核心库、Spring MVC、servlet , JSTL等。我们在父依赖中已经引入了!
4:配置web.xml

注意点:
• 注意web.xml版本问题,要最新版!
• 注册DispatcherServlet
• 关联SpringMVC的配置文件
• 启动级别为1
• 映射路径为 / 【不要用/*,会404】

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!--1.注册servlet-->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!--通过初始化参数指定SpringMVC配置文件的位置,进行关联-->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc-servlet.xml</param-value>
        </init-param>
        <!-- 启动顺序,数字越小,启动越早 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--所有请求都会被springmvc拦截 -->
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

/ 和 /* 的区别:
< url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;
即:.jsp 不会进入spring的 DispatcherServlet类 。
< url-pattern > /* </ url-pattern > 会匹配 *.jsp,
会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

<?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: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.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
    <context:component-scan base-package="com.kuang.controller"/>
    <!-- 让Spring MVC不处理静态资源 -->
    <mvc:default-servlet-handler />
    <!--
    支持mvc注解驱动
        在spring中一般采用@RequestMapping注解来完成映射关系
        要想使@RequestMapping注解生效
        必须向上下文中注册DefaultAnnotationHandlerMapping
        和一个AnnotationMethodHandlerAdapter实例
        这两个实例分别在类级别和方法级别处理。
        而annotation-driven配置帮助我们自动完成上述两个实例的注入。
     -->
    <mvc:annotation-driven />
    <!-- 视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/" />
        <!-- 后缀 -->
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

5,创建Controller

@Controller
@RequestMapping("/HelloController")
public class HelloController {
    
    
    //真实访问地址 : 项目名/HelloController/hello
    @RequestMapping("/hello")
    public String sayHello(Model model){
    
    
        //向模型中添加属性msg与值,可以在JSP页面中取出并渲染
        model.addAttribute("msg","hello,SpringMVC");
        //web-inf/jsp/hello.jsp
        return "hello";
    }
}

测试:http://localhost:8080/projectName/HelloController/hello
这里路径中的projectName是tomcat发布设置的,/HelloController/hello是@RequestMapping指定的
7,创建视图层,也就是画页面

总结

​ 1,新建一个web项目
​ 2,导入相关jar
​ 3,编写web.xml,注册DispatcherServlet
​ 4,编写springmvc.xml
​ 5,创建对应的Controller
​ 6,完善前端视图和Controller之间的对应
​ 7,测试和运行

使用springmvc必须配置的三大组件:
处理器映射器 + 处理器适配器 + 视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器and处理器适配器 只需要开启注解驱动即可,省去了大量的xml配置。

自定义数据转换器

数据转换器是指将客户端 HTTP 请求中的参数转换为业务方法中定义的形参,自定义表示开发者可以自主设计转换的方式,HandlerApdter 已经提供了通用的转换,String 转 int,String 转 double,表单数据的封装等,但是在特殊的业务场景下,HandlerAdapter 无法进行转换,就需要开发者自定义转换器。

String 转 Date

客户端输入 String 类型的数据 “2019-03-03”,自定义转换器将该数据转为 Date 类型的对象。

  • 创建 DateConverter 转换器,实现 Conveter 接口。
public class DateConverter implements Converter<String, Date> {
    
    

    private String pattern; //yyyy-mm-ss

    public DateConverter(String pattern){
    
    
        this.pattern = pattern;
    }

    @Override
    public Date convert(String s) {
    
    
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(this.pattern);
        Date date = null;
        try {
    
    
            date = simpleDateFormat.parse(s);
        } catch (ParseException e) {
    
    
            e.printStackTrace();
        }
        return date;
    }
}
  • springmvc.xml 配置转换器。
<!-- 配置自定义转换器 -->
<bean id="myComversion" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.southwind.converter.DateConverter">
                <constructor-arg type="java.lang.String" value="yyyy-MM-dd"></constructor-arg>
            </bean>
        </list>
    </property>
</bean>

<mvc:annotation-driven conversion-service="myComversion">
    <!-- 消息转换器 -->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
        </bean>
        <!-- 配置fastjson -->
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4"></bean>
    </mvc:message-converters>
</mvc:annotation-driven>
  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/converter/date" method="post">
        请输入日期:<input type="text" name="date"/>(yyyy-MM-dd)<br/>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>
  • Handler
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;

@RestController
@RequestMapping("/converter")
public class ConverterHandler {
    
    

    @RequestMapping("/date")
    public String date(Date date){
    
    
        return date.toString();
    }
}

String 转 Student

Student 类 属性 long id; String name; int age

StudentConverter.java

public class StudentConverter implements Converter<String, Student> {
    
    
    @Override
    public Student convert(String s) {
    
    
        String[] args = s.split("-");
        Student student = new Student();
        student.setId(Long.parseLong(args[0]));
        student.setName(args[1]);
        student.setAge(Integer.parseInt(args[2]));
        return student;
    }
}

springmvc.xml

<!-- 配置自定义转换器 -->
<bean id="myConversion2" class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.southwind.converter.DateConverter">
                <constructor-arg type="java.lang.String" value="yyyy-MM-dd"></constructor-arg>
            </bean>
            <bean class="com.southwind.converter.StudentConverter"></bean>
        </list>
    </property>
</bean>

<mvc:annotation-driven conversion-service="myConversion2">
    <!-- 消息转换器 -->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
        </bean>
        <!-- 配置fastjson -->
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4"></bean>
    </mvc:message-converters>
</mvc:annotation-driven>

JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/converter/student" method="post">
        请输入学生信息:<input type="text" name="student"/>(id-name-age)<br/>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

Handler

@RequestMapping("/student")
public String student(Student student){
    
    
    return student.toString();
}

模型数据解析

JSP 四大作用域对应的内置对象:pageContext、request、session、application。
模型数据的绑定是由 ViewResolver 来完成的

SpringMVC在调用方法前会自动创建一个隐含的数据模型,用于存储模型数据。
关于这一点,spring里面有一个注解:
@ModelAttribute :被该注解修饰的方法,在请求时优先被执行,用于接收前台页面传入的参数

Spring MVC 提供了以下几种方式添加模型数据:

  • Map
  • Model
  • ModelMap
  • ModelAndView (主要区别是:比Model ModelMap 多了viewName视图名)
  • @ModelAttribute
  • @SessionAttribute

方式一,将模式数据绑定到 request 对象
其实以下方法的目的都是将模式数据绑定到 request 对象。有的直接存入request ,有的间接而已。

Map

@RequestMapping("/map")
public String map(Map<String,User> map){
    
    
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    map.put("user",user);
    return "view";
}

Model

@RequestMapping("/mode")
public String model(Model model){
    
    
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    model.addAttribute("user",user);
    return "view";
}

ModelAndView

@RequestMapping("/test")
public ModelAndView test(){
    
    
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    ModelAndView modelAndView = new ModelAndView();
    // 另一种构造方法:ModelAndView modelAndView = new ModelAndView("view");
    modelAndView.addObject("user",user);
    modelAndView.setViewName("view");
    return modelAndView;
    // 所传数据有多个属性时使用map new ModelAndView("viewName",mapObj);
    // 也可以使用Model或者ModelMap,他俩与map使用很类似,因为继承了Map接口
}

HttpServletRequest

@RequestMapping("/request")
public String request(HttpServletRequest request){
    
    
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    request.setAttribute("user",user);
    return "view";
}

@ModelAttribute

有注解@ModelAttribute的方法 在业务方法return之前会被调用,
专门用来返回模型数据。将对象放入request对象中

@ModelAttribute
public void getUser(Model model){
    
     // 得到Model对象
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    model.addAttribute("user",user);
}

业务方法中无需再处理模型数据,只需返回视图名即可。

@RequestMapping("/test")
public String test(){
    
    
    return "view";
}

方式二,将模型数据绑定到 session 对象

1、直接使用原生的 Servlet API。

@RequestMapping("/testSession") //从request中取session
public String testSession(HttpServletRequest request){
    
     
    HttpSession session = request.getSession();
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    session.setAttribute("user",user);
    return "view";
}

@RequestMapping("/testSession2") //直接获取session
public String testSession2(HttpSession session){
    
    
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    session.setAttribute("user",user);
    return "view";
}

2、@SessionAttribute

@SessionAttributes(value = {
    
    "user","address"})
public class ViewHandler {
    
     //注意这里是给类加注解
}

对于 ViewHandler 中的所有业务方法,只要向 request 中添加了 key = “user”、key = “address” 的对象时,Spring MVC 会自动将该数据添加到 session 中,保存 key 不变。

@SessionAttributes(types = {
    
    User.class,Address.class})
public class ViewHandler {
    
    
}

对于 ViewHandler 中的所有业务方法,只要向 request 中添加了数据类型是 User 、Address 的对象时,Spring MVC 会自动将该数据添加到 session 中,保存 key 不变。

方式三,将模型数据绑定到 application 对象

@RequestMapping("/application")
public String application(HttpServletRequest request){
    
    
    ServletContext application = request.getServletContext();
    User user = new User();
    user.setId(1L);
    user.setName("张三");
    application.setAttribute("user",user);
    return "view";
}

乱码

<filter>
    <filter-name>encoding</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>encoding</filter-name>
    <url-pattern>/</url-pattern>   这里写成/*
</filter-mapping>

各种类型数据绑定

数据绑定:在后端的业务方法中直接获取客户端 HTTP 请求中的参数,将请求参数映射到业务方法的形参中,Spring MVC 中数据绑定的工作是由 HandlerAdapter 来完成的。

1,基本数据类型

@RequestMapping("/baseType")
@ResponseBody
public String baseType(int id){
    
    
    return id+"";
}

@ResponseBody 表示 Spring MVC 会直接将业务方法的返回值响应给客户端,也就是说返回一个int值;
如果不加 @ResponseBody 注解,Spring MVC 会将业务方法的放回值传递给 DispatcherServlet,再由 DisptacherServlet 调用 ViewResolver 对返回值进行解析,映射到一个 JSP 资源,即就是xxx.jsp

2,包装类

@RequestMapping("/user")
@ResponseBody
public String add(@RequestParam(value = "num", required = false, defaultValue = "0") Integer id){
    
    
    return id+"";
}

包装类可以接收 null,当 HTTP 请求没有参数时,使用包装类定义形参的数据类型,程序不会抛出异常。

@RequestParam
value = “num”:将 HTTP 请求中名为 num 的参数赋给形参 id。
requried:设置 num 是否为必填项,true 表示必填,false 表示非必填,可省略。
defaultValue = “0”:如果 HTTP 请求中没有 num 参数,默认值为0.
这里@RequestParam还起到了一个作用,将num的值赋给id,类似数据库字段起别名一样

3,数组

一个url中同一个参数传了多次,比如num=1&num=2会报错,

@RestController
@RequestMapping("/data")
public class DataBindHandler {
    
    
    @RequestMapping("/array")
    public String array(String[] name){
    
    
        String str = Arrays.toString(name);
        return str;
    }
}

4,List

Spring MVC 不支持 List 类型的直接转换,需要对 List 集合进行包装。

@Data
public class UserList {
    
    
    private List<User> users;
}
    <form action="/data/list" method="post">
        用户1编号:<input type="text" name="users[0].id"/><br/>
        用户1名称:<input type="text" name="users[0].name"/><br/>
        用户2编号:<input type="text" name="users[1].id"/><br/>
        用户2名称:<input type="text" name="users[1].name"/><br/>
        用户3编号:<input type="text" name="users[2].id"/><br/>
        用户3名称:<input type="text" name="users[2].name"/><br/>
        <input type="submit" value="提交"/>
    </form>
@RequestMapping("/list")
@ResponseBody
public String list(UserList userList){
    
    
    StringBuffer str = new StringBuffer();
    for(User user:userList.getUsers()){
    
    
        str.append(user);
    }
    return str.toString();
}

处理 @ResponseBody 中文乱码,在 springmvc.xml 中配置消息转换器。

<mvc:annotation-driven>
    <!-- 消息转换器 -->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

5,Map

自定义封装类

@Data
public class UserMap {
    
    
    private Map<String,User> users;
}

业务方法

@RequestMapping("/map")
public String map(UserMap userMap){
    
    
    StringBuffer str = new StringBuffer();
    for(String key:userMap.getUsers().keySet()){
    
    
        User user = userMap.getUsers().get(key);
        str.append(user);
    }
    return str.toString();
}

JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/data/map" method="post">
        用户1编号:<input type="text" name="users['a'].id"/><br/>
        用户1名称:<input type="text" name="users['a'].name"/><br/>
        用户2编号:<input type="text" name="users['b'].id"/><br/>
        用户2名称:<input type="text" name="users['b'].name"/><br/>
        用户3编号:<input type="text" name="users['c'].id"/><br/>
        用户3名称:<input type="text" name="users['c'].name"/><br/>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

补充:controller用map传参看不出里面到底传的啥,swaggwer也识别不了接口的含义,就不能直接展示接口说明了,但有个优点就是后期可以任意扩展内部参数,但这个有点几乎很少用到,还是尽量少用map

6,JSON

客户端发生 JSON 格式的数据,直接通过 Spring MVC 绑定到业务方法的形参中。

因为把请求jquery.js当成了请求url,而不是请求资源文件,处理 Spring MVC 无法加载静态资源,在 web.xml 中添加配置即可。

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>

JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
    <script type="text/javascript">
        $(function(){
     
     
           var user = {
     
     
               "id":1,
               "name":"张三"
           };
           $.ajax({
     
     
               url:"/data/json",
               data:JSON.stringify(user),
               type:"POST",
               contentType:"application/json;charset=UTF-8",
               dataType:"JSON",
               success:function(data){
     
     
                   alert(data.id+"---"+data.name);
               }
           })
        });
    </script>
</head>
<body>
</body>
</html>

业务方法
注意这里是@RequestBody,表示接受json格式数据;
@ResponseBody是返回json格式数据

@RequestMapping("/json")
public User json(@RequestBody User user){
    
    
    System.out.println(user);
    user.setId(6);
    user.setName("张六");
    return user;
}

Spring MVC 中的 JSON 和 JavaBean 的转换需要借助于 fastjson,pom.xml 引入相关依赖。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.32</version>
</dependency>

springmvc.xml 添加 fastjson 配置。

<mvc:annotation-driven>
    <!-- 消息转换器 -->
    <mvc:message-converters register-defaults="true">
        <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property>
        </bean>
        <!-- 配置fastjson -->
        <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter4"></bean>
    </mvc:message-converters>
</mvc:annotation-driven>

转发 重定向

1.通过ModuleAndView实现转发和重定向

设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .
页面 : {视图解析器前缀} + viewName +{视图解析器后缀}

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      id="internalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>
public class ControllerTest1 implements Controller {
    
    
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
    
    
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","ControllerTest1");
        mv.setViewName("test");
        return mv;
    }
}

2.通过ServletAPI实现转发和重定向

不需要视图解析器:
1.通过HttpServletResponse进行输出
2.通过HttpServletResponse实现重定向
3.通过HttpServletRequest实现转发

@Controller
public class ModelAndViewTest {
    
    

   @RequestMapping("/result/t1")
    public void test1(HttpServletRequest req, HttpServletResponse rsp)throws IOException,IOException {
    
    
        rsp.getWriter().println("Hello,Spring BY servlet API");
    }

    @RequestMapping("/result/t2")
    public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
    
    
       rsp.sendRedirect("/index.jsp");
    }

    @RequestMapping("/result/t3")
    public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
    
    
        req.setAttribute("msg","/result/t3");
        req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
    }
}

3.通过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: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.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--自动扫描包,让指定包下面的注解生效,由IOC容器同一管理-->
    <context:component-scan base-package="com.hh.controller"/>

    <!--让Springmvc不处理静态资源-->
    <mvc:default-servlet-handler/>

    <!--支持springmvc注解驱动-->
    <mvc:annotation-driven/>
</beans>
@Controller
public class ModelTest1 {
    
    
    @RequestMapping("/m1/t1")
    public String test(Model model){
    
    
        model.addAttribute("msg","modelTest1");
        //转发 请求前后,地址栏没有改变,转发跳转
        return "forward:/WEB-INF/jsp/test.jsp";
        //重定向 地址改变,重定向跳转
        //设置重定向不能写逻辑视图,必须写明目标资源的物理路径,如"redirect:/index.jsp"。
        return "redirect:/index.jsp";
    }
}

单文件上传

底层是使用 Apache fileupload 组件完成上传,Spring MVC 对这种方式进行了封装。

  • pom.xml
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.5</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
  • html
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/file/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="img"/>
        <input type="submit" value="上传"/>
    </form>
    <img src="${path}">
</body>
</html>

1、input 的 type 设置为 file。
2、form 的 method 设置为 post(get 请求只能将文件名传给服务器)
3、from 的 enctype 设置为 multipart-form-data(如果不设置只能将文件名传给服务器)

  • Handler
@Controller
@RequestMapping("/file")
public class FileHandler {
    
    

    @PostMapping("/upload")
    public String upload(MultipartFile img, HttpServletRequest request){
    
    
        if(img.getSize()>0){
    
    
            //获取接收到的文件的保存路径
            String path = request.getServletContext().getRealPath("file");
            //.getOriginalFilename() 获取上传的文件原名
            String name = img.getOriginalFilename();
            File file = new File(path,name);
            try {
    
    
                img.transferTo(file);// 将上传文件存到指定目录中
                //保存上传之后的文件路径
                request.setAttribute("path","/file/"+name);
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
        return "upload";
    }
}

这是比较原生的写法

@Controller
public class FileController {
    
    
    //@RequestParam("file") 将name=file控件得到的文件封装成CommonsMultipartFile 对象
    //批量上传CommonsMultipartFile则为数组即可
    @RequestMapping("/upload")
    public String fileUpload(@RequestParam("file") CommonsMultipartFile file , HttpServletRequest request) throws IOException {
    
    
        //获取文件名 : file.getOriginalFilename();
        String uploadFileName = file.getOriginalFilename();
        //如果文件名为空,直接回到首页!
        if ("".equals(uploadFileName)){
    
    
            return "redirect:/index.jsp";
        }
        System.out.println("上传文件名 : "+uploadFileName);
        //上传路径保存设置
        String path = request.getServletContext().getRealPath("/upload");
        //如果路径不存在,创建一个
        File realPath = new File(path);
        if (!realPath.exists()){
    
    
            realPath.mkdir();
        }
        System.out.println("上传文件保存地址:"+realPath);
        InputStream is = file.getInputStream(); //文件输入流
        OutputStream os = new FileOutputStream(new File(realPath,uploadFileName)); //文件输出流
        //读取写出
        int len=0;
        byte[] buffer = new byte[1024];
        while ((len=is.read(buffer))!=-1){
    
    
            os.write(buffer,0,len);
            os.flush();
        }
        os.close();
        is.close();
        return "redirect:/index.jsp";
    }
}
  • springmvc.xml
<!-- 配置上传组件 这个bean的id必须为:multipartResover,否则报400-->
<bean id="myMmultipart" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	<property name="defaultEncoding" value="utf-8"/>
    <!-- 上传文件大小上限,单位为字节(10485760=10M) -->
    <property name="maxUploadSize" value="10485760"/>
    <property name="maxInMemorySize" value="40960"/>
</bean>
  • web.xml 添加如下配置,否则客户端无法访问静态资源png
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.png</url-pattern>
</servlet-mapping>

多文件上传

pom.xml

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>

<dependency>
    <groupId>taglibs</groupId>
    <artifactId>standard</artifactId>
    <version>1.1.2</version>
</dependency>

JSP

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form action="/file/uploads" method="post" enctype="multipart/form-data">
        file1:<input type="file" name="imgs"/><br/>
        file2:<input type="file" name="imgs"/><br/>
        file3:<input type="file" name="imgs"><br/>
        <input type="submit" value="上传"/>
    </form>
    <c:forEach items="${files}" var="file" >
        <img src="${file}" width="300px">
    </c:forEach>
</body>
</html>

Handler

@PostMapping("/uploads")
public String uploads(MultipartFile[] imgs,HttpServletRequest request){
    
    
    List<String> files = new ArrayList<>();
    for (MultipartFile img:imgs){
    
    
        if(img.getSize()>0){
    
    
            //获取保存上传文件的file路径
            String path = request.getServletContext().getRealPath("file");
            //获取上传的文件名
            String name = img.getOriginalFilename();
            File file = new File(path,name);
            try {
    
    
                img.transferTo(file);
                //保存上传之后的文件路径
                files.add("/file/"+name);
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
    request.setAttribute("files",files);
    return "uploads";
}

下载

  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <a href="/file/download/1">1.png</a>
    <a href="/file/download/2">2.png</a>
    <a href="/file/download/3">3.png</a>
</body>
</html>
  • Handler
@GetMapping("/download/{name}")
public void download(@PathVariable("name") String name, HttpServletRequest request, HttpServletResponse response){
    
    
    if(name != null){
    
    
        name += ".png";
        String path = request.getServletContext().getRealPath("file");
        File file = new File(path,name);
        OutputStream outputStream = null;
        if( file.exists() ){
    
    
            response.setContentType("application/forc-download");
            response.setHeader("Content-Disposition","attachment;filename="+name);
            try {
    
    
                outputStream = response.getOutputStream();
                outputStream.write(FileUtils.readFileToByteArray(file));
                outputStream.flush();
            } catch ( IOException e)  {
    
    
                e.printStackTrace();
            } finally {
    
    
                if( outputStream != null ){
    
    
                    try {
    
    
                        outputStream.close();
                    } catch (IOException e) {
    
    
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

拦截器

只需要实现HandlerInterceptor接口即可成为拦截器
class MyInterceptor implements HandlerInterceptor

<!--关于拦截器的配置-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--/** 包括路径及其子路径-->
        <!--/admin/* 拦截的是/admin/add等等这种 , /admin/add/user不会被拦截-->
        <!--/admin/** 拦截的是/admin/下的所有-->
        <mvc:mapping path="/**"/>
        <!--bean配置的就是拦截器-->
        <bean id="loginInterceptor" class="com.kuang.interceptor.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
public class MyInterceptor implements HandlerInterceptor {
    
    
    //在请求处理的方法之前执行
    //如果返回true执行下一个拦截器,放行程序
    //如果返回false就不执行下一个拦截器,停止程序
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    
    
        System.out.println("------------处理前------------");
        return true;
    }
    //在请求处理方法执行之后执行
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    
    
        System.out.println("------------处理后------------");
    }
    //在dispatcherServlet处理后执行,做清理工作.
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
    
        System.out.println("------------清理------------");
    }
}

此时写一个Controller类,print一句"666666666666666666666666",调用请求后
------------处理前------------
666666666666666666666666
------------处理后------------
------------清理--------------

数据校验

Spring MVC 提供了两种数据校验的方式:1、基于 Validator 接口。2、使用 Annotation JSR - 303 标准进行校验。
基于 Validator 接口的方式需要自定义 Validator 验证器,每一条数据的验证规则需要开发者手动完成;
使用 Annotation JSR - 303 标准则不需要自定义验证器,通过注解的方式可以直接在实体类中添加每个属性的验证规则(推荐)

基于 Validator 接口

  • 实体类 Account
@Data
public class Account {
    
    
    private String name;
    private String password;
}
  • 自定义验证器 AccountValidator,实现 Validator 接口。
public class AccountValidator implements Validator {
    
    
    
    @Override
    public boolean supports(Class<?> aClass) {
    
     // 用来验证数据类型是否可验证,是则进行validatate方法,不是就放行不管
        return Account.class.equals(aClass);
    }

    @Override
    public void validate(Object o, Errors errors) {
    
    
        ValidationUtils.rejectIfEmpty(errors,"name",null,"姓名不能为空");
        ValidationUtils.rejectIfEmpty(errors,"password",null,"密码不能为空");
    }
    
}
  • 控制器
@Controller
@RequestMapping("/validator")
public class ValidatorHandler {
    
    

    @GetMapping("/login")
    public String login(Model model){
    
    
        model.addAttribute("account",new Account());
        return "login";
    }

    @PostMapping("/login")
    public String login(@Validated Account account, BindingResult bindingResult){
    
    
        if(bindingResult.hasErrors()){
    
    
            return "login";
        }
        return "index";
    }
}
  • springmvc.xml 配置验证器。
<bean id="accountValidator" class="com.southwind.validator.AccountValidator"></bean>
<mvc:annotation-driven validator="accountValidator"></mvc:annotation-driven>
  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="from" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form:form modelAttribute="account" action="/validator/login" method="post">
        姓名:<form:input path="name"/><from:errors path="name"></from:errors><br/>
        密码:<form:input path="password"/><from:errors path="password"></from:errors><br/>
        <input type="submit" value="登录"/>
    </form:form>
</body>
</html>

Annotation JSR - 303 标准

使用 Annotation JSR - 303 标准进行验证,需要导入支持这种标准的依赖 jar 文件,这里我们使用 Hibernate Validator。

  • pom.xml
<!-- JSR-303 -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>5.3.6.Final</version>
</dependency>

<dependency>
  <groupId>javax.validation</groupId>
  <artifactId>validation-api</artifactId>
  <version>2.0.1.Final</version>
</dependency>

<dependency>
  <groupId>org.jboss.logging</groupId>
  <artifactId>jboss-logging</artifactId>
  <version>3.3.2.Final</version>
</dependency>
  • 通过注解的方式直接在实体类中添加相关的验证规则。
@Data
public class Person {
    
    
    @NotEmpty(message = "用户名不能为空")
    private String username;
    @Size(min = 6,max = 12,message = "密码6-12位")
    private String password;
    @Email(regexp = "^[a-zA-Z0-9_.-]+@[a-zA-Z0-9-]+(\\\\.[a-zA-Z0-9-]+)*\\\\.[a-zA-Z0-9]{2,6}$",message = "请输入正确的邮箱格式")
    private String email;
    @Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\\\\\\\d{8}$",message = "请输入正确的电话")
    private String phone;
}
  • ValidatorHandler
@GetMapping("/register")
public String register(Model model){
    
    
    model.addAttribute("person",new Person());
    return "register";
}

@PostMapping("/register")
public String register(@Valid Person person, BindingResult bindingResult){
    
    
    if(bindingResult.hasErrors()){
    
    
        return "register";
    }
    return "index";
}
  • springmvc.xml
<mvc:annotation-driven />
  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <form:form modelAttribute="person" action="/validator/register2" method="post">
        用户名:<form:input path="username"></form:input><form:errors path="username"/><br/>
        密码:<form:password path="password"></form:password><form:errors path="password"/><br/>
        邮箱:<form:input path="email"></form:input><form:errors path="email"/><br/>
        电话:<form:input path="phone"></form:input><form:errors path="phone"/><br/>
        <input type="submit" value="提交"/>
    </form:form>
</body>
</html>

校验规则

@Null 被注解的元素必须为null
@NotNull 被注解的元素不能为null
@Min(value) 被注解的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注解的元素必须是一个数字,其值必须小于于等于指定的最大值
@Email 被注解的元素必须是电子邮箱地址
@Pattern 被注解的元素必须符合对应的正则表达式
@Length 被注解的元素的大小必须在指定的范围内
@NotEmpty 被注解的字符串的值必须非空

Null 和 Empty 是不同的结果,String str = null,str 是 null,String str = “”,str 不是 null,其值为空。

表单标签库

  • Handler
@GetMapping("/get")
public ModelAndView get(){
    
    
    ModelAndView modelAndView = new ModelAndView("tag");
    Student student = new Student(1L,"张三",22);
    modelAndView.addObject("student",student);
    return modelAndView;
}
  • JSP
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>学生信息</h1>
    <form:form modelAttribute="student">
        学生ID:<form:input path="id"/><br/>
        学生姓名:<form:input path="name"/><br/>
        学生年龄:<form:input path="age"/><br/>
        <input type="submit" value="提交"/>
    </form:form>
</body>
</html>

1、JSP 页面导入 Spring MVC 表单标签库,与导入 JSTL 标签库的语法非常相似,前缀 prefix 可以自定义,通常定义为 from。

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

2、将 form 表单与模型数据进行绑定,通过 modelAttribute 属性完成绑定,将 modelAttribute 的值设置为模型数据对应的 key 值。

Handeler:modelAndView.addObject("student",student);
JSP:<form:form modelAttribute="student">

3、form 表单完成绑定之后,将模型数据的值取出绑定到不同的标签中,通过设置标签的 path 属性完成,将 path 属性的值设置为模型数据对应的属性名即可。

学生ID:<form:input path="id"/><br/>
学生姓名:<form:input path="name"/><br/>
学生年龄:<form:input path="age"/><br/>

常用的表单标签

  • form
<form:form modelAttribute="student"/>

渲染的是 HTML 中的<form></from>,通过 modelAttribute 属性绑定具体的模型数据。

  • input
<form:input path="name"/>

渲染的是 HTML 中的 <input type="text"/>,from 标签绑定的是模型数据,input 标签绑定的是模型数据中的属性值,通过 path 属性可以与模型数据中的属性名对应,并且支持及联操作。

<from:input path="address.name"/>
  • password
<form:password path="password"/>

渲染的是 HTML 中的 <input type="password"/>,通过 path 属性与模型数据的属性值进行绑定,password 标签的值不会在页面显示。

  • checkbox
<form:checkbox path="hobby" value="读书"/>
student.setFlag(false);
checkbox:<form:checkbox path="flag" value="flag"></form:checkbox><br/>

渲染的是 HTML 中的 <input type="checkbox"/>,通过 path 与模型数据的属性值进行绑定,可以绑定 boolean、数组和集合。

如果绑定 boolean 值,若该变量的值为 true,则表示该复选框选中,否则表示不选中。

如果绑定数组或者集合,数组/集合中的元素等于 checkbox 的 value 值,则选中。

student.setHobby(Arrays.asList("读书","看电影","玩游戏"));
modelAndView.addObject("student",student);
爱好:<form:checkbox path="hobby" value="摄影"></form:checkbox>摄影<br/>
<form:checkbox path="hobby" value="读书"></form:checkbox>读书<br/>
<form:checkbox path="hobby" value="听音乐"></form:checkbox>听音乐<br/>
<form:checkbox path="hobby" value="看电影"></form:checkbox>看电影<br/>
<form:checkbox path="hobby" value="旅游"></form:checkbox>旅游<br/>
<form:checkbox path="hobby" value="玩游戏"></form:checkbox>玩游戏<br/>
<input type="submit" value="提交"/>
  • checkboxes
<form:checkboxes items=${student.hobby} path="selecHobby"/>

渲染的是 HTML 中的一组 <input type="checkbox"/>,是对 <form:checkbox/> 的一种简化,需要结合 items 和 path 属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的集合或数组,可以这样理解,items 为全部可选集合,path 为默认的选中集合。

student.setHobby(Arrays.asList("摄影","读书","听音乐","看电影","旅游","玩游戏"));
student.setSelectHobby(Arrays.asList("摄影","读书","听音乐"));
modelAndView.addObject("student",student);
爱好:<form:checkboxes path="selectHobby" items="${student.hobby}"/><br/>

需要注意的是 path 可以直接绑定模型数据的属性值,items 则需要通过 EL 表达式的形式从域对象中获取数据,不能直接写属性名。

  • rabiobutton
<from:radiobutton path="radioId" value="0"/>

渲染的是 HTML 中的一个 <input type="radio"/>,绑定的数据与标签的 value 值相等则为选中,否则不选中。

student.setRadioId(1);
modelAndView.addObject("student",student);
radiobutton:<form:radiobutton path="radioId" value="1"/>radiobutton<br/>
  • radiobuttons
<form:radiobuttons itmes="${student.grade}" path="selectGrade"/>

渲染的是 HTML 中的一组 <input type="radio"/>,这里需要结合 items 和 path 两个属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的值,items 为全部的可选类型,path 为默认选中的选项,用法与 <form:checkboxes/> 一致。

Map<Integer,String> gradeMap = new HashMap<>();
gradeMap.put(1,"一年级");
gradeMap.put(2,"二年级");
gradeMap.put(3,"三年级");
gradeMap.put(4,"四年级");
gradeMap.put(5,"五年级");
gradeMap.put(6,"六年级");
student.setGradeMap(gradeMap);
student.setSelectGrade(3);
modelAndView.addObject("student",student);
学生年级:<form:radiobuttons items="${student.gradeMap}" path="selectGrade"/><br/>
  • select
<form:select items="${student.citys}" path="selectCity"/>

渲染的是 HTML 中的一个 <select/> 标签,需要结合 items 和 path 两个属性来使用,items 绑定被遍历的集合或数组,path 绑定被选中的值,用法与 <from:radiobuttons/>一致。

Map<Integer,String> cityMap = new HashMap<>();
cityMap.put(1,"北京");
cityMap.put(2,"上海");
cityMap.put(3,"广州");
cityMap.put(4,"深圳");
student.setCityMap(cityMap);
student.setSelectCity(3);
modelAndView.addObject("student",student);
所在城市:<form:select items="${student.cityMap}" path="selectCity"></form:select><br/>
  • options

form:select 结合 form:options 的使用,from:select 只定义 path 属性,在 form:select 标签内部添加一个子标签 form:options ,设置 items 属性,获取被遍历的集合。

所在城市:<form:select path="selectCity">
  				<form:options items="${student.cityMap}"></form:options>
				</form:select><br/>
  • option

    form:select 结合 form:option 的使用,from:select 定义 path 属性,给每一个 form:option 设置 value 值,path 的值与哪个 value 值相等,该项默认选中。

所在城市:<form:select path="selectCity">
            <form:option value="1">杭州</form:option>
            <form:option value="2">成都</form:option>
            <form:option value="3">西安</form:option>
        </form:select><br/>
  • textarea

渲染的是 HTML 中的一个 <textarea/> ,path 绑定模型数据的属性值,作为文本输入域的默认值。

student.setIntroduce("你好,我是...");
modelAndView.addObject("student",student);
信息:<form:textarea path="introduce"/><br/>
  • errors
    处理错误信息,一般用在数据校验,该标签需要结合 Spring MVC 的验证器结合起来使用。

Restful

基础

http:127.0.0.1/item/1 查询,GET
http:127.0.0.1/item 新增,POST
http:127.0.0.1/item 更新,PUT
http:127.0.0.1/item/1 删除,DELETE
原来的 http:localhost:8080/add?id=1&name=zhangsan
Restful http://localhost:8080/add/id/name

SpringMVC支持Restful

观察这个案例中的内容,当使用 @RequestMapping标注在类上面声明了一个路径之后,方法的配置分为这样几种情况:

1)GET方式请求路径“/appointments”,将会进入 get() 方法
2)POST方式请求路径“/appointments”,将会进入 add() 方法
3)GET方式请求“/appointment/new”,将会进入 getNewForm() 方法
4)GET方式请求“/appointment/2016-12-06”,将会进入 getForDay() 方法【参考路径模版】

传统类型:http://localhost:8080/hello/index?name=zhangsan&id=10
REST:http://localhost:8080/hello/index/zhangsan/10

@RequestMapping("/rest/{name}/{id}")
public String rest(@PathVariable("name") String name,@PathVariable("id") int id){
    
    
    System.out.println(name);
    System.out.println(id);
    return "index";
}

SpringMVC通过映射可以直接在业务方法中获取Cookie的值。

    @RequestMapping("/cookieTest")
    public String getCookie(@CookieValue(value="JSESSIONID") String sessionId){
    
    
        System.out.println(sessionId);
        return "index";
    }

值得注意

以下两者等价
@RequestMapping("hello")
@RequestMapping(value="hello")

method:指定请求的method类型, GET、POST、PUT、DELETE等。

@Controller@RestController  前者的类中可以自定义方法返回的是视图名还是json对象,
通过方法前@ResponseBody注解来区分,后者的类中所有方法就只能是json了

params:指定request中必须包含某些参数值,此处必须包含name和id两个参数,并且id的值必须为10,才能调用paramsTest方法
@RequestParam:表示将url获取到的参数name赋给指定的形参str
代码如下:
    @RequestMapping(value="paramsTest",params={
    
    "name","id=10"})
    public String paramsTest( @RequestParam("name") String str,@RequestParam("id") int age ){
    
    
    	System.out.println(str);
    	System.out.println(age); // 此处 HandlerAdapter 自动完成了 string 10 转换为 int 10
        System.out.println("paramsTest");
        return "index";
    }

参数绑定:
public String paramsBind(@RequestParam("name") String name,@RequestParam("id") int id)
将URL请求的参数name和id分别赋给形参name和id,同时进行了数据类型的转换,
URL参数都是String类型的,根据形参的数据类型,将id转换为int类型

使用 JavaBean 绑定参数

SpringMVC会根据请求参数名和pojo属性名进行自动匹配,自动为该对象填充属性值。并且支持级联属性。
1.创建实体类Address,User并进行级联设置。

public class Address {
    
    

    private int id;
    private String name;
    
    public int getId() {
    
    
        return id;
    }
    public void setId(int id) {
    
    
        this.id = id;
    }
    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    @Override
    public String toString() {
    
    
        return "Address [id=" + id + ", name=" + name + "]";
    }
}
public class User {
    
    

    private int id;
    private String name;
    private Address address;

    public Address getAddress() {
    
    
        return address;
    }
    public void setAddress(Address address) {
    
    
        this.address = address;
    }
    public int getId() {
    
    
        return id;
    }
    public void setId(int id) {
    
    
        this.id = id;
    }
    public String getName() {
    
    
        return name;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    @Override
    public String toString() {
    
    
        return "User [id=" + id + ", name=" + name + ", address=" + address
                + "]";
    }

}

2.创建addUser.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>Insert title here</title>
</head>
<body>
    <form action="addUser" method="post">
        编号:<input type="text" name="id"/><br/>
        姓名:<input type="text" name="name"/><br/>
        地址:<input type="text" name="address.name"/><br/>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

3.业务方法

    @RequestMapping("/addUser")
    public String getPOJO(User user){
    
    
        System.out.println(user);
        return "index";
    }

4,通过jsp页面将表单数据以post形式提交,便会打印出user信息

中文乱码在web.xml中增加固定内容

    <filter>  
        <filter-name>encodingFilter</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>encodingFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
    </filter-mapping>

常用注解

@GetMapping、@PostMapping、@PutMapping、@DeleteMapping

@RequestMapping(path = "/new", method = RequestMethod.GET) 等同于 @GetMapping("/new")

@PostMapping@PutMapping@DeleteMapping类似

@PathVariable

http://www.example.com/users/{
    
    userId}
// 注意这个模版,看起来与普通的URL没有什么区别,但是仔细看,其路径的最后一部分是 {userId} ,
// 这样一个使用大括号括起来的字符串,这个就是一个URI模版,与这个模版对应的实际请求如下:
http://www.example.com/users/12345
// 在SpringMVC中,对于在 @RequestMapping中配置的这种请求路径,可以使用 @PathVariable注解来获取值:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable String ownerId, Model model) {
    
    
    Owner owner = ownerService.findOwner(ownerId);
    model.addAttribute("owner", owner);
    return "displayOwner";
}
// 在这个例子中的URI路径模版就是“/owners/{ownerId}”,然后在控制器方法的参数中使用 @PathVariable 注解
// 标注一个参数,Spring MVC就会在获取请求i之后自动的将{ownerId}所表示的内容设置到这个参数中了。

// 注意,这里方法的参数名称要与URI中的{var}名称一样。
// 此外,@PathVariable注解可以指定方法参数对应的URI模版中占位符的名称:
@GetMapping("/owners/{ownerId}")
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
    
    
    // implementation omitted
}

@RequestParam

若请求url参数是name,那么直接传给accept方法的参数name
若url中参数是username,与name不一致,可以使用@RequestParam("bookname") String name解决
    
@RequestMapping(value = "/query", method = RequestMethod.GET)
public String accept(@RequestParam("bookname") String name, @RequestParam("count") int count) {
    
    
    System.out.println("bookname: " + name);
    System.out.println("count: " + count);
    return "requestInput";
}

这个方法有两个参数,分别使用 @RequestParam注解进行标注了,
这种传统的URL请求,一个“?”后面跟着若干个键值对参数,可以使用 @RequestParam的方式获取参数。

@PathVariable 和 @RequestParam 区别

@RequestParam 只作为参数:http://localhost:8989/xxx/emp?pageNo=2
@PathVariable 作为参数,也作为路径的一部分:http://localhost:8989/xxx/emp/7

建议:
1、当URL指向的是某一具体业务资源(或资源列表),例如博客,用户时,使用@PathVariable
2、当URL需要对资源或者资源列表进行过滤,筛选时,用@RequestParam
    
定义了@RequestParam变量,如果URL中不带有相应的参数,就会抛出异常。但有时,参数不一定存在,这时我们可以通过定义required属性:
@RequestParam(value = "id", required = false)
当然,在参数不存在的情况下,可能希望变量有一个默认值:
@RequestParam(value = "id", required = false, defaultValue = "0")

@Controller @RestController @RequestMapping

@Controller
@RequestMapping("/appointments")
public class AppointmentsController {
    
    
    private final AppointmentBook appointmentBook;
    @Autowired
    public AppointmentsController(AppointmentBook appointmentBook) {
    
    
        this.appointmentBook = appointmentBook;
    }
    @RequestMapping(method = RequestMethod.GET)
    public Map<String, Appointment> get() {
    
    
        return appointmentBook.getAppointmentsForToday();
    }
}

猜你喜欢

转载自blog.csdn.net/vayne_xiao/article/details/112793891