目前大多数使用的是spring4.x.
springboot基于Spring Framework 5.0 on JDK 8 & 9
Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块,本学习基于Spring MVC框架:spring-framework-5.0.2.RELEASE
什么是MVC
MVC是一个架构,或者说是一个设计模式,它就是强制性使应用程序的输入,处理和输出分开。将一个应用程序分为三个部分:Model,View,Controller。
原理图 :
Model模型:负责完成业务逻辑:由JavaBean构成,在MVC的三个部件中,模型拥有最多的处理任务。例如它可能用像EJB和javabean这样的构件对象来处理数据库。由于应用于模型的代码只需写一次就可以被多个视图重用,所以减少了代码的重复性。
View视图:就是负责跟用户交互的界面。一般就是由HTML,css元素组成的界面,当然现在还有一些像js,ajax,flex一些也都属于视图层。 在视图层里没有真正的处理发生,只负责数据输出,并允许用户操纵的方式。MVC能为应用程序处理很多不同的视图。
Controller控制器:负责接收请求—>调用模型—>根据结果派发页面并经过模型处理返回相应数据。
MVC的优点
1、分工明确:使用MVC可以把数据库开发,程序业务逻辑开发,页面开发分开,每一层都具有相同的特征,方便以后的代码维护。它使程序员可以集中精力于各自的模型开发领域。
2、松耦合:视图层和业务层分离,这样就允许更改视图层代码而不用重新编译模型和控制器代码,同样,一个应用的业务流程或者业务规则的改变只需要改动MVC的模型层即可。因为模型与控制器和视图相分离,所以很容易改变应用程序的数据层和业务规则。
3、复用性高(利于各层逻辑的复用):像多个视图能够共享一个模型,不论你视图层是用flash界面或是wap界面,用一个模型就能处理他们。将数据和业务规则从表示层分开,就可以最大化从用代码。
4、有利于标准化.
MVC的缺点
1、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
2、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
3、增加理解的复杂度。由于它没有明确的定义,所以完全理解MVC并不是很容易。
常见的MVC框架
常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常见前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外一些模式如:MVP、MVVM。
什么是Spring MVC
Spring MVC概述
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。Spring MVC的特点:
- 轻量
- 高效
- 与Spring兼容性好
- 功能强大。RESTful、数据验证、格式化、绑定机制、本地化、主题等
- 简洁灵活
Spring MVC功能
Spring MVC围绕DispatcherServlet设计。 DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的Controller声明方式。官网上说Spring的Web模块提供了大量独特的功能,包括:
- 清晰的角色划分:控制器(controller)、验证器(validator)、 命令对象(command object)、表单对象(form object)、模型对象(model object)、 Servlet分发器(DispatcherServlet)、 处理器映射(handler mapping)、视图解析器(view resolver)等等。 每一个角色都可以由一个专门的对象来实现。
- 强大而直接的配置方式:将框架类和应用程序类都能作为JavaBean配置,支持跨多个context的引用,例如,在web控制器中对业务对象和验证器(validator)的引用。
- 可适配、非侵入:可以根据不同的应用场景,选择合适的控制器子类 (simple型、command型、form型、wizard型、multi-action型或者自定义),而不是从单一控制器 (比如Action/ActionForm)继承。
- 可重用的业务代码:可以使用现有的业务对象作为命令或表单对象,而不需要去扩展某个特定框架的基类。
- 可定制的绑定(binding) 和验证(validation):比如将类型不匹配作为应用级的验证错误, 这可以保存错误的值。再比如本地化的日期和数字绑定等等。在其他某些框架中,你只能使用字符串表单对象, 需要手动解析它并转换到业务对象。
- 可定制的Handler Mapping和View Resolution:Spring提供从最简单的URL映射, 到复杂的、专用的定制策略。与某些web MVC框架强制开发人员使用单一特定技术相比,Spring显得更加灵活。
- 灵活的Model转换:在Springweb框架中,使用基于Map的 键/值对来达到轻易地与各种视图技术的集成。
- 可定制的本地化和主题(Theme)解析:支持在JSP中可选择地使用Spring标签库、支持JSTL、支持Velocity(不需要额外的中间层)等等。
- 简单而强大的JSP标签库(Spring Tag Library):支持包括诸如数据绑定和主题(Theme) 之类的许多功能。它提供在标记方面的最大灵活性。
- JSP表单标签库:在Spring2.0中引入的表单标签库,使得在JSP中编写 表单更加容易。
Spring MVC 快速入门
示例:
1、可以通过module创建工程,再通过"JBLJavaToWeb"插件把Java项目改为Web项目。
2、直接maven创建一个webapp工程,再添加对应的文件夹和包。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>hellospringmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>hellospringmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置文件加载路径
1)默认情况下,读取WEB-INF下面的default-servlet.xml文件
2)可以改为加载类路径下(resources目录),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>
</init-param>
<!--init-param必须放在load-on-startup前,否则会报错:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet对象创建时间问题
1)默认情况下,第一次访问该Servlet时创建对象,默认是访问时创建,意味着在这个时间才去加载hellospringmvc-servlet.xml
2)可以改变为在项目启动时候就创建该Servlet,提高用户访问体验。
<load-on-startup>1</load-on-startup>
数值越大,对象创建优先级越低! (数值越低,越先创建)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hellospringmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注意事项:
- DispathcerServlet是Spring MVC提供的核心控制器,这是一个Servlet程序,该Servlet会接收所有请求。
- 核心控制器会读取一个hellospringmvc-servlet.xml配置,加载Spring MVC的核心配置
-
<url-pattern>
配置/,代表拦截所以请求。 -
<load-on-startup>
代表在项目启动时实例化DispathcerServlet,如果没有配置,则在第一次访问Servlet时进行实例化
hellospringmvc-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.开启mvc注解驱动-->
<!--不添加也能使用,高版本spring已经默认实现了。
在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。
-->
<!--<mvc:annotation-driven/>-->
</beans>
Controller控制器
控制器类是开发Spring MVC程序过程写的最多的类了。控制器类通常叫Controller,在里面编写接收参数,调用业务方法,返回视图页面等逻辑。
@Controller注解是为了让Spring IOC容器初始化时自动扫描到该Controller类;@RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/hello;方法返回的结果是视图的名称success,该名称不是完整页面路径,最终会经过视图解析器解析为完整页面路径并跳转。
@Controller
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Spring MVP</title>
</head>
<body>
<h1>Hello Spring MVPPPPPPPPPPPPPPPPPPPPPP!</h1>
</body>
</html>
请求:http://localhost:8080/hellospringmvc/hello
输出:
报错:
1、UnsupportedClassVersionError : Unsupported major.minor version 52.0 。
报错如下。
Caused by: java.lang.UnsupportedClassVersionError: org/springframework/web/SpringServletContainerInitializer : Unsupported major.minor version 52.0 (unable to load class org.springframework.web.SpringServletContainerInitializer)
A:这是因为JDK版本过低了,spring5.X要求JDK版本要8及以上,目前配置的是JDK7,版本不符合要求了。换成JDK8即可。
2、请求404,获取/hellospringmvc/WEB-INF/pagessuccess.jsp 没有结果。
报错如下:
A:这是因为在配置springmvc配置文件hellospringmvc-servlet.xml时配置视图解析器没配置好,很明显缺失了/斜杠分隔符。这视图解析器视图根据前后缀来拼接jsp视图的路径时由于前缀少了斜杠而导致映射路径不对,加上即可,在新增jsp视图时也要按照格式规范在/WEB-INF/pages/路径下添加jsp页面,否则会导致视图解析器拼接时报错。
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
Spring MVC 执行流程分析
Spring MVC执行流程图
- 用户发送出请求到前端控制器DispatcherServlet。
- DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
- HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter(处理器适配器)。
- HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
- Controller执行完成返回ModelAndView对象。
- HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
- ViewReslover解析后返回具体View(视图)。
- DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
- DispatcherServlet响应用户。
关键组件分析
-
前端控制器:
DispatcherServlet
(不需要程序员开发),由框架提供,在web.xml中配置。
作用:接收请求,响应结果,相当于转发器,中央处理器。 -
处理器映射器:
HandlerMapping
(不需要程序员开发),由框架提供。
作用:根据请求的url查找Handler(处理器/Controller),可以通过XML和注解方式来映射。 -
处理器适配器:
HandlerAdapter
(不需要程序员开发),由框架提供。
作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。 -
处理器:
Handler
(也称之为Controller,需要工程师开发)。
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。
作用:接受用户请求信息,调用业务方法处理请求,也称之为后端控制器。 -
视图解析器:
ViewResolver
(不需要程序员开发),由框架提供。
作用:进行视图解析,把逻辑视图名解析成真正的物理视图。 -
视图:
View
(需要前端工程师开发)。
作用:把数据展现给用户的页面,View是一个接口,实现类支持不同的View技术(Jsp、Freemaker、Pdf等)
Spring MVC 三大组件
- 处理器映射器(HandlerMapper)
- 处理器适配器(HandlerAdapter)
- 视图解析器(ViewResolver)。
处理映射器
处理映射器作用
通过处理器映射,你可以将Web 请求映射到正确的处理器 Controller 上。当接收到请求时,DispactherServlet 将请求交给 HandlerMapping 处理器映射,让他检查请求并找到一个合适的HandlerExecutionChain,这个HandlerExecutionChain 包含一个能处理该请求的处理器 Controller。然后,DispactherServlet 执行在HandlerExecutionChain 中的处理器 Controller。
Spring内置了许多处理器映射策略,目前主要由三个实现。
SimpleUrlHandlerMapping、
BeanNameUrlHandlerMapping
RequestMappingHandlerMapping。
//注意:Spring MVC3.1之前使用DefaultAnnotationHandlerMapping,Spring MVC3.1之后改为RequestMappingHandlerMapping。
1)SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 在应用上下文中可以进行配置,并且有Ant风格的路径匹配功能。例如我们在springmvc.xml 中配置一个SimpleUrlHandlerMapping 处理器映射。
实例:
simple-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建SimpleUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello">simpleController</prop>
<!--如果有更多的请求映射就在这边添加映射配置-->
</props>
</property>
</bean>
<!--2.创建Controller对象-->
<bean id="simpleController" class="com.self.controller.SimpleController"/>
<!--每个请求Controller或者说Handler就要配置一个bean对象-->
<!-- 3.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<!--<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>-->
<param-value>WEB-INF/simple-servlet.xml</param-value>
</init-param>
SimpleController
//实现org.springframework.web.servlet.mvc.Controller接口的handleRequest方法
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------SimpleController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
pom.xml
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
报错:
1、@Override is not allowed when implementing interface method
A:这是因为idea的java编译设置中设置的是jdk1.5的,跟编译器版本问题有关。编译器1.5只支持@Override注释重写父类方法,不支持实现接口方法。将language level设置高于jdk1.5版本即可 。
2)BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping 将收到的Http请求映射到bean的名字上。
示例:
beanname-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
<!--如果要继续添加映射只要在这里配置好映射路径和注册bean即可-->
<bean id="/hello" class="com.self.BeanNameController"/>
<!-- 3.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
//实现org.springframework.web.servlet.mvc.Controller接口的handleRequest方法
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------BeanNameController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
pom.xml
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
web.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beanname-servlet.xml</param-value>
<!--<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>-->
</init-param>
请求:http://localhost:8080/hellospringmvc/hello
注意:在bean的id中要加上斜杆,Controller的代码跟前面的SimpleUrlHandlerMapping一样,实现Controller,重写handlerRequest()方法即可。
在默认情况下,如果没有在上下文中没有找到处理器映射,DispactherServlet 会为你创建一个BeanNameUrlHandlerMapping。
3)RequestMappingHandlerMapping
RequestMappingHandlerMapping是三个中最常用的HandlerMapping,因为注解方式比较通俗易懂,代码界面清晰,只需要在代码前加上@RequestMapping()的相关注释就可以了 。
实例:
@Controller
public class HelloController {
//@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
hellospringmvc-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.开启mvc注解驱动-->
<!--不添加也能使用,高版本spring已经默认实现了。
在Spring中一般采用@RequestMapping注解来完成映射关系,要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例,这两个实例分别在类级别和方法级别处理。而<mvc:annotation-driven/>配置帮助我们自动完成上述两个实例的注入。
-->
<!--<mvc:annotation-driven/>-->
</beans>
注意:重点是添加<mvc:annotation-driven/>
标签! 这个要在有其他HandlerMapping接口的情况下才需要。
处理适配器
处理器适配器作用
HandlerAdapter字面上的意思就是处理适配器,它的作用就是调用具体的方法对用户发来的请求来进行处理。当HandlerMapping获取到执行请求的Controller时,DispatcherServlte会根据Controller对应的Controller类型来调用相应的HandlerAdapter来进行处理。
HandlerAdapter的实现有:
HttpRequestHandlerAdapter
SimpleServletHandlerAdapter
SimpleControllerHandlerAdapter
-
——(Spring MVC 3.1后已废弃)AnnotationMethodHandlerAdapter
RequestMappingHandlerAdapter。
1)HttpRequestHandlerAdapter
HttpRequestHandlerAdapter可以处理类型为HttpRequestHandler的handler,对handler的处理是调用HttpRequestHandler的handleRequest()方法。
示例:
public class HttpRequestHandlerController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
//HttpRequestHandlerAdapter用来处理HttpRequestHandler类型的Controller
//注意该类型Controller都没有ModelAndView,相当于servlet
httpServletResponse.getWriter().write("Hello HttpRequestHandler.");
}
}
httprequesthandler-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.创建HttpRequestHandlerAdapter,用来处理HttpRequestHandler类型的Controller-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
<!--3.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
<!--如果要继续添加映射只要在这里配置好映射路径和注册bean即可-->
<bean id="/hi" class="com.self.HttpRequestHandlerController"/>
</beans>
输出:
报错:The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler 。
A:这是因为创建HttpRequestHandlerAdapter,用来处理HttpRequestHandler类型的Controller,而public class BeanNameController implements Controller 不是HttpRequestHandler类型的Controller,处理不了,所以报错,应该换上合适的HandlerAdapter,如RequestMappingHandlerAdapter。
2)SimpleServletHandlerAdapter
SimpleServletHandlerAdapter可以处理类型为Servlet,就是把Servlet当做Controller来处理,使用Servlet的service方法处理用户请求。 业务Controller继承HttpServlet 实现doGet()或doPost()方法。
示例:
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//SimpleServletHandlerAdapter用来处理HttpServlet类型的Controller
resp.getWriter().write("Hello HttpServlet.");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
httpservletHandler-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.创建SimpleServletHandlerAdapter-->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<!--3.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
<!--如果要继续添加映射只要在这里配置好映射路径和注册bean即可-->
<bean id="/hi" class="com.self.SimpleServlet"/>
</beans>
输出:
3)SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter可以处理类为Controller的控制器,使用Controller的handlerRequest方法处理用户请求。
beanname-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.创建BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.创建Controller对象,这里的id必须页面访问的路径(以斜杠开头)-->
<!--如果要继续添加映射只要在这里配置好映射路径和注册bean即可-->
<bean id="/hi" class="com.self.BeanNameController"/>
<!-- 3.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
<!--默认不配置SimpleControllerHandlerAdapter, 也能处理Controller类型为org.springframework.web.servlet.mvc.Controller的接口,
SimpleControllerHandlerAdapter会默认注册-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
</beans>
//实现org.springframework.web.servlet.mvc.Controller接口的handleRequest方法
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------BeanNameController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
4)RequestMappingHandlerAdapter
RequestMappingHandlerAdapter可以处理类型为HandlerMethod的控制器,通过Java反射调用HandlerMethod的方法来处理用户请求。
补充:类型为HandlerMethod的控制器是在容器初始化时,通过RequestMappingHandlerMapping在添加映射时把我们写业务Controller转化为HandlerMethod类型,存储在LinkedHashMap中。
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#createHandlerMethod
注意:
如果有在web.xml中配置指定的HandlerMapping 和 HandlerAdapter 的话,则只注册配置的处理器。
示例:
hellospringmvc-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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.扫描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--2.创建RequestMappingHandlerMapping-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--3.创建RequestMappingHandlerAdapter-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 4.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 4.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 4.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
@Controller
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
视图解析器
视图解析器作用
Spring MVC中的视图解析器的主要作用就是将逻辑视图转换成用户可以看到的物理视图。
流转逻辑:
当用户对SpringMVC应用程序发起请求时,这些请求都会被Spring MVC的DispatcherServlet处理,通过处理器找到最为合适的HandlerMapping定义的请求映射中最为合适的映射,然后通过HandlerMapping找到相对应的Handler,然后再通过相对应的HandlerAdapter处理该Handler。返回结果是一个ModelAndView对象,当该ModelAndView对象中不包含真正的视图,而是一个逻辑视图路径的时候,ViewResolver就会把该逻辑视图路径解析为真正的View视图对象,然后通过View的渲染,将最终结果返回给用户。
SpringMVC中处理视图最终要的两个接口就是ViewResolver
和View
,ViewResolver的作用是将逻辑视图解析成物理视图,View的主要作用是调用其render()方法将物理视图进行渲染。
常见的视图解析器
以下为Spring MVC提供常见视图解析器:
视图类型 | 说明 |
---|---|
BeanNameViewResolver | 将逻辑视图名称解析为一个Bean,Bean的Id等于逻辑视图名 |
InternalResourceViewResolver | 将视图名解析为一个URL文件,一般使用该解析器将视图名映射为一个保存在WEB-INF目录下的程序文件,如 JSP |
JaperReportsViewResolver | JapserReports是基于Java的开源报表工具,该解析器解析为报表文件对应的URL |
FreeMarkerViewResolver | 解析为基于FreeMarker模板的模板文件 |
VelocityViewResolver | 解析为Velocity模板技术的模板文件 |
VelocityLayoutViewResolver | 解析为Velocity模板技术的模板文件 |
其中,最常用的是InternalResourceViewResolver
,配置如下:
<!-- 4.配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 4.1 页面前缀 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 4.2 页面后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
Spring MVC 核心源码分析
DispathcerServlet属性配置
Spring MVC核心包里面有一个配置文件:DispathcerServlet.properties
E:/localwarehouse/org/springframework/spring-webmvc/5.0.2.RELEASE/spring-webmvc-5.0.2.RELEASE.jar!/org/springframework/web/servlet/DispatcherServlet.properties
该配置提供许多的默认组件,这些组件为Spring MVC的执行提供了支持,其中划线部分就是我们之前说到的Spring MVC三大组件。
doDispatch方法源码追踪
DispatcherServlet类的层次结构 :
从Spring MVC的执行流程我们知道,用户的请求最先到达就是DispatcherServlet,它是Spring MVC的核心,可以把它叫做中央处理器,因此我们分析源码之前,先看看他是什么样的流程,通过源码可以看出,它是继承FrameworkServlet,它也是Spring MVC提供的类,继续往下继承关系看,FrameworkServlet继承HttpServletBean,它是Spring提供的类,最终直到到它继承HttpServlet。
DispathcerServlet既然是Servlet,那么它肯定有一个service方法(Servlet最核心的方法),我们看这个方法在哪里实现的,一个个看,发现HttpServletBean并没有,在FrameworkServlet中,因此Spring实现这个serivice方法在这里实现的。
serivice()方法的作用的就是得到客户端的请求,然后判断这个请求是不是PATCH请求方式,如果不是就调用父类(HttpServlet)中的service方法,我们调用父类这个service方法其实实际是调用该类的doGet方法或者doPost方法等等,拿到不同的请求方式处理不同的业务,我们以get方式为例:
进入到processRequest方法,直接进入doService方法.跳到DispatcherServlet这个类里面来了,其实doSerivice可以猜到被子类各种重写 。
到此为止,我们知道DispatcherServlet作为Spring MVC的核心控制器,把用户请求最终转到了它里面的doDispatch()方法来完成具体工作。
处理器映射器核心源码
在doDispathcer方法里面,首先是创建一个ModelAndView对象 = null,判断当前请求是不是二进制(文件上传)的请求。
processedRequest = this.checkMultipart(request)
再往下看代码:
mappedHandler = this.getHandler(processedRequest);
这句代码非常重要!这是根据当前请求去拿一个Handler(控制器),这个Handler其实就是我们写的Controller类,进入到getHandler方法的源码:
有个handlerMappings的List集合,该集合存储了当前环境所有HandlerMapping对象,通过遍历该List集合,通过最合适的HandlerMapping找到Handler对象。
找到了Handler对象后,返回的是一个HandlerExecutionChain类型的Handle,这里面封装了一个HelloController,也就是我们自己的创建Controlller,如果有配置拦截器,还会使用一个interceptors集合封装所有拦截器。
处理器适配器核心源码
doDispatcher方法我们往下看,会到以下代码:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Spring MVC会根据当前环境的配置和代码去选择一个合适的HandlerAdapter实现类来执行Handler。
最后,handler方法会返回ModelAndView对象。该对象交给给后面的视图解析器解析处理。
视图解析器核心源码
回到DispathcerServlet的doDispatcher方法,我们往下走,看到这行代码:
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
这行代码包含了Spring MVC获取对应的View以及选择合适的ViewResolver解析视图的逻辑。
@RequestMapping注解
@RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
@RequestMapping常用属性
value属性
指定控制器的方法URI 。
如果类和方法上都指定value值,那么方法的最终方法路径为:http://localhost:8080/hellospringmvc/say/hello
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
method属性
指定请求的method类型,可以接受GET,POST,PUT,DELETE等.
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
注意:当注解有两个值时,就要有键值对对应关系,不能使用默认的值。
//这里不指定默认是value属性
@RequestMapping("/hello")
//注解有两个值时使用下面的写法
@RequestMapping(value = "/hello",method = RequestMethod.GET)
consumes、produces属性
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。
@RequestMapping(value = "/hello.do",consumes = "application/json",produces = "application/json")
public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException {
response.getWriter().write("Hello-World");
}
报错:
使用时会报415.The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
就是我们对接口设置了请求的consumes = "application/json",produces = "application/json"属性导致的。
params、headers属性
params:指定request中必须包含某些参数值,才让该方法处理。
headers:指定request中必须包含某些指定的header值,才能让该方法处理请求。
params示例:
@RequestMapping(value = "/hello",method = RequestMethod.GET,params = "id=3")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
输出:
headers示例:
@RequestMapping(value = "/hello.do",headers = "Referer=http://www.hello.com/")
public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException {
response.getWriter().write("Hello");
}
Spring MVC支持对多种类型的请求参数进行封装
- 基本类型
- Pojo对象类型
- 包装Pojo对象类型
- List集合类型
- Map集合类型
Spring MVC 基本类型封装
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本类型参数封装</h2>
<form action="http://localhost:8080/hellospringmvc/say/helloParam">
用户名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
@RequestMapping("/helloParam")
public String helloParam(String name, Integer age) {
System.out.println("用户名:" + name);
System.out.println("年龄:" + age);
return "success";
}
输出:
用户名:恶龙吟风
年龄:null
------------------------------
用户名:恶龙吟风
年龄:365
-------------------------------
用户名:
年龄:365
注意:
当接收的基本类型参数不是包装类时,如 int age,这时候要求age必须有值传进来,否则报错HTTP Status 400 -错误。The request sent by the client was syntactically incorrect.
如果是用包装类接收,如Integer age,则没有传值时age = null,String name 没有值默认空字符串 name=""
Spring MVC Post中文乱码
在Spring MVC表单如果是Post方法提交中文内容时,会出现乱码 。
这是我们可以配置Spring MVC提供字符编码过滤器来解决问题。
配置字符编码过滤器
web.xml
<!--字符编码过滤器-->
<!-- 字符编码过滤器的配置效果是当我们没有指定请求响应的编码格式时,该字符编码过滤器会
默认以配置的编码格式去解码和编码,如配置的是utf-8-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!--指定转换的编码-->
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 下面配置是否强制设置编码格式为指定的转换编码格式:utf-8
如果强制,则所以的请求响应都会按照utf-8来解码和编码,当请求的格式是GBK等其他编码时,
就会因此而乱码,所以一般是不配置这个选项的。
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>-->
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@RequestParam注解
在SpringMvc后台进行获取数据,一般有三种:
- request.getParameter(“参数名”)
- 用@RequestParam注解获取
- Springmvc默认支持的数据类型接收参数,可直接通过controller方法参数对应jsp中请求参数name直接获取
其实可以不使用@RequestParam注解,直接接收,如果不使用该注解要求controller方法中的参数名称要跟form中name名称一致,使用该注解只是方便随意取参数名称,不过value属性还是要与name一致,该注解只适合一些任性的盆友使用 。
使用
@RequestParam作用:将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解) 。
在使用SpringMVC接收基本参数类型的时候,我们发现如果控制器中形参的名称与表单的name名称不一致时,无法接收参数。这是可以使用@RequestParam注解解决这个问题。
注意:页面表单的name和控制器的形参(方法参数)并不一致,但是@RequestParam注解的value值(即接收请求参数的名称)必须和页面表单的name保持一致。否则就接收不到参数。
@RequestParam里name和value的区别:没有区别。源码注解的意思就是name的别名是value,value的别名是name。二者皆可,并且开发中两个都能获得参数,获得一样的结果。
@RequestParam注解还有两个属性:
- required:参数是否必须。默认为true。代表页面是否必须传递该参数。如果该值为true,但没有传递参数,会报错。
- defaultValue:默认值。代表如果页面没有传递该参数,使用defaultValue的值代替。当有设置defaultValue时,required属性失效,默认是false。如果没有传该参数,就使用默认值 。
实例:
@ApiOperation(value = "获取列表")
@ApiResponse(code = 10000, message = "成功", response =InfoVO.class)
@GetMapping(value = "/listUnused")
public R<List<InfoVO>> listUnused(@ApiParam(value = "规格", required = true) @RequestParam(value = "specId") BigInteger specId,
@ApiParam(value = "排除资产编码列表") @RequestParam(value = "excludeCodeList", required = false) List<String> excludeCodeList) {
return R.data(result);
}
@RequestMapping(value ="/requestParam",method = RequestMethod.GET)
public String requestParam(@RequestParam("userName") String name, @RequestParam("userAge")Integer userAge) {
System.out.println("用户名:" + name);
System.out.println("年龄:" + userAge);
return "success";
}
/**
*
* @param name 默认defaultValue= "大青山",required = true失效,为false
* @param userAge value = "userAge" ,required = false 可以不传,不传时为null
* @return
*/
@RequestMapping(value ="/requestParam1",method = RequestMethod.GET)
public String requestParam1(@RequestParam(value = "userName" ,required = true,defaultValue = "大青山") String name, @RequestParam(value = "userAge" ,required = false)Integer userAge) {
System.out.println("用户名:" + name);
System.out.println("年龄:" + userAge);
return "success";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本类型参数封装</h2>
<form action="http://localhost:8080/hellospringmvc/say/requestParam1">
用户名:<input type="text" name="userName"><br>
年龄:<input type="text" name="userAge"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
Spring MVC Pojo参数封装
之前我们接收参数的时候都是定义一个个的基本类型来接收,这样比较繁琐,Spring MVC提供了使用Pojo(或者称为JavaBean)类型来封装请求参数。
实例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本类型参数封装</h2>
<%--设置请求类型为post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamPojo" method="post">
用户名:<input type="text" name="userName"><br>
年龄:<input type="text" name="userAge"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
/**
* method = RequestMethod.POST 可以不标记,不标记就都能匹配
* @param user
* @return
*/
@RequestMapping(value ="/helloParamPojo" ,method = RequestMethod.POST)
public String helloParamPojo(User user) {
System.out.println("用户名:" + user.getUserName());
System.out.println("年龄:" + user.getUserAge());
return "success";
}
public class User {
private String userName;
private Integer userAge;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getUserAge() {
return userAge;
}
public void setUserAge(Integer userAge) {
this.userAge = userAge;
}
}
Spring MVC 包装参数封装——Pojo嵌套Pojo对象
在Spring MVC的应用过程中,我们在后端经过需要将表单数据封装在一个包装Pojo类型中,所谓包装Pojo类型,就是Pojo对象中包含另一个Pojo对象。
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本类型参数封装</h2>
<%--设置请求类型为post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamPojos" method="post">
用户名:<input type="text" name="userName"><br>
年龄:<input type="text" name="userAge"><br>
<%--封装用户的地址信息,name为address.province这种写法,这代表把数据封装到User对象->Address对象的province属性中。--%>
省份:<input type="text" name="address.province"><br>
城市:<input type="text" name="address.city"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
public class User {
private String userName;
private Integer userAge;
private Address address;
//......
}
public class Address {
private String province;
private String city;
//......
}
@RequestMapping(value ="/helloParamPojos" ,method = RequestMethod.POST)
public String helloParamPojos(User user) {
System.out.println("用户名:" + user.getUserName());
System.out.println("年龄:" + user.getUserAge());
System.out.println("省份:"+user.getAddress().getProvince());
System.out.println("城市:"+user.getAddress().getCity());
return "success";
}
输出:
用户名:艾米
年龄:18
省份:艾米帝国
城市:帝都
Spring MVC List集合参数封装
我们是一个Address对象来接收一个地址信息,如果有多个地址信息怎么呢?这时我们可以使用List集合来封装。
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本类型参数封装</h2>
<%--设置请求类型为post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamList" method="post">
用户名:<input type="text" name="userName"><br>
年龄:<input type="text" name="userAge"><br>
<%--address[0].province,代表给User对象->List<Address>集合->第一个Address对象的province属性赋值--%>
省份1:<input type="text" name="address[0].province"><br>
城市1:<input type="text" name="address[0].city"><br>
省份2:<input type="text" name="address[1].province"><br>
城市2:<input type="text" name="address[1].city"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
public class User {
private String userName;
private Integer userAge;
private List<Address> address;
//......
}
@RequestMapping(value ="/helloParamList" ,method = RequestMethod.POST)
public String helloParamList(User user) {
System.out.println("用户名:" + user.getUserName());
System.out.println("年龄:" + user.getUserAge());
List<Address> address = user.getAddress();
for (Address addressTemp : address) {
System.out.println("省份:"+addressTemp.getProvince());
System.out.println("城市:"+addressTemp.getCity());
}
return "success";