Spring MVC环境部署
maven新建web-MVC项目,pom.xml中导入核心依赖包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.19.RELEASE</version>
</dependency>
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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!--开启上下文连接注解驱动-->
<context:annotation-config></context:annotation-config>
<!--开启包的注解扫描-->
<context:component-scan base-package="spring"/>
<!--开启控制器注解驱动-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--上面只是Spring mvc核心配置,如果用到了Spring其他功能自行配置-->
</beans>
web.xml核心文件的配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://JAVA.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--注释一 <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.gif</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.png</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> -->
<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-mvc.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>
DispatcherServlet负责管理和分发控制器,除了两个注释是最核心的配置。具体看之前的文章Spring MVC 框架基础知识注释二作用是引入spring MVC的配置文件初始化配置参数。DispatcherServlet 的工作流程 :
1、向服务器发送 HTTP 请求,请求被前端控制器 DispatcherServlet 捕获。
2、 DispatcherServlet 根据 WEB-INF下的xxx-servlet.xml
中的配置对请求的 URL 进行解析,得到请求资源标识符(URI)。然后根据该 URI,调用 HandlerMapping获得该 Handler(控制器) 配置的所有相关的对象(包括 Handler 对象以及 Handler 对象对应的拦截器),最后以 HandlerExecutionChain 对象的形式返回。
3、DispatcherServlet 根据获得的 Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(…)方法)。
4、提取 Request 中的模型数据,填充 Handler 入参,开始执行 Handler( Controller)。在填充 Handler 的入参过程中,根据你的配置,Spring 将帮你做一些额外的工作:
-
数据转换:对请求消息进行数据转换。比如将String转换为Integer等。
-
HttpMessageConveter:将请求消息(如Json、xml等数据)转换为对象,并将对象转换为指定的响应信息。
-
数据格式化:对请求消息的数据进行格式化。比如将字符串转换为格式化数字或者格式化日期等。
-
数据验证:验证数据的有效性(长度、格式等),并将验证结果存储在BindingResult或Error中。
-
Handler(Controller)执行完成后,返回一个ModelAndView对象给DispatcherServlet。
-
根据返回的ModelAndView,选择一个合适的ViewResolver(必须是Spring容器中注册的ViewResolver)返回给DispatcherServlet。
-
ViewResolver 结合 Model 和 View 来渲染视图。
-
视图负责将渲染结果返回给客户端。
spring配置文件一般会放在Maven的resources
目录下,但DispatcherServlet默认再WEB-INF
目录下寻找,通过注释二的初始化配置路径。当然如果直接放在WEB-INF
目录下就不需要配置了。
静态资源放行
完成上面的配置后启动服务器,上下文连接是根项目名,web资源的上下文配置maven自动配置了上下文连接:
只需要对应webapp下的目录即可,但是会发现路径对应了仍然找不到文件css,html,js等静态资源:
原因在于web.xml
配置的DispatcherServlet
的<url-pattern>/</url-pattern>
为/
表示DispatcherServlet会拦截除jsp
的所有资源并解析(/*
表示拦截所有资源),静态资源经过其解析后就不是所需要的了,因此静态资源不需要其解析。
方法一:更改映射路径
将DispatcherServlet
的<url-pattern></url-pattern>
的路径映射为*.form
或者*.do
就不会再拦截静态资源了。
方法二:服务器默认Servlet(defaultServlet)处理静态资源
将.html,.css,.js,.png,.jpg
等资源交由服务器默认的sevlet处理,如web.xml
的·注释一:
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
使用该方法不能再引入javax.servlet-api
的jar
工具包了,会起冲突,造成DispatcherServlet
处理异常。
方法三:DispatcherServlet实现静态资源的释放
<!--DispatcherServlet无法处理的交由默认servlet处理-->
<mvc:default-servlet-handler default-servlet-name="default"></mvc:default-servlet-handler>
<!--静态资源的放行,不拦截以下目录的资源-->
<mvc:resources mapping="/css/** " location="classpath:**/css/" />
<mvc:resources mapping="/templates/** " location="classpath:**/templates/" />
<mvc:resources mapping="/js/** " location="/js/" />
<mvc:resources mapping="/img/** " location="/img/" />
location
属性可以通过classpath:或者file:
声明位置。
如果以上两种方法不能并存,则选择其中一种即可。
经过上面的配置,Spring MVC的基础环境就搭建好了,静态资源也可以访问:
在配置文件中我们经常看见文件和类路径这两个参数,其表示的意义:
file: xxx-xxx.xml
在根目录下的配置文件。
classpath:xxx-xxx.xml
在resources
目录下的配置文件。
后端接口
后端接口主要是由控制器来完成的,控制器包含众多解析器,如上述的DispatcherServlet
执行流程一样,众多解析器分工合作共同完成。
控制器定义
必须定义一个控制器来解析请求。控制器必须包含上图的每个部分。有些需要声明,有些可以独立创建。
@控制器声明一个处理器,解析URI。具体看之前的文章Spring MVC 框架基础知识当然也可以声明 @RestController。
@RestController 注解,则 Controller 中的方法无法返回 jsp 或者 html 页面,配置的视图解析器 InternalResourceViewResolver 也不起作用,返回的内容就是 Return 里的内容。也就是只能返回数据。
@RestController注解,该类将被视为一个Controller,该类中所有使用@RequestMapping注解的方法默认使用@ResponseBody注解。 getJson方法会将List集合数据转换为JOSN格式返回给客户端。
// 该类会被看成一个Controller
@RestController
@RequestMapping("/json")
public class BookController
{
// 同时该类中所有使用@RequestMapping注解的方法都默认使用了@ResponseBody注解,
// 所以getJson方法会将List集合数据转换成JSON格式并返回客户端。
@RequestMapping(value = "/testRequestBody")
public Object getJson()
{
List<Book> list = new ArrayList<Book>();
list.add(new Book(1, "书名称1", "书的作者1"));
list.add(new Book(2, "书的名称2", "书的作者2"));
return list;
}
}
@Controller既可以返回视图也可以返回数据。默认返回视图, 配合视图解析器返回指定视图,配合@ResponseBody注解,返回数据。
@RequestMapping声明处理器映射器,URI与方法的映射。
@GetMapping是一个组合注解,是@RequestMapping(method = RequestMethod.GET)的缩写。
@PostMapping是一个组合注解,是@RequestMapping(method = RequestMethod.POST)的缩写。
如下没有method属性:
value 属性
一个@Controller 所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法 所匹配的 URI 是不同的。这些不同的 URI被指定在注解于方法之上的@RequestMapping 的 value 属性中。URI的请求 是相对于 Web 的根目录(由配置Tomcat服务器是上下文连接决定)。
method属性
用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。Method 属性的取值为 RequestMethod 枚举常量。RequestMethod.GET RequestMethod.POST
若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配 即对于请求的提交方式无要求。
headers 属性
: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
params属性
:指定request中必须包含某些参数值时,才让该方法处理。
consumes属性
: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces属性
: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。
@ResponseBody控制器指定解析数据的注解。
获取请求参数
表单参数
- 应用程序/json
该类型的数据是最常用的数据传递的类型,只支持POST方法,servlet要用字符流接收。
doPost(HttpServletResquest request,....) throw HttpServletException,IoException{
BufferedReader reader=request.getReader();
String params = reader.readLine();
//params的道德是json字符串,需要用第三方工具包将其转化为javaBean再进行操作
}
后端接收的是一个json字符串,需要共第三方工具包转换为javaBean或其他数据类型。
在Spring MVC框架中,用@RequestBody
注解接收请求日的内容返回的是json字符串需要用第三方框架将json字符串转化为Java Bean。Spring MVC默认了jackson
工具包,也可以使用第三方包fastjson
。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6</version>
</dependency>
没导包会出现如下错误:
06-Mar-2022 08:57:41.773 警告 [http-nio-8080-exec-6] org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json' not supported]
- 应用程序/x-www-form-urlencoded
表单提交的默认格式,不支持文件类型.它的请求格式是键值对。
GET方法会将参数放在请求行传递,URL中可以看到。Spring MVC通过@RequestParam
注解获取,如果参数名与声明的变量一致可省略注解。
@RequestMapping("/one")
@ResponseBody
public String one(@RequestParam("name") String name){
return name;
} //@RequestParam获取请求行参数
@RequestMapping("/two")
@ResponseBody
public String two(String name){
return name;
} //当请求行参数于自定义参数一致是可省略注解
POST方法提交数据会封装到请求体中,以xxx=xxx
的字符串类型传递。
<form action="/display" method="post">
<input type="text" name="name"><br>
<input type="text" name="address"><br>
<button type="submit">提交</button>
</form>
@RequestMapping("/display")
@ResponseBody
public String five(@RequestBody String params){
return params;
}
-
多部分/表单数据
上传文件的格式,以二进制传输,同文件上传
章节。 -
文本/纯文本
text/plain是以纯文本格式(就是一段字符串)发送的. 如果你发送一个对象例如{ name:“leiwuyi”, age:12 }一定要对它做JSON.stringfiy()处理,否则将传送[object Object]
超链接数据
超连接主要有两种传递参数的方式,一种类似application/x-www-form-urlencoded
格式后台获取的方式一致@RequestParam
。
<a href="http://localhost:8080/springMVC?type='news'&page=2"></a>
第二种是URI传参,使用@PathVariable
获取模板参数:
<a href="http://localhost:8080/springMVC/page/2"></a>
//动态接收连接传递的参数
@RequestMapping(path="/page/{id}}", method=RequestMethod.GET)
public String page(@PathVariable("id") int id, Model model) {
// 具体的方法代码…
}
还有类似下面的URI使用@MatrixVariable
具体看之前的文章Spring MVC 框架基础知识:
<a href="http://localhost:8080/springMVC?/cars;color=red;year=2012"></a>
请求行数据
请求行参数就和上面的application/x-www-form-urlencoded
一致。
请求头数据
请求头参数实在请求头中自定义key-value
在接口通过@RequestHeader("key")
来获取:
//http请求头中添加name=xwh,请求体中携带了{address:"beijing"}字符串
var obj={
address:"beijing"};
var jsonString=JSON.stringify(obj);
//alert(jsonString);
const xhr=new XMLHttpRequest();
document.getElementById("btn3").onclick=function(){
//alert("hello");
xhr.open("POST", "http://localhost:8080/springMVC/book/header", true);
xhr.setRequestHeader("name", "xwh");
xhr.send(jsonString);
}
//后端接口
//请求头传递少量参数
@RequestMapping("/header")
public void seven(@RequestHeader("name") String name,@RequestBody String params){
System.out.println("请求头携带参数name:"+name);
System.out.println("请求体数据params:"+params);
}
请求体数据
在pom.xml中导入包(框架默认使用jackson,不需要引用,接口也不需要做任何修改):
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6</version>
</dependency>
如果使用的是fastjson,则需要在接口处调用(领先的fastjosn工具包):
@RequestMapping("/json")
@ResponseBody
//先以json字符串的方式接收在调用工具包的对象转化
public Book six(@RequestBody String params){
//调用该方法将传入类型转化为声明类型
Book book = JSON.parseObject(params,Book.class);
//String param=JSON.toJsonString(args);将传入类型转化为json字符串
return book;
}
这时如果传递了错误的参数类型,就会出现如下错误信息。
Unrecognized token ‘asdd’: was expecting (JSON String, Number, Array, Object or token ‘null’, ‘true’ or ‘false’);
Cannot deserialize value of type spring.model.Book
from Array value (token JsonToken.START_ARRAY
);
正确示范:
//前端发送json字符串
var obj={
name:"xwh",address:"beijing"};
var jsonString=JSON.stringify(obj);
const xhr=new XMLHttpRequest();
document.getElementById("btn1").onclick=function(){
//alert("hello");
xhr.open("POST", "http://localhost:8080/springMVC/book/json", true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(jsonString);
}
//后端默认jackson转化(需要导包)
@RequestMapping("/json")
@ResponseBody
public Book six(@RequestBody Book book){
return book;
}
<!-- 配置fastjson中实现HttpMessageConverter接口的转换器 -->
<bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
<!-- 加入支持的媒体类型:返回contentType -->
<property name="supportedMediaTypes">
<list>
<!-- 这里顺序不能反,一定先写text/html,不然ie下会出现下载提示 -->
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
默认参数
除了上述请求中携带的参数外,控制器还有默认参数。
- ØHttpServlet请求
- ØHttpServletResponse
- ØHttpSession
其他注解参数
这些默认参数的使用方式与 servlet 相同。
请求参数中文乱码问题
以下配置是配置spring mvc的编码格式:
web.xml
配置
<!--过滤器-->
<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>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如图响应头的编码是ISO-8859-1而编码格式是utf-8不一致造成乱码,可以修改响应头编码为utf-8或将jsp页面的解码格式改为utf-8即可。配置html或jsp的解码编码未utf-8:
html
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
原则是请求的编码格式必须与解码格式一致。
处理器方法的返回值
@控制器默认是响应视图,且是jsp
视图,需要通过视图解析器进行配置,转发到对应视图。
@RestController只能返回数据作用单一。转发视图的的核心是配置视图解析器(路径和视图名,视图类型)
➢ ModelAndView
如果处理器方法处理完成后,需要跳转到其他资源,并且需要在跳转的资源之间传输数据,那么处理器方法最好返回到ModelAndView。 ModelAndView 对象在以下处理程序方法中定义:
@RequestMapping(value = "/student")
public ModelAndView studentdo(Student student){
ModelAndView mv = new ModelAndView();
mv.addObject("name",student.getName()); //请求域存储参数
mv.setViewName("/WEB-INF/hello.jsp"); //转发到指定视图
System.out.println(student.getName());
return mv;
}
ModelAndView
同时继承了Model接口和View接口,一个负责将转发的数据存储到请求域,一个负责转发到对应视图。Map和ModelMap都是Model的实现类,完成转存请求域的功能。View还有一个SmartView的接口需要实现。
@RequestMapping("/three")
protected View eight(){
View view =new View() {
@Override
public void render(Map<String, ?> map, javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception {
//逻辑代码
}
}
return view;
}
因此,这些接口不用作返回类型,而是用作配置 ModelView 的参数。
➢ String
处理器方法返回的字符串可以指定逻辑视图名称,可以通过视图解析器解析将其转换为物理视图地址(字符串连接)。
//逻辑视图
@Controller
public class viewCtrl {
@RequestMapping("/display")
public String five(@RequestBody String params){
return "index.jsp";
}
}
/dispaly/index.jsp 视图解析器会在该路径下寻找资源
//物理视图
@Controller
public class viewCtrl {
@RequestMapping("/display")
public String five(@RequestBody String params){
return "/index.jsp";
}
}
//视图解析在上下文连接的路径下即根目录下寻找视图
视图查找机制:物理视图默认在WEB-INF
目录下查找return
的视图,例如return "/templates/two.html"
会在WEB-INF下寻找templates下的two.html,如果没找到在WEB-INF的上级目录继续查找templates下的two.html一次递归知道找到,否则404。逻辑视图直接字符串拼接,[上下文]+/templates/two.html
例如 return "templates/two.html
,会在loclahost:8080/[项目名]/templates/two.html。
➢ 无返回值 void
如果没有返回值,则可以将其视为普通的HttpServlet。
➢ 自定义类型对象
自定义类型多种多样,不利于前端解析。一般只传递JSON、List、Map等数据类型。当然,这些数据在发送之前需要解析成String类型,并且在前端接收到之后也需要进行还原。但是Ajax、axios等框架会自动恢复。
自定义类型通过 @ResponseBody注解,将转换后的 JSON 字符串放入到响应体中。将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由<mvc:annotation-driven/>
来完成。转换器依赖Jackson工具包。需要导入依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6</version>
</dependency>
前端发送的数据需要转换为json,请求头声明类型:
var obj={
name:"xwh",address:"beijing"};
var jsonString=JSON.stringify(obj);
const xhr=new XMLHttpRequest();
document.getElementById("btn1").onclick=function(){
//alert("hello");
xhr.open("POST", "http://localhost:8080/springMVC/book/json", true);
xhr.setRequestHeader("Content-type", "application/json");
xhr.send(jsonString);
}
后端通过jackson工具自动解析为声明的类型(默认工具只需引入,无需配置):
//共调用两次工具解析
@RequestMapping("/json")
@ResponseBody
public Book six(@RequestBody Book book){
//第一次获取请求数据时解析为声明类型
System.out.println(book);
return book; //响应数据时调用解析转为String打印出来是一样的
}
再上面的/json
接口将前端发送的数据又返回给前端,再传递的过程中数据没有变化,变化的只有数据类型。
后台打印的Book类型:
前端响应json字符串(ajax,axios的回调函数会自动将json字符串转化为json,但是需要在响应头中声明Content-Type:application/json
这个声明只可选择枚举的类型,前端支持的类型)。
处理器的响应传值
返回值类型是ModelAndView它有图上的方法来向请求域中添加参数,key-value
类型。
返回值是其他类型通过Model接口或Model接口的实现类ModelMap来实现(用来处理数据转发的接口)。该类或接口只起对象的作用用来携带参数:
@RequestMapping("/three")
protected String eight(Model model ){
model.addAttribute("name","xwh");
return "/index.jsp";
}
常用注解
Spring MVC 常用注解感谢作者@蓝蓝的读书笔记笔记库
@RequestPart
注解用于绑定multipart/form-data
参数,即文件类型。
页面转发
@CrossOrigin注解解决跨域问题
@CrossOrigin(maxAge=3600)
@Controller
public class CrossoriginController
{
...
}
表示CrossOriginController
控制器的所有方法可以处理http://www.fkit.org
域上的请求:
@CrossOrigin(
origins="http://www.fkit.org",
maxAge=3600
)
@Controller
public class CrossOriginController
{
....
}
@CookieValue注解
注解用于将请求的Cookie数据映射到请求处理方法的形式参数上。使用@CookieValue
注解可指定如下表所示的属性:
package org.fkit.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class CookieValueController
{
// 测试@CookieValue注解
// 该方法映射的请求为 /cookieValueTest
@GetMapping(value = "/cookieValueTest")
// 将cookie中的JSESSIONID值赋值给方法的参数sessionId
public void cookieValueTest(
@CookieValue(value = "JSESSIONID", defaultValue = "") String sessionId)
{
System.out.println("通过@CookieValue获得JSESSIONID: " + sessionId);
}
}
@RequestAttribute
注解用于访问由请求处理方法、过滤器或拦截器创建的、预先存在于request作用域中的属性,并将该request作用域中的属性的值设置到请求处理方法的形式参数上。
@RequestMapping(value="/arrtibuteTest")
public void arrtibuteTest(
@RequestAttribute(value="username") String username){
... }
上面的代码会自动将请求范围内名为 username 的属性的值设置为 username 参数。
@SessionAttribute
注解用将session作用域中的属性赋值给目标方法的形式参数,这些属性由请求处理方法、过滤器或拦截器创建并存在于session作用域中。
@RequestMapping(value="/arrtibuteTest")
public void arrtibuteTest(
@SessionAttribute(value="username") String username) {
...}
上面的代码会自动将session范围内名为username的属性的值设置为请求处理方法的username形参。
@SessionAttributes
注解允许我们有选择地指定Model中的哪些属性转存到HttpSession对象当中。
package org.fkit.controller;
import org.fkit.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
// 将Model中名为user的的属性转存HttpSession对象当中
@SessionAttributes("user")
public class SessionAttributesController
{
// 该方法映射的请求为http://localhost:8080/SessionAttributesTest/login
// 把表单提交的请求参数loginname赋值给方法的loginname参数
@RequestMapping(value = "/login")
public String login(@RequestParam("loginname") String loginname,
@RequestParam("password") String password, Model model)
{
// 创建User对象,装载用户信息
User user = new User();
user.setLoginname(loginname);
user.setPassword(password);
user.setUsername("admin");
// 将user对象添加到Model当中
model.addAttribute("user", user);
// 浏览器端重定向到其他请求处理方法,这样会重新生成一个请求对象
// 因为是新的对象,所以request作用域内将不再存在user属性
return "redirect:/sessionScope";
}
@RequestMapping(value = "/sessionScope")
public String sessionScope()
{
return "welcome";
}
}
异常处理
在Controller
的请求处理方法中手动使用try… catch
块捕捉异常,当捕捉到特定异常时,返回特定逻辑视图名
,但这种处理方式非常烦琐,需要在请求处理方法中书写大量的catch
块。
- servlet配置文件web.xml处理错误页面:
<error-page>
<error-code>404</error-code>
<location>/error.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/base.html</location>
</error-page>
这种方式过于笼统,不利于处理多种类型的错误。
<error-page>
<exception-type>Exception</exception-type>
<location>/error.html</location>
</error-page>
这种Java异常的处理需要熟悉各种异常类,并不好处理。
- Spring MVC中提供了两种异常处理方法:
- 使用Spring MVC提供的简单异常处理器
SimpleMappingExceptionResolver
。
<!-- p:defaultErrorView="error"表示所有没有指定的异常都跳转到异常处理页面error, -->
<!-- p:exceptionAttribute="ex"表示在异常处理页面中可以访问的异常对象变量名是ex。 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="error" p:exceptionAttribute="ex">
<!-- 异常映射 exceptionMappings表示映射的异常, -->
<!-- 接受参数是一个Properties key是异常类名,value是处理异常的页面 -->
<property name="exceptionMappings">
<props>
<prop key="IOException">ioerror</prop>
<prop key="SQLException">sqlerror</prop>
</props>
</property>
</bean>
重点是异常处理的配置。 SimpleMappingExceptionResolver是Spring提供的一个用于处理异常的类。所有抛出的异常都会被这个类捕获。
ExceptionHandlerExceptionResolver
异常处理器,使用@ExceptionHandler
注解实现局部异常处理或使用@Controlleradvice
注解实现统一异常处理或@ResponseStatus
处理http异常。这两个是实现ExceptionHandlerExceptionResolver
@ResponseStatus
注解是处理异常最简单的方式,其可以修饰一个类或者一个方法,当修饰一个类的时候,通常修饰的是一个异常类。
使用时,先声明一个自定义异常类,在自定义异常类上面加上@ResponseStatus注解,就表示在系统运行期间,当抛出自定义异常的时候,使用@ResponseStatus注解中声明的value属性和reason属性将异常信息返回给客户端,
@ExceptionHandler
注解作用对象为方法,并且在运行时有效,value()可以指定异常类。@ExceptionHandler注解的方法可以支持的参数除了HttpServletRequest、HttpServletResponse等对象之外,还支持一个异常参数,包括一般的异常或自定义异常。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController
{
@GetMapping("/test")
public String test() throws Exception
{
// 模拟异常,调用本类中定义的异常处理方法
@SuppressWarnings("unused")
int i = 5 / 0;
return "success";
}
// 在异常抛出的时候,Controller会使用@ExceptionHandler注解的方法去处理异常
// value=Exception.class表示处理所有的Exception类型异常。
@ExceptionHandler(value = Exception.class)
public ModelAndView testErrorHandler(Exception e)
{
ModelAndView mav = new ModelAndView();
mav.setViewName("error");
mav.addObject("ex", e);
return mav;
}
}
@ExceptionHandler
注解, value = Exception.class
表示处理所有的Exception类型异常。当TestController类抛出异常的时候,会使用@ExceptionHandler注解的方法去处理异常,而不会直接抛给浏览器。 testErrorHandler()
方法将捕捉到的异常对象保存到ModelAndView
当中,传递到JSP页面。
在基于Controller的@ExceptionHandler注解方法处理异常时,每个Controller都需要编写@ExceptionHandler注解的异常处理方法,在实际开发中非常繁琐。可以写一个父类,在父类中完成用@ExceptionHandler注解的异常处理方法。如果所有的Controller都继承这个父类,那么所有的Controller都会有@ExceptionHandler注解的异常处理方法。
//父类Exception
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
public class BaseExceptionController
{
// 表示这是一个异常处理方法
// value = Exception.class表示处理的异常类型为Exception
// 也就是处理所有的异常
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(Exception e) throws Exception
{
ModelAndView mav = new ModelAndView();
// 设置模型数据
mav.addObject("ex", e);
// 设置视图
mav.setViewName("error");
return mav;
}
}
//子类Exception
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class UserController extends BaseExceptionController
{
@GetMapping("/login")
public String login(String username) throws Exception
{
if (username == null)
{
// 调用本类的异常处理方法
throw new NullPointerException("用户名不存在!");
}
return "success";
}
}
@ControllerAdvice注解
该注解使用@Component注解,也就是说可以使用<context: component-scan>扫描该注解。 Spring官方文档说明,扫描到@Controlleradvice注解之后,会将@ControllerAdvice注解修饰的类的内部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的请求处理方法上。
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
// GlobalExceptionHandler类使用了@ControllerAdvice注解来修饰,
// 其会被<context:component-scan>扫描,
// 这使得该类中使用@ExceptionHandler注解修饰的方法都被应用到所有请求处理方法上
// 也就是所有请求处理方法抛出的异常都将由该类中对应的@ExceptionHandler注解修饰的方法处理.
@ControllerAdvice
public class GlobalExceptionHandler
{
// 处理Exception类型异常
@ExceptionHandler(value = Exception.class)
public ModelAndView globalErrorHandler(Exception e) throws Exception
{
ModelAndView mav = new ModelAndView();
mav.addObject("ex", e);
mav.setViewName("error");
return mav;
}
// 处理OrderException自定义异常
@ExceptionHandler(value = OrderException.class)
// 返回的结果将会被封装成JSON数据,并返回给客户端
@ResponseBody
public Object OrderErrorHandler(Exception e) throws Exception
{
// 创建返回对象Map并设置属性,会被@ResponseBody注解转换为JSON返回
Map<String, Object> map = new HashMap<>();
map.put("code", 100);
map.put("message", e.getMessage());
map.put("data", "请求失败");
return map;
}
}
GlobalExceptionHandler
类使用了@ControllerAdvice
注解来修饰,则该类会被<context: component-scan>
扫描,该类中使@ExceptionHandler注解修饰的方法将被应用到所有
请求处理方法上。
@RestControlleradvice
注解org.springframework.web.bind.annotation.RestControlleradvice
注解本身使用@ControllerAdvice
和@ResponseBody
注解。使用了@RestControllerAdvice
注解的类会被看作一个@ControllerAdvice
,而该类中所有使用@ExceptionHandler
注解的方法都默认使用@ResponseBody
注解。
package org.fkit.controller;
import java.util.HashMap;
import java.util.Map;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** * @RestController注解本身使用@ControllerAdvicer和@ResponseBody注解。 * 使用了@RestControllerAdvice注解的类会被看作一个ControllerAdvicer, * 而该类中所有使用@ExceptionHandler注解的方法都默认使用了的@ResponseBody注解。 */
@RestControllerAdvice
public class GlobalExceptionHandler
{
// 处理OrderException自定义异常
@ExceptionHandler(value = OrderException.class)
// 默认使用了的@ResponseBody注解,会将方法返回的Map转换为JSON数据发送给浏览器
public Object OrderErrorHandler(Exception e) throws Exception
{
// 创建返回对象Map并设置属性,会被@ResponseBody注解转换为JSON返回
Map<String, Object> map = new HashMap<>();
map.put("code", 100);
map.put("message", e.getMessage());
map.put("data", "请求失败");
return map;
}
}
在实际开发中@ExceptionHandler
注解的功能最强大。异常处理
文件上传
Spring MVC的文件上传十分方便不同于于Servlet上传需要写大量配置,因为它直接提供了对文件上传的直接支持即MultipartResolver
接口。该接口用于处理上传请求,并将上传的数据包装成可以直接获取的文件。
MultpartiResolver
接口有以下两个实现类:
StandardServletMultipartResolver
:使用了 Servlet 3.0 标准的上传方式。CommonsMultipartResolver
:使用了 Apache 的 commons-fileupload 和commons-io=来完成具体的上传操作。
MultpartiResolver
接口具有以下方法。
使用 CommonsMultipartResolver
来完成文件上传,分为单文件上传和多文件上传两部分介绍:
- 单个文件上传
- 导入依赖项
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.2.2</version>
</dependency>
- 配置MultipartResolver
<!-- 配置MultipartResolver,用于上传文件,使用spring的CommonsMultipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="5000000" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!--defaultEncoding:请求的编码格式,默认为 ISO-8859-1,此处设置为 UTF-8 (注:defaultEncoding 必须和 JSP 中的 pageEncoding 一致,以便正确读取表单的内容)。-->
<!--maxUploadSize:上传文件大小上限,单位为字节。-->
resolveLazily:延迟解析,默认为false--立即解析multipart request;
defaultEncoding:解析请求的默认字符编码 ; 默认值为"ISO-8859-1"。通常设置为"UTF-8";
maxUploadSize:文件上传最大值; 默认值为 -1(表示没有限制);
maxUploadSizePerFile:每个文件上传最大值;默认值为 -1(表示没有限制);
maxInMemorySize:存储在内存的最大值;默认值为10240B(10KB);
uploadTempDir:上传文件的临时目录;默认值为WEB应用程序的临时目录;
servletContext:the servletContext to use;
- 文件上传表单页面
对于表单的文件上传,需要使用enctype属性,将其值设置为multipart/form-data,并将表单提交方式设置为post。
<!--负责文件上传表单的编码类型必须是“multipart/form-data”类型。-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/springOne/view/getFile" method="post" enctype="multipart/form-data">
<input type="file" name="fileInfo">
<input type="text" name="description">
<button type="submit">上传</button>
</form>
</body>
</html>
- 创建文件映射对象POJO类
在该 POJO 类中声明一个 MultipartFile
即(前端的uplaodaFile
)类型的属性封装被上传的文件信息,属性名与前端页面 <input type=file name="myfile">
的name
属性的相同,代码如下:
import org.springframework.web.multipart.MultipartFile;
public class File {
private String description;
private MultipartFile fileInfo;
}
- 编写一个文件上传控制器
//测试是否可以传递到后端
@PostMapping("/getFile")
protected String uploadFile(String description, MultipartFile fileInfo){
System.out.println(description);
System.out.println(fileInfo);
return "success";
}
如图所示文件成功传递到后端,调用MultpartiResolver
接口的方法对其解析和转存即可。
File
类型是java内置处理文件的。
解析并存储的代码:
@PostMapping("/getFile")
@ResponseBody
protected String uploadFile(String description, MultipartFile fileInfo, HttpServletRequest request) {
//System.out.println(description);
//System.out.println(fileInfo);
//获取原始名,目的时获取后缀名
String originalFileName=fileInfo.getOriginalFilename();
//获取后缀名
String extensionName=originalFileName.substring(originalFileName.lastIndexOf("."));
System.out.println(extensionName);
//生成随机文件名,避免上传的文件同名,时间戳加随机数
//String.valueOf(new Date().getTime());
//String.valueOf(Math.random()*100);
String newName=String.valueOf(new Date().getTime())+String.valueOf(Math.random()*100);
System.out.println(newName);
//生成完整文件名,如:23482493242.jpg
String finallyName=newName+extensionName;
System.out.println(finallyName);
//获取需要转存的位置即服务器的路径
//controller继承的HttpServlet携带两个基本参数(res,resp)
//spring MVC默认的路径时WEB-INF,如果没有会向上查找
String dirname=request.getServletContext().getRealPath("imgs");
String savePath=dirname+"/"+finallyName;
System.out.println(savePath);
//保存文件
try {
fileInfo.transferTo(new File(savePath));
}catch (IOException e){
e.printStackTrace();
System.out.println("save error");
}
return "success";
}
后台输出结果:
处理处理文件解析与保存的过程中除了Spring MVC提供的接口的方法外主要就是String dirname=request.getServletContext().getRealPath("imgs");
请求中的这个方法了,看源代码配置:
private File getCommonDocumentRoot() {
for (String commonDocRoot : COMMON_DOC_ROOTS) {
File root = new File(commonDocRoot);
if (root.exists() && root.isDirectory()) {
return root.getAbsoluteFile();
}
}
return null;
}
private static final String[] COMMON_DOC_ROOTS = {
"src/main/webapp", "public",
"static" };
request.getServletContext().getRealPath()
返回的是一个临时文件夹的绝对地址,会根据服务器地址变化,例如,小编在调用是获取的本机地址是E:JavaWeb\xxx\...\webapp\imgs
如上面dirname
传递的参数是imgs
那么就会映射到该目录(首先要存在)。正如源代码配置的一样该地址默认映射到项目
的webapp目录。
简单说该临时地址由三部分组成:项目所在位置的绝对地址
+src/main/webapp
+参数
。第一部分随项目部署的位置改变。
文件下载
浏览器支持多种文件的解析和存储,因此下载文件只需要提供数据流的接口即可。 (以图像文件为例)
文件下载只能是异步请求。通过Ajax发送请求,获取服务器图片目录下的所有图片地址并显示在前端页面上:
获取服务器文件名
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../js/axios.min.js"></script>
</head>
<body>
<h1>bookShow</h1>
<button type="button" id="btn3" >set header</button><!--οnclick="alert('hello')"-->
<script> document.getElementById("btn3").onclick=function(){
axios({
method:'get', url:'http://localhost:8080/springOne/view/download', }).then(function(response){
console.log(response.data); }) } </script>
</body>
</html>
//文件下载
@GetMapping("/download")
@ResponseBody
protected String[] downloadFile(HttpServletRequest request,HttpServletResponse response){
//从imgs目录下获取所有图片,并响应前端
String dirname=request.getServletContext().getRealPath("imgs");
//img是一个目录本身也是java的File对象,通过该对象的接口获取所有图片的名称
File imgFileName=new File(dirname);
//返回文件夹下所有图片名称
String [] nameList=imgFileName.list();
return nameList;
}
返回图片名称与服务器目录下一致:
前端能够拿到数据后,将数据可视化,并为每个图片提供下载按钮,这个按钮发送异步请求,这时返回的就是数据流了,由浏览器解析。
可视化图片并提供下载功能
//使用bootstrap框架对数据解析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="../js/jquery.js"></script>
<script src="../js/axios.min.js"></script>
<script src="../js/bootstrap.min.css"></script>
<link rel="stylesheet" href="../css/bootstrap.min.css">
</head>
<body>
<div class="row" id="container">
</div>
<script> axios({
method:'get', url:'http://localhost:8080/springOne/view/download', //responseType:'stream' }).then(function(response){
//console.log(response.data); for(var i=0; i<response.data.length; i++){
var addPicHtml="<div class='col-sm-6 col-md-4 col-sm-4 col-xs-6' style='width:200px'><div class='thumbnail'><img src='/springOne/imgs/"+response.data[i]+"' alt='...' width='100%'><div class='caption'><p><a href='#' class='btn btn-primary' role='button'>下载</a></p></div></div></div>"; //document.getElementById("container").innerHTML = addPicHtml; $("#container").append(addPicHtml); } }) </script>
</body>
</html>
下载功能实现
浏览器可以自主解析图片文件,后端接口提供图片的数据流即可,也是异步请求:
如上面的gif中上传后鼠标移动到下载后左下角地址的变化,将图片名称通过url传递给后端接口。
在可视化图片时已经获取了图片名称,将图片名称传到下载API通过名称找到图片传回数据流:
//传输图片数据流
@GetMapping("/imgbases")
public void imgBase(@RequestParam("img") String imgName ,HttpServletRequest request,HttpServletResponse response){
//获取图片目录
String dirname=request.getServletContext().getRealPath("imgs");
//获取图片文件路径
String filePath=dirname+"/"+imgName;
//图片的输出流(内存中)
try{
FileInputStream inputStream=new FileInputStream(filePath);
//图片数据流浏览器可以直接识别,设置响应头,让浏览器无法识别,从而调用保存接口
response.setContentType("application/unknown");
//把文件名也传递过去,保存的时候需要,专门接口的响应头,不是普通响应头
response.addHeader("Content-Disposition","attachment;filename="+imgName);
//使用commons-io的流处理将数据流写入响应体
IOUtils.copy(inputStream,response.getOutputStream());
}catch (IOException e){
e.printStackTrace();
}
}
上面的代码已经将图片数据流发送给前端,response.setContentType()
方法将响应内容设置为浏览器无法解析的对象,这样就会调用保存的系统接口,同时把文件名也传过去,便于保存。(如果没有两步设置,点击下载会发现图片会打开而不是调用保存接口,这是由于浏览器能够解析图片文件,不会直接调用系统保存文件接口。在响应头设置其为无法识别的接口就可以直接存储了。
拦截器
Servlet 中有过滤器,Spring IoC 容器中有拦截器,它们有很大不同:
- 过滤器和拦截器的触发时机不同。过滤器在请求进入容器之后、请求进入 servlet 之前进行预处理。请求完成后、Servlet处理完毕后、返回给前端之前的返回也是如此。
- 拦截器可以获取IOC容器中的各个bean,但是过滤器不能,因为拦截器是由spring提供和管理的。 Spring的功能可以被拦截器使用。将服务注入拦截器即可调用业务逻辑。 Filter是JavaEE标准,只需要依赖servlet api,不需要依赖spring。
- 过滤器的实现基于回调函数。拦截器(代理模式)的实现是基于反射的。
- Filter依赖于Servlet容器,是Servlet规范的一部分,而拦截器则独立存在,可以与Spring一起在任何情况下使用。
- Filter的生命周期由Servlet容器管理,而拦截器可以由IoC容器管理,因此可以通过注入等方式获取其他Bean的实例,所以使用起来比较方便。
在Servlet开发中,每个请求对应一个Servlet,因此可以通过Filter进行管理;但在Spring MVC中,只有一个DispatcherServlet,通过映射访问Filter已经不能满足需求,需要通过Ierceptor(拦截器)来实现。
在Spring MVC框架中定义拦截器需要定义和配置拦截器。主要有两种方式。
- 通过实现
HandlerInterceptor
接口或继承HandlerInterceptor
接口的实现类(例如
HandlerInterceptorAdapter)来定义;
//首先是实现接口
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class TestInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法在控制器的处理请求方法调用之后,解析视图之前执行");
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法在控制器的处理请求方法调用之前执行");
return true;
} /******注意return true 放行******/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion方法在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
}
}
//控制器映射
@RequestMapping(value = "/three",produces = "application/html;charset=utf-8")
protected String eight(Model model){
model.addAttribute("name","张三");
return "/index.jsp";
}
<!--通过配置文件对three接口拦截-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/view/three"/>
<bean class="spring.controller.Interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 多个拦截器会构成拦截器链,按配置顺序拦截 -->
结果:
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 配置一个全局拦截器,拦截所有请求 -->
<bean class="net.biancheng.interceptor.TestInterceptor" />
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 配置不需要拦截作用的路径 -->
<mvc:exclude-mapping path="" />
<!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
<bean class="net.biancheng.interceptor.Interceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/gotoTest" />
<!-- 定义在<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
<bean class="net.biancheng.interceptor.Interceptor2" />
</mvc:interceptor>
</mvc:interceptors>
- 通过实现
WebRequestInterceptor
接口或继承WebRequestInterceptor
接口的实现类来定义。
WebRequestInterceptor和HandlerInterceptor接口中定义的方法作用也是一样的。不同点是
WebRequestInterceptor的入参WebRequest是包装了HttpServletRequest 和HttpServletResponse的,通过WebRequest获取Request中的信息更简便。其次preHandle是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行,所以这个接口实现就是为了获取Request中的信息,或者预设一些参数供后续流程使用。
SSM整合
Maven创建web项目,并配置Tomcat。
添加war
的打包依赖,导入servlet和jsp依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
</project>
Mybatis配置
添加持久层框架时,不要忘记相应的数据库驱动工具包。
- 导入mybatis依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.5</version>
</dependency>
- 创建mybatis-config.xml用于创建SqlSessionFactory
文件不需要任何配置,在IoC容器中通过MapperScannerConfigurer
对象配置
<?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>
<!--DataResource配置-->
<!--Mapper.xml配置-->
</configuration>
Spring MVC 与 Mybatis 的集成依赖于 IoC 容器。 DataSource、SqlSessionFactory、SqlSession、Mapper等对象可以由IoC容器管理。同时Spring AOP提供了声明式事务管理,更加方便。
部署Spring、Spring MVC
- 添加依赖项
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<!--jdbc的orm用于整合其他持久层框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.19.RELEASE</version>
</dependency>
<!--spring默认解析数据的工具,导入即用无需配置-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
- 创建配置文件
如果所有的配置文件都集中在一个xml中,那就太冗余了。使用多个配置文件分别管理(不独立分工):
beans.xml
配置注解声明,以及bean管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config></context:annotation-config>
<context:component-scan base-package="cms.ssm"></context:component-scan>
</beans>
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: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></mvc:annotation-driven>
</beans>
mybatis-config.xml
进行Mybatis的配置。
<?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>
spring-mybatis.xml
进行spring于mybatis整合配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/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>
web.xml
配置Spring MVC的控制器。
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--服务器启动加载web.xml文件contextConfigLocation用于加载spring相关配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-*.xml</param-value>
</init-param>
<!--classpath映射为resources目录下,file映射为根目录下 spring-*标识加载所有以此开头的文件-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
在web.xml中配置contextConfigLocation
对象并配置路径,就会加载配置文件并启动IoC容器,不需要再java代码中通过ClassPathApplicationContext
等上下文对象再次加载。
Spring整合Mybatis
以上配置实现了spring配置文件集成到controller中,进而实现了spring集成到mybatis中。
- 导入mybatis-spring依赖
<!--mybatis与spring结合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
- 配置连接池,这里使用druid
<!--德鲁伊连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
- 配置基于jdbc的连接池参数
druid.driver=com.mysql.cj.jdbc.Driver
druid.url=jdbc:mysql://localhost:3306/${数据库名}?characterEncoding=utf-8
druid.username=${username}
druid.password=${password}
#连接处参数
druid.pool.init=1
druid.pool.minIdle=3
druid.pool.maxActive=20
druid.pool.timeout=30000
- 在spring-mybatis.xml文件中配置数据源
<!--导入配置信息-->
<context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
<!--基于IoC容器创建数据源DataResource-->
<bean id="druidDataResource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${druid.driver}"></property>
<property name="url" value="${druid.url}"></property>
<property name="username" value="${druid.username}"></property>
<property name="password" value="${druid.password}"></property>
<property name="initialSize" value="${druid.pool.init}"></property>
<property name="minIdle" value="${druid.pool.minIdle}"></property>
<property name="maxActive" value="${druid.pool.maxActive}"></property>
<property name="maxWait" value="${druid.pool.timeout}"></property>
</bean>
<!--赋值表达式会自动将导入的配置信息按名称赋给对应属性-->
- 在spring-mybatis.xml文件中配置SqlSessionFactory
<!--生产mybatis-spring提供的SqlSessionFactoryBean接收mybatis的SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--SqlSessionFactory中需要配置Mapper和DataResource-->
<property name="dataSource" ref="druidDataResource"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<!--设置POJO的包名,typeAliasesPackage指定POJO包名,mapper.xml的resultType,parameterType就不需要写全限定名了-->
<property name="typeAliasesPackage" value="cms.ssm.model"></property>
</bean>
<!--扫描dao包下的全部接口,并交由IoC容器管理-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
<!--指定Mapper接口的包名-->
<property name="basePackage" value="cms.ssm.dao"></property>
</bean>
第一个bean实现注入xml文件并创建SqlSessionFactory
,第二个bean负责生产basePackage包下的所有Mapper类并注入sql语句,返回SqlSession
的getMapper()
等方法返回的Mapper类。
- 在spring-mybatis.xml文件中配置事务管理
<!--声明式事务事务管理,配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="druidDataResource"></property>
</bean>
<!--开启事务注解扫描-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
把配置文件交给spring来加载,最好不要加载到springMVC中,以免出错,因为spring的监控是在配置web.xml时首先启动的,而springMVC的Dispatcherservlet在收到请求时会初始化springMVC的配置文件。
测试是否配置成功
resources
的mapper中创建UserMapper.xml
<?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="cms.ssm.dao.UserMapper">
<select id="allUser" resultType="User"> <!--mapper.xml文件导入到了IoC容器,resultTyoe直接从容器中获取-->
select *
from user
</select>
</mapper>
dao
中创建UserMapper.java的映射接口
public interface UserMapper {
List<User> allUser();
}
- 基于 spring 测试创建单元测试
在测试目录中创建 UserMapperTest 的测试文件,内容如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-context.xml","classpath:spring-mybatis.xml","classpath:spring-servlet.xml"})
public class UserMapperTest {
@Resource
private UserMapper userMapper;
@Test
public void allUser() {
List<User> list=userMapper.allUser();
System.out.println(list);
}
}
数据表:
测试的时候遇到如下错误:
在properties文件url后添加&useSSL=false&serverTimezone=GMT%2B8
druid.url=jdbc:mysql://localhost:3306/db1?characterEncoding=utf-8
druid.url=jdbc:mysql://localhost:3306/db1?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
主要原因式数据库驱动版本过低,mysql8.0Driver变更由com.mysql.jdbc.Driver
更新为com.mysql.cj.jdbc.Driver
,更改连接URL,设置useSSL=false
。更改连接URL,增加服务器时区值配置serverTimezone=GMT%2B8
GMT%2B8表示时区东八区
开始也是这个问题看了这个博客的解析,更改后就没问题了。初始化数据库错误,初始化数据源错误,url:jdbc:mysql://localhost:3306/
需要注意的是测试类中需要使用注解加载spring及spring mvc相关文件,当配置了Tomcat后配置文件在web.xml中就加载了,由Tomcat初始化。
配置Tomcat实现后端后端接口
写一个控制器
@Controller
public class Login {
@Resource
private UserMapper userMapper;
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String identify(Model model){
List<User> list=userMapper.allUser();
model.addAttribute("list",list);
return "index.jsp";
}
}
jsp作为显示页面
<%@ page isELIgnored="false" %>
<html>
<body>
<h2>Hello World!</h2>
${list}
</body>
</html>
配置并启动服务器,浏览器访问接口
案件解压后直接运行,免费下载!有一个对象相信会更快搭建ssm环境。