springMVC01-入门
0.学习视频地址:
SpringMVC教程IDEA版-3天-2018黑马SSM-03
springMVC是基于ServletAPI开发的框架。
1.简单使用
**一: ** 导包
spring-web-4.0.0.RELEASE.jar
spring-webmvc-4.0.0.RELEASE.jar
// 上面的两个类库是springMVC的主要far包。
spring-aop-4.0.0.RELEASE.jar
commons-logging-1.2.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
从springMVC需要的far包中可以看出,springMVC也只是spring框架的一个应用方向罢了。
二: web.xml
<!-- web.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置DispatcherServlet的一个初始化参数:作用:配置springMVC的配置文件的位置和名称 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 使用DispatcherServlet在Tomcat加载的时候就操作 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern><!-- /** -->
</servlet-mapping>
</web-app>
在以前,使用Servlet获取请求时,都是交给自己定义的处理方法,现在是把所有的请求都交给DispatcherServlet类处理,也即是springMVC的内置处理方法,然后我们再在springMVC提供的方法中进行各种操作。
SpringMVC之配置DispatcherServlet的一些坑
三: springMVC的配置文件
<!-- springmvc.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- 组件扫描器 -->
<context:component-scan
base-package="com.start.handler"></context:component-scan>
<!-- 开启springmvc框架注解支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 配置视图解析器:如何把 @RequestMapping("/helloworld") 注解映射的方法的返回值解析成实际的物理视图 :通过prefix + @RequestMapping注解方法的返回值 + suffix(后缀),来解析得到实际的物理视图地址,然后做转发操作。-->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
InternalResourceViewResolver类的作用:内部视图资源解析器。作用是把类似@RequestMapping注解得到的返回值,和该类的属性值(prefix、suffix)组合起来,构成一个完整的视图地址,然后进行重定向或转发等操作。
组合方法:prefix(相当于是开始目录) + @RequestMapping注解方法的返回值(相当于是文件名) + suffix(相当于是后缀名)。
四: 编程视图处理器
// HelloWorld.java
package com.start.handler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
// 必须是使用@Controller注解才行。
@Controller
public class HelloWorld {
/**
* 1.使用@RequestMapping("/helloworld")来映射请求的URL
* 2.返回值会通过视图解析器解析为实际的物理视图:
* 对于InternalResourceViewResolver类来说,会在内部做如下的解析:
* 通过prefix + @RequestMapping注解方法的返回值 + suffix(后缀),来解析得到实际的物理视图地址,然后做转发操作。
*
* @return
*/
@RequestMapping("/helloworld")
public String hello() {
System.out.println("HelloWorld.hello()///------------->>>>>.");
return "success";
}
}
使用@Controller注解注册组件,使该类起作用。
@RequestMapping注解的作用是设置请求URL,其中 /
表示项目根路径。
五: 编写视图
<!-- index.jsp -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="helloworld">hello world</a>
</body>
</html>
<!-- success.jsp -->
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath%>">
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<p>hello--->>>>>123456789</p>
</body>
</html>
六: 测试
1.1.入门小结
1.启动服务器,加载一些配置文件
- DispatcherServlet对象被创建
- springmvc.xml文件被加载
- …
2.发送请求,后台处理请求
…
比较完整的执行图(组件使用):
DispatcherServlet:前端控制器
HandlerMapping:处理器映射器
Handler:处理器
HandlerAdapter:处理器适配器
视图解析器:View roslver
视图:View
2.@RequestMapping注解
2.1@RequestMapping注解的作用
作用:用于建立请求URL和处理请求方法之间的对应关系。
出现位置:根据源码@Target({ElementType.METHOD, ElementType.TYPE})
可知可以在类上和方法上。
-
类上:
请求URL的第一级访问目录。
此处不写的话,就相当于应用的根目录。
写的话需要以/
开头。
出现的目的就是为了我们的URL可以按照模块话进行管理。例如:/accout
/add/accout
/delete/accout
/update- …
- 有标志的部分就是把@RequestMapping注解写在类上,使我们的URL更加精细。
-
方法上:
请求URL的第二级目录。
属性:
@RequestMapping注解的属性的关系都是与
的关系
- value:用于指定请求的URL。和path属性一样。
- method:用于指定请求的方式。
- params:用于指定限制请求参数的条件,也就是请求参数中必须带有符合该属性设置的值,若不符号则不执行方法。支持简单的表达式。要求请求参数的key和value必须和配置的一样,否则不执行方法。例如:
- params={“accoutName”},表示请求参数中必须有名为accoutName的key
- params={“money!=100”},表示请求参数中money不能是100
- headers:用于指定限制请求信息头的条件。发送请求中必须包含的请求头。
3.请求参数绑定
- 请求参数的绑定说明.
- 绑定机制
- 表单提交的数据都是key=value格式的
- springMVC的参数绑定过程是把表单提交的请求参数,作为控制器中方法的参数进行绑定的
- 要求:提交表单的name必须和参数的名称是一样的。
- 支持的数据类型
- 基本数据类型和字符串类型
- 实体类型(javaBean)
- 集合数据类型(List、map等)
- 绑定机制
- 基本数据类型和字符串类型
- 提交表单的name和参数的名称是相同的
- 区分大小写
- 实体类型(javaBean)
- 提交表单的name和javaBean中的属性名称需要一致
- 如果一个javaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性
- 给集合属性数据封装
- jsp页面编写方式:list[0].属性…
- 请求参数中文乱码的解决
- 在web.xml中配置spring提供的过滤器类
3.1.例子一:基本数据类型和字符串类型
<!-- 视图 :一部分-->
<a href="helloworld?username=参数绑定abc">hello world</a>
// 一个简单的请求处理方法. :一部分
@RequestMapping("/helloworld")
public String hello(String username) {
System.out.println("HelloWorld.hello()------------->>>>>:" + username);
return "success";
}
结果:
HelloWorld.hello()------------->>>>>: 参数绑定abc
3.2.例子二:实体类型(javaBean)
// 一个javaBean:Account
package com.start.handler;
import java.io.Serializable;
// 要继承Serializable接口
public class Account implements Serializable {
private String username;
private String password;
private Double money;
private User user;
// 其他的种种方法...
}
// 一个javaBean:User
package com.start.handler;
public class User implements Serializable {
private String uname;
private int age;
// 其他的种种方法...
}
<!-- 视图 :一部分-->
<form action="setAccount" method="get">
用户名:<input name="username" type="text"><br>
密码:<input name="password" type="text"><br>
金额:<input name="money" type="text"><br>
<!-- 引用类型-->
用户名:<input name="user.uname" type="text"><br>
用户年龄:<input name="user.age" type="text"><br>
<input value="提交" type="submit"><br>
</form>
// 一个简单的请求处理方法. :一部分
@RequestMapping("/setAccount")
// 参数部分直接写实体类就好,也可以写多个实体类。
public String setAccount(Account account, User user) {
System.out.println("HelloWorld.setAccount()>>" + account);
System.out.println("HelloWorld.setAccount()>>" + user);
return "success";
}
测试结果:
HelloWorld.setAccount()>>Account [username=123, password=456, money=789.0, user=User [uname=111, age=222]]
HelloWorld.setAccount()>>User [uname=123, age=123]
代码分析:
一:在逻辑方法中,参数可以只写一个实体类,这样,表单提交的name要和实体类的属性名称一样。
二:也可以在逻辑方法中写多个实体参数,但是这样的话,多个实体javaBean的属性名就不能够有相同的,否则会不知道表单提交name要和那一个实体javaBean的属性相对应了。
三:如果一个javaBean类中包含其他的引用类型,那么表单的name属性需要编写成:对象.属性
3.3.例子三:请求参数中文乱码的解决****
使用过滤器的方式来解决请求参数中中文乱码的问题。
要注册springMVC提供的解决编码方式的组件:CharacterEncodingFilter类。然后设置该组件的encoding属性的值为UTF-8。类似于Servlet中的解决方法:rep.setCharacterEncoding("utf-8")
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<!-- 配置DispatcherServlet 前端控制器 -->
.....
<!-- 配置过滤器:用于处理中文乱码 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3.4.例子三:集合属性数据封装
// 一个javaBean:Account
package com.start.handler;
import java.io.Serializable;
// 要继承Serializable接口
public class Account implements Serializable {
private String username;
private String password;
private Double money;
// 集合:List和Map
private List<User> list;
private Map<String, User> map;
// 其他的种种方法...
}
<form action="setListAndMap" method="get">
姓名:<input name="username" type="text"><br>
密码:<input name="password" type="text"><br>
金额:<input name="money" type="text"><br>
<!--设置List类型的值 -->
用户名:<input name="list[0].uname" type="text"><br>
用户年龄:<input name="list[0].age" type="text"><br>
<!-- 设置Map类型的值 -->
用户名:<input name="map['ont'].uname" type="text"><br>
用户年龄:<input name="map['ont'].age" type="text"><br>
<input value="提交" type="submit"><br>
</form>
@RequestMapping("/setListAndMap")
public String setListAndMap(Account account) {
System.out.println("HelloWorld.setAccount()>>" + account);
return "success";
}
测试结果:
HelloWorld.setAccount()>>Account [username=123, password=132, money=132.0, list=[User [uname=去去去, age=12]], map={ont=User [uname=呃呃呃, age=123]}]
3.5.自定义类型转换器*
我们提交数据的时候,request中的数据都是以String的类型存在的,Spring会做一些类型转换,将这些数据转换成我们所需要的数据类型(int、float等)。对于日期来说,Spring支持的格式是2019/11/11,当我们传入2019-11-11,程序会报错,这时候就需要我们自定义类型转换器来满足我们的需要。 点这里
-
定义一个类实现Converter接口,该接口有两个泛型。
public interface Converter<S, T> { //S:表示接受的类型,T:表示目标类型 @Nullable T convert(S source); }
-
类具体代码如下:
```java
public class StringToDataConverter implements Converter<String, Date>
{
@Override
public Date convert(String source)
{
if (null == source) throw new RuntimeException("source is null!");
try
{
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
return df.parse(source);
} catch (ParseException e)
{
throw new RuntimeException("Converter String to Date failed!");
}
}
}
```
- 在springmvc.xml配置文件中添加配置:
```xml
<!--配置自定义类型转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lin.utils.StringToDataConverter"/>
</set>
</property>
</bean>
<!--开启springMVC框架注解的支持,增加类型转换器,使其生效-->
<mvc:annotation-driven conversion-service="conversionService"/>
```
---
4.获取Servlet原生的API
直接在控制器方法中添加两个参数:
HttpServletRequest request , HttpServletResponse response
@RequestMapping("/getServletApi")
public String getServletApi( HttpServletRequest request , HttpServletResponse response) {
HttpSession session = request.getSession();
System.out.println(session);
ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);
System.out.println(request);
System.out.println(response);
return "success";
}springmvc之获取servlet原生对象
5.常用注解
5.1.@RequestParam
作用:把请求中指定名称的参数给控制器中的形参赋值。
属性:
-
value:请求参数中的名称
-
required:请求参数中是否必须提供此参数。默认是true。表示必须提供,否则报错。
在不使用该注解时,要获取到请求中的参数,则是要求表单提交的name必须和控制器方法中形参的名称一样才行,现在,使用了该注解,则是不在要求表单提交的name必须和控制器方法中形参的名称一样了,可以自定义表单提交的name和制器方法中的那一个形参对应,并赋值给形参。
<!-- jsp页面 -->
<a href="getRequestParam?username=123456">RequestParam</a>
// 控制器方法
@RequestMapping("/getRequestParam")
public String getRequestParam(@RequestParam("username") String name) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getRequestParam()"+ name);
return "success";
}
### 5.2.@RequestBody
与之对应的@ResponseBody在第8章节(响应json数据)有讲到。
作用:
- 用于获取请求体内容。直接使用得到的是key1=value1&key2=value2…结果的数据。
- get请求方式不适用。
属性:
required:
- 是否必须有请求体。默认是true。
- 当取值是true时,get请求方式后报错。
- 如果前置是false,则get请求方式得到的是Null
<!-- jsp页面 -->
<form action="getRequestBody" method="post">
用户名:<input name="username" type="text"><br>
密码:<input name="password" type="text"><br>
金额:<input name="money" type="text"><br>
<input value="提交" type="submit"><br>
</form>
// 控制器方法
@RequestMapping("/getRequestBody")
public String getRequestBody(@RequestBody String body) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getRequestParam()"+ body);
return "success";
}
测试结果:
执行了>>>>>>
Anno.getRequestBody()>>>>>>username=123&password=44&money=55
5.3.@PathVariable
作用:用于绑定URl中的占位符。例如:请求URL中/deleted/{id}
,这个id
就是URl占位符,URl支持占位符是spring3.0之后加入的,是springMVC支持rest风格URL的一个重要标志。
属性:
value:
- 用于指定URL中占位符名称。
requried:
- 是否必须提供占位符。
<!-- jsp页面 -->
<a href="getPathVariable/124">getPathVariable</a>
// 控制器方法
@RequestMapping("/getPathVariable/{id}")
public String getPathVariable(@PathVariable("id") String name) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getPathVariable()>>>>>>" + name);
return "success";
}
测试结果:
执行了>>>>>>
Anno.getPathVariable()>>>>>>124
5.4.@RequestHeader
作用:获取请求消息头
属性:
value:
- 用于获取请求消息头
required:
- 是否必须有此消息头
<!-- jsp页面 -->
<a href="getRequestHeader">getRequestHeader</a>
@RequestMapping("/getRequestHeader")
public String getRequestHeader(@RequestHeader("Accept") String name) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getRequestHeader()>>>>>>" + name);
return "success";
}
测试结果:
执行了>>>>>>
Anno.getRequestHeader()>>>>>>application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg, application/x-ms-xbap, /
5.5.@CookieValue
作用:用于把指定Cookie名称的值传入控制器方法参数
属性:
value:
- 用于指定Cookie名称
required:
- 是否必须有Cookie
<!-- jsp页面 -->
<a href="getCookieValue">getCookieValue</a>
@RequestMapping("/getCookieValue")
public String getCookieValue(@CookieValue("JSESSIONID") String name) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getCookieValue()>>>>>>" + name);
return "success";
}
测试结果:
执行了>>>>>>
Anno.getCookieValue()>>>>>>4E4AE9E316A410C51BC358141EE03937
5.6.@ModelAttribute
作用:
- 可以用于修饰方法或者参数。
- 出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。可以修饰有返回值的方法,也可以修饰没有返回值的方法。类中只要有标注了该注解的方法,那么所有在该类中的映射方法在执行之前都要先执行标志了该注解的方法。
- 出现在参数上,获取指定的数据赋值给参数。
属性:
value:
- 用于获取数据的key。key可以是POJO的属性名称,也可以是map结构的key。
出现在方方法上:
<!-- jsp页面 -->
<form action="getModelAttribute" method="post">
用户名:<input name="uname" type="text">
密码:<input name="age" type="text">
<input value="提交" type="submit">
</form>
@RequestMapping("/getModelAttribute")
public String getModelAttribute(User2 user) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getModelAttribute()>>>>>>" + user);
return "success";
}
@ModelAttribute
public User2 u(String uname) {
User2 user2 = new User2();
user2.setUname(uname);
user2.setAge(20);
user2.setDate(new Date());
System.out.println("Anno.u()" + ">>>>" + user2);
return user2;
}
测试结果:
Anno.u()>>>>User2 [uname=123,
age=20
, date=Fri Feb 21 12:19:57 CST 2020]
执行了>>>>>>
Anno.getModelAttribute()>>>>>>User2 [uname=123,age=11
, date=Fri Feb 21 12:19:57 CST 2020]
分析一:标志了@ModelAttribute的方法在控制器方法之前先执行。类中只要有标注了该注解的方法,那么所有在该类中的映射方法在执行之前都要先执行标志了该注解的方法。
分析二:标志了@ModelAttribute的方法能够获取到表单传过来的值,用法和请求参数绑定一样(章节3)
分析三:标志了@ModelAttribute的方法只能填补表单没有传过来的值,不能修改表单传过来的值,也不能重新添加一个变量(对单一POJO整体来说)。
出现在参数上:
@RequestMapping("/getModelAttribute2")
public String getModelAttribute2(@ModelAttribute("abc") User2 user2) {
System.out.println("执行了>>>>>>");
System.out.println("Anno.getModelAttribute()>>>>>>" + user2);
return "success";
}
@ModelAttribute
public void getModelAttributeModelAttribute2(String uname, Map<String, User2> map) {
User2 user2 = new User2();
user2.setUname(uname);
user2.setAge(20);
user2.setDate(new Date());
map.put("abc", user2);
}
5.7.@SessionAttributes
只能作用在类上。
作用:
- 用于多次执行控制器方法间的参数共享
- 把request作用域的数据保存到Session作用域
属性:
value:
- 用于指定存入属性的名称
type:
- 用指定存入数据的类型
总结:
- @SessionAttributes注解保存的数据是保存到全局的Session域中的,在应用中都能获取到数据。
- 使用
SessionStatus.setComplete();
方法全部删除保存到Session域中的数据,不过,只能删除本类中使用了@SessionAttributes注解保存到Session域的数据,其他类使用@SessionAttributes注解保存的数据不能删除。
1、可以通过SpringMVC特有的ModelMap、Model在Controller中自动保存数据到session,也可以通过传统的HttpSession等参数保存session数据
2、保存session数据必须使用@SessionAttributes注解,该注解有2种参数声明方式(value和types),且该注解声明必须写在类上,不能在方法上
3、保存的session数据必须与@SessionAttributes注解中的参数列表对应,未被声明的参数无法保存到session中
4、使用SessionStatus可以清除session中保存的数据,注意是全部清除,无法单独删除指定的session数据。同时,清除时有效权限遵循上述第2、3条规则(借用此规则可人为达到删除指定session数据的效果)
5、通过ModelMap等读取session中数据时,也有上述的参数权限限制
6、使用ModelMap或Model等保存session数据时,ModelMap必须作为方法参数传入,在方法中新定义的无效。同时,只要把ModelMap作为参数传入,即使是被别的方法调用也能起效
7、使用@ResponseBody注解时(一般配合ajax使用),无法保存session数据
8、@SessionAttributes注解可以使用value和types 2种参数列表
9、使用HttpSession的传统方式操作没有上述注解及权限等限制,下面有简单测试,但是不做具体说明
以下还有几个应该算是常识性的知识点
10、操作session数据可以跨类,与包或者url的路径等也没有关系
11、同一个session值操作,后面的值会覆盖前面的值
6.响应数据和结果视图
6.1.返回值是String类型
标志了@RequestMapping注解的方法的返回值是String类型。返回值就是视图文件的名称,然后再通过内部资源视图解析器,找到真正的视图物理位置。
内部资源视图解析器:
org.springframework.web.servlet.view.InternalResourceViewResolver
要初始化两个参数:
/WEB-INF/views/:表示视图所在的物理文件夹
.jsp:表示视图文件的后缀名
6.2.返回值是void类型
若返回值是void类型,则会自动的使用映射路径作为视图文件名进行视图解析,所以,当没有和映射名称一样的视图文件,则会报错。
//页面:
//<a href="user/testVoid">testVoid</a>
//控制器方法:
@RequestMapping("/user/testVoid")
public void testVoid() {
System.out.println("Response.testVoid()");
}
视图解析器的规则如上面章节一样。
当然,不是说返回值是void类型的控制器方法,就一定要有一个和映射路径名一样的页面页面,也是可以使用Servlet原生API进行转发或重定向。
6.3.返回值是ModelAndView对象
ModelAndView是spring提供的一个对象,可以用来调整具体的JSP视图。
ModelAndView对象要自己创建一个新的。
//页面:
//<a href="user/ModelAndView">testVoid</a>
//控制器方法:
@RequestMapping("/user/ModelAndView")
public ModelAndView testModelAndView() {
System.out.println("Response.testModelAndView()");
ModelAndView mv = new ModelAndView();
// successResponse是视图的文件名称,不要后缀名,会自动调用视图解析器。
mv.setViewName("successResponse");
mv.addObject("bb", "张三");
return mv;
}
ModelAndView对象是视图和Model的结合。可以在对象中设置视图映射,和返回值是String类型的过程类似,还可以在对象中设置Model,给视图传递数据。
6.4.使用forward和redirect关键字进行页面转发和重定向
@RequestMapping("/ForwardOrRedirect")
public String testForwardOrRedirect() {
System.out.println("Response.ForwardOrRedirect()");
// 转发
// return "forward:/anno.jsp";
// 重定向
return "redirect:/user/ModelAndView";
}
在关键字后加上路径。
6.5.Model、ModelMap
Model:
作用:保存数据到request域。传递数据。
@RequestMapping("/...")
public String setModel(Model model) {
model.addAttribute("abc", "李四");
return "success";
}
ModelMap:
作用:获取数据。还可以存储数据。
@RequestMapping("/...")
public String getSessionAttribute(ModelMap modelMap) {
Object object = modelMap.get("abc");
modelMap.addAttribute("abc", "李四");
System.out.println("Anno.getSessionAttribute()" + object);
return "success";
}
Spring中Model、ModelMap及ModelAndView之间的区别+传递参数
SpringMvc中Model、ModelMap、ModelAndView理解和具体使用总结
7.过滤静态资源
DispatcherServlet会拦截到所有的资源,导致一个问题就是静态资源(img/css/js)也会被拦截到,从而不能被使用。解决问题就是需要配置静态资源不进行拦截,springMVC.xml配置文件按添加如下配置:<mvc:resources/>配置不过滤。
<mvc:resources location="/js/**" mapping="/js/**"/>
<mvc:resources location="/css/**" mapping="/css/**"/>
<mvc:resources location="/img/**" mapping="/img/**"/>
location:表示根目录(WebContent)下的所有文件
mapping:表示以/
开头的所有静态资源请求路径,如/css/a.css或者/js/a/b.js等等
8.响应json数据-Ajax
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
<input value="发送" id="bot" type="button">
<script >
$("#bot").click(function() {
alert("jjjj");
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=utf-8",
data:"{'username':'李四','password':'123','age':'22'}",
dataType:"json",
type:"post",
success:function(data){
alert(data);
}
})
})
</script>
</body>
</html>
@RequestMapping("/user/testAjax")
@ResponseBody // 表示是返回数据,而不是进行查找视图什么的。。。这里返回 成功 两个子给客户端。
public String testAjax(@RequestBody String name) {
System.out.println("Response.testAjax()");
System.out.println(name);
return "成功";
}
测试结果:
Response.testAjax()
{‘username’:‘李四’,‘password’:‘123’,‘age’:‘22’}
@ResponseBody // 表示是返回数据,而不是进行查找视图什么的。。。
可以知道,通过@RequestBody注解能够获取到请求的请求体,但是只能够获取到请求体的String类型,不能自动的转换成POJO类型。
针对于这一点,springMVC已为我们想到了,我们只要导入对应的jar包,就能够实现自动把请求体转换成我们的POJO对象,但是要注意一点,就是请求体中的属性名要和POJO类型的属性名一样。
要导入的jar包:
jackson-annotations-2.10.2
jackson-core-2.10.2
jackson-databind-2.10.2
完成导包之后,@RequestBody注解就能够自动帮我们完成转换了,这时我们就可以这样写了:
@RequestMapping("/user/testAjax")
@ResponseBody
public User3 testAjax(@RequestBody User3 user) {
System.out.println("Response.testAjax()");
System.out.println(user);
user.setAge(110);
return user;
}
返回一个经过spring后台处理过的User3对象给客户端,处理过的User3会转换成json类型的数据。
Spring MVC 中如何自定义 Gson 的消息转换器?
9.文件上传
9.1.文件上传的必要前提
- form表单的enctype取值必须是:multipart/form-data (默认值是:application/x-www-form-urlencoded)
- enctype:是表单请求正文的类型
- method属性取值必须是Post
- 提供一个文件选择域:
9.2.文件上传原理
当form表单的enctype取值不是默认值后,**request.getParameter()**将失效。
enctype="application/x-www-form-urlencoded"时,form表单的正文内容是:key=value&key=value…的形式;
当form表单的enctype取值文Mutilpart/form-data时,请求正文内容就编程:每一部分都是MIME类型描述的正文:
---------------------------------7dela433602ac 分界符
Content-Disposition:form-data;name="userName" 协议头
aaa 协议的正文
---------------------------------7dela433602ac 分界符
Content-Disposition:form-data;name="file";filename="E:\wenjian\README.md"
Content-Disposition:text/plain 协议的类型(MIME类型)
bbbbbbbbbbbbbb 文件内容
bbbbbbbbbbbbbb
bbbbbbbbbbbbbb
---------------------------------7dela433602ac 分界符
9.3.借助第三方组件实现文件上传
如果只是用原生的方式上传文件,则只会得到上面的MIME类型的正文,我们好要解析才能得到真正的文件内容。不过,我们可以借用第三方组件帮我们解析出真正的文件的内容,剩下解析这一过程而直接得到文件的内容。
第三方组件:
commons-fileupload-1.4.jar
commons-io-2.6.jar
commons-io不属于文件上传组件的开发jar文件,但commons-fileupload组件从1.1版本开始,它工作时需要commons-io包的支持
9.4.传统方式上传文件代码实现
<form action="file/fileupload" method="post" enctype="multipart/form-data">
<input name="file1" type="file"><br><br>
<input type="submit" value="上传文件">
</form>
@RequestMapping("/file/fileupload")
public String testFileUpLoading(HttpServletRequest request) throws Exception {
// 上传的位置
// String realPath =
// request.getSession().getServletContext().getRealPath("/uploads/");
// System.out.println(realPath);
String realPath = "E:\\project\\springMVC\\WebContent\\uploads";
// 判断该文件夹是否存在
File file = new File(realPath);
if (!file.exists()) {
// 不存在则创建文件夹
file.mkdirs();
}
// 解析request对象,获取上传文件项:重点*************
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
// 解析request
List<FileItem> parseRequest = fileUpload.parseRequest(request);
// 遍历文件项
for (FileItem fileItem : parseRequest) {
// 进行判断,当前fileItem对象是否是上传文件项
if (fileItem.isFormField()) {// 说明是普通表单项
System.out.println("普通表单项");
} else {// 说明是上传文件项
// 获取上传表单文件域名称
String fieldName = fileItem.getFieldName();
// 获取上传文件名称
String name = fileItem.getName();
// 使用UUID类获取绝对的id
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 5);
String uuidFileName = uuid + "_" + name;
// 完成文件上传
fileItem.write(new File(realPath, uuidFileName));
// 删除临时文件
fileItem.delete();
}
}
return "successResponse";
}
9.5.springMVC方式上传文件代码实现
原理:
代码实现:
<form action="file/testFileUpLoadingSpringMVC" method="post" enctype="multipart/form-data">
<input name="file2" type="file"><br><br>
<input type="submit" value="上传文件">
</form>
@RequestMapping("/testFileUpLoadingSpringMVC")
// 直接使用MultipartFile对象作为参数,但是参数名必须和form表单的文件域的那么属性一样。******
public String testFileUpLoadingSpringMVC(MultipartFile file2) throws Exception {
String realPath = "E:\\project\\springMVC\\WebContent\\uploads";
// 判断该文件夹是否存在
File file = new File(realPath);
if (!file.exists()) {
// 不存在则创建文件夹
file.mkdirs();
}
// 获取上传文件名称******
String originalFilename = file2.getOriginalFilename();
System.out.println(originalFilename);
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 5);
String uuidFileName = uuid + "_" + originalFilename;
// 完成文件上传*******
file2.transferTo(new File(realPath, uuidFileName));
return "successResponse";
}
注意点:在映射方法的参数中直接使用MultipartFile对象作为参数,用于接收input文件域,但是参数名必须和form表单的文件域的那么属性一样。
在执行上面的代码之前,要先在springmvc.xml文件中配置好文件解析对象:
<!-- 配置文件解析对象,要求id必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857460"></property>
</bean>
9.6.跨服务器上传文件代码实现
有时候会有单独的服务器进行存储文件,而不是在应用服务器中存储文件,这就要求我们跨服务器上传文件了。
要导入两个jar包:
jersey-client-1.19.4.jar
jersey-core-1.19.4.jar
<form action="file/testFileUpLoadingSpringMVC" method="post" enctype="multipart/form-data">
<input name="file2" type="file"><br><br>
<input type="submit" value="上传文件">
</form>
@RequestMapping("/testOther")
public String testOther(MultipartFile file2) throws Exception {
// 文件服务器的URL地址。
String url = "http://localhost:8089/springMVCFileServer/upload/";
// 获取上传表单文件域名称
String originalFilename = file2.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replace("-", "").substring(0, 5);
String uuidFileName = url + uuid + "_" + originalFilename;
// 创建客户端对象
Client client = new Client();
// 和文件服务器进行连接
WebResource resource = client.resource(uuidFileName);
// 上传文件
resource.put(file2.getBytes());
return "successResponse";
}
10.异常处理
异常处理过程:[
10.1.实现 HandlerExceptionResolver
接口处理异常
这时全局异常处理方式,只要有异常,都会调用:
// 处理异常的类
public class MyExceptionControllerResolver implements HandlerExceptionResolver {
// Exception: 接送被抛出 的异常
// 返回值类型为 ModelAndView
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
System.out.println("------------->>>>---------------");
System.out.println(ex.getMessage());
ModelAndView andView = new ModelAndView();
andView.addObject("abc", "123456789");
andView.setViewName("successException");
return andView;
}
}
处理异常的类要在容器中注册才能有效:
<!-- 注册自定义异常类 -->
<bean id="myExceptionControllerResolver" class="com.start.handler.MyExceptionControllerResolver"></bean>
// 控制器---
@Controller
@RequestMapping("/user")
public class MyExceptionController {
@RequestMapping("/testMyException")
public String testMyException() throws Exception{
try {
int i = 11/0;
}catch (Exception e) {
e.printStackTrace();
throw new Exception("不能运算。。。");
}
return "successException";
}
}
<!-- 一个请求页面-->
<a href="user/testMyException">发送异常</a>
<!-- 处理异常页面: successException.jsp-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>成功了</h1>
${abc}
</body>
</html>
结果:
10.2. @ExceptionHandler
注解处理异常
按照异常类进行处理异常。
不像10.1章节处理异常那样处理全部的异常,使用了该注解处理异常,是能够按照符合对应异常类型的异常才能进行处理异常,若是出现的异常不符号对应的异常类型,则是直接抛出,不进行处理。
使用:在控制器中声明时,此类方法适用于该控制器(或其任何子类)的@RequestMapping
方法引发的异常,将会交给标志了@ExceptionHandler注解的方法进行处理异常,但前提是引发的异常类型符合标志了@ExceptionHandler注解方法的处理异常类型,否则不进行处理。
下面是一个例子:
// 这是一个自定的异常类型
public class MyException extends Exception {
String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public MyException(String message) {
super();
this.message = message;
}
}
// 一个控制器
@Controller
@RequestMapping("/user")
public class MyExceptionController {
@RequestMapping("/testExceptionHandlerController")
public String testExceptionHandlerController() throws MyException{
try {
int i = 11/0;
}catch (Exception e) {
e.printStackTrace();
throw new MyException("不能运算。。。");
}
return "successException";
}
// 处理异常的方法
@ExceptionHandler
public ModelAndView testExceptionHandler(MyException exception) {
System.out.println("____-----------------...>>>>>====-----");
System.out.println(exception.getMessage());
ModelAndView view = new ModelAndView();
view.addObject("abc", "159487");
view.setViewName("successException");
return view;
}
}
上面代码说明:在MyExceptionController类中,所有的控制器方法(@RequestMapping标志的方法)只要发生异常,并且异常类型是MyException类型,都会调用标志了@ExceptionHandler注解的方法,进行处理异常。
特性:
-
若
@ExceptionHandler
注解标志的方法是在标志@Controller注解的类中,则表示该方法只能处理该类中引发的异常(异常类型要对应),其他类引发的异常不能处理。 -
可以在标志了
@ControllerAdvice
注解的类中声明@ExceptionHandler
方法,在这种情况下,它会处理来自许多控制器的@RequestMapping
方法的异常,前提是异常类型对应。 -
设置
@ExceptionHandler
注解标志的方法能处理的异常类型:- 直接在方法的参数中定义处理的异常类型
@ExceptionHandler
注解的vale属性能够设置处理的异常类型,而且能设置多个异常类型
-
与使用
@RequestMapping
annotation 注释的标准控制器方法非常相似,@ExceptionHandler
方法的方法 arguments(参数) 和 return 值可以是灵活的。例如,可以在 Servlet 环境中访问HttpServletRequest
,在 Portlet 环境中访问PortletRequest
。 return 类型可以是String
,它被解释为视图 name,ModelAndView
object,ResponseEntity
,或者您也可以添加@ResponseBody
以使用消息转换器转换方法 return value 并将其写入响应流。
10.3. @ResponseStatus
https://blog.csdn.net/qq_17586821/article/details/79822669
用处:可以自定义的设置不想让人看到的页面。。。
11.拦截器
11.1.拦截器的作用
SpringMVC的拦截器的作用类似Servlet的过滤器Filter,用于对处理器进行预处理和后处理。
拦截器链:拦截器链解释将拦截器按照一定的顺序联结成一条莲。在访问到被拦截的方法或字段时,连接器链中的拦截器就会按照之前定义的顺序调用拦截器。
拦截器和过滤器的区别:
-
过滤器是Servlet规范中的一部分,任何java web工程都可以使用。
-
拦截器是SpringMVC自己的框架,只有使用了SpringMVC框架的工程才可以使用。
-
过滤器在 url-pattern 中配置了
/*
后,可以对所有的资源进行拦截。 -
拦截器它只会拦截访问的控制器方法,如果是 访问jsp、html、css、image或者js是不会进行拦截的。
它也是AOP思想的具体应用。
要想自定义拦截器,要切必须实现:HandlerInterceptor接口。
11.2.简单使用
// 自定义拦截器
package com.start.handler;
@Controller
public class MyInterceptor implements HandlerInterceptor{
/**
* 预处理:在控制器方法之前执行
* return true 表示放行,只想能够下一个拦截器,如果没有,则执行控制器中的方法
* return false 表示不放行,但是可以用提供的参数进行各种操作,例如转发或重定向
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return false;
}
/**
* 后处理方法: 表示在控制器方法执行完之后,并且在响应页面之前,执行拦截器的方法
*
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
/**
* 在响应页面之后执行拦截器方法
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
在自定义拦截器后,还要器IOC容器中注册拦截器。在springmvc.xml配置文件中注册:
<!--springmvc.xml配置文件-->
<!-- 配置拦截器 **能配置多个 -->
<mvc:interceptors>
<!-- 拦截器链的顺序是安装配置的拦截器的先后顺序执行的 -->
<mvc:interceptor>
<!-- <mvc:mapping>和<mvc:exclude-mapping>标签只能出现一个 -->
<!-- 要拦截的具体方法 : -->
<mvc:mapping path="/user/*" />
<!-- 不要拦截的具体方法,其他的具体方法都拦截 -->
<mvc:exclude-mapping path="/user/*" />
<!-- 配置拦截对象 -->
<bean class="com.start.handler.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
12.映射路径的一个*和两个**
例如
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.web.interceptor.user.UserAuthInterceptor" />
</mvc:interceptor>
/**
的意思是所有文件夹及里面的子文件夹(能映射到多层个子级)
/*
是所有文件夹,不含子文件夹(只能映射到子级)
/
是web项目的根目录
13.web.xml中url-pattern /和/*之间的区别
在写springMVC配置web.xml的时候会碰到下面有时候写/,有的时候又写/*;
那么这两者有什么区别呢?我现在进行一些讲解:
1.当配置/
的时候,它会匹配到路径型的url,就不会匹配到模式为*.jsp型的url
例如:
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
这个路径可以匹配/login /hello类型的url,但是不会匹配到index.jsp类型的url。
2.当配置/*
的时候,它就会匹配到所有类型的url,包括路径型的,有各种后缀的等等。
例如:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--强制编码-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/**</url-pattern>
</filter-mapping>
这个能过滤以各种类型的url,我们以.jsp、.html 、 /hello 等各种类型的url都会被处理成UTF-8的编码。
最后总结:
springMVC-总结
<!--配置扫描器-->
<!--配置适配器和映射器-->
<!--配置视图解析器-->
<!--配置文件上传-->
<!--配置拦截器-->
<!--配置静态资源放行-->
<!--配置扫描器-->
<context:component-scan
base-package="com.wwj.web.controller"></context:component-scan>
<!--配置适配器和映射器-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--配置视图解析器-->
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!--配置文件上传-->
<!-- 配置文件解析对象,要求id必须是multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="104857460"></property>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<!-- 拦截器链的顺序是安装配置的拦截器的先后顺序执行的 -->
<mvc:interceptor>
<!-- <mvc:mapping>和<mvc:exclude-mapping>标签只能出现一个 -->
<!-- 要拦截的具体方法 : -->
<mvc:mapping path="/user/*" />
<!-- 不要拦截的具体方法,其他的具体方法都拦截 -->
<mvc:exclude-mapping path="/user/*" />
<!-- 配置拦截对象,该对象要implements HandlerInterceptor接口 -->
<bean class="com.start.handler.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!--配置静态资源放行-->
<mvc:default-servlet-handler/>:
该标签的作用:https://blog.csdn.net/codejas/article/details/80055608
<mvc:resources location="/js/**" mapping="/js/"/>