一、Spring与Web环境集成
1.1 AppliacationContext应用上下文获取方式
应用上下文对象是通过new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但每次从容器中获取Bean时都要编写此配置文件,这样的弊端是配置文件加载多次,应用上下文对象创建多次。
处理方式:
① 静态块:
② 监听器:
在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在web应用启动时,就加载spring的配置文件,创建应用上下文对象ApplicationContext,再将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象。
创建监听器实现类: 把容器存储到最大的域ServletContext中
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
// 读取web.xml中的全局参数
String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
//初始化是创建ApplicationContext的容器
ApplicationContext app = new ClassPathXmlApplicationContext(contextConfigLocation);
//将Spring的应用上下文对象存储到ServletContext域中
servletContext.setAttribute("app",app);
System.out.println("spring容器创建完毕");
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
web.xml中配置监听器:
<!--配置监听器-->
<listener>
<listener-class>com.chu.listener.ContextLoaderListener</listener-class>
</listener>
<!--全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
Uservlet代码如下:
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
//ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
//ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
1.2 spring提供获取应用上下文的工具
1.1中不用手动去实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
① pom.xml中导入依赖坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
② web.xml中配置监听器和全局初始化参数
<!--配置spring中的监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置spring中的全局初始化参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
获取容器:
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
ServletContext servletContext = this.getServletContext();
//ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
UserService userService = app.getBean(UserService.class);
userService.save();
}
}
二、SpringMVC的简介
2.1 SpringMVC概述
SpringMVC是一种基于Java的实现MVC设计模式的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在SPringle Web Flow中。
SpringMVC已经目前最主流的MVC框架之一,它通过一套注解,让一个简单的Java类成为处理请求的控制器(servlet),而无需实现任何接口。 同时还支持RESTful编程风格的请求。
使用SpringMVC控制Servlet的共有行为。
从上图知SpringMVC的开发步骤:
① 导入SpringMVC相关坐标
pop.xml中:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
② 配置SpringMVC核心控制器DispatcherServlet(共有行为)
web.xml中:
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
③ 编写Controller和视图页面(在springMVC中习惯把此简单的Bean称为Controller)
④ 将Controller使用注解配置到Spring容器中(@Controller)、配置Controller类中业务方法的映射地址(特有行为)
Controller类:
@Controller
//@RequestMapping("/user")
public class UserController {
@RequestMapping("/quick") //配置业务方法映射地址
public String save(){
System.out.println("Controller save running...");
return "success.jsp"; //表示当前资源所在地址
//如果类上加入/user,在访问此资源时要使用/success.jsp
}
}
success.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>创建成功</h1>
</body>
</html>
⑤ 配置spring-mvc.xml文件(配置组件扫描)
<!--Controller的组件扫描-->
<context:component-scan base-package="com.chu.controller"/>
同时要在web.xml中定义全局初始化参数,来加载此配置文件:
<!--配置SpringMVC的前端控制器-->
<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:spring_mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
⑥ 客户端发起请求测试
http://localhost:8080/spring_mvc/quick 页面输出创建成功,控制台输出Controller save running...
2.2 SpringMVC流程图示
三、SpringMVC的组件解析
3.1 SpringMVC的执行流程
处理器Handler是上述例子中创建的Controller。
3.2 SpringMVC注解解析
3.3 SpringMVC的XML配置解析 (资源解析器)
success.jsp前省略了forword:(请求转发-默认)或redirect:(重定向)
@Controller
public class UserController {
@RequestMapping("/quick") //配置业务方法映射地址
public String save(){
System.out.println("Controller save running...");
return "success.jsp";
}
}
例如访问jsp包下的success.jsp文件: 减轻开发复杂度
spring_mvc.xml配置如下:
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--拿到前缀,视图的名称,再拿到后缀 /jsp/success.jsp-->
<property name="prefix" value="/jsp"></property>
<property name="suffix" value=".jsp"></property>
</bean>
3.4 知识要点
四、SpringMVC的数据响应
4.1 SpringMVC的数据响应方式
1)页面跳转
① 直接返回字符串
② 通过ModleAndView对象返回
2)回写数据
① 直接返回字符串
② 返回对象或集合
4.2 页面跳转
1)返回字符串形式
直接返回字符串:此方式会将返回的字符串域视图解析器的前后缀拼接后跳转。
2)返回ModleAndView对象的几种方式
@Controller
public class UserController {
// 不常用,尽量使用框架提供的对象
@RequestMapping("/quick5") //配置业务方法映射地址
public String save5(HttpServletRequest request){ // request原生的javaWeb对象
request.setAttribute("username","good");
return "success";
}
@RequestMapping("/quick4") //配置业务方法映射地址
public String save4(Model model){ //model是springMVC封装好的对象
model.addAttribute("username","jie");
return "success";
}
@RequestMapping("/quick3") //配置业务方法映射地址
public ModelAndView save3(ModelAndView modelAndView){
// 对方法参数进行设置模型数据
modelAndView.addObject("username","xiao");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
@RequestMapping("/quick2") //配置业务方法映射地址
public ModelAndView save2(){
/*
* Model:模型 封装数据
* View:视图 展示数据
* */
ModelAndView modelAndView = new ModelAndView();
//设置模型数据
modelAndView.addObject("username","chu");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
}
spring_mvc.xml中配置如下:
<!--Controller的组件扫描-->
<context:component-scan base-package="com.chu.controller"/>
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--拿到前缀,视图的名称,再拿到后缀 /jsp/success.jsp-->
<property name="prefix" value=""></property>
<property name="suffix" value=".jsp"></property>
</bean>
success.jsp:
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>创建成功 ${username}</h1>
</body>
</html>
4.3 回写数据
1)直接返回字符串
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print() 即可。
response.getWriter().print(),不仅可以打印输出文本格式的(包括html标签),还可以将一个对象以默认的编码方式转换为二进制字节输出
response.getWriter().writer(),只能打印输出文本格式的(包括html标签),不可以打印对象。
那么在Controller中该如何直接回写字符串:
① 通过SpringMVC框架注入的response对象,使用response.getWriter().print()回写数据,此时不需要试图跳转,业务方法返回值为void。
@RequestMapping("/quick6") //配置业务方法映射地址
public void save6(HttpServletResponse response) throws Exception{ // response原生的javaWeb对象
response.getWriter().print("hello chu");
}
② 将需要回写的字符串直接返回,但此时需要通过 @ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。(不加注解默认与跳转与第一种相同了。)如果返回类型是void,则说明响应体为空,什么都不回写。
@RequestMapping("/quick7") //配置业务方法映射地址
@ResponseBody // 不加注解与第一种相同了,要加注解告知框架,方法返回的字符串不是跳转而是在http响应体中返回
public String save7() throws Exception{ // response原生的javaWeb对象
return "hello chu";
}
补:
返回json格式的字符串:
@RequestMapping("/quick8") //配置业务方法映射地址
@ResponseBody // 不加注解与第一种相同了,要加注解告知框架,方法返回的字符串不是跳转而是在http响应体中返回
public String save8() throws Exception{ // response原生的javaWeb对象
return "{\"username\":\"zhangsan\",\"age\":18}"; //返回json格式数据
}
手打格式过于繁琐,导入包,直接转换成json格式:
导入pop.xml坐标:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.0</version>
</dependency>
代码:
@RequestMapping("/quick9") //配置业务方法映射地址
@ResponseBody // 不加注解与第一种相同了,要加注解告知框架,方法返回的字符串不是跳转而是在http响应体中返回
public String save9() throws Exception{ // response原生的javaWeb对象
User user = new User();
user.setUsername("lisi");
user.setAge(99);
//使用json的转换工具将对象转换成json格式字符串再返回
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
}
2)返回对象或集合
期望SpringMVC自动将User转换成json格式的字符串。
要再spring_mvc.xml中配置处理器映射器(即,注入json转换的转换器):
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
测试如下:
@RequestMapping("/quick10") //配置业务方法映射地址
@ResponseBody // 不加注解与第一种相同了,要加注解告知框架,方法返回的字符串不是跳转而是在http响应体中返回
//期望SpringMVC自动将User转换成json格式的字符串
public User save10() throws Exception{ // response原生的javaWeb对象
User user = new User();
user.setUsername("lisi");
user.setAge(99);
return user;
}
在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样的配置比较麻烦,配置的代码比较多,因此,可以使用mvc的注解驱动代替上述配置。
在spring_mvc.xml文件中替换掉上面的处理器映射器配置;
注:
要先在上方导入mvc的命名空间:
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--mvc的注解驱动,默认底层就会继承jackson进行对象或集合的json格式字符串的转换-->
<mvc:annotation-driven/>
五、SpringMVC的请求
5.1 获得请求参数
客户端请求参数的格式是:name=value&name=value… …
服务器端要获得请求的参数,有时还需要进行数据的封装, SpringMVC可以接受如下类型的参数:
基本数据参数
POJO类型参数(简单JavaBean)
数组类型参数
集合类型参数
5.2 获得基本类型参数
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
返回类型void,说明响应体为空,什么都不回写。
@RequestMapping("/quick11") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save11(String username,int age) throws Exception{ // void表示什么都不回写,即:响应体什么都没有
System.out.println(username);
System.out.println(age);
}
发送请求:
http://localhost:8080/spring_mvc/quick11?username=chu&age=25
控制台输出:
chu
25
5.3 获得POJO类型参数
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
业务方法的参数要是POJO(简单bean对象):
@RequestMapping("/quick12") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save12(User user) throws Exception{ // void表示什么都不回写,即:响应体什么都没有
System.out.println(user); //重写了user的toString,输出...
}
发送请求:
http://localhost:8080/spring_mvc/quick12?username=chu&age=99
控制台输出:
User{username='chu', age=99}
5.4 获得数组类型参数
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
@RequestMapping("/quick13") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save13(String[] strs) throws Exception{ // void表示什么都不回写,即:响应体什么都没有
System.out.println(Arrays.asList(strs)); //重写了user的toString,输出...
}
发送请求:
http://localhost:8080/spring_mvc/quick13?strs=aaa&strs=bbb&strs=ccc
控制台输出:
[aaa, bbb, ccc]
5.5 获得集合类型参数
1)常用方式:
获得集合参数时,要将集合参数包装到一个POJO中才可以。
VO包装的POJO泛型是User:
public class VO {
private List<User> userList;
public VO() {
}
public VO(List<User> userList) {
this.userList = userList;
}
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
}
UserController如下: VO是POJO类型,故业务方法的POJO参数的属性名与请求参数的name一致
@RequestMapping("/quick14") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save14(VO vo) throws Exception{
// VO是泛型为user的list集合,请求的数据要是集合形式
// 请求参数不好在网页输入,故写入form.jsp中放入表单进行提交
System.out.println(vo);
}
由于VO是泛型为user的list集合,请求的数据要是集合形式,请求参数不好在网页输入,故写入form.jsp中放入表单进行提交。
form.jsp:
<body>
<%--请求参数不好在网页输入,故放入表单进行提交--%>
<form action="${pageContext.request.contextPath}/quick14" method="post">
<%--表明是第几个User对象(userList[])的username age--%>
用户名1:<input type="text" name="userList[0].username"><br/>
年龄1:<input type="text" name="userList[0].age"><br/>
用户名2<input type="text" name="userList[1].username"><br/>
年龄2<input type="text" name="userList[1].age"><br/>
提交:<input type="submit">
</form>
</body>
发送请求:
http://localhost:8080/spring_mvc/form.jsp
输入表单信息...,之后提交跳转请求,其中参数的表单输入信息后的集合。
控制台输出:
VO{userList=[User{username='zhangsan', age=12}, User{username='lisi', age=11}]}
2)特殊情况:
当使用ajax提交时,可以指定contextType为json形式,那么在方法参数位置使用 @RequestBody 可以直接接收集合数据而无需使用POJO进行包装。
ajax.jsp代码如下:
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/jquery-3.3.1.js"></script>
<script>
var userList=new Array();
userList.push({username:"zhangsan",age:12});
userList.push({username:"lisi",age:15});
//ajax请求,其中将集合转为json字符串
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/quick15",
data:JSON.stringify(userList), //把集合转为json字符串
contentType:"application/json;charset=utf-8"
});
</script>
</head>
请求代码: 使用ajax请求,无需把集合参数封装成POJO类型,使用@RequestBody注解修饰。
@RequestBody是请求体,上述是把请求体的内容封装到userList集合中。
@RequestMapping("/quick15") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save15(@RequestBody List<User> userList) throws Exception{
// @RequestBody是请求体,上述是把请求体的内容封装到userList集合中
System.out.println(userList);
}
发出请求后,可能出现找不到jquery-3.3.1.js的情况,要在spring_mvc.xml中配置访问静态资源:
<!--开放静态资源的访问
方式一:mapping代表映射地址 location代表是哪个路径下的资源开放
-->
<!--<mvc:resources mapping="/**" location="//"/>-->
<!--方式二:先找匹配地址,如果找不到就交给原始容器(Tomcat)找对应的静态资源-->
<mvc:default-servlet-handler/>
发送请求:
http://localhost:8080/spring_mvc/ajax.jsp
控制台输出:
[User{username='zhangsan', age=12}, User{username='lisi', age=15}]
5.6 请求数据乱码问题
当post请求时,数据会出现乱码(例如上述集合表单中文数据提交后,控制台就会出现乱码)。可以设置一个过滤器来进行编码的过滤。
web.xml:
<!--配置全局过滤的filter-->
<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>
发送请求:
http://localhost:8080/spring_mvc/form.jsp 表单提交之后
控制台输出:
VO{userList=[User{username='张三', age=12}, User{username='李四', age=11}]}
5.7 参数绑定注解@requestParam
当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。
@RequestParam把请求参数name映射到username上:
@RequestMapping("/quick16") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save16(@RequestParam(value = "name") String username) throws Exception{
// @RequestParam把请求参数name映射到username上
System.out.println(username);
}
发送请求:
http://localhost:8080/spring_mvc/quick16?name=iii
控制台输出:
iii
5.8 获得Restful风格的参数
@PathVariable把占位符的值映射给username:
//localhost:8080/user/quick17/zhangsan
@RequestMapping("/quick17/{name}") //配置业务方法映射地址,{}占位符
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save17(@PathVariable(value = "name") String username) throws Exception{
// @PathVariable把占位符的值映射给username
System.out.println(username);
}
发送请求:
http://localhost:8080/spring_mvc/quick17/zhangsan
控制台输出:
zhangsan
5.9 自定义类型转换器(不常用)
SpringMVC默认已经提供了一些常用的类型转换器。例如客户端提交的字符串转换成int型进行参数设置。
但是不是所有的数据类型都提供了转换器,每提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
① 定义转换器类实现Converter接口
com.chu.converter包下:
public class DateConverter implements Converter<String, Date> {
public Date convert(String dateStr){
//将日期字符串转换成日期对象返回
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
② 在spring_mvc.xml文件中声明转换器
<!--声明转换器-->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.chu.converter.DateConverter"></bean>
</list>
</property>
</bean>
③ 在<annotation-driven>中引用转换器
<!--mvc的注解驱动,默认底层就会继承jackson进行对象或集合的json格式字符串的转换-->
<mvc:annotation-driven conversion-service="conversionService"/>
发送请求:
http://localhost:8080/spring_mvc/quick18?date=2021-07-13
控制台输出:
Tue Jul 13 00:00:00 CST 2021
5.10 获得Servlet相关API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
HttpServletRequest
HttpServletResponse
HttpSession
@RequestMapping("/quick19") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save19(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception{
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
发送请求:
http://localhost:8080/spring_mvc/quick19
控制台输出:
org.apache.catalina.connector.RequestFacade@6fc2ebae
org.apache.catalina.connector.ResponseFacade@162b9ab4
org.apache.catalina.session.StandardSessionFacade@76d21d1b
5.11 获得请求头
1)@ReqyestHeader
@RequestHeader把请求头的值映射给user_agent:
@RequestMapping("/quick20") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save20(@RequestHeader(value = "User-Agent",required = false) String user_agent) throws Exception{
//@RequestHeader把请求头的值映射给user_agent
System.out.println(user_agent);
}
发送请求:
http://localhost:8080/spring_mvc/quick20
控制台输出:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
2)@CookieValue
@RequestMapping("/quick21") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save21(@CookieValue(value = "JSESSIONID",required = false) String jsessionid) throws Exception{
//@CookieValue把cookie的键映射给jsessionid,得到cookie的值
System.out.println(jsessionid);
}
发送请求:
http://localhost:8080/spring_mvc/quick21
控制台输出:
419A304875A3E039A5CFDDAA5D2781A4
5.12 文件上传(也属于客户端把数据请求发送到服务器端)
1)文件上传客户端三要素:
① 表单项type="file"
② 表单的提交方式时post
③ 表单的enctype属性是多部份表单形式,即enctype="multipart/form-data"
upload.jsp写入三要素(在表单项中):
<body>
<form action="${pageContext.request.contextPath}/quick22" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件<input type="file" name="upload"><br/>
<input type="submit" value="提交"><br/>
</form>
</body>
2)文件上传原理(解释改为多部分表单时…):
5.13 单文件上传步骤
① 导入fileupload和io坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.3</version>
</dependency>
② spring_mvc.xml中配置文件上传解析器
<!--配置文件上传解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--上传文件的编码类型-->
<property name="defaultEncoding" value="UTF-8"/>
<!--上传文件总大小-->
<property name="maxUploadSize" value="5242800"/>
<!--上传单个文件的大小-->
<property name="maxUploadSizePerFile" value="5242800"/>
</bean>
③ 编写文件上传代码
注意:文件上传业务的参数名字要与表单file类型的name相同。
先获取原始文件名称,然后利用transferTo方法将文件保存到本地:
@RequestMapping("/quick22") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save22(String username, MultipartFile uploadFile) throws Exception{
System.out.println(username);
//获得上传文件的名称
String originalFilename = uploadFile.getOriginalFilename();
//把文件保存到D盘根目录,且带上文件名称
uploadFile.transferTo(new File("D:\\"+originalFilename));
}
发送请求:
http://localhost:8080/spring_mvc/upload.jsp 表单提交数据: 张三、(上传的文件)
控制台输出:
张三
(本地D盘有上传的文件)
5.14 多文件上传实现
1)文件名不一样时(uploadFile1、uploadFile2):
<form action="${pageContext.request.contextPath}/quick22" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件1<input type="file" name="uploadFile1"><br/>
文件2<input type="file" name="uploadFile2"><br/>
<input type="submit" value="提交"><br/>
</form>
则分两个参数传入:
@RequestMapping("/quick22") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save22(String username, MultipartFile uploadFile1,MultipartFile uploadFile2) throws Exception{
System.out.println(username);
//获得上传文件的名称
String originalFilename1 = uploadFile1.getOriginalFilename();
//把文件保存到D盘根目录,且带上文件名称
uploadFile1.transferTo(new File("D:\\"+originalFilename1));
String originalFilename2 = uploadFile2.getOriginalFilename();
uploadFile2.transferTo(new File("D:\\"+originalFilename2));
}
2)文件名一样时(uploadFile1、uploadFile1、uploadFile2):
<form action="${pageContext.request.contextPath}/quick23" method="post" enctype="multipart/form-data">
名称<input type="text" name="username"><br/>
文件1<input type="file" name="uploadFile1"><br/>
文件2<input type="file" name="uploadFile1"><br/>
文件3<input type="file" name="uploadFile2"><br/>
<input type="submit" value="提交"><br/>
</form>
文件名相同的并到一个数组参数中传入、文件名不同的分为两个参数:
@RequestMapping("/quick23") //配置业务方法映射地址
@ResponseBody // 加注解告知框架,方法返回的字符串不是跳转而是在http响应体中回写数据
public void save23(String username, MultipartFile[] uploadFile1,MultipartFile uploadFile2) throws Exception{
System.out.println(username);
// 遍历获得上传文件的名称,并保存到D盘根目录
for (MultipartFile multipartFile:uploadFile1){
String originalFilename = multipartFile.getOriginalFilename();
multipartFile.transferTo(new File("D:\\"+originalFilename));
}
String originalFilename2 = uploadFile2.getOriginalFilename();
uploadFile2.transferTo(new File("D:\\"+originalFilename2));
}
六、SpringMVC拦截器
6.1 拦截器(interceptor)的作用
SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的孙旭联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
6.2 拦截器和过滤器的区别
6.3 拦截器的快速入门
自定义拦截器步骤:
① 创建拦截器类实现HandlerInterceptor接口
public class MyInterceptor1 implements HandlerInterceptor {
//在目标方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("priHandle...");
return false; //false表示拦截后,后面方法都不执行
}
//在目标方法执行之后,视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
//在整个流程都执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
② spring_mvc.xml配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.chu.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
③ 测试拦截器的拦截效果
情况1: 拦截器preHandle()方法最后return false(false表示拦截,后面方法都不执行)时:
发送请求:
http://localhost:8080/spring_interceptor/target 页面无显示
控制台输出:
priHandle...
情况2: 拦截器preHandle()方法最后return true(true表示放行,后面方法可以执行)时:
发送请求:
http://localhost:8080/spring_interceptor/target 页面显示Hello World! chu
控制台输出:
priHandle...
目标资源执行...
postHandle...
afterCompletion...
6.4 拦截器实现类的方法以及配置说明
① 方法说明:
例如:
preHandle方法可以利用request对请求参数进行判定;
postHandle方法中的参数modelAndView可以对目标方法中的结果进行修改。
public class MyInterceptor1 implements HandlerInterceptor {
//在目标方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("priHandle...");
//利用request对请求参数进行判定
String param = request.getParameter("param");
if ("yes".equals(param)){
return true;
}else {
request.getRequestDispatcher("/error.jsp").forward(request,response);
return false;
}
}
//在目标方法执行之后,视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//方法中的参数modelAndView可以对目标方法中的结果进行修改
modelAndView.addObject("name","xiao");
System.out.println("postHandle...");
}
//在整个流程都执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
② 配置说明:
两个拦截器时:
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.chu.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="com.chu.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
发送请求: 控制台输出与配置的上下顺序有关,与Filter一样
http://localhost:8080/spring_interceptor/target 页面显示Hello World! xiao
控制台输出:
priHandle...
priHandle222...
目标资源执行...
postHandle222...
postHandle...
afterCompletion222...
afterCompletion...
七、SpringMVC异常处理机制
7.1 异常处理的思路
7.2 异常处理两种方式
1)使用SprigMVC提供的简单异常处理器SimpleMappingExceprionResolver
SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置
spring_mvc.xml文件中配置如下:
<!--配置异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView" value="error"/>
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error1"/>
<entry key="com.chu.exception.MyException" value="error2"/>
</map>
</property>
</bean>
2)实现Spring的异常处理接口HandlerExceptionResolver自定义自己的异常处理器
① 创建异常处理器类实现HandlerExceptionResolver
public class MyExceptionResolver implements HandlerExceptionResolver {
/*
参数Exception:异常对象
返回值ModelAndView:跳转到错误视图信息
*/
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if(e instanceof MyException){
modelAndView.addObject("info","自定义异常");
}else if(e instanceof ClassCastException){
modelAndView.addObject("info","类转换异常");
}
modelAndView.setViewName("error");
return modelAndView;
}
}
② spring_mvc.xml中配置异常处理器(让spring容器知道有个自定义…)
<!--自定义异常处理器-->
<bean class="com.chu.resolver.MyExceptionResolver"/>
③ 编写异常页面error.jsp
<body>
<h1>通用的错误提示页面</h1>
<h1>${info}</h1>
</body>
④ 测试异常跳转
发送请求:
http://localhost:8080/spring_exception/show 页面显示 error页面信息
控制台输出:
show running......
自定义异常....