SpringMVC
学习笔记
【SpringMVC
官方文档】https://docs.spring.io/spring-framework/reference/web/webmvc.html
一、SpringMVC
简介
Spring MVC
是Spring Framework
的一部分,是基于Java
实现MVC
的轻量级Web
框架。
Spring MVC
的特点:
- 轻量级,简单易学
- 高效,基于请求响应的
MVC
框架 - 与
Spring
兼容性好,无缝结合 - 约定优于配置
- 功能强大:
RESTful
、数据验证、格式化、本地化、主题等 - 简洁灵活
Spring
的web
框架围绕DispatcherServlet
设计。SpringMVC
工作原理图如下**【重要!!】**。
1、DispatcherServlet
表示前置控制器,是整个SpringMVC
的控制中心。用户发出请求,DispatcherServlet
接收请求并拦截请求。
- 我们假设请求的
url
为:http://localhost:8080/SpringMVC/hello
url
拆分成三部分:http://localhost:8080
为服务器域名;SpringMVC
为部署在服务器上的web
站点;hello
为控制器- 因此,如上
url
表示:请求位于服务器localhost:8080
上的SpringMVC
站点的hello
控制器。
2、HandlerMapping
为处理器映射。DispatcherServlet
调用HandlerMapping
,HandlerMapping
根据请求url
查找Handler
。
3、DispatcherServlet
调用 HandlerAdapter
适配器执行 Handler
。
4、Handler
完成对用户请求的处理后,会返回一个 ModelAndView
对象给DispatcherServlet
,ModelAndView
顾名思义,包含了数据模型以及相应的视图的信息。Model
是返回的数据对象,View
是个逻辑上的 View
。
5、ViewResolver
会根据逻辑 View
查找实际的 View
。
6、DispaterServlet
把返回的 Model
传给 View
(视图渲染)。
7、把 View
返回给请求者(浏览器)。
二、第一个SpringMVC
项目
2.1、配置版本
项目的总体结构如下。
2.1.1、创建module
新建一个module
,springmvc-02-hellomvc
,添加web
支持。
确定导入了SpringMVC
的依赖。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
2.1.2、配置web.xml
配置web.xml
,注册DispatcherServlet
。
<?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">
<!-- Register a DispatcherServlet: SpringMVC's core component -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- associate a springmvc's configuration file:【servlet-name】- servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- start grade - 1 : together with the server -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
/ fit all requests (exclude .jsp pages)
/* fit all requests (include .jsp pages)
一般使用 / 即可,因为我们不希望匹配 .jsp 的时候再加前后缀(变成 .jsp.jsp)
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- filter supplied by Spring -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2.1.3、servlet
配置文件
编写SpringMVC
的配置文件。名称:springmvc-servlet.xml
([servletname]-servlet.xml
)
【说明】这里的命名格式是按照官方的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 处理映射器(可省略,即使用默认) -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!-- 处理适配器(可省略,即使用默认) -->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 视图解析器 : DispatcherServlet 给它 ModelAndView
1、获取 ModelAndView
2、解析 ModelAndView 中视图名字
3、拼接视图名字,找到对应的视图
4、将数据渲染到视图
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="InternalResourceViewResolver">
<!-- prefix -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- postfix -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- Handler (BeanNameUrlHandlerMapping 需要的 bean) -->
<bean id="/hello" class="com.wzq.controller.HelloController"/>
</beans>
2.1.4、编写controller
可以使用实现Controller
接口,或者使用注解这两种方式。
这里使用实现接口的方式(缺点是一个控制器中只有一个方法)。
package com.wzq.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 实现 Controller 接口,说明这就是一个控制器
public class HelloController implements Controller {
// 处理请求并返回一个 ModelAndView 对象
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ModelAndView 模型和视图
ModelAndView mv = new ModelAndView();
// encapsulate Object, put into ModelAndView
mv.addObject("msg", "HelloSpringMVC!");
// encapsulate the View we need to jump to, put into ModelAndView
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
2.1.5、编写jsp
页面
编写jsp
页面,并显示ModelandView
存放的数据。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
2.1.6、运行结果
在tomcat
中添加当前模块并配置访问路径。
运行结果如下所示。
【注】如果出现
404
问题,可以检查一下IDEA
的项目结构中,是不是缺少了lib
目录和其中的依赖,如果缺少了的话可以手动添加。然后clean
一下重新启动即可。
2.2、注解版本
项目的总体结构如下。
2.2.1、创建module
新建一个module
,springmvc-04-annotation
,添加web
支持。
确定导入了SpringMVC
的依赖。
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
2.2.2、配置web.xml
配置web.xml
,注册DispatcherServlet
。
<?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">
<!-- Register a DispatcherServlet: SpringMVC's core component -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- associate a springmvc's configuration file:【servlet-name】- servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- start grade - 1 : together with the server -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--
/ fit all requests (exclude .jsp pages)
/* fit all requests (include .jsp pages)
一般使用 / 即可,因为我们不希望匹配 .jsp 的时候再加前后缀(变成 .jsp.jsp)
-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- filter supplied by Spring -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
2.2.3、servlet
配置文件
编写SpringMVC
的配置文件。名称:springmvc-servlet.xml
([servletname]-servlet.xml
)
【说明】这里的命名格式是按照官方的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描指定的包,下面所有注解类交给 IOC 容器统一管理 -->
<context:component-scan base-package="com.wzq.controller"/>
<!-- 设置 SpringMVC 不处理静态资源(可省略,即使用默认) -->
<mvc:default-servlet-handler />
<!-- 注解驱动(可省略,即使用默认)
在 spring 中一般采用 @RequestMapping 注解来完成映射关系
要想使 @RequestMapping 注解生效
必须向上下文中注册 DefaultAnnotationHandlerMapping
和 AnnotationMethodHandlerAdapter 实例
这两个实例分别在类级别和方法级别处理。
而 annotation-driven 配置帮助我们自动完成上述两个实例的注入
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
在视图解析器中我们把所有的视图都存放在/WEB-INF/
目录下,这样可以保证视图安全,因为这个目录下的文件客户端不能直接访问。
2.2.4、编写controller
可以使用实现Controller
接口,或者使用注解这两种方式。这里使用实现接口的方式。
package com.wzq.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/h1")
public String hello(Model model){
// 封装数据(也可以使用 ModelMap 类)
model.addAttribute("msg", "Hello, SpringMVC Annotation!");
return "hello"; // 视图名字,会被视图解析器处理
}
}
@Controller
是为了让Spring IOC
容器初始化时自动扫描到。(Spring
可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring
能找到你的控制器,需要在配置文件中声明component-scan
)。
@RequestMapping
是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/hello/h1
。
2.2.5、编写jsp
页面
编写jsp
页面,并显示从Controller
带回的信息(通过EL
表达式)。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
2.2.6、运行结果
运行结果如下所示。
三、RestFul
风格(重要)
RestFul
就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
package com.wzq.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
@Controller
public class RestFulController {
// 原来: http://localhost:8080/add?a=1&b=2
@RequestMapping("/add")
public String test1(int a, String b, Model model){
String res = a + b;
model.addAttribute("msg", "结果为" + res);
return "test";
}
// Restful: http://localhost:8080/add/1/2
// @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.GET)
@GetMapping("/add/{a}/{b}")
public String test2(@PathVariable int a, @PathVariable String b, Model model){
String res = a + b;
model.addAttribute("msg", "结果1为" + res);
return "test";
}
// @RequestMapping(value = "/add/{a}/{b}", method = RequestMethod.POST)
@PostMapping("/add/{a}/{b}")
public String test3(@PathVariable int a, @PathVariable String b, Model model){
String res = a + b;
model.addAttribute("msg", "结果2为" + res);
return "test";
}
}
在
SpringMVC
中可以使用@PathVariable
注解,让方法参数的值对应绑定到一个URL
模板变量上。
四、转发和重定向
4.1、Servlet API
通过使用ServletAPI
进行转发和重定向,不需要视图解析器。
@Controller
public class ResultGo {
// 输出
@RequestMapping("/result/t1")
public void test1(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
rsp.getWriter().println("Hello, Spring by servlet API");
}
// 重定向
@RequestMapping("/result/t2")
public void test2(HttpServletRequest req, HttpServletResponse rsp)
throws IOException {
rsp.sendRedirect("/index.jsp");
}
// 转发
@RequestMapping("/result/t3")
public void test3(HttpServletRequest req, HttpServletResponse rsp)
throws Exception {
req.setAttribute("msg", "/result/t3");
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, rsp);
}
}
4.2、SpringMVC
4.2.1、无视图解析器版
通过SpringMVC
来实现转发和重定向——无需视图解析器版。
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 转发
return "/index.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
// 转发二
return "forward:/index.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";
}
}
4.2.2、需视图解析器版
通过SpringMVC
来实现转发和重定向——需要视图解析器版。
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
// 转发
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
// 重定向(和无需视图解析器版一样)
return "redirect:/index.jsp";
}
}
五、数据处理
接收前端传递参数的两种方式:
- 1、接收前端用户传递的参数,判断参数的名字,假设名字直接在方法上,可以直接使用。
- 2、假设传递的是一个对象
User
,匹配User
对象中的字段名。如果名字一致则OK
,否则匹配不到的字段为null
。
package com.wzq.controller;
import com.wzq.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
@RequestMapping("/user")
public class UserController {
// localhost:8080/user/t1?username=xxx
@GetMapping("/t1")
public String test1(@RequestParam("username") String name, Model model){
// 接收前端参数
System.out.println("接收到前端的参数为: " + name);
// 将返回的结果传递给前端
model.addAttribute("msg", name);
// 视图跳转
return "test";
}
// localhost:8080/user/t2?id=x&name=xxx&age=xx
@GetMapping("/t2")
public String test2(User user){
System.out.println(user); // User(id=8, name=little, age=24)
return "test";
}
}
User
类的定义如下。
package com.wzq.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private int age;
}
六、过滤器
以下给出使用过滤器解决乱码问题的示例。
前端表单提交页面如下。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/e/t1" method="post">
<input type="text" name="name">
<input type="submit">
</form>
</body>
</html>
6.1、自拟过滤器
package com.wzq.filter;
import javax.servlet.*;
import java.io.IOException;
public class EncodingFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
chain.doFilter(request, response);
}
public void destroy() {
}
}
6.2、注册过滤器
在web.xml
文件中,注册过滤器。此时过滤器可以过滤到所有的请求和jsp
页面。
<!-- 注册自拟过滤器 -->
<!-- <filter>-->
<!-- <filter-name>encoding</filter-name>-->
<!-- <filter-class>com.wzq.filter.EncodingFilter</filter-class>-->
<!-- </filter>-->
<!-- <filter-mapping>-->
<!-- <filter-name>encoding</filter-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- </filter-mapping>-->
<!-- 注册 Spring 提供的过滤器 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<!-- 注意,使用 /* 才能过滤到 jsp 页面 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
七、JSON
数据返回
JSON
(JavaScript Object Notation
,JS
对象标记)是一种轻量级的数据交换格式,使用特别广泛。
- 采用完全独立于编程语言的文本格式来存储和表示数据。
- 简洁和清晰的层次结构使得
JSON
成为理想的数据交换语言。 - 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。
7.1、jackson
使用
导入jackson
依赖。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
乱码问题配置(springmvc-servlet.xml
文件中)。
<!-- JSON error code problem solve -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
也可以直接通过
@RequestMaping
的produces
属性来解决乱码。// produces: 指定响应体返回类型和编码 @RequestMapping(value = "/j11", produces ="application/json;charset=utf-8")
在Controller
中使用。
package com.wzq.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.wzq.pojo.User;
import com.wzq.utils.JsonUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@RestController // 此类中所有的方法返回的都是字符串,不走视图解析器
public class UserController {
@RequestMapping("/j11")
// @ResponseBody // 不走视图解析器,会直接返回一个字符串(配合 Controller 使用)
public String json11() throws JsonProcessingException {
User user = new User("你好", 3, "male");
return user.toString();
}
@RequestMapping("/j12")
public String json12() throws JsonProcessingException {
// 使用 jackson 包的 ObjectMapper 类
ObjectMapper mapper = new ObjectMapper();
User user = new User("你好", 3, "male");
return mapper.writeValueAsString(user);
}
@RequestMapping("/j2")
public String json2() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
List<User> userList = new ArrayList<User>();
User user1 = new User("你好1", 3, "male");
User user2 = new User("你好2", 3, "male");
User user3 = new User("你好3", 3, "male");
User user4 = new User("你好4", 3, "male");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
return mapper.writeValueAsString(userList);
}
@RequestMapping("/j3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 使用 ObjectMapper 来格式化输出,关闭时间戳
mapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
// 自定义日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
mapper.setDateFormat(sdf);
return mapper.writeValueAsString(new Date());
}
}
7.2、fastjson2
使用
导入fastjson2
依赖。
<!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.47</version>
</dependency>
在Controller
中使用。
@RequestMapping("/j6")
public String json6() {
List<User> userList = new ArrayList<User>();
User user1 = new User("你好1", 3, "male");
User user2 = new User("你好2", 3, "male");
User user3 = new User("你好3", 3, "male");
User user4 = new User("你好4", 3, "male");
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
return JSON.toJSONString(userList); // 转化成 JSON 字符串格式
}
八、SSM
框架整合案例(重点)
整体最终项目结构如下。
SSMbuild
- src
-- main
--- java
* com.wzq
** controller
*** BookController.java // controller 实现
** dao
*** BookMapper.java // 数据库表操作接口
*** BookMapper.xml // 数据库表操作实现
** pojo
*** Books.java // 实体类,对应数据库 books 表
** service
*** BookService.java // service 接口
*** BookServiceImpl.java // service 实现类
--- resources
* applicationContext.xml // Spring 核心配置文件
* database.properties // 数据库配置文件
* mybatis-config.xml // mybatis 核心配置文件
* spring-dao.xml // spring 连接 dao 层配置
* spring-mvc.xml // spring-mvc 配置
* spring-service.xml // spring 连接 service 层配置
-- test
--- java
* MyTest.java // 单元测试类
- web
-- statics
--- bootstrap-3.3.7.min.css
-- WEB-INF
--- jsp
* addBook.jsp
* allBook.jsp
* updateBook.jsp
--- web.xml
-- index.jsp // 首页
- pom.xml // maven 项目文件
8.1、数据库环境搭建
数据库创建,详见ssm_build.sql
文件。
book_id book_name book_counts detial
1 Java 1 Java核心技术
2 MySQL 4 MySQL核心技术
3 Linux 10 操作系统技术
8.2、项目基本环境搭建
创建Maven
项目,导入依赖,并配置静态文件过滤。
<!-- dependency -->
<dependencies>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- database connect pool: c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
<!-- servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
<!-- static source output problem -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
8.3、Mybatis
部分实现
8.3.1、数据库配置文件
数据库配置文件编写。
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/ssm_build?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=******
8.3.2、实体类
实体类Books.java
创建(可使用lombok
简化)。
package com.wzq.pojo;
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
public Books() {
}
public Books(int bookID, String bookName, int bookCounts, String detail) {
this.bookID = bookID;
this.bookName = bookName;
this.bookCounts = bookCounts;
this.detail = detail;
}
public int getBookID() {
return bookID;
}
public void setBookID(int bookID) {
this.bookID = bookID;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public int getBookCounts() {
return bookCounts;
}
public void setBookCounts(int bookCounts) {
this.bookCounts = bookCounts;
}
public String getDetail() {
return detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
@Override
public String toString() {
return "Books{" +
"bookID=" + bookID +
", bookName='" + bookName + '\'' +
", bookCounts=" + bookCounts +
", detail='" + detail + '\'' +
'}';
}
}
8.3.3、数据库操作接口
创建数据库操作接口BookMapper.java
。
package com.wzq.dao;
import com.wzq.pojo.Books;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface BookMapper {
// 增加一本书
int addBook(Books books);
// 删除一本书
int deleteBookById(@Param("bookID") int id);
// 更新一本书
int updateBook(Books books);
// 查询一本书
Books queryBookById(@Param("bookID") int id);
// 查询全部的书
List<Books> queryAllBook();
// 根据书名查询一本书
Books queryBookByName(@Param("bookName") String bookName);
}
8.3.4、Mapper
实现
创建BookMapper.xml
文件,编写实际的SQL
语句。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wzq.dao.BookMapper">
<insert id="addBook" parameterType="Books">
insert into ssm_build.books (book_name, book_counts, detail)
VALUES (#{bookName}, #{bookCounts}, #{detail});
</insert>
<delete id="deleteBookById" parameterType="int">
delete from ssm_build.books where book_id = #{bookID};
</delete>
<update id="updateBook" parameterType="Books">
update ssm_build.books
set book_name = #{bookName}, book_counts = #{bookCounts}, detail = #{detail}
where book_id = #{bookID};
</update>
<select id="queryBookById" resultType="Books">
select * from ssm_build.books where book_id = #{bookID};
</select>
<select id="queryAllBook" resultType="Books">
select * from ssm_build.books;
</select>
<select id="queryBookByName" resultType="Books">
select * from ssm_build.books where book_name = #{bookName};
</select>
</mapper>
8.3.5、Mybatis
配置文件
Mybatis
核心配置文件编写,注册mapper
。
<?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">
<!-- Mybatis' core configuration file-->
<configuration>
<!-- config data source (now let Spring to do) -->
<!-- 设置部分 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
<!-- 别名设置 -->
<typeAliases>
<package name="com.wzq.pojo"/>
</typeAliases>
<!-- mapper 注册 -->
<mappers>
<mapper class="com.wzq.dao.BookMapper"/>
</mappers>
</configuration>
8.3.6、Service
层
编写业务层接口BookService.java
。
package com.wzq.service;
import com.wzq.pojo.Books;
import java.util.List;
public interface BookService {
// 增加一本书
int addBook(Books books);
// 删除一本书
int deleteBookById(int id);
// 更新一本书
int updateBook(Books books);
// 查询一本书
Books queryBookById(int id);
// 查询全部的书
List<Books> queryAllBook();
// 根据书名查询一本书
Books queryBookByName(String bookName);
}
编写业务层实现类BookServiceImpl.java
。
package com.wzq.service;
import com.wzq.dao.BookMapper;
import com.wzq.pojo.Books;
import java.util.List;
public class BookServiceImpl implements BookService{
// service 层调 dao 层:使用组合
private BookMapper bookMapper;
public void setBookMapper(BookMapper bookMapper) {
this.bookMapper = bookMapper;
}
public int addBook(Books books) {
return bookMapper.addBook(books);
}
public int deleteBookById(int id) {
return bookMapper.deleteBookById(id);
}
public int updateBook(Books books) {
return bookMapper.updateBook(books);
}
public Books queryBookById(int id) {
return bookMapper.queryBookById(id);
}
public List<Books> queryAllBook() {
return bookMapper.queryAllBook();
}
public Books queryBookByName(String bookName) {
return bookMapper.queryBookByName(bookName);
}
}
8.4、Spring
部分实现
8.4.1、spring
整合dao
层
编写spring-dao.xml
文件,用于整合dao
层。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1 关联数据库配置文件 -->
<context:property-placeholder location="classpath:database.properties"/>
<!-- 2 数据库连接池:
dbcp: 半自动化操作,不能自动连接
c3p0: 自动化操作,自动化加载配置文件,并且可以自动设置到对象中
druid, hikari -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.Driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0 连接池的其他属性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<property name="autoCommitOnClose" value="false"/>
<property name="checkoutTimeout" value="10000"/>
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!-- 3 sqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 绑定 Mybatis 的配置文件 -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 4 配置 dao 接口扫描包,动态实现 dao 接口注入到 Spring 容器中
(也可以根据 Spring 整合 Mybatis 笔记中的方式,手动编写实现类来实现) -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入 sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 需要扫描的 dao 包 -->
<property name="basePackage" value="com.wzq.dao"/>
</bean>
</beans>
8.4.2、spring
整合service
层
编写spring-service.xml
文件,用于整合service
层。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 1 扫描 service 包 -->
<context:component-scan base-package="com.wzq.service"/>
<!-- 2 将所有的 service 类, 注入到 Spring, 可以通过配置或注解实现
(如果通过注解实现,只需要在 BookServiceImpl 类上添加 @Service,在 bookMapper 属性上添加 @Autowired 即可)-->
<bean id="BookServiceImpl" class="com.wzq.service.BookServiceImpl">
<property name="bookMapper" ref="bookMapper"/>
</bean>
<!-- 3 声明式事务配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 4 aop 事务支持 (可以不加,Spring 会自动处理事务) -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.wzq.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>
8.5、Spring-MVC
部分实现
8.5.1、web.xml
文件
编写web.xml
文件。
<?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">
<!-- DispatcherServlet -->
<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:applicationContext.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>
<!-- 乱码过滤 -->
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Session -->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
8.5.2、spring-mvc
配置文件
编写spring-mvc.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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让特定包中的注解生效,通过 IOC 容器管理 -->
<context:component-scan base-package="com.wzq.controller"/>
<!-- 让 Spring MVC 不处理静态资源 -->
<mvc:default-servlet-handler />
<!-- 注解驱动 -->
<mvc:annotation-driven />
<!-- JSON error code problem solve -->
<!-- <mvc:annotation-driven>-->
<!-- <mvc:message-converters register-defaults="true">-->
<!-- <bean class="org.springframework.http.converter.StringHttpMessageConverter">-->
<!-- <constructor-arg value="UTF-8"/>-->
<!-- </bean>-->
<!-- <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">-->
<!-- <property name="objectMapper">-->
<!-- <bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">-->
<!-- <property name="failOnEmptyBeans" value="false"/>-->
<!-- </bean>-->
<!-- </property>-->
<!-- </bean>-->
<!-- </mvc:message-converters>-->
<!-- </mvc:annotation-driven>-->
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- <bean id="/t1" class="com.wzq.controller.ControllerTest1"/>-->
</beans>
8.4.4、spring
配置整合文件
通过spring
核心配置文件,整合上述配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Spring's core configuration file-->
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
</beans>
8.6、功能实现
8.6.1、首页编写
创建首页文件index.jsp
。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
<style>
a{
text-decoration: none;
color: black;
font-size: 18px;
}
h3{
width: 180px;
height: 38px;
margin: 100px auto;
text-align: center;
line-height: 38px;
background: cornflowerblue;
border-radius: 5px;
}
</style>
</head>
<body>
<h3>
<a href="${pageContext.request.contextPath}/book/allBook">进入书籍页面</a>
</h3>
</body>
</html>
8.6.2、书籍列表页
编写书籍列表页前端allBook.jsp
。
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍展示页面</title>
<%-- BootStrap --%>
<link href="${pageContext.request.contextPath}/statics/bootstrap-3.3.7.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>书籍列表 —— 显示所有书籍</small>
</h1>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增书籍</a>
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">显示全部书籍</a>
</div>
<div class="col-md-8 column">
<%-- 查询书籍 --%>
<form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook"
method="post" style="float: right;">
<span style="color: #ff834e; font-weight: bold;">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="请输入要查询的书籍名称">
<input type="submit" value="查询" class="btn btn-primary">
</form>
</div>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>书籍编号</th>
<th>书籍名称</th>
<th>书籍数量</th>
<th>书籍详情</th>
<th>操作</th>
</tr>
</thead>
<%-- 书籍从数据库中查询出来,遍历 foreach--%>
<tbody>
<c:forEach var="book" items="${list}">
<tr>
<td>${book.bookID}</td>
<td>${book.bookName}</td>
<td>${book.bookCounts}</td>
<td>${book.detail}</td>
<td>
<a href="${pageContext.request.contextPath}/book/toUpdate?id=${book.bookID}">修改</a>
|
<a href="${pageContext.request.contextPath}/book/deleteBook/${book.bookID}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
8.6.3、controller
编写
创建BookController.java
文件。
package com.wzq.controller;
import com.wzq.pojo.Books;
import com.wzq.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/book")
public class BookController {
// controller 层调用 service 层
@Autowired
@Qualifier("BookServiceImpl")
private BookService bookService;
// 查询全部的书籍,并且返回到一个书籍展示页面
@RequestMapping("/allBook")
public String list(Model model){
List<Books> list = bookService.queryAllBook();
model.addAttribute("list", list);
return "allBook";
}
// 跳转到增加书籍页面
@RequestMapping("/toAddBook")
public String toAddPaper(){
return "addBook";
}
// 添加书籍的请求
@RequestMapping("/addBook")
public String addBook(Books books){
System.out.println("addBook=>" + books);
bookService.addBook(books);
return "redirect:/book/allBook"; // 重定向到 @RequestMapping("/allBook") 请求
}
// 跳转到修改页面
@RequestMapping("/toUpdate")
public String toUpdateBook(int id, Model model){
Books books = bookService.queryBookById(id);
model.addAttribute("qBooks", books);
return "updateBook";
}
// 修改书籍
@RequestMapping("updateBook")
public String updateBook(Books books){
System.out.println("updateBook=>" + books);
bookService.updateBook(books);
return "redirect:/book/allBook";
}
// 删除书籍
@RequestMapping("/deleteBook/{bookId}")
public String deleteBook(@PathVariable("bookId") int id){
bookService.deleteBookById(id);
return "redirect:/book/allBook";
}
// 查询书籍
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model){
Books books = bookService.queryBookByName(queryBookName);
List<Books> list = new ArrayList<Books>();
list.add(books);
if (books == null){
list = bookService.queryAllBook();
model.addAttribute("error", "未查到");
}
model.addAttribute("list", list);
return "allBook";
}
}
九、拦截器
SpringMVC
的处理器拦截器类似于Servlet
开发中的过滤器Filter
,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
**过滤器与拦截器的区别:**拦截器是AOP
思想的具体应用。拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js
是不会进行拦截的。
9.1、拦截器编写
package com.wzq.config;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
// 在请求处理的方法之前执行(如果返回 true 执行下一个拦截器,如果返回 false 就不继续执行下一个拦截器)
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("=========处理前=========");
return true; // 放行,进入下一个拦截器
}
// 在请求处理方法执行之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=========处理后=========");
}
// 在 dispatcherServlet 处理后执行,做清理工作
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("=========清理=========");
}
}
9.2、配置拦截器
在springmvc
的配置文件中配置拦截器。
<!-- 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- /** 表示拦截这个请求下的所有请求(包括路径及其子路径) -->
<!-- /admin/* 拦截的是 /admin/add 等, /admin/add/user 不会被拦截 -->
<!-- /admin/** 拦截的是 /admin/ 下的所有 -->
<mvc:mapping path="/**"/>
<!-- bean 配置的就是拦截器 -->
<bean class="com.wzq.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
9.3、测试效果
编写一个controller
如下。
package com.wzq.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("t1")
public String test(){
System.out.println("Test Controller Running");
return "OK";
}
}
控制台的输出结果如下。
=========处理前=========
Test Controller Running
=========处理后=========
=========清理=========
十、文件上传和下载
文件上传是项目开发中最常见的功能之一,SpringMVC
可以很好的支持文件上传,但是 SpringMVC
上下文中默认没有装配 MultipartResolver
,因此默认情况下其不能处理文件上传工作。如果想使用 Spring
的文件上传功能,则需要在上下文中配置 MultipartResolver
。
前端表单要求:为了能上传文件,必须将表单的 method
设置为 POST
,并将 enctype
设置为 multipart/form-data
。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器。
对表单中的
enctype
属性的详细说明:
application/x-www=form-urlencoded
:默认方式,只处理表单域中的value
属性值,采用这种编码方式的表单会将表单域中的值处理成URL
编码方式。multipart/form-data
:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。text/plain
:除了把空格转换为 "+
" 号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
10.1、文件上传
10.1.1、依赖导入
导入文件上传的jar
包,commons-fileupload
,Maven
会自动帮我们导入它的依赖包 commons-io
包。
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
10.1.2、配置Bean
在applicationContext.xml
文件中,配置bean
(注意这个bean
的 id
必须为 multipartResolver
, 否则上传文件会报 400
的错误)。
<!-- 文件上传配置 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 请求的编码格式,必须和 jsp 的 pageEncoding 属性一致,以便正确读取表单内容,默认为 ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8" />
<!-- 上传文件大小上限,单位字节(10485760 = 10M) -->
<property name="maxUploadSize" value="10485760" />
<property name="maxInMemorySize" value="40960" />
</bean>
10.1.3、前端页面
前端页面编写如下。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file">
<input type="submit" value="upload">
</form>
</body>
</html>
10.1.4、Controller
编写
Controller
代码编写如下。
package com.wzq.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.\*;
import java.net.URLEncoder;
@Controller
public class FileController {
// @RequestParam("file") 将 name = file 得到的文件封装成 CommonsMultipartFile 对象
// 批量上传 CommonsMultipartFile 则为数组即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
// 获取文件名: file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
// 如果文件名为空,直接回到首页!
if ("".equals(uploadFileName)) {
return "redirect:/index.jsp";
}
System.out.println("上传文件名: " + uploadFileName);
// 上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
// 如果路径不存在,创建一个
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
System.out.println("上传文件保存地址:" + realPath);
InputStream is = file.getInputStream(); // 文件输入流
OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); // 文件输出流
// 读取写出
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
// 采用 file.transferTo 来保存上传的文件
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file,
HttpServletRequest request) throws IOException {
// 上传路径保存设置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()){
realPath.mkdir();
}
// 上传文件地址
System.out.println("上传文件保存地址:" + realPath);
// 通过 CommonsMultipartFile 的方法直接写文件(注意这个时候)
file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
return "redirect:/index.jsp";
}
}
CommonsMultipartFile
的常用方法:
String getOriginalFilename()
:获取上传文件的原名。InputStream getInputStream()
:获取文件流。void transferTo(File dest)
:将上传文件保存到一个目录文件中。
10.2、文件下载
文件下载步骤:
- 1、设置
response
响应头。 - 2、读取文件 ——
InputStream
- 3、写出文件 ——
OutputStream
- 4、执行操作
- 5、关闭流 (先开后关)
对应的Controller
代码如下。
@RequestMapping(value = "/download")
public String downloads(HttpServletResponse response, HttpServletRequest
request) throws Exception {
// 要下载的图片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "demo.png";
// 1、设置 response 响应头
response.reset(); // 设置页面不缓存,清空 buffer
response.setCharacterEncoding("UTF-8"); // 字符编码
response.setContentType("multipart/form-data"); // 二进制传输数据
// 设置响应头
response.setHeader("Content-Disposition",
"attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path, fileName);
// 2、 读取文件——输入流
InputStream input = new FileInputStream(file);
// 3、 写出文件——输出流
OutputStream out = response.getOutputStream();
byte[] buff = new byte[1024];
int index = 0;
// 4、执行 写出操作
while ((index = input.read(buff)) != -1) {
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
评论区