引入坐标
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
SpringMVC 配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.springMVC.controller"></context:component-scan>
<!--配置视图解析器对象,请求时,可以帮你跳转到指定的页面-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--文件路经-->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 指明后缀-->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 开启springMVC框架注解的支持-->
<mvc:annotation-driven/>
</beans>
DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自己定义,把拦截下来的请求,依据相应的规则分发到目标Controller来处理,是配置spring MVC的第一步。
- 在工程的webapp/WEB_INF目录下配置web.xml:
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 配置解决中文乱码的过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 前端控制器-->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 加载配置文件-->
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springMVC.xml</param-value>
</init-param>
<!-- 启动时创建的(在第一次时候)-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<!-- /是拦截请求的内容(表示拦截所有)-->
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
SpringMVC测试
如果是在开始的时候,系统就给你创建了jsp文件,你就先把它删了,然后重新创建一个,这样就解决了页面中文乱码了。
- 在index.jsp中:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>测试</h3>
<a href="user/testString">测试</a>
</body>
</html>
在Controller类中:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testString")
public String testString(){
return "success";
}
}
- 在工程的webapp/WEB_INF/pages目录下创建成功跳转视图success.jsp如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>执行success</h3>
</body>
</html>
- 配置tomcat
- 配置完,点击启动,就会自动打开浏览器访问了。
执行流程分析
- 启动Tomcat服务器时,由于配置了标签,所以首先创建DispatcherServlet对象并加载bean.xml配置文件
- 由于bean.xml中开启了注解扫描,HelloController对象被创建并加入Spring容器中
- 浏览器请求index.jsp,请求会先到达DispatcherServlet核心控制器,根据配置@RequestMapping注解找到具体要执行的方法helloHandler
- 执行方法helloHandler,得到返回值. 的视图解析器解析返回值,查找到对应的JSP文件success.jsp
- Tomcat服务器渲染页面,做出响应
核心流程
具体步骤:
- 第一步:发起请求到前端控制器(DispatcherServlet)
- 第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找)
- 第三步:处理器映射器HandlerMapping向前端控制器返回Handler,HandlerMapping会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象,多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略
- 第四步:前端控制器调用处理器适配器去执行Handler
- 第五步:处理器适配器HandlerAdapter将会根据适配的结果去执行Handler
- 第六步:Handler执行完成给适配器返回ModelAndView
- 第七步:处理器适配器向前端控制器返回ModelAndView (ModelAndView是springmvc框架的一个底层对象,包括 Model和view)
- 第八步:前端控制器请求视图解析器去进行视图解析 (根据逻辑视图名解析成真正的视图(jsp)),通过这种策略很容易更换其他视图技术,只需要更改视图解析器即可
- 第九步:视图解析器向前端控制器返回View
- 第十步:前端控制器进行视图渲染 (视图渲染将模型数据(在ModelAndView对象中)填充到request域)
- 第十一步:前端控制器向用户响应结果
(其中处理器映射器HandlerMapping,处理器适配器HandlAdapter,视图解析器ViewResolver称为SpringMVC三大组件.在bean.xml中声明<mvc:annotation-driven conversion-service=“conversionService”/>标签相当于自动配置了处理器映射器和处理器适配)
请求路径匹配
@RequestMapping注解: 匹配路径与处理器
- @RequestMapping注解用于建立请求URL路径和处理器之间的对应关系.
-
出现位置: 可以出现在类上,也可以出现在方法上.
- 当它既出现在类上也出现在方法上时,类上注解值为请求URL的一级目录,方法上注解值为请求URL的二级目录
- 当它只出现在方法上时,该注解值为请求URL的一级目录
-
- 其属性如下:
path: value属性的别名,指定请求的URL,支持Ant风格表达式,通配符如下:
通配符 | 说明 |
---|---|
? | 匹配文件(路径)名中的一个字符 |
* | 匹配文件(路径)名中的任意数量(包括0个)的字符 |
** | 匹配任意数量(包括0个)的路径 |
例如
-
路径/project/*.a匹配项目根路径下所有在/project路径下的.a文件
-
路径/project/p?ttern匹配项目根路径下的/project/pattern和/project/pXttern,但不能匹配/project/pttern
-
路径/**/example匹配项目根路径下的/project/example,/project/foo/example,和/example
-
路径/project/**/dir/file.*匹配项目根路径下的/project/dir/file.jsp,/project/foo/dir/file.html,/project/foo/bar/dir/file.pdf
-
路径//.jsp匹配项目根路径下的所有jsp文件
另外,遵循最长匹配原则,若URL请求了/project/dir/file.jsp,现在存在两个匹配模式://.jsp和/project/dir/.jsp,那么会根据/project/dir/.jsp来匹配. -
method: 指定HTTP请求方法(可选RequestMethod.GET,RequestMethod.HEAD,RequestMethod.POST,RequestMethod.PUT等),多个值之间是或的关系.
-
params: 指定请求参数的限制,支持简单的表达式,如:
- @RequestMapping(params={“param1”}),表示请求参数中param1必须出现
- @RequestMapping(params={"!param1"}),表示请求参数中param1不能出现
- @RequestMapping(params={“param1=value1”}),表示请求参数中param1必须出现且为value1
- @RequestMapping(params={“param1!value1”}),表示请求参数中param1必须出现且不为value1
多个值之间是与的关系
-
headers: 限定HTTP请求中必须包含的请求头,同样支持简单的表达式
其中path和method属性较常用
@PathVaribale注解: 绑定URL占位符,支持REST风格URL
REST风格URL
REST风格URL有以下特点:
- 资源Resources: 通过一个URI来指定一个具体的资源
- 表现层Representation: 把资源具体呈现出来的形式
- 状态转化State Transfer: 通过HTTP请求方法来决定状态转化
例如:
REST风格URL | 含义 |
---|---|
GET /accounts | 查找所有用户 |
POST /accounts | 新建用户 |
GET /accounts/{ID} | 查找对应ID的用户 |
PUT /accounts/{ID} | 更新对应ID的用户 |
DELETE /accounts/{ID} | 删除对应ID的用户 |
@PathVaribale注解的使用
在@RequestMapping注解的path属性中用{param}声明占位符,将展位符@PathVariable注解的name属性来修饰处理器参数来将占位符对应的值赋给方法的参数.例如
对于以下的请求和方法:
<a href="account/findAccount/10">查询账户</a>
// 控制器类
@Controller
@RequestMapping(path = "/account")
public class HelloController {
@RequestMapping("/findAccount/{id}")
public void findAccount(@PathVariable(name = "id") Integer accountId) {
// accountId = 10
// 方法体...
}
}
访问URLhttp://localhost:8080/myProject/account/findAccount/10会将10传给findAccount方法的accountId参数.
请求参数的绑定
参数绑定的示例
SpringMVC将请求参数中的param=value中的value传递给控制器方法的param参数,例如:
对于以下请求和方法:
<a href="account/findAccount?accountId=10">查询账户</a>
// 控制器类
@Controller
@RequestMapping(path = "/account")
public class HelloController {
@RequestMapping(path = "/findAccount")
public void findAccount(Integer accountId) {
// accountId = 10
// 方法体...
}
}
SpringMVC中会将10传给findAccount方法的accountID参数传递给HandlerAdapter执行.
@RequestParam注解: 为处理器方法参数起别名
-
@RequestParam注解作用在方法参数上,把请求中指定名称的参数给处理器方法中的形参赋值,相当于给处理器方法起别名.其属性如下:
- name: value属性的别名,指定请求参数的名称
- required: 指定该请求参数是否必须的,默认为true
例如jsp中发送请求如下
<a href="testRequestParam?param1=value">测试requestParam注解</a>
处理器方法中给对应参数加上@RequestParam(name=“param1”)注解来接收参数
@RequestMapping("/testRequestParam")
public String handlerMethod(@RequestParam("param1") String username) {
// 方法体...
}
各种类型请求参数的绑定
SpringMVC内置参数绑定类型
- SpringMVC支持三种类型的参数绑定
- 基本数据类型和String类型
- JavaBean类型
- 集合类型
数据绑定要求请求参数名和方法中的参数名相同,或使用@RequestParam为方法参数起别名.
基本数据类型和String类型的参数绑定
对于基本数据类型,只需要以方法参数名作为请求参数名即可.示例如下:
<a href="account/findAccount?accountId=10&accountName=zhangsan">查询账户</a>
// 控制器类
@Controller
@RequestMapping(path = "/account")
public class HelloController {
@RequestMapping("/findAccount")
public String findAccount(Integer accountId, String accountName) {
// accountId = 10, accountName = "zhangsan"
// 方法体...
}
}
JavaBean类型的参数绑定
- JavaBean类型的参数,要想实现绑定,就必须实现其空参构造函数和所有属性的get,set方法
- 若JavaBean参数的属性中只包含基本数据类型和String类型属性,以属性名作为请求参数名,则SpringMVC会自动将其封装成JavaBean对象.示例如下:
例如JavaBean类的定义如下:
// JavaBean类
public class Account implements Serializable {
private String username;
private Integer age;
// 所有属性的getset方法...
}
则其对应的请求参数名如下:
<form action="account/updateAccount" method="post">
<label>名称</label><input type="text" name="username"><br/>
<label>年龄</label><input type="text" name="age"><br/>
<input type="submit" value="保存">
</form>
- 若JavaBean参数的属性中包含其它JavaBean对象,则以外层类属性名.内层类属性名作为请求参数,示例如下:
例如JavaBean类的定义如下:
public class Account implements Serializable {
private String username;
private Intger age;
private User user;
// 所有属性的getset方法...
}
public class User implements Serializable{
private String uname;
private Double umoney;
// 所有属性的getset方法...
}
则其对应的请求参数名如下:
<form action="account/updateAccount" method="post">
<label>名称</label><input type="text" name="username"><br/>
<label>年龄</label><input type="text" name="age"><br/>
<label>用户名</label><input type="text" name="user.uname"><br/>
<label>用户余额</label><input type="text" name="user.umoney"><br/>
<input type="submit" value="保存">
</form>
集合类型的参数绑定
对JavaBean类中的集合属性进行参数绑定,可以分为List类型的参数绑定和Set类型的参数绑定
- 对于List类型参数,其请求参数名为集合名[下标],List类型参数可以对List,Set,数组类型成员进行赋值,但是诡异的是对Set和数组成员进行赋值时,要在Bean类的构造函数中new出对应的Set或数组,然而对于List就不用,下边的代码就可以直接跑,不知道为什么,希望知道的朋友不吝赐教
- 对于Set类型参数,其请求参数名为集合名[键],Set类型参数可以对Set,Propertis类型成员进行赋值.
例如JavaBean类的定义如下:
public class Account implements Serializable {
private String username;
private Intger age;
private List<User> list; // List集合属性
private Map<String, User> map; // Map集合属性
// 所有属性的getset方法...
}
public class User implements Serializable{
private String uname;
private Double umoney;
// 所有属性的getset方法...
}
则其对应的请求参数名如下:
<form action="account/updateAccount" method="post">
用户名称:<input type="text" name="username"><br/>
用户密码:<input type="password" name="password"><br/>
用户年龄:<input type="text" name="age"><br/>
账户1名称:<input type="text" name="accounts[0].name"><br/>
账户1金额:<input type="text" name="accounts[0].money"><br/>
账户2名称:<input type="text" name="accounts[1].name"><br/>
账户2金额:<input type="text" name="accounts[1].money"><br/>
账户3名称:<input type="text" name="accountMap['one'].name"><br/>
账户3金额:<input type="text" name="accountMap['one'].money"><br/>
账户4名称:<input type="text" name="accountMap['two'].name"><br/>
账户4金额:<input type="text" name="accountMap['two'].money"><br/>
<input type="submit" value="保存">
</form>
另外还有一个比较诡异的地方.我尝试将集合类型的参数绑定到控制器方法的参数上,然而不行
若我将方法参数类型设为List<>,则会报500错误,提示List不能初始化
若我将方法参数设为ArrayList<>,不会报错,但是不能注入
下面是错误的代码
@RequestMapping("/saveAccounts")
//public String saveAccounts(List<Account> accounts) { // 若设为List类型,则会报500错误
public String saveAccounts(LinkedList<Account> accounts) { // 若设为LinkedList类型,不报错但是初始化不上
System.out.println(accounts);
return "success";
}
上面是错误的代码
不知道为什么会这样,若有知道时怎么回事的朋友,还望不吝赐教
自定义数据类型参数绑定
表单提交的任何数据类型都是字符串类型,SpringMVC定义了转换器,将字符串转化为我们方法参数的各种类型.我们也可以实现自定义的转换器以实现自定义的参数类型转换
自定义的类型转换器要实现Converter<String, T>接口,并在Spring容器配置bean.xml中配置该实现类. 示例如下:
在工程的java目录下创建控制器类cn.maoritian.util.StringToDateConverter类,实现Converter<String, Date>接口,完成从String类到Date类的转换:
// 自定义的类型转换器,完成从String类到Date类的转换
public class StringToDateConverter implements Converter<String, Date> {
public Date convert(String source) {
try {
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
Date date = df.parse(source);
return date;
} catch (Exception e) {
throw new RuntimeException("类型转换错误");
}
}
}
在Spring容器配置bean.xml中加入如下配置:
<!-- 配置的类型转换器工厂 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<!-- 诸如我们自定义的类型转换器 -->
<bean class="cn.maoritian.utils.StringToDateConverter"/>
</set>
</property>
</bean>
通过原始ServletAPI对象处理请求
SpringMVC支持使用原始ServletAPI作为控制器方法的参数,包括HttpServletRequest,HttpServletResponse,HttpSession对象,他们都可以直接用做控制器方法的参数.示例如下:
@RequestMapping("/path")
public void myHandler(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println(request.getParameter("param1"));
System.out.println(request.getParameter("param1"));
response.getWriter().println("<h3>操作成功</h3>");
}
解决请求参数绑定中文乱码问题
在web.xml中配置编码转换过滤器,即可解决请求参数中文乱码的问题.
```xml
<!-- 配置编码转换过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filterclass>
<!-- 指定字符集 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<!-- 过滤所有请求 -->
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
详细请查看原文:
作者:ncepu_Chen
来源:CSDN
原文:https://blog.csdn.net/ncepu_Chen/article/details/95060670