一、SpringMVC概述
Spring MVC是由Spring官方提供的基于MVC设计理念的web框架。
Spring MVC是基于Servlet封装的用于实现MVC控制的框架,实现前端、服务端的交互。
1.1 SpringMVC优势
- 严格遵守了分层思想;
- 采用了松耦合、插件式结构,相比较于自己封装的BaseServlet以及其他的一些MVC框架,更加灵活、更具扩展性;
- SpringMVC是基于Spring的扩展,提供了一套完善的MVC注解;
- SpringMVC在数据绑定、视图解析都提供了多种处理方式,可灵活配置;
- SpringMVC对RESTFUL URL设计风格提供了良好的支持。
1.2 SpringMVC工作原理
- 接受并解析请求;
- 处理请求;
- 数据渲染、响应请求;
二、SpringMVC框架部署
2.1 基于Maven创建一个web工程
2.2 添加SpringMVC依赖
- spring-context
- aspects
- jdbc
- web
- webmvc
- junit
<properties>
<spring.version>5.3.2</spring.version> <!--统一定义版本-->
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</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>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2.3 创建SpringMVC配置文件
- 在resource目录下创建名为spring-servlet.xml的文件
- 添加MVC命名空间
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.xsbc"/>
<!-- 声明MVC使用注解驱动 -->
<mvc:annotation-driven />
</beans>
2.4 在web.xml中配置SpringMVC的前端控制器
SpringMVC提供了一个名为DispatcherServlet的类(SpringMVC中央处理器-前端控制器),用于拦截用户请求交由SpringMVC处理
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
三、SpringMVC框架使用
在SpringMVC中,将接受用户请求、处理用户请求的类称为Controller(控制器)
3.1 创建控制器
1)创建控制器类
- 创建一个名为‘com.xsbc.controllers’的包(此包需在Spring注解扫描的范围内);
- 创建一个类(无需做任何继承和实现);
- 在类上添加‘@Controller’注解,声明此类为SpringMVC的控制器;
- 在类上添加‘@RequestMapping("url")’,声明此控制器类的请求url;
@Controller
@RequestMapping("/book")
public class BookController {
}
2) 在控制器类中定义处理请求的方法
- 在一个控制器类中可以定义多个方法处理不同请求;
- 在每个方法上添加@requestMapping("url")用于声明当前方法的请求url;
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public void addBook(){
System.out.println("----book add-----");
}
@RequestMapping("/list")
public void listBooks(){
System.out.println("----book list-----");
}
}
3) 访问
http://localhost:8080/springmvc_demo1/book/addhttp://localhost:8080/springmvc_demo1/book/add
3.2 静态资源配置
静态资源:项目中的HTML、css、js、图片、字体等
1)/*和/的区别
- /*表示拦截所有的HTTP请求,包括静态资源请求(html、css、js、图片),包括.jsp请求,都做为控制器类的请求路径来处理;
- /表示拦截所有的HTTP请求,包括静态资源请求(html、css、js、图片),但不包括.jsp请求,都做为控制器类的请求路径来处理;
2)静态资源的放行配置
- 在SpringMVC的配置文件,添加如下静态资源执行的配置
<mvc:resources mapping="/css/**" location="/css/" />
<mvc:resources mapping="/js/**" location="/js/" />
<mvc:resources mapping="/img/**" location="/img/" />
<mvc:resources mapping="/page/**" location="/page/" />
3.3 前端提交数据到控制器
- book-add.jsp 表单的action属性设置控制器类的url和对应方法的url组合路径
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>添加图片</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text"/></p>
<p>图书作者:<input type="text"/></p>
<p>图书价格:<input type="text"/></p>
<p><input type="submit" value="提交"/></p>
</form>
</body>
</html>
1)表单提交
- 表单提交:输入框需要提供name属性,SpringMVC控制器是通过name属性值的;
<html>
<head>
<title>Title</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<h3>添加图片</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text"/></p>
<p>图书作者:<input type="text"/></p>
<p>图书价格:<input type="text"/></p>
<p><input type="submit" value="提交"/></p>
</form>
</body>
</html>
2)URL提交
- 超链接(URL)提交:<a href="book/add?bookName=Java">URL提交</a>;
3)AJAX提交
- AJAX提交:请求行、请求头、请求体都可以用来传值;
<H3>AJAX提交</H3>
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function(){
var obj={};
obj.bookName="Java";
obj.bookAutor="张三";
obj.bookPrice=3.33;
$.ajax({
url:"book/add",
type:"post",
headers:{
},
contentType:"application/json",
data:obj,
success:function(res){
console.log(res);
}
})
});
</script>
3.4 控制器接受前端提交的数据
1)请求行传值
- 表单提交
- URL提交
- $.ajax请求的url传值
- $.post( )/$.get( )中的{ }传值
@RequestParam注解用于接受请求行传递的数据
- 前端接收数据
<h3>表单提交</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text" name="name"/></p>
<p>图书作者:<input type="text" name="author"/></p>
<p>图书价格:<input type="text" name="price"/></p>
<p><input type="submit" value="提交"/></p>
</form>
- 控制器接受数据
@Controller
@RequestMapping("/book")
public class BookController {
// 接受请求行传输的数据
@RequestMapping("/add")
public void addBook(@RequestParam(value="name") String name,
@RequestParam(value="author")String author,
@RequestParam(value="price")double price){
System.out.println(name+"\n"+author+"\n"+price);
}
}
注意:
如果控制器方法中接收的参数名与请求行传值的key一致,则@RequestParam注解可省略
@Controller
@RequestMapping("/book")
public class BookController {
// 接受请求行传输的数据
@RequestMapping("/add")
public void addBook(String name,
String author,
double price){
System.out.println(name+"\n"+author+"\n"+price);
}
}
2)请求头传值
$.ajax({
....
headers:{
},
....
})
@RequestHeader注解用于接受请求头传递的数据
- 前端
<h3>AJAX提交</h3>
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function(){
var obj={};
obj.bookName="Java";
obj.bookAutor="张三";
obj.bookPrice=3.33;
$.ajax({
url:"book/list",
type:"post",
headers:{
token: "Hello, world"
},
success:function(res){
console.log(res);
}
});
});
</script>
- 控制器
@RequestMapping("/list")
public void listBooks(@RequestHeader("token") String token){
System.out.println(token);
System.out.println("----book list-----");
}
3)请求体传值
- ajax封装请求体数据
$.ajax({
....
data:{
},
....
})
@RequestBody 注解用于接受请求行头传递的数据
- 前端
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function(){
var obj={};
obj.bookName="Java";
obj.bookAuthor="张三";
obj.bookPrice=2.33;
var s=JSON.stringify(obj);
$.ajax({
url:"book/update",
type:"post",
contentType:"application/json",
data:s,
success:function(res){
console.log(res);
}
});
});
</script>
注意:data的值为json格式字符串 ,因此contentType必须设置为"application/json"
- 后端
@RequestBody 将前端请求体提交的JSON格式数据转换成Java对象,依赖jackson包
- 导入jackson依赖
<!-- json依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
- 控制器
@RequestMapping("/update")
public void update(@RequestBody Book book){
System.out.println("----book update----");
/*
try {
//Servlet的处理方式使用request的输入流接受请求体数据
ServletInputStream is=request.getInputStream();
StringBuffer res=new StringBuffer();
byte[] buf=new byte[100];
int len=-1;
while((len=is.read(buf))!=-1){
String s=new String(buf,0,len);
res.append(s);
}
System.out.println(res.toString());
}catch(IOException e){
e.printStackTrace();
}
*/
System.out.println(book);
}
3.5 控制器响应前端请求
1)控制器响应同步请求
同步请求:form、超链接
- 处理同步请求的方法返回类型定义为String或ModelAndView,以实现页面的跳转
- 返回类型为String
请求跳转(转发)
@RequestMapping("/add")
public String addBook(@RequestParam("name") String name,
@RequestParam("author")String author,
@RequestParam("price")double price){
System.out.println(name+"\n"+author+"\n"+price);
return "/tips.jsp";
}
重定向
@RequestMapping("/add")
public String addBook(@RequestParam("name") String name,
@RequestParam("author")String author,
@RequestParam("price")double price){
System.out.println(name+"\n"+author+"\n"+price);
return "redirect:/tips.jsp";
}
- 返回类型是ModelAndView
请求跳转(转发)
@RequestMapping("/add")
public ModelAndView addBook(@RequestParam("name") String name,
@RequestParam("author")String author,
@RequestParam("price")double price){
System.out.println(name+" "+author+" "+price);
return new ModelAndView("/tips.jsp");
}
重定向
@RequestMapping("/add")
public ModelAndView addBook(@RequestParam("name") String name,
@RequestParam("author")String author,
@RequestParam("price")double price){
System.out.println(name+" "+author+" "+price);
return new ModelAndView("redirect:/tips.jsp");
}
2)控制器响应异步请求
异步请求:ajax请求
- 使用response中的输出流进行响应
- 控制器方法的返回类型为void
- 控制器方法添加'HttpServletResponse response'参数
- 在方法中通过response获取输出流,使用流响应AJAX请求
@RequestMapping("/update")
public void update(@RequestBody Book book, HttpServletResponse response)
throws IOException {
System.out.println("----book update----");
System.out.println(book);
//使用ObjectMapper将对象转换为JSON格式字符串
String s=new ObjectMapper().writeValueAsString(book);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out=response.getWriter();
out.println(s);
out.flush();
out.close();
}
- 直接在控制器方法返回响应的对象
- 控制器方法的返回类型设置为响应给ajax请求的对象类型
- 在控制器方法前添加@ResponseBody注解,将返回的对象转换为JSON响应给ajax请求
- 如果一个控制器类中的所有方法都是响应ajax请求,则可以直接在控制器类前添加
@RequestMapping("/update")
@ResponseBody
public List<Book> update(@RequestBody Book book){
System.out.println("----book update----");
System.out.println(book);
List<Book> books=new ArrayList<>();
books.add(new Book(1,"Java","老王",2.44));
books.add(new Book(2,"C++","王阿姨",4.25));
return books;
}
3)控制器响应同步请求的数据传递
对于同步请求的转发响应,可以传递参数到转发的页面
- 返回类型为String
- 在控制器方法中定义一个Model类型的参数
- 在return页面之前,向model中添加键值对,添加的键值对就会被传递到转发的页面
@RequestMapping("/add")
public String addBook(String name, String author, double price,
Model model){
System.out.println(name+" "+author+" "+price);
model.addAttribute("key1","key2");
model.addAttribute("book",new Book(1,"Java","老王",2.44));
return "/tips.jsp";
}
注意:
除了使用Model对象传值以外,还可以直接使用HttpServletRequest对象
@RequestMapping("/add")
public String addBook(String name, String author, double price,
HttpServletRequest request){
System.out.println(name+" "+author+" "+price);
request.addAttribute("key1","key2");
request.addAttribute("book",new Book(1,"Java","老王",2.44));
return "/tips.jsp";
}
- 返回类型为ModelAndView
@RequestMapping("/add2")
public ModelAndView addBook2(String name, String author, double price){
System.out.println(name+" "+author+" "+price);
ModelAndView modelAndView=new ModelAndView("/tips.jsp");
modelAndView.addObject("key1","value2");
modelAndView.addObject("book",new Book(1,"C++","老王",2.44));
return modelAndView;
}
3.6 解决中文乱码问题
1)前端编码
- JSP页面
<%@ page contentType="text/html;charset=UTF-8"
language="java" pageEncoding="UTF-8" %>
- HTML页面
<meta charset="UTF-8">
2)服务器编码
- tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
3)设置SpringMVC的编码方式
- 在web.xml中配置SpringMVC编码过滤器的编码方式
<!-- 设置编码格式,解决乱码问题 -->
<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>
四、SpringMVC的请求处理流程
4.1 请求处理过程
SpringMVC通过前端控制器(DispatcherServlet)拦截并处理用户请求的
- 前端发送请求被前端控制器DispatcherServlet拦截;
- 前端控制器调用处理器映射器HandlerMapping对请求URL进行解析,解析之后返回调用给前端控制器;
- 前端控制器调用处理器适配器处理调用链;
- 处理器适配器基于反射通过适配器设计模式完成处理器(控制器)的调用处理用户请求;
- 处理器适配器将控制器返回的视图和数据信息封装成ModelAndView响应给前端控制器;
- 前端控制器调用视图解析器ViewResolver对ModelAndView进行解析,将解析结果(视图资源和数据)响应给前端控制器;
- 前端控制器调用视图view组件将数据进行渲染,将渲染结果(静态视图)响应给前端控制器;
- 前端控制器响应用户请求;
4.2 SpringMVC的核心组件
- DispacherServlet前端控制器、总控制器
- 由SpringMVC提交,无需新一代农民工(码农)开发;
- 作用:接收请求,协同各个组件工作、响应请求;
- HanlderMapping处理器映射
- 由SpringMVC提交,无需新一代农民工(码农)开发;
- 作用:负责根据用户请求的URL找到对应的Handler;
- 可配置:SpringMVC提供多个处理器映射的实现,可以根据需要进行配置;
- HanlderAdapter处理器适配器
- 由SpringMVC提交,无需新一代农名工(码农)开发;
- 作用:按照处理器映射器解析的用户请求的调用链,通过适配器模式完成Hanlder的调用;
- Handler处理器
- 由农民工根据业务的的需求进行开发;
- 作用:处理请求;
- ModelAndView视图模型
- 作用:用于封装处理器返回的数据以及相应的视图;
- ModelAndView=Model+view;
- ViewResolver视图解析器
- 作用:对ModelAndView进行解析;
- 可配置:SpringMVC提供多个处理器映射的实现,可以根据需要进行配置;
- View视图
- 作用:完成数据渲染;
4.3 处理器映射
不同的处理器映射对URL处理的方式也不相同,使用对应的处理器映射之后前端的请求规则也需要发生相应的变化。
- BeanNameUrlHandlerMapping 根据控制器ID访问控制器
- SimpleUrlHandlerMapping 根据控制器配置的URL访问(默认)
配置处理器映射器:
- 在SpringMVC的配置文件中通过bean标签声明处理器映射器
- 配置BeanNameUrlHandlerMapping
<bean class="
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />
- 配置 SimpleUrlHandlerMapping(默认情况下,无需配置)
<bean class=
"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/aaa">bookController</prop>
<prop key="/bbb">studentController</prop>
</props>
</property>
</bean>
4.4 视图解析器
Spring提供了多个视图解析器:
- UrlBasedViewResolver
- InternalResourceViewResolver
1)UrlBasedViewResolver
- 需添加JSTL的依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
- 配置视图解析器
<bean id="viewResolver" class="
org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
2)InternalResourceViewResolver
<bean id="viewResolver" class="
org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/" />
<property name="suffix" value=".jsp" />
</bean>
五、日期格式处理
5.1 在控制器中使用对象接收数据
- 前端
<form action="test/add" method="post">
<p>图书名称:<input type="text" name="bookName"/></p>
<p>图书作者:<input type="text" name="bookAuthor"/></p>
<p>图书价格:<input type="text" name="bookPrice"/></p>
<p><input type="submit" value="提交"/></p>
</form>
- 后端
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/add")
//表单提交的多个数据,在控制器方法中可以使用对象进行接收
//但是提交的数据key必须要与对象的属性名一致
public String addBook(Book book){
System.out.println(book);
return "/tips.jsp";
}
}
5.2 日期格式处理
如果前端需输入日期数据,在控制器中转换成Date对象,SpringMVC要求前端输入的日期格式必须为:yyyy/MM/dd
如果甲方要求日期格式必须为指定的格式,而这个指定格式SpringMVC不接受,该如何处理?
- 自定义日期格式
1)创建自定义日期转换器
- 创建一个类实现Converter接口,泛型指定从什么类型转换为什么类型
- 实现convert转换方法
public class MyDateConverter implements Converter<String,Date> {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日");
public Date convert(String s) {
Date date=null;
try {
date=sdf.parse(s);
}catch(ParseException e){
e.printStackTrace();
}
return date
}
}
2)配置自定义转换器
<!-- 声明MVC使用注解驱动 -->
<mvc:annotation-driven conversion-service="converterFactory"/>
<bean id="converterFactory" class=
"org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.xsbc.utils.MyDateConverter" />
</set>
</property>
</bean>
六、文件上传
6.1 SpringMVC框架部署
- 基于Maven创建web工程
- 添加SpringMVC所需的依赖
- Spring:context、aspects、jdbc、test、webmvc、jackson
- 创建SpringMVC配置文件
- 在web.xml中配置SpringMVC的前端控制器
- 在web.xml中配置SpringMVC的编码过滤器
- 配置springmvc静态资源处理策略
6.2 文件上传
案例:添加图书,同时提交图书的封面图片
1)前端提交文件
- 表单提交方式必须为post
- 表单enctype属性设置为mutilpart/form-data
<h4>添加图书信息</h4>
<form action="book/add" method="post" enctype="multipart/form-data">
<p>图书名称:<input type="text" name="bookName" /></p>
<p>图书作者:<input type="text" name="bookAuthor" /></p>
<p>图书价格:<input type="text" name="bookPrice" /></p>
<p>图书封面:<input type="file" name="imgFile" /></p>
<p><input type="submit" value="提交" /></p>
</form>
2)控制器接收数据和文件
SpringMVC处理上传文件需借助于CommonsMultipartResolver文件解析器
- 添加依赖:commons-io、commons-fileupload
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
- 在spring-servlet.xml中配置文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.
commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10240000" />
<property name="maxInMemorySize" value="102400" />
<property name="defaultEncoding" value="utf-8" />
</bean>
- 控制器接收文件
- 在处理文件上传的方法中定义一个MultipartFile类型的对象,就可以接收图片
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public String addBook(Book book, MultipartFile imgFile,
HttpServletRequest request) throws IOException {
System.out.println("------------add");
//imgFile表示上传的图片
// 1、截取上传文件的后缀名,生成新的文件名
String originalFilename=imgFile.getOriginalFilename();
assert originalFilename != null;
String ext=originalFilename.substring(
originalFilename.lastIndexOf("."));//.jpg
String fileName=System.currentTimeMillis()+ ext;
//2、获取img目录在服务器的路径
String dir=request.getServletContext().getRealPath("img");
String savePath=dir+"/"+fileName;//两个斜杆\\代表\
//3、保存文件
imgFile.transferTo(new File(savePath));
//4、将图片的访问路径设置为book对象
book.setBookImg("img/"+fileName);
//5、调用service保存book到数据库
return "/tips.jsp";
}
}
6.3 文件下载
1)文件下载流程
- list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu"
crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css"
integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ"
crossorigin="anonymous">
<body>
<h4>文件列表</h4>
<div class="row" id="container">
</div>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd"
crossorigin="anonymous">
</script>
<script type="text/javascript">
$.get("book/list",function(res){
for(var i=0;i<res.length;i++){
var fn=res[i];
var htmlStr="<div class='col-lg-2 col-md-3 col-sm-4 col-xs-6'><div class='thumbnail'><img src='img/"+fn+"' alt='...'> <div class='caption'> <p><a href='#' class='btn btn-primary' role='button'>下载</a></p> </div> </div> </div>";
$("#container").append(htmlStr);
}
},"json");
</script>
</body>
</html>
- BookController
@RequestMapping("/list")
@ResponseBody
public String[] listImg(HttpServletRequest request){
//1、从img目录下获取所有的图片信息
String dir=request.getServletContext().getRealPath("img");
File imgDir=new File(dir);
String[] fileNames=imgDir.list();
return fileNames;
}
2)实现文件下载
- list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu"
crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题文件(一般不用引入) -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css"
integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ"
crossorigin="anonymous">
<body>
<h4>文件列表</h4>
<div class="row" id="container">
</div>
<script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd"
crossorigin="anonymous">
</script>
<script type="text/javascript">
$.get("book/list",function(res){
for(var i=0;i<res.length;i++){
var fn=res[i];
var htmlStr="<div class='col-lg-2 col-md-3 col-sm-4 col-xs-6'><div class='thumbnail'><img src='img/"+fn+"' alt='...'> <div class='caption'> <p><a href='book/download?fname="+fn+"' class='btn btn-primary' role='button'>下载</a></p> </div> </div> </div>";
$("#container").append(htmlStr);
}
},"json");
</script>
</body>
</html>
- BookController
@RequestMapping("/download")
public void downloadImg(String fname, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//从img目录找到当前文件
String dir=request.getServletContext().getRealPath("img");
String filePath=dir+"/"+fname;
FileInputStream fileInputStream = new FileInputStream(filePath);
//设置浏览器不能识别,让其自动下载而不是在浏览器中显示
response.setContentType("application/exe");
// 避免浏览器自动打开文件
response.addHeader("Content-Disposition","attachment;filename="+fname);
IOUtils.copy(fileInputStream,response.getOutputStream());
}
七、统一异常处理
在应用系统运行过程中,可能由于运行环境、用户操作、资源不足等各个方面的原因导致系统出现异常(HTTP状态异常、Exception);如果系统出现异常,这些异常将会通过浏览器呈现给用户,而这些异常的显示是没有必要的,因此可以在服务器进行特定的处理,即当系统出现异常之后,呈现给用户一个统一的、可读的异常显示页面。
7.1 HTTP异常状态统一处理
HTTP Status 404
- 创建一个用于进行异常提示的页面:404.jsp
- 在web.xml文件中进行配置
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
7.2 Java代码异常的统一处理
1)基于Servlet-api的处理
- 创建异常提示页面:err.jsp
- 在web.xml中进行配置
<error-page>
<exception-type>java.lang.NumberFormatException</exception-type>
<location>/err.jsp</location>
</error-page>
2)SpringMVC处理
- 定义一个异常处理类
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public String nullHandler(){
return "/err1.jsp";
}
@ExceptionHandler(NumberFormatException.class)
public String formatHandler(){
return "/err2.jsp";
}
}
八、拦截器
8.1 拦截器介绍
SpringMVC提供的拦截器就类似于Servlet-api中的过滤器,可以对控制器的请求进行拦截实现相关的预处理和后处理
- 过滤器
- 是Servlet规范的一部分,所有的web项目都可以使用;
- 过滤器在web.xml中配置(可以使用注解),能够拦截所有的web请求;
- 拦截器
- 是SpringMVC框架的实现,只有在SpringMVC框架中使用;
- 拦截器在SpringMVC配置文件进行配置,不会拦截SpringMVC放行的资源(jsp\html\css);
8.2 自定义拦截器
1)创建拦截器
public class MyInterceptor1 implements HandlerInterceptor {
@Override
//预处理方法
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("---------预处理-------");
Enumeration<String> keys=request.getParameterNames();
while(keys.hasMoreElements()){
String key=keys.nextElement();
if("bookId".equals(key)){
return true;
}
}
response.setStatus(400);
//当拦截器的preHandle返回false后的处理
//后续的拦截器以及请求处理方法等都不会再执行
return false;
}
@Override
//后处理方法
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("tips","这是通过拦截器后处理添加的数据");
System.out.println("---------后处理--------");
}
}
2)配置拦截器
<!--
拦截器的配置
mapping表示拦截所有的/student/**
exclude-mapping表示拦截/student下的除了/add外
-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/book/query" />
<mvc:mapping path="/book/add" />
<mvc:mapping path="/student/**" />
<mvc:exclude-mapping path="/student/add" />
<bean class="com.xsbc.utils.MyInterceptor1" />
</mvc:interceptor>
</mvc:interceptors>
8.3 拦截器链
将多个拦截器按照一定的顺序构成一个执行链,执行顺序按照配置文件中的声明顺序。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/book/query" />
<mvc:mapping path="/book/add" />
<mvc:mapping path="/student/**" />
<mvc:exclude-mapping path="/student/add" />
<bean class="com.xsbc.utils.MyInterceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/book/query" />
<bean class="com.xsbc.utils.MyInterceptor2" />
</mvc:interceptor>
</mvc:interceptors>
九、SSM整合
9.1 创建web项目
- 创建maven工程
- 修改pom文件packaging=war
<packaging>war</packaging>
- 完成maven工程web项目结构
- 添加web项目依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
- 配置服务器运行环境
9.2 部署MyBatis
- 添加MyBatis依赖
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
- 创建MyBatis配置文件:mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
</configuration>
9.3 部署Spring、SpringMVC
1)添加依赖
<properties>
<spring.version>5.3.2</spring.version>
</properties>
<!-- context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- json依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.1</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
2)创建Spring配置(多配置文件分开配置) :降低耦合度
- spring-context.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 声明使用注解配置 -->
<context:annotation-config/>
<!-- 声明Spring工厂注解的扫描范围 -->
<context:component-scan base-package="com.xsbc"/>
</beans>
- spring-mvc.xml:进行mvc相关的配置,例如静态资源配置、拦截器配置等
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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">
<!-- 声明MVC使用注解配置 -->
<mvc:annotation-driven/>
</beans>
- spring-mybatis.xml:进行Spring与MyBatis整合的相关配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
3)配置SpringMVC前端控制器
- 在web.xml进行配置
<!-- *表示后缀名 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
9.4 整合配置
1)导入spring-mybatis依赖
<!-- mybatis兼容spring的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
2)配置druid连接池
- 添加druid依赖
<!-- druid连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
- 创建druid.properties文件并配置
druid.driver=com.mysql.jdbc.Driver
druid.url=jdbc:mysql://localhost:3306/db_2021_mybatis?characterEncoding=UTF-8
druid.username=root
druid.password=1234
### 连接池参数
druid.pool.init=2
druid.pool.minIdle=3
druid.pool.maxActive=20
druid.pool.timeout=30000
- 在spring-mybatis.xml配置数据源
<context:property-placeholder location="classpath:druid.properties" />
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${druid.driver}"/>
<property name="url" value="${druid.url}" />
<property name="username" value="${druid.username}" />
<property name="password" value="${druid.password}" />
<property name="initialSize" value="${druid.pool.init}" />
<property name="minIdle" value="${druid.pool.minIdle}" />
<property name="maxActive" value="${druid.pool.maxActive}" />
<property name="maxWait" value="${druid.pool.timeout}" />
</bean>
3)配置SqlSessionFactory
- 在spring-mybatis.xml配置数据源
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource" />
<property name="mapperLocations" value="classpath:mappers/*.xml" />
<property name="typeAliasesPackage" value="com.xsbc.bean" />
<property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
4)配置MapperScannerConfigurer
- 在spring-mybatis.xml配置数据源
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<property name="basePackage" value="com.xsbc.dao" />
</bean>
9.5 整合配置AOP
使用Spring提供的事务管理完成DAO操作的事务管理
基于注解的事务管理配置:
- 将Spring提供的事务管理切面类配置到Spring容器
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
9.6 整合测试
1)完成User的查询操作
- 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private int userId;
private String userName;
private String userPwd;
private String userRealName;
private String userImg;
}
- 在DAO包中创建接口
public interface UserDAO {
public User queryUserByName(String name);
}
- 在mappers目录下创建映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xsbc.dao.UserDAO">
<resultMap id="userMap" type="User">
<id column="user_id" property="userId" />
<result column="user_name" property="UserName" />
<result column="user_pwd" property="userPwd" />
<result column="user_realname" property="userRealName" />
<result column="user_img" property="userImg" />
</resultMap>
<select id="queryUserByName" resultMap="userMap">
select user_id,user_name,user_pwd,user_realname,user_img
from users
where user_name={userName}
</select>
</mapper>
2)对DAO单元测试
- 添加juint依赖
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.13</version>
<scope>test</scope>
</dependency>
- 创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath*:spring-*.xml")
//@ContextConfiguration(locations={"classpath:spring-context.xml",
// "classpath:spring-mvc.xml","classpath:spring-mybatis.xml"})
public class UserDAOTest {
@Resource
private UserDAO userDAO;
@Test
public void testQueryUserByName() {
User user=userDAO.queryUserByName("WangWu");
System.out.println(user);
}
}
3)完成service层
- 创建UserService接口
public interface UserService {
public User checkLogin(String username, String userPwd);
}
- 创建service实现类,并添加注解@Service
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDAO userDAO;
public User checkLogin(String userName, String userPwd) {
User user=userDAO.queryUserByName(userName);
//加密
if(user.getUserPwd().equals(userPwd)){
return user;
}
return null;
}
}
- 创建测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath*:spring-*.xml")
public class UserServiceImplTest {
@Resource
private UserService userService;
@Test
public void testCheckLogin() {
User user=userService.checkLogin("ZhangSan", "123123");
assertNotNull(user);
System.out.println(user);
}
}
4)完成controller层
- 创建实体类
@Controller
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/login")
public String login(String userName, String userPwd,
HttpServletRequest request){
User user=userService.checkLogin(userName, userPwd);
if(user==null){
request.setAttribute("tips","用户名或密码错误");
return "/login.jsp";
}
request.getSession().setAttribute("user",user);
return "/index.jsp";
}
}
- 添加jsp文件测试
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<base href="${pageContext.request.contextPath}/">
<title>Title</title>
</head>
<body>
<h3>登录页面</h3>
${tips}
<form action="user/login" method="post">
<p>账号:<input type="text" name="userName" /></p>
<p>密码:<input type="text" name="userPwd" /></p>
<p><input type="submit" value="登录" /></p>
</form>
</body>
</html>
注意:添加<base href="${pageContext.request.contextPath}/">可以解决路径重定向再次登录的问题。