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)执行完成后,向 DispatcherServlet 返回一个ModelAndView 对象。
-
根据返回的 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和classpath这两个参数,其表示的意义:
file: xxx-xxx.xml
在根目录下的配置文件。
classpath:xxx-xxx.xml
在resources
目录下的配置文件。
后端接口
后端接口主要是由控制器来完成的,控制器包含众多解析器,如上述的DispatcherServlet
执行流程一样,众多解析器分工合作共同完成。
控制器定义
定义一个控制器才能对请求进行解析,一个控制器就必须包含上图的每个部分,有的需要声明,有的自主创建。
@Controller声明一个处理器,解析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控制器指定解析数据的注解。
获取请求参数
表单参数
- application/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]
- application/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;
}
-
multipart/form-data
上传文件的格式,以二进制传输,同文件上传
章节。 -
text/plain
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>
默认参数
除了上面请求携带的参数外控制器还有默认参数。
- ➢HttpServletRequest
- ➢ 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"%>
其原理是请求的编码格式要和解码格式一致。
处理器方法的返回值
@Controller默认是响应视图,且是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){ ... }
以上代码会自动将request作用域中名为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的功能可以被拦截器使用,在拦截器里注入一个service,可以调用业务逻辑。而过滤器是JavaEE标准,只需依赖servlet api ,不需要依赖spring。
- 过滤器的实现基于回调函数。而拦截器(代理模式)的实现基于反射。
- Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在Spring的任何情况下使用。
- Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
在Servlet开发中,每个请求对应一个Servlet,因此可以通过Filter管理;但在Spring MVC中,只有一个DispatcherServlet,通过映射来访问Filter不再满足需求,需要通过Ierceptor(拦截器来实现)。
在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,主要有以下 2 种方式。
- 通过实现
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配置文件到控制器的整合,接下来实现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>
将配置文件交给sping加载,最好不要交给springMVC加载 避免出现错误,因为web.xml配置时spring的监听先启动,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 test创建单元测试
在test目录下创建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表示时区东八区
开始也是这个问题看了这个博客的解析,更改后就没问题了。初始化数据库错误,init datasource error, 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环境。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/156312.html