SpringMVC自学笔记

git源码

SpringMVC

简介

Servlet发展史

Struts1.x–>Struts2.x–>SpringMVC

springmvc:

第一个SpringMVC程序

jar

spring-aop.jar
spring-bean.jar
spring-context.jar
spring-core.jar
spring-web.jar
spring-webmvc.jar
commons-logging.jar

报错NoClassDefFoundError:缺少jar

Servet - Springmvc
jsp ->Servlet (Springmvc)->Jsp

url

springmvc配置文件 springmvc.xml

选中常用的命名空间:beans aop context mvc

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
        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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <!-- 扫描 有注解的包 -->
    <context:component-scan base-package="org.lanqiao.handler"></context:component-scan>

    <!--配置视图解析器(InternalResourceViewResolver)  
    配置的prefix和suffix会给所有返回的地址加上前缀/views/加上后缀.jsp。
    例return "success"
    实际的结果为/views/success.jsp
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
</beans>

普通的servlet流程:

请求-url-pattern -交给对应的servlet去处理

如果现在想用springmvc,而不是普通的servlet,如何告知程序?-如何让springmvc 介入程序:

需要配置一个 Springmvc自带的servlet

通过以下配置,拦截所有请求,交给SpringMVC处理:web.xml

<servlet>
    <servlet-name>springDispatcherServlet</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>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

其中:

<url-pattern>.action</url-pattern>

/               :一切请求  ,注意不是 /*
/user           :拦截以 /user开头的请求
/user/abc.do    :只拦截该请求
.action         :只拦截 .action结尾的请求

通过

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

指定springmvc配置文件的路径,如果要省略,必须放到 默认路径:

/WEB-INF/springDispatcherServlet-servlet.xml

严格的来说是

<servlet>
    <servlet-name>springDispatcherServlet</servlet-name>
<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>

/WEB-INF/servetname的值-servlet.xml

请求

<a href="welcome">first springmvc - welcome</a>

servlet

@RequestMapping(value="welcome",method=RequestMethod.POST,params= {"name=zs","age!=23","!height"})//映射
public String  welcome() {
    return "success" ;//  views/success.jsp
}

项目中同时兼容 springMVC和Servlet

<servlet-mapping>
    <servlet-name>springDispatcherServlet</servlet-name>
    <url-pattern>.action</url-pattern>
</servlet-mapping>

@RequestMapping

映射是 去匹配@RequestMapping注解,可以和方法名、类名不一致,

//接口/类    注解   配置
@Controller
@RequestMapping(value="handler") //映射
public class SpringMVCHandler {
    @RequestMapping(value="welcome")//映射
    public String  welcome() {
        return "success" ;
    }
}

默认值的属性为value

@RequestMapping(value="welcome")
@RequestMapping("welcome")

二者相等

此时请求的路径为

handler/welcome

先去找类前面的,再去找方法前面的,默认使用了 请求转发的 跳转方式,地址栏的地址没有改变

method

通过method指定 请求方式(get post delete put)

@RequestMapping(value="welcome",method=RequestMethod.POST)//映射

params

参数值

params= {"name"}

请求参数值中一定要有name属性

params= {"name=zs"}

请求参数值中一定要有name属性,并且参数值一定是zs params= {“name=zs”,“age!=23”})

  • name:必须有name=“zs”参数

  • age!=23 :

    1. 如果有name=“age”,则age值不能是23
    2. 要么没有age

      params= {"name=zs","age!=23","!height"})
      

      请求参数中不能有height

headers

请求头

headers= {"Accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","Accept-Encoding=gzip, deflate"}

多个参数使用,隔开

ant风格的请求路径

?  单字符
*  任意个字符(0或多个)
** 任意目录
@RequestMapping(value="welcome3/a?c/test")

接受示例:

    a href="welcome3/axc/test"
    a href="welcome3/ayc/test"
@RequestMapping(value="welcome3/*/test")

接受示例:

a href="welcome3/abc/test"
a href="welcome3/xxx/test"
@RequestMapping(value="welcome3/**/test")

接受示例:

a href="welcome3/abc/xyz/abccc/test"
a href="welcome3/xxx/xxx/xxx/test"

@PathVariable

通过@PathVariable获取动态参数

传参

<a href="hander/welcome5/zs">

zs为传递的值

@RequestMapping(value="welcome5/{name}")
public String  welcome5(@PathVariable("name") String name ) {
    System.out.println(name);
    return "success" ;
}

name的值为zs

REST风格 :软件编程风格

conf.xml快速生成配置文件

ctrl+/  选择#dispatcherservlet

然后修改拦截请求,和配置文件的位置

Springmvc:

GET     :查
POST    :增
DELETE  :删
PUT     :改

delete与put

普通浏览器 只支持get post方式 ;其他请求方式 如 delelte | put请求是通过过滤器(HiddenHttpMethodFilter)新加入的支持。

过滤器拦截delelte|put请求的条件

  1. 请求方式为post

  2. 标签有隐藏域,并且隐藏域的name为_method,value为delete或put

    <input type="hidden" name="_method" value="delete|put">
    

springmvc实现 :put|post请求方式的步骤

增加过滤器,web.xml

<!-- 增加HiddenHttpMethodFilte过滤器:目的是给普通浏览器 增加 put|delete请求方式 -->
<filter>
    <filter-name>HiddenHttpMethodFilte</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>HiddenHttpMethodFilte</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

delete

表单

<form action="handler/testRest/1234" method="post">
    <input type="hidden"  name="_method" value="DELETE"/>
    <input type="submit" value="删">
</form>

控制器


@RequestMapping(value="testRest/{id}",method=RequestMethod.DELETE)
public String  testDelete(@PathVariable("id") Integer id) {
    System.out.println("delete:删 " +id);
    return "success" ;
}

put

表单

<form action="handler/testRest/1234" method="post">
    <input type="hidden"  name="_method" value="PUT"/>
    <input type="submit" value="改">
</form>

控制器

@RequestMapping(value="testRest/{id}",method=RequestMethod.PUT)
public String  testPut(@PathVariable("id") Integer id) {
    System.out.println("put:改 "+id);
    return "success" ;
}

get

表单

<form action="handler/testRest/1234" method="get">
    <input type="submit" value="查">
</form>

控制器

@RequestMapping(value="testRest/{id}",method=RequestMethod.GET)
public String  testGet(@PathVariable("id") Integer id) {
    System.out.println("get:查 " +id);
    return "success" ;
}

post

表单

<form action="handler/testRest/1234" method="post">
    <input type="submit" value="增">
</form>

控制器

@RequestMapping(value="testRest/{id}",method=RequestMethod.POST)
public String  testPost(@PathVariable("id") Integer id) {
    System.out.println("post:增 " +id);
    return "success" ;
}

通过method=RequestMethod.DELETE匹配具体的请求方式

此外,可以发现 ,当映射名相同时即@RequestMapping(value="testRest)相同,可以通过method处理不同的请求。

处理delete|put的源码分析

过滤器中 处理put|delete请求的部分源码:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {
    HttpServletRequest requestToUse = request;
    if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
        String paramValue = request.getParameter(this.methodParam);
        if (StringUtils.hasLength(paramValue)) {
            requestToUse = new HttpMethodRequestWrapper(request, paramValue);
        }
    }
    filterChain.doFilter(requestToUse, response);
}

原始请求:request,改请求默认只支持get\ post \ header]

但是如果 是”POST” 并且有隐藏域

<input type="hidden"  name="_method" value="DELETE"/>

则,过滤器 将原始的请求 request加入新的请求方式DELETE,并将原始请求 转为 requestToUse 请求(request+Delete请求)

最后将requestToUse 放入 请求链中, 后续再事情request时 实际就使用改造后的 requestToUse

普通传值

表单

<form action="handler/testParam" method="get">
    name:<input name="uname" type="text" />
    <input type="submit" value="查">
</form>

控制器

@RequestMapping(value="testParam")
public String  testParam(@RequestParam("uname") String name) {
    String name = request.getParameter("uname");
    System.out.println(name);
    return "success" ;
}

前台传多个参数值,后台可以接收其中几个值,但是不可以接收多的值,即后台接收的的值,前台一定要发送;前台发送的值,后台可以不接收

@RequestParam("uname") String name,@RequestParam(value="uage",required=false,defaultValue="23")

设置默认值,不传的时候为23

@RequestParam("uname")

接受前台传递的值,等价于

request.getParameter("uname");
required=false:该属性 不是必须的。
defaultValue="23":默认值23

获取请求头信息 @RequestHeader

<a href="handler/testRequestHeader">testRequestHeader</a>
public String  testRequestHeader(@RequestHeader("Accept-Language") String al ) {}

通过@RequestHeader("Accept-Language") String al 获取请求头中的Accept-Language值,并将值保存再al变量中

通过mvc获取cookie值(JSESSIONID)

@CookieValue (前置知识: 服务端在接受客户端第一次请求时,会给该客户端分配一个session (该session包含一个sessionId)),并且服务端会在第一次响应客户端时 ,将该sessionId赋值给JSESSIONID 并传递给客户端的cookie中

<a href="handler/testCookieValue">testCookieValue</a><br/>
@RequestMapping(value="testRequestHeader")
public String  testRequestHeader(@RequestHeader("Accept-Language")  String al  ) {
    System.out.println( al);
    return "success" ;
}

小结:

SpringMVC处理各种参数的流程/逻辑:

请求: 前端发请求a-> @RequestMappting(“a”)

处理请求中的参数xyz:

@RequestMappting("a") 
public String  aa(@Xxx注解("xyz")  xyz)
{
}

使用对象(实体类Student)接受请求参数

public class Student {
    private int id;
    private String name;
    private 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;
    }
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }

}
public class Address {
    private String homeAddress ; 
    private String schoolAddress ;
    public String getHomeAddress() {
        return homeAddress;
    }
    public void setHomeAddress(String homeAddress) {
        this.homeAddress = homeAddress;
    }
    public String getSchoolAddress() {
        return schoolAddress;
    }
    public void setSchoolAddress(String schoolAddress) {
        this.schoolAddress = schoolAddress;
    }
}
<form action="handler/testObjectProperties" method="post">
    id:<input name="id" type="text" />
    name:<input name="name" type="text" />
    家庭地址:<input name="address.homeAddress" type="text" />
    学校地址:<input name="address.schoolAddress" type="text" />
    <input type="submit" value="查">
</form>
@RequestMapping(value="testObjectProperties")
public String  testObjectProperties(Student student) {//student属性 必须 和 form表单中的属性Name值一致(支持级联属性)
/* 以前的方式
    String name = 	request.getParameter("name");
    int age= Integer.parseInt(request.getParameter("age")s)	;
    String haddrss = 	request.getParameter("homeaddress");
    String saddress = 	request.getParameter("schooladdress");
    Address address = new Address();
    address.setHomeAddress(haddrss);
    address.setSchoolAddress(saddress);
    Student student = new Student();
    student.setName(name);
     student.setAddress(address);
*/	
    System.out.println(student.getId()+","+student.getName()+","+student.getAddress().getHomeAddress()+","+student.getAddress().getSchoolAddress());
    return "success" ;
}

@InitBinder注解

参考博客

实现参数绑定

<form action="/testBean" method="post">
    name: <input type="text" name="u.name"> <br>
    age: <input type="text" name="u.age"> <br>
    name: <input type="text" name="s.name"> <br>
    age: <input type="text" name="s.age"> <br>
    <input type="submit">
</form>
@InitBinder("user")
    public void init1(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("u.");
    }
    @InitBinder("stu")
    public void init2(WebDataBinder binder) {
        binder.setFieldDefaultPrefix("s.");
    }
    @RequestMapping("/testBean")
    public ModelAndView testBean(User user, @ModelAttribute("stu") Student stu) {
        System.out.println(stu);
        System.out.println(user);
        String viewName = "success";
        ModelAndView modelAndView = new ModelAndView(viewName);
        modelAndView.addObject("user", user);
        modelAndView.addObject("student", stu);
        return modelAndView;
    }
}

@InitBinder(“user”)括号内的参数为类的首字母小写(默认命名规则),也可以用@ModelAttribute(“stu”)做限定.

在SpringMVC中使用原生态的Servlet API

HttpServletRequest ,HttpServletResponse

<a href="handler/testServletAPI">testServletAPI</a>
@RequestMapping(value="testServletAPI")
public String testServletAPI(HttpServletRequest  request,HttpServletResponse response) {
//	request.getParameter("uname") ;
    System.out.println(request);
    return "success" ;
}

直接将 servlet-api中的类、接口等 写在springMVC所映射的方法参数中即可:

处理模型数据

如果跳转时需要带数据:V、M,则可以使用以下方式:

ModelAndView、ModelMap  、Map、Model   -数据放在了request作用域 
@SessionAttributes、@ModelAttribute

ModelAndView

<a href="handler/testModelAndView">testModelAndView</a>
@RequestMapping(value="testModelAndView")
public ModelAndView testModelAndView() {
    //ModelAndView:既有数据,又有视图
    //ModelAndView:Model -M     View-V
    ModelAndView mv = new ModelAndView("success");//view:  views/success.jsp,仍然会加前缀和后缀

    Student student = new Student() ;
    student.setId(2);
    student.setName("zs");
    mv.addObject("student", student);//相当于request.setAttribute("student", student);
    return mv;
}

取值

${requestScope.student.id } -${requestScope.student.name } <br/>

ModelMap

<a href="handler/testModelMap">testModelMap</a>
@RequestMapping(value="testModelMap")
public String testModelMap(ModelMap mm) {
    Student student = new Student() ;
    student.setId(2);
    student.setName("zs");
    mm.put("student2", student);//request域
    return "success";  //view
}
${requestScope.student2.id } -${requestScope.student2.name }  <br/>

Map

<a href="handler/testMap">testMap</a>
@RequestMapping(value="testMap")
public String testMap(Map<String,Object> m) {
    Student student = new Student() ;
    student.setId(2);
    student.setName("zs");
    m.put("student3", student);//request域
    return "success";
}
${requestScope.student3.id } -${requestScope.student3.name }

Model

<a href="handler/testModel">testModel
@RequestMapping(value="testModel")
public String testModel(Model model) {
    Student student = new Student() ;
    student.setId(2);
    student.setName("zs");
    model.addAttribute("student4",student);//request域
    return "success";
}
${requestScope.student4.id } -${requestScope.student4.name }

会将x对象 放入request域中

如何将上述数据放入session中

加上注解

@SessionAttributes(..)

在Handlee类前加注解

@SessionAttributes(value="student4") 

如果要在request中存放student4对象,则同时将该对象 放入session域中

@SessionAttributes(types= {Student.class,Address.class})  

如果要在request中存放Student类型的对象,则同时将该类型对象放入session域中

@ModelAttribute

  1. 经常在 更新时使用
  2. 在不改变原有代码的基础之上,插入一个新方法。

    <form action="handler/testModelAttribute" method="post">
    编号:<input name="id" type="hidden" value="31" />
    姓名:<input name="name" type="text" />
    <input type="submit" value="修改">
    </form>
    
    @ModelAttribute//在任何一次请求前,都会先执行@ModelAttribute修饰的方法
    //@ModelAttribute  在请求 该类的各个方法前 均被执行的设计是基于一个思想:一个控制器 只做一个功能
    public void queryStudentById(Map<String,Object> map) {
    //StuentService stuService = new StudentServiceImpl();
    //Student student = stuService.queryStudentById(31);
    //模拟调用三层查询数据库的操作
    Student student = new Student();
    student.setId(31);
    student.setName("zs");
    student.setAge(23);
    //map.put("student", student) ;//约定:map的key 就是方法参数 类型的首字母小写
    map.put("stu", student) ;//约定:map的key 就是方法参数 类型的首字母小写
    }
    
    //修改:Zs-ls
    @RequestMapping(value="testModelAttribute")
    public String testModelAttribute(@ModelAttribute("stu")Student student) {
    student.setName(student.getName());//将名字修改为ls
    System.out.println(student.getId()+","+student.getName()+","+student.getAge());
    return "success";
    }
    

通过@ModelAttribute修饰的方法 ,会在每次请求前先执行; 并且该方法的参数map.put()可以将 对象 放入 即将查询的参数中;

必须满足的约定: map.put(k,v) 其中的k 必须是即将查询的方法参数的首字母小写

 //map.put("student", student) ;

public String testModelAttribute(Student student) 

如果不一致,需要通过@ModelAttribute声明。

map.put("stu", student);
public String testModelAttribute(@ModelAttribute("stu")Student student) {

一个Servlet 对应一个功能:

增删改查 对应于 4个Servlet

@ModelAttribute会在 该类的每个方法执行前 均被执行一次,因为使用时需要注意。

视图、视图解析器

流程

Controller返回值–>ModeAndView–>ViewResolver(视图解析器)–>视图view(渲染)(Jsp/PDF/Excel)

视图的顶级接口:

View

视图解析器顶级接口:

ViewResolver

常见的视图和解析器:

InternalResourceView、InternalResourceViewResolver

View实现类简介

public class JstlView extends InternalResourceView:

springMVC解析jsp时 会默认使用InternalResourceView,如果发现Jsp中包含了jstl语言相关的内容,则自动转为JstlView。

JstlView: 可以解析jstl\实现国际化操作

国际化: 针对不同地区、不同国家 ,进行不同的显示

中国:           欢迎
美国:          welcome  

常见的资源文件命名

具体实现国际化步骤:

  1. 创建资源文件

    基名_语言_地区.properties
    基名_语言.properties
    

    中国

    i18n_zh_CH.properties

    resource.welcome=你好
    resource.exist=退出
    

    i18n_en_US.properties

    resource.welcome=WELCOME
    resource.exist=EXIST
    

    i18n.properties

    xxx
    

    系统会先查询i18n_zh_CH.properties文件中的内容,如果其他文件中没有设置一些属性的值,则在i18n.properties中寻找

    文件中存放的是ASCLL值,如果输入时,没有进行转化,可以使用jdk中的工具:

    cd到jdk安装位置/bin
    native2ascii.exe回车
    输入要转化的值  回车
    

    然后将转换后的值放入文件中

    i18n:internationalization的首末字符i和n,18为中间的字符数

  2. 配置springmvc.xml,加载资源文件

    <!-- 加载国际化资源文件 -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basename" value="i18n"></property>
    </bean>
    

    ResourceBundleMessageSource会在springmvc响应程序时 介入(解析国际化资源文件)

    springmvc在启动时,会自动查找一个id=“messageSource”的bean,如果有 则自动加载

  3. 通过jstl使用国际化

    jar

    jstl.jar  standar.jar
    

    导入命名空间success.jsp

    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="fmt" %>  
    

    使用

    <fmt:message key="resources.welcome"></fmt:message>
    <fmt:message key="resources.exit"></fmt:message>
    

    ResourceBundleMessageSource:在MVC响应时介入,因此不能直接访问success.jsp,需要响应请求后访问

    <a href="handler/testI18n">testI18n</a>
    
    @RequestMapping(value="testI18n")
    public String testI18n() {
        return "success";
    }
    

InternalResourceViewResolver其他功能:

<mvc:view-controller ...>

index.jsp -> Controller(@RequsetMapping("a")) ->succes.jsp

要用SpringMVC实现:index.jsp -> succes.jsp :

<a href="handler/testMvcViewController">testMvcViewController</a>

配置,springMVC.xml

<!--view-name会被视图解析器 加上前缀、后缀  -->
<mvc:view-controller path="handler/testMvcViewController" view-name="success"/>

以上注解 ,会让所有的请求 转入<mvc:..>标签中匹配映射地址,而会忽略调@RequsetMapping();(controller)

如果想让 @RequsetMapping(“a”) 和<mvc:..>共存,则需要加入一个注解:

<!--此配置是SpringMVC的基础配置,很功能都需要通过该注解来协调  -->
<mvc:annotation-driven></mvc:annotation-driven>

指定请求方式

指定跳转方式: 请求转发:地址栏不改变

return "forward:/views/success.jsp";    

重定向:地址栏改变 return “redirect:/views/success.jsp”;

forward: redirect: ,需要注意 此种方式,不会被视图解析器加上前缀(/views)、后缀(.jsp)

处理静态资源:

html css js  图片 视频

动态(百度:天气 ):可以与用户交互、因为时间/地点的不同 而结果不同的内容

在SpringMVC中,如果直接访问静态资源:404 。

原因:之前将所有的请求 通过通配符/拦截,进而交给 SPringMVC的入口DispatcherServlet去处理:找该请求映射对应的 @requestMapping

http://localhost:8888/SpringMVCProject/img.png

@RequsetMapping("img.png")
return sucess

解决:如果是 需要mvc处理的,则交给@RequsetMapping(“img.png”)处理;如果不需要springmvc处理,则使用 tomcat默认的Servlet去处理。

tomcat默认的Servlet去处理:如果有 对应的请求拦截,则交给相应的Servlet去处理;如果没有对应的servlet,则直接访问。

tomcat默认的Servlet在哪里?

在tomcat配置文件\conf\web.xml中

解决静态资源方案:如果有springmvc对应的@requestMapping则交给spring处理;如果没有对应@requestMapping,则交给服务器tomcat默认的servlet去处理 :

实现方法,只需要增加2个注解即可 springmvc.xml:

<!-- 该注解 会让 springmvc: 接收一个请求,并且该请求 没有对应的@requestmapping时,将该请求 交给服务器默认的servlet去处理(直接访问)  -->
<mvc:default-servlet-handler></mvc:default-servlet-handler>

<mvc:annotation-driven></mvc:annotation-driven>

总结:要让springmvc访问静态资源,只需要加入以下2个注解:

<mvc:default-servlet-handler></mvc:default-servlet-handler>
<mvc:annotation-driven></mvc:annotation-driven>

类型转换

Spring自带一些 常见的类型转换器

public String  testDelete(@PathVariable("id") String id) 

既可以接受int类型数据id 也可以接受String类型的id

可以自定义类型转换器

  1. 编写 自定义类型转器的类 (实现Converter接口)

    //将字符串变成Student
    public class MyConverter  implements Converter<String,Student>{
        @Override
        public Student convert(String source) {//source:2-zs-23
            //source接受前端传来的String:2-zs-23
            String[] studentStrArr = source.split("-") ;
            Student student = new Student();
            student.setId(  Integer.parseInt(  studentStrArr[0]) );
            student.setName(studentStrArr[1]);
            student.setAge(Integer.parseInt(studentStrArr[2] ));
            return student;
        }
    }
    
  2. 配置:将MyConverter加入到springmvc中

    1. 将 自定义转换器 纳入SpringIOC容器

      <!-- 1将 自定义转换器 纳入SpringIOC容器 -->
      <bean  id="myConverter" class="org.lanqiao.converter.MyConverter"></bean>
      
    2. 将myConverter再纳入 SpringMVC提供的转换器Bean

      <!-- 2将myConverter再纳入 SpringMVC提供的转换器Bean -->
      <bean id="conversionService"  class="org.springframework.context.support.ConversionServiceFactoryBean">
          <property name="converters">
              <set>
                  <ref bean="myConverter"/>
              </set>
          </property>
      </bean>
      
    3. 将conversionService注册到annotation-driven中

      <!-- 3将conversionService注册到annotation-driven中 -->
      <!--此配置是SpringMVC的基础配置,很功能都需要通过该注解来协调  -->
      <mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
      

测试转换器:

<form action="handler/testConverter" method="post">
    学生信息:<input name="studentInfo" type="text"  /><!-- 2-zs-23  -->
    <input type="submit" value="转换">
</form>
@RequestMapping(value="testConverter")
public String testConverter(@RequestParam("studentInfo")  Student student) {// 前端:2-zs-23 
    System.out.println(student.getId()+","+student.getName()+","+student.getAge());
    return "success";
}

其中@RequestParam("studentInfo")是触发转换器的桥梁: @RequestParam("studentInfo")接收的数据 是前端传递过来的:2-zs-23 ,但是需要将该数据赋值给修饰的目的对象Student;因此SPringMVC可以发现 接收的数据 和目标数据不一致,并且 这两种数据分别是 StringStudent,正好符合public Student convert(String source)转换器。因此将类型进行转换

数据格式化

主要用于对数字和日期的转换

SimpleDateForamt sdf = new SimpleDateFormat("yyyy-MM-dd  hh:mm:ss");

SPringMVC提供了很多注解,方便我们数据格式化

    @DateTimeFormat(pattern="yyyy-MM-dd")
    @NumberFormat(parttern="###,#")  

实现步骤: 1. 配置,springMVC.xml xml <!-- 配置 数据格式化 注解 所依赖的bean --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> </bean> 2. 通过注解使用

Student.java
```java
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday ;//  2018-12-13
@NumberFormat(pattern="###,#") 
private int id;
```
格式化:前台传递来的数据,将前台传递来到数据 固定为yyyy-MM-dd

当输入格式不正确时会报错

测试

<form action="handler/testDateTimeFormat" method="post">
    编号:<input name="id" type="text" value="31" />
    姓名:<input name="name" type="text" />
    出生日期:<input name="birthday" type="text" />
    <input type="submit" value="修改">
</form>
@RequestMapping(value="testDateTimeFormat")
//如果Student格式化出错,会将错误信息 传入result中,这样控制台会显示错误信息,但是前端不显示了,类似异常处理,将异常进行了处理
public String testDateTimeFormat(Student student , BindingResult result) {
    System.out.println(student.getId()+","+student.getName()+","+student.getBirthday());
    if(result.getErrorCount() >0) {
        for(FieldError error:  result.getFieldErrors() ) {
            System.out.println(error.getDefaultMessage());
        }
    }
    return "success";
}

在配置时发现格式化和类型转换所用到的类为

FormattingConversionServiceFactoryBean
ConversionServiceFactoryBean

因此可以将类型转化在格式化中配置,即FormattingConversionServiceFactoryBean:既可以实现格式化、又可以实现类型转换

<!--
    <bean id="conversionService"  class="org.springframework.context.support.ConversionServiceFactoryBean">
        <property name="converters">
            <set>
                <ref bean="myConverter"/>
            </set>
        </property>
    </bean>
-->

整合到

<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <ref bean="myConverter"/>
        </set>
    </property>
</bean>	

错误消息:

上面我们可知,发生异常时在控制台打印后就不在页面打印了,此时想要两处都提示:

思路:将异常信息传到前端页面中即可

@RequestMapping(value="testDateTimeFormat")//如果Student格式化出错,会将错误信息 传入result中
public String testDateTimeFormat(@Valid Student student, BindingResult result ,Map<String,Object> map) {
    
//System.out.println(student.getId()+","+student.getName()+","+student.getBirthday());
    if(result.getErrorCount() >0) {
        for(FieldError error:  result.getFieldErrors() ) {
            System.out.println(error.getDefaultMessage());
            map.put("errors", result.getFieldErrors()  ) ;//将错误信息传入requset域中的errors中
//					result.getFieldErrors().get(0).getDefaultMessage()
        }
    }
    return "success";
}
<c:forEach  items="${requestScope.errors}" var="error">
        ${error.getDefaultMessage()}<br/>
</c:forEach>

取值时使用了jstl表达式,因此需要配置

jar包

jstl.jar    standaed.jar

引入jstl标签库xx.jsp

 <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>  
public String testDateTimeFormat(Student student, BindingResult result ,Map<String,Object> map) {}

需要验证的数据是 Student中的birthday, SPringMVC要求 如果校验失败 则将错误信息 自动放入 该对象之后紧挨着的 BindingResult中。即Student student, BindingResult result之间 不能有其他参数。

如果要将控制台的错误消息 传到jsp中显示,则可以将 错误消息对象放入request域中,然后 在jsp中 从request中获取。

数据校验

JSR303  
Hibernate Validator 

JSR303注解

Hibernate Validator 是JSR 303的扩展。Hibernate Validator 提供了 JSR 303中所有内置的注解,以及自身扩展的4个注解

Hibernate-Validator扩展注解

使用Hibernate Validator步骤:

jar

(注意各个jar之间可能存在版本不兼容)

hibernate-validator-5.0.0.CR2.jar   classmate-0.8.0.jar     jboss-logging-3.1.1.GA.jar
validation-api-1.1.0.CR1.jar    hibernate-validator-annotation-processor-5.0.0.CR2.jar

配置springMVC.xml

<mvc:annotation-driven ></mvc:annotation-driven>

此时mvc:annotation-driven的作用:要实现Hibernate Validator/JSR303 校验(或者其他各种校验),必须实现SpringMVC提供的一个接口:ValidatorFactory

springMVC已经帮我们写了一个ValidatorFactory的实现类LocalValidatorFactoryBean

<mvc:annotation-driven ></mvc:annotation-driven>会在springmvc容器中 自动加载一个LocalValidatorFactoryBean类,因此可以直接实现数据校验。

直接使用注解

  1. 在属性前加注解

    public class Student {
        @Past//当前时间以前
        private Date birthday ;
    }
    
  2. 在校验的Controller中 ,给校验的对象前增加 @Valid

    public String testDateTimeFormat(@Valid Student student, BindingResult result ,Map<String,Object> map) {}
    

Ajax请求SpringMVC,并且返回JSON格式的数据

jar

jackson-annotations-2.8.9.jar
jackson-core-2.8.9.jar
jackson-databind-2.8.9.jar

ajax请求

<script type="text/javascript" src="js/jquery-1.8.3.js"></script>
<script type="text/javascript">
    $(document).ready(function(){
        $("#testJson").click(function(){
            //通过ajax请求springmvc
            $.post(
                "handler/testJson",//服务器地址
                //{"name":"zs","age":23}
                function(result){//服务端处理完毕后的回调函数 List<Student> students, 加上@ResponseBody后, students实质是一个json数组的格式
                    for(var i=0;i<result.length ;i++){
                        alert(result[i].id +"-"+result[i].name +"-"+result[i].age);
                    }
                }
            );
        });
    });
@ResponseBody//告诉SpringMVC,此时的返回 不是一个 View页面,而是一个 ajax调用的返回值(Json数组)
@RequestMapping(value="testJson")
public List<Student> testJson() {
    //Controller-Service-dao
    //StudentService studentService = new StudentServiceImp();
//List<Student> students =  studentService.qeuryAllStudent();
    //模拟调用service的查询操作
    Student stu1 = new Student(1,"zs",23);
    Student stu2 = new Student(2,"ls",24);
    Student stu3 = new Student(3,"ww",25);
    List<Student> students = new ArrayList<>();
    students.add(stu1) ;
    students.add(stu2) ;
    students.add(stu3) ;
    return students;
}

SpringMVC实现文件上传

和Servlet方式的本质一样,都是通过

commons-fileupload.jar和commons-io.jar

SpringMVC可以简化文件上传的代码,但是必须满足条件:实现MultipartResolver接口 ;而该接口的实现类SpringMVC也已经提供了CommonsMultipartResolver

具体步骤:(直接使用CommonsMultipartResolver实现上传) 1. jar包

    commons-fileupload.jar、commons-io.jar
  1. 配置CommonsMultipartResolver

    将其加入SpringIOC容器

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            <property name="defaultEncoding" value="UTF-8"></property>
            <!-- 上传单个文件的最大值,单位Byte;如果-1,表示无限制 -->
            <property name="maxUploadSize"  value="104857600"></property>
    </bean>
    

    配置CommonsMultipartResolver,用于实现文件上传 将其加入SpringIOC容器.

    springIoc容器在初始化时,会自动寻找一个Id="multipartResolver"的bean,并将其加入Ioc容器

  2. 处理方法

    <form action="handler/testUpload" method="post"  enctype="multipart/form-data">
        <input type="file" name="file" />
        描述:<input name="desc" type="text" />
        <input type="submit" value="上传">
    </form>
    
    //文件上传处理方法
    @RequestMapping(value="testUpload") //abc.png
    public String testUpload(@RequestParam("desc") String desc  , @RequestParam("file") MultipartFile file  ) throws IOException {
        System.out.println("文件描述信息:"+desc);
        //jsp中上传的文件:file
        InputStream input = file.getInputStream() ;//IO
        String fileName = file.getOriginalFilename() ;
        OutputStream out = new FileOutputStream("d:\\"+fileName) ;
        byte[] bs = new byte[1024];
        int len = -1;
        while(( len = input.read(bs)) !=-1 ) {
            out.write(bs, 0, len);
        }
        out.close();
        input.close();
        //将file上传到服务器中的 某一个硬盘文件中
        System.out.println("上传成功!");
        return "success";
    }
    

    框架: 将原来自己写的1000行代码,变成:框架帮你写900行,剩下100行自己写

控制器:handler servlet controller action

拦截器

拦截器

拦截器的原理和过滤器相同。

SpringMVC:要想实现拦截器,必须实现一个接口

HandlerInterceptor
  1. 编写拦截器

    implements HandlerInterceptor

    拦截器一

    public class MyInterceptor  implements HandlerInterceptor{
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println("拦截请求");  //
            return true;//true:拦截操作之后,放行 ;false:拦截之后不放行,请求终止;
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("拦截响应");
        }
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("视图(jsp)被渲染完毕");
        }
    }
    

    拦截器二

    public class MySecondInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println("第二个拦截器,拦截请求...22222");
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println("第二个拦截器,拦截响应...22222");
        }
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println("第二个拦截器,afterCompletion");
        }
    }
    
  2. 配置

    将自己写的拦截器 配置到springmvc中(spring)

    <!-- 将自己写的拦截器 配置到springmvc中(spring);默认拦截全部请求 -->
    <mvc:interceptors>
        <!--拦截一切请求时放在这
            <bean  class="org.lanqiao.interceptor.MySecondInterceptor"></bean>
        -->
    <!-- 配置具体的拦截路径,二者取交集 -->
        <mvc:interceptor>
            <!-- 指定拦截的路径,基于ant风格 -->
            <mvc:mapping path="/**"/>  
            <!-- 指定拦不截的路径 -->
            <mvc:exclude-mapping path="/handler/testUpload"/> 
            <bean  class="org.lanqiao.interceptor.MyInterceptor"></bean>
        </mvc:interceptor>
            <!-- 配置具体的拦截路径 -->
        <mvc:interceptor>
            <!-- 指定拦截的路径,基于ant风格 -->
            <mvc:mapping path="/**"/>  
            <!-- 指定拦不截的路径 -->
            <mvc:exclude-mapping path="/handler/testUpload"/> 
            <bean  class="org.lanqiao.interceptor.MySecondInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
    

拦截器1拦截请求- 拦截器2拦截请求 - 请求方法 - 拦截器2处理相应-拦截器1处理相应- 只会被 最后一个拦截器的afterCompletion()拦截

如果有多个拦截器,则每个拦截器的preHandle postHandle 都会在相应时机各被触发一次;但是afterCompletion, 只会执行最后一个拦截器的该方法。

异常处理

SpringMVC实现异常处理的顶级接口

HandlerExceptionResolver

该接口的每个实现类 都是异常的一种处理方式:

ExceptionHandlerExceptionResolver

主要提供了@ExceptionHandler注解,并通过该注解处理异常

controller中出现异常

@RequestMapping("testExceptionHandler")
public String testExceptionHandler() {
    //try {
        System.out.println( 1/0 );//
    //}catch(ArithmeticException e) e
    //}catch(Exception e) e
return "success" ;
}

controller中定义捕获异常的方法

//该方法 可以捕获本类中  抛出的ArithmeticException异常,并返回error页面,将错误信息传递给error页面
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handlerArithmeticException(ArithmeticException e) {
    ModelAndView mv = new ModelAndView("error");
    System.out.println(e +"============");
    mv.addObject("er", e) ;
    return  mv;
}

error获取异常信息

${requesrScope.e}

数组越界异常

@RequestMapping("testExceptionHandler2")
public String testExceptionHandler2() {
    int[] nums = new int[2];
    System.out.println(nums[2]);//ArrayIndexOutOfBoundsException
    return "success" ;
}
//该方法 可以捕获本类中  抛出的ArithmeticException异常
@ExceptionHandler({ArithmeticException.class,ArrayIndexOutOfBoundsException.class})
public ModelAndView handlerArithmeticException(Exception e) {
    ModelAndView mv = new ModelAndView("error");
    System.out.println(e +"============");
    mv.addObject("er", e) ;
    return  mv;
}

@ExceptionHandler标识的方法的参数 必须在异常类型(Throwable或其子类) ,不能包含其他类型的参数

异常处理路径:最短优先

如果有方法抛出一个ArithmeticException异常,而该类中 有2个对应的异常处理方法:

@ExceptionHandler({Exception.class  })
public ModelAndView handlerArithmeticException2(Exception e) {}

@ExceptionHandler({ArithmeticException.class  })
public ModelAndView handlerArithmeticException1(Exception e) {}

则优先级: 最短优先。

@ExceptionHandler默认只能捕获 当前类中的异常方法。

如果发生异常的方法 和处理异常的方法 不在同一个类中:@ControllerAdvice

//@ControllerAdvice
public class MyExceptionHandler {//不是控制器,仅仅是 用于处理异常的类

将处理异常的方法放到该类中

@ExceptionHandler({Exception.class  })
public ModelAndView handlerArithmeticException2(Exception e) {
    ModelAndView mv = new ModelAndView("error");
    System.out.println(e +"============"+"该@ControllerAdvice中的异常处理方法,可以处理任何类中的异常");
    mv.addObject("er", e) ;
    return  mv;
    }
}

总结:如果一个方法用于处理异常,并且只处理当前类中的异常:@ExceptionHandler

如果一个方法用于处理异常,并且处理所有类中的异常: 类前加@ControllerAdvice、 处理异常的方法前加@ExceptionHandler

ResponseStatusExceptionResolver

自定义异常显示页面@ResponseStatus

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="数组越界222!!!")
public class MyArrayIndexOutofBoundsException extends Exception {//自定义异常
}
@RequestMapping("testMyException")
public String testMyException(@RequestParam("i") Integer i) throws MyArrayIndexOutofBoundsException {
    if(i == 3) {
        throw new MyArrayIndexOutofBoundsException();//抛出异常
    }
    return "success" ;
}

@ResponseStatus也可以标志在方法前:

@RequestMapping("testMyException2")
public String testMyException2(@RequestParam("i") Integer i) {
    if(i == 3) {
        return "redirect:testResponseStatus" ;//跳转到某一个 异常处理方法里
    }
    return "success" ;
}
@ResponseStatus(value=HttpStatus.CONFLICT  ,reason="测试。。。")
@RequestMapping("testResponseStatus")
public String testResponseStatus() {
    return "success" ;
}

DefaultHandlerExceptionResolver

SPringMVC在一些常见异常的基础上(300 500 405),新增了一些异常,例如:

* @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
* @see #handleNoSuchRequestHandlingMethod
* @see #handleHttpRequestMethodNotSupported  :如果springmvc的处理方法限制为post方式,如果实际请求为get,则会触发此异常显示的页面
* @see #handleHttpMediaTypeNotSupported
* @see #handleMissingServletRequestParameter
* @see #handleServletRequestBindingException
* @see #handleTypeMismatch
* @see #handleHttpMessageNotReadable
* @see #handleHttpMessageNotWritable
* @see #handleMethodArgumentNotValidException
* @see #handleMissingServletRequestParameter
* @see #handleMissingServletRequestPartException
* @see #handleBindException

SimpleMappingExceptionResolver

通过配置来实现异常的处理

@RequestMapping(value="testSimpleMappingExceptionResolver")
public String  testSimpleMappingExceptionResolver() {
    System.out.println(1/0);
    return "success" ;
}
<!-- SimpleMappingExceptionResolver:以配置的方式 处理异常 -->
<bean  class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 如果发生异常,异常对象会被保存在  exceptionAttribute的value值中;并且会放入request域中 ;异常变量的默认值是 exception-->
    <!--<property name="exceptionAttribute" value="exception"></property>-->
    <property name="exceptionMappings">
        <props>
            <!-- 相当于catch(ArithmeticException ex){ 跳转:error } -->
            <prop key="java.lang.ArithmeticException">
                error
            </prop>
            <prop key="java.lang.NullPointerException">
                error
            </prop>
        </props>
    </property>
</bean>
${requestScope.exception}

表单标签:

启动tomcat报错:

Caused by: java.lang.IllegalArgumentException: More than one fragment with the name [spring_web] was found. This is not legal with relative ordering. See section 8.2.2 2c of the Servlet specification for details.

需要在web.xml添加标签

<absolute-ordering/>

自定义标签el/ jstl

Spring EL : 1. 支持各种类型的请求方式(查询doGet增 加doPost、删除doDelete 、修改doPut) 2. 可以将对象和表单绑定起来;对象的属性–表单path值 一一对应

IDEA开发springmvc

SpringMVC项目:

选择Springmvc:自动下载springmvc相关的jar

处理Jar :artifacts - fix : miss all…

开发代码:

引入标签库

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

使用:


public class Persion {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

表单

<form:form>
    姓名:<form:input path="name"/><br>
    年龄:<form:input path="age"/>
    <input type="submit" value="提交">
</form:form>

控制器

@Controller
@RequestMapping("/FormDemo")
public class FormDemo {
    public String testForm(Map<String,Object> map){
        Persion per = new Persion();
        per.setAge(23);
        per.setName("zs");
        map.put("command", per);//将per放入了request域中的command中
        return "forward:/views/springForm.jsp";
    }
}

<form id="command">:SpringMVC标签会默认自动从名为”command”的对象中获取值,因此传值时将值放入command中

如果在map.put()时,名字不是command,则需要手工指定

<form:form commandName="person">
或
<form:form modelAttribute="person">

则传值时

 map.put("person", per);//将per放入了request

各种表单的提交方式

  1. 编写method属性

    <form:form action="FormDemo/testMethod" method="post">
    
    <input type="submit" value="增加">
    </form:form>
    <form:form action="FormDemo/testMethod" method="delete">
    
    <input type="submit" value="删除">
    </form:form>
    <form:form action="FormDemo/testMethod" method="put">
    
    <input type="submit" value="修改">
    </form:form>
    <form:form action="FormDemo/testMethod" method="get">
    
    <input type="submit" value="查询">
    </form:form>
    
    @RequestMapping(value = "/testMethod",method = RequestMethod.POST)
    public String testPost(){
        System.out.println("post");
        return "/index.jsp";
    }
    @RequestMapping(value = "/testMethod",method = RequestMethod.DELETE)
    public String testDelete(){
        System.out.println("delete");
        return "/index.jsp";
    }
    
    @RequestMapping(value = "/testMethod",method =RequestMethod.PUT)
    public String testPut(){
        System.out.println("put");
        return "/index.jsp";
    }
    @RequestMapping(value = "/testMethod",method = RequestMethod.GET)
    public String testGet(){
        System.out.println("get");
        return "/index.jsp";
    }
    
    
  2. 配置过滤器

    <!--增加过滤器 支持delete和put请求-->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    HiddenHttpMethodFilter会将全部请求中名为”method”的隐藏域进行Put|Delete处理

可以配置

 <filter>
    <filter-name>HiddenHttpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    <init-param>
        <param-name>methodParam</param-name>
        <param-value>myMethod</param-value>
    </init-param>
</filter>

指定要处理的隐藏的名字

<form action="FormDemo/testMethod" method="post">
    <input type="hidden" name="myMethod" value="delete">
    <input type="submit" value="修改">
</form>

如果是使用的是SpringMvc标签:

method="put|delete"

如果不是SpringMVC标签,是传统的htnl标签

method="post"
<input type="hidden" name="myMethod" value="delete|put">

checkbox

自动绑定request域中的值
  1. 通过boolean值绑定

    @RequestMapping("/testForm2")
    public String testForm2(Map<String,Object> map){
    Persion per = new Persion();
    //per.setAge(23);
    //per.setName("zs");
    per.setSex(false);
    map.put("per", per);
    return "springForm";
    //return "forward:/views/springForm.jsp";
    }
    
    <form:form commandName="per" action="textForm2">
    <for:checkbox path="sex"/>
    <input type="submit" value="提交">
    </form:form>
    

绑定集合(有值的打上勾)

@RequestMapping("/testForm3")
public String testForm3(Map<String,Object> map){
    Persion per = new Persion();
    List<String> hobbirs=new ArrayList<>();
    hobbirs.add("football");
    hobbirs.add("basketball");
    per.setHobbies(hobbirs);
    map.put("per", per);//将per放入了request域中
    return "springForm";
}
<form:form commandName="per" action="textForm3">
    <form:checkbox path="hobbies" value="football"></form:checkbox>
    <form:checkbox path="hobbies" value="basketball"></form:checkbox>
    <form:checkbox path="hobbies" value="pingpang"></form:checkbox>
    <input type="submit" value="提交">
</form:form>

(了解)嵌套对象的toString()返回值

@RequestMapping("/testForm4")
public String testForm4(Map<String,Object> map){
    Persion per = new Persion();
    Other other = new Other();
    per.setOther(other);
    map.put("per", per);//将per放入了request域中
    return "springForm";
}
<form:form commandName="per" action="textForm">
    <form:checkbox path="other" value="football"></form:checkbox>
    <form:checkbox path="other" value="basketball"></form:checkbox>
    <form:checkbox path="other" value="pingpang"></form:checkbox>
    <input type="submit" value="提交">
</form:form>
public class Other {
    @Override
    public String toString() {
        return "pingpang";
    }
}

path:选中的选项(选中)

item:所有的选项 a,b ,c d:如果是list,set,数组,则标签名默认就是选项值,如果想自定义标签名需要使用map

@RequestMapping("/testForm5")
public String testForm5(Map<String,Object> map){

    Persion per = new Persion();
    List<String> hobbies=new ArrayList<>();
    hobbies.add("football");
    hobbies.add("basketball");
    per.setHobbies(hobbies);
    map.put("per", per);//将per放入了request域中

    List<String> allHobbies=new ArrayList<>();
    allHobbies.add("football");
    allHobbies.add("basketball");
    allHobbies.add("pingpang");
    allHobbies.add("aaa");
    allHobbies.add("bbb");
    map.put("allHobbies", allHobbies);
    return "springForm";
}
<form:form commandName="per" action="textForm">
    <form:checkboxes path="hobbies" items="${allHobbies}"/>
    <input type="submit" value="提交">
</form:form>

加载所有的选项allHobbies并且选中选中的hobbies

指定标签名

可以通过 Map<Value值,标签名>

Map<String, String> allHobbiesMap = new HashMap<>();
allHobbiesMap.put("football","足球");
allHobbiesMap.put("basketball","篮球");
allHobbiesMap.put("pingpang","乒乓球");
allHobbiesMap.put("aaa","bbb");
map.put("allHobbies", allHobbiesMap);

单选按钮

@RequestMapping("/testForm6")
public String testForm6(Map<String,Object> map){
    Persion per = new Persion();
    per.setCountry("China");
    map.put("per", per);//将per放入了request域中
    return "springForm";
}
<form:form commandName="per" action="textForm">
   中国:<form:radiobutton path="country" value="China"/>
   美国:<form:radiobutton path="country" value="USE"/>
    <input type="submit" value="提交">
</form:form>

一批

@RequestMapping("/testForm7")
public String testForm7(Map<String,Object> map){
    Persion per = new Persion();
    per.setFavouriteBall("pingpang");
    map.put("per", per);//将per放入了request域中
    Map<String, String> allHobbiesMap = new HashMap<>();
    allHobbiesMap.put("football","足球");
    allHobbiesMap.put("basketball","篮球");
    allHobbiesMap.put("pingpang","乒乓球");
    allHobbiesMap.put("aaa","bbb");
    map.put("allBallMap", allHobbiesMap);
    return "springForm";
}
<form:form commandName="per" action="textForm">
    <form:radiobuttons path="favouriteBall" items="${allBallMap}"/>
    <input type="submit" value="提交">
</form:form>

属性delimiter可以指定选项与选项之间的分隔符

<form:radiobuttons path="favouriteBall" items="${allBallMap}" delimiter="、"/>

select

写法一

<form:form commandName="per" action="textForm">
    <form:select path="favouriteBall" items="${allBallMap}"/>
    <input type="submit" value="提交">
</form:form>

写法二:指定选定的值,选项是自己写的,后端不传所有的选项只传favouriteBall

<form:form commandName="per" action="textForm">
    <form:select path="favouriteBall">
        <form:option value="football">足球11</form:option>
        <form:option value="basketball">篮球11</form:option>
        <form:option value="pingpang">乒乓球11</form:option>
        <form:option value="aaa">bbb11</form:option>
    </form:select>
    <input type="submit" value="提交">
</form:form>

写法三

<form:form commandName="per" action="textForm">
    <form:select path="favouriteBall">
        <form:options items="${allBallMap}"></form:options>
       <%-- <form:option value="football">足球11</form:option>
        <form:option value="basketball">篮球11</form:option>
        <form:option value="pingpang">乒乓球11</form:option>
        <form:option value="aaa">bbb11</form:option>--%>
    </form:select>
    <input type="submit" value="提交">
</form:form>
@RequestMapping("/testForm7")
public String testForm7(Map<String,Object> map){
    Persion per = new Persion();
    per.setFavouriteBall("pingpang");
    map.put("per", per);//将per放入了request域中
    Map<String, String> allHobbiesMap = new HashMap<>();
    allHobbiesMap.put("football","足球");
    allHobbiesMap.put("basketball","篮球");
    allHobbiesMap.put("pingpang","乒乓球");
    allHobbiesMap.put("aaa","bbb");
    map.put("allBallMap", allHobbiesMap);
    return "springForm";
}

如果方式三和方式二同时存在则会优先选择方式二

如果方式一和方式二同时存在则会优先选择一

如果普通方式和springmvc方式同时存在则会使用springmvc的方式,普通的option没有匹配的功能。

git源码