SpringMVC的工作原理(底层Servlet的工作流程)
- DispatcherServlet
- HandlerMapping ,返回值 HandlerExecutionChain
- HandlerAdapter
- ViewResolver
- View
~~老版本(过去式)
一、搭建springmvc
第一步:配置打包方式
<!--配置打包方式-->
<packaging>war</packaging>
第二步:重新定义webapp目录
第三步:导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
第四步:构建不同类型的实现类/继承类
1. 实现接口 HttpRequestHandler
package com.qf.mvc01.controller;
import org.springframework.web.HttpRequestHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author:lyz
* @since: jdk 1.8
**/
public class BookController implements HttpRequestHandler {
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.getWriter().write("hello book");
}
}
2. 实现接口 Controller
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author:lyz
* @since: jdk 1.8
**/
/**
* 这个是SpringMVC 中的接口定义
*
* http://localhost:8080/m01/hello
*/
public class HelloController implements Controller {
/**
* 这个就是具体的接口,当前端的请求到达服务端之后,就是由这个方法来处理
* @param request
* @param response
* @return 返回值类型是ModelAndView(数据模型+视图):实际上呢就是数据+页面
* @throws Exception
*/
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//创建一个ModelAndView 对象,构造参数就是 视图的名称,(注意:只要写名称即可,不用写后缀)
ModelAndView mv = new ModelAndView("hello");
//添加数据模型
mv.addObject("msg","hello springmvc");
return mv;
}
}
(1). 其前端校验例子
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
3. 继承 HttpServlet
package com.qf.mvc01.controller;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author:lyz
* @since: jdk 1.8
**/
public class UserController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
第五步:配置 resources(spring-servlet)
1. 将 各Controller 注册到 Spring 容器中
2. 配置 处理器映射器 HandlerMapping
3. 配置 处理器适配器 HandlerAdapter
4. 配置视图解析器 ViewResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 1.先将HelloController注册到Spring容器中-->
<bean class="com.qf.mvc01.controller.HelloController" id="/hello"/>
<bean class="com.qf.mvc01.controller.BookController" id="bookController"/>
<!-- 2.配置 处理器映射器 HandlerMapping
处理器映射器的作用就是:将前端请求的地址和服务端的 Controller 给关联起来
例如:如果前端请求的地址是 http://localhost:8080/m01/hello , 那么就将这个请求交给 HelloController 去处理
(1)BeanNameUrlHandlerMapping:这个处理器映射器,就是将请求的 URL地址和 Spring 容器中的 Bean 名称给莪关联起来,例如,前端请求地址是http://localhost:8080/m01/hello ,首先提取出来接口名字是 /hello,那么此时就回去 Spring 容器中自动查找一个名为 /hello 的 Bean 去处理这个请求
(2)SimpleUrlHandlerMapping:这也是一个处理器映射器,这个处理器映射器需要开发者“手动”去配置请求路径和 Bean 直接的对应关系
-->
<!--(可以多个多种 HandlerMapping 共存的)-->
<!--配置(1)-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--配置(2)-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/book">bookController</prop>
<!--
<prop key="/....">..............</prop>
<prop key="/book">bookController</prop>
根据实际情况可以配很多个
<prop key="/book">bookController</prop>
<prop key="/book">bookController</prop>
-->
</props>
</property>
</bean>
<!-- 3.配置处理器适配器 HandlerAdapter
第二步,找到了请求对应的 Spring 容器中的 Bean,但是这个 Bean 的定义,有很多种不同的方式(实现接口、继承类、注解...等等)
,不同的方式对应不同的调用方法,所以我们这里需要一个适配器,通过适配器,去调用不同的 Bean。
如果处理器映射器:实现接口为 Controller ==》 那么处理器适配器 就要配置 SimpleControllerHandlerAdapter
实现接口为 Servlet ==》 那么处理器适配器 就要配置 SimpleServletHandlerAdapter
继承类 HttpRequestHandler ==》 那么处理器适配器 就要配置 HttpRequestHandlerAdapter
注解 ==》 那么处理器适配器 就要配置 RequestMappingHandlerAdapter
-->
<!--(可以多个多种 HandlerAdapter 共存的,当时要与HandlerMapping的实现接口或继承类相互一一对应)-->
<!--此处由于例子为HelloController实现接口Controller,所以处理器适配器 选择配置SimpleControllerHandlerAdapter-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--此处由于例子为BookController实现接口HttpRequestHandler,所以处理器适配器 选择配置HttpRequestHandlerAdapter-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
<!-- 4.配置视图解析器 ViewResolver
配置视图解析器的目的是:让接口返回的 ModelAndView 能够正确的找到视图
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--这个属性是:视图的前缀(其实就是视图的路径)-->
<!--注意最后面有一个 / 不能省略-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--这个属性是:视图的后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
第六步:在 web.xml 中加载 spring-servlet.xml配置文件信息
1. 加 spring-servlet.xml 配置文件信息
2. 同时配置 springmvc 拦截器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- SpringMVC 的核心是一个名为 DispatcherServlet 的类,
这个类本质上,实际上就是一个Servlet,
当系统启动的时候,我们需要在这个 Servlet 种读取 SpringMVC 的配置文件
-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--指定具体的配置文件-->
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--这里的拦截规则为 / ,表示拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
~~新(现在时)
一、搭建springmvc
第一步:配置打包方式
<!--配置打包方式-->
<packaging>war</packaging>
第二步:重新定义webapp目录
第三步:导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
</dependencies>
第四步:构建不同类型的实现类/继承类
给创建的 Controller 类上添加 @Controller注解
package com.qf.mvc02.controller;
import org.springframework.stereotype.Controller;
/**
* @author:lyz
* @since: jdk 1.8
**/
@Controller
public class TestController {
}
第五步:配置 resources(spring-servlet)
1. 添加 springframework 的 xmlns
2. 通过注解扫描注册 Controller
3. 同时配置 处理器映射器、处理器适配器
4. 配置视图解析器 ViewResolver
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--Ctrl+Alt+O :自动整理导入包 (上方)-->
<!-- 1.扫描所有的 Controller-->
<context:component-scan base-package="com.qf.mvc02.controller"/>
<!--
下面这个可以代替 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter,
但是,下面这个默认的配置,大于RequestMappingHandlerMapping + RequestMappingHandlerAdapter
(及<mvc:annotation-driven/>的功能大于两个bean的配置功能)
-->
<mvc:annotation-driven/><!--实际开发中,大多数都是用这个-->
<!-- 2.配置处理器映射器-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
<!-- 3.处理器适配器-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->
<!-- 4.配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
第六步:配置 web.xml 配置
1. 配置 spring-servlet.xml 配置文件信息
2. 配置过滤器
3. 配置请求体乱码过滤器
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置请求体乱码过滤器-->
<filter>
<filter-name>encodingFilter</filter-name>
<!--这个是 SpringMVC 默认提供的一个处理请求/响应乱码的过滤器-->
<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>encodingFilter</filter-name>
<!--表示拦截所有请求-->
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
~~!!相关知识
/* * 1.方法修饰符,一般来说主要是 public , * 如果写成 private ,也是可以运行的(因为在真正执行该方法之前,会自动修改方法的修饰符) * 2.方法名随意 */
一、请求窄化
1. 实例Controller类
package com.qf.mvc02.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.util.Arrays;
/**
* @author:lyz
* @since: jdk 1.8
**/
@Controller
/**
* 类上面添加 @RequestMapping("/book") 注解,
* (表示类中的所有方法的请求路径都包含这个前缀),
* 这个叫做 “请求窄化”
*/
@RequestMapping("/book")
public class BookController {
/**
* 这个接口最终的访问路径是 http://localhost:8080/m02/book/add
*
* 默认情况下,这个接口,四种常见的请求都是支持的 GET、POST、PUT、DELETE
*
* method = RequestMethod.GET 表示该方法只支持 GET 请求
* @return
*/
//① @RequestMapping(value = "/add",method = RequestMethod.GET)
//② @RequestMapping(value = "/add",method = {RequestMethod.GET,RequestMethod.POST})
//这个等价于上面的①
@GetMapping("/add")
public ModelAndView addBook(){
System.out.println("===================");
return new ModelAndView("book");
}
// 请求地址可以重复,但请求方法不能重复
// (地址名可以重复用,但不能存在不同操作的同一请求方法 “即 单一请求方法对应单一操作”)
@DeleteMapping("/add")
public ModelAndView deleteBook(){
System.out.println("--------------------");
return null;
}
/**
* 添加一本书
* @param name
* @param author
* @param price
* @param tags 需要注意的是,默认情况下,SpringMVC 无法直接处理 List 集合参数
*/
@PostMapping("/add")
public void addBook(String name,String author,Double price,String[] tags){
System.out.println("name = " + name);
System.out.println("author = " + author);
System.out.println("price = " + price);
System.out.println("tags = " + Arrays.toString(tags));
}
}
2. 实例 jsp
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>book.jsp</h1>
<form action="/m02/book/add" method="post">
<table>
<tr>
<td>书名:</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>作者:</td>
<td><input type="text" name="author"></td>
</tr>
<tr>
<td>价格:</td>
<td><input type="text" name="price"></td>
</tr>
<tr>
<td>标签:</td>
<td>
<input type="checkbox" name="tags" value="小说">小说
<input type="checkbox" name="tags" value="散文">散文
<input type="checkbox" name="tags" value="科技">科技
<input type="checkbox" name="tags" value="工程">工程
<input type="checkbox" name="tags" value="数学">数学
</td>
</tr>
<tr>
<td><input type="submit" value="添加"></td>
</tr>
</table>
</form>
</body>
</html>
二、方法返回值
/* * 3.方法返回值 * i:ModelAndView * ii:String * a. 可以是服务端跳转/客户端跳转 * (客户端跳转:浏览器发两个请求,地址栏会变。 服务端跳转:浏览器只发生一个请求) * (客户端跳转 也叫 重定向 “浏览器发两个请求,地址栏会变,可以重定向到站内+站外”) * (服务端跳转 也叫 请求转发 “浏览器只发生一个请求,地址栏不变,只可以转发到站内 不能到转发到站外”) * b. 可是一个逻辑视图名 * c. 可以是一个真正的字符串 * iii:void * iv:JSON */
1. java实例:方法hello1 --> hello6(见三内)
2. 实例 jsp(见三内)
三、方法参数
/* * 4.方法参数 * i:默认参数(前端请求的时候不需要提交,服务端在执行的时候,会自动注入这个参数) * a. HttpServletRequest/HttpServletResponse/HttpSession * b. Model/ModelMap : 当返回值是逻辑视图名的时候,可以使用这个 Model 来存储数据 * c. Principal : 这个进来在 Spring Security 中,可以添加这个参数,这个参数就是当前登录的用户对象 * ii:自定义参数 * a. key-value 形式 (请求地址栏和请求体,最终都有可能是 key-value 形式的参数) * b. json 形式 (这个主要是请求体中的参数) * c. 路径形式 (地址栏的参数) */
1. java实例:方法hello7 --> hello12
package com.qf.mvc02.controller;
import com.qf.mvc02.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author:lyz
* @since: jdk 1.8
*
* 1.方法修饰符,一般来说主要是 public ,
* 如果写成 private ,也是可以运行的(因为在真正执行该方法之前,会自动修改方法的修饰符)
* 2.方法名随意
* 3.方法返回值
* i:ModelAndView
* ii:String
* a. 可以是服务端跳转/客户端跳转
* (客户端跳转:浏览器发两个请求,地址栏会变。 服务端跳转:浏览器只发生一个请求)
* (客户端跳转 也叫 重定向 “浏览器发两个请求,地址栏会变,可以重定向到站内+站外”)
* (服务端跳转 也叫 请求转发 “浏览器只发生一个请求,地址栏不变,只可以转发到站内 不能到转发到站外”)
* b. 可是一个逻辑视图名
* c. 可以是一个真正的字符串
* iii:void
* iv:JSON
* 4.方法参数
* i:默认参数(前端请求的时候不需要提交,服务端在执行的时候,会自动注入这个参数)
* a. HttpServletRequest/HttpServletResponse/HttpSession
* b. Model/ModelMap : 当返回值是逻辑视图名的时候,可以使用这个 Model 来存储数据
* c. Principal : 这个进来在 Spring Security 中,可以添加这个参数,这个参数就是当前登录的用户对象
* ii:自定义参数
* a. key-value 形式 (请求地址栏和请求体,最终都有可能是 key-value 形式的参数)
* b. json 形式 (这个主要是请求体中的参数)
* c. 路径形式 (地址栏的参数)
*
**/
@Controller
@RequestMapping("/user")
public class UserController {
@GetMapping("/hello")
public String addUser(Model model){
//在传入的形参中加入 Model model,可以通过model.addAttribute / .addAllAttributes来携带数据
model.addAttribute("hello","hello springmvc");
/*方法返回值:b. 一个逻辑视图名*/
//这里返回的 user 表示一个视图的名称,返回 user 之后,系统会去自动根据我们配置的视图解析器查找到一个名为 user 的视图
return "user";
}
/**
* (服务端跳转(也叫请求转发))
* 当我们请求 /hello2 这个地址的时候,我希望将这个请求转发到 /hello
* @return
*/
@GetMapping("/hello2")
public String hello2(){
//这就是服务端跳转(请求转发)--(只能转发到站内,不能转发到站外)
return "forward:/user/hello";
}
/**
* (客户端跳转(也叫重定向))
* 当我们请求 /hello3 这个地址的时候,我希望将这个请求自动重定向到 /hello
* (希望当访问 /hello03 的时候,自动重定向到 /hello)
* @return
*/
@GetMapping("/hello3")
public String hello3(){
//这就是客户端跳转(重定向)--(可以重定向到站外,当然也可以到站内)
/*以前我们在 servlet 中的重定向,需要写上工程名,选择这里不需要*/
return "redirect:/user/hello";
}
@GetMapping(value = "/hello4",produces = "text/html;charset=utf-8")
//@ResponseBody 表示这个方法的返回值就是要写到浏览器的内容
@ResponseBody
public String hello4(){
return "你好hello4";
}
@GetMapping("/hello5")
@ResponseBody
public void hello5(){
System.out.println("===========");
}
@GetMapping("/hello6")
public void hello6(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException, ServletException {
/*session.getAttribute();
session.setAttribute(,);*/
// request.getRequestDispatcher("").forward();//服务端跳转
// response.sendRedirect("");//重定向
response.getWriter().write("hello");
request.setAttribute("hello", "hello");
request.getRequestDispatcher("/WEB-INF/jsp/user.jsp").forward(request,response);
}
/**
* (这里的不支持是spring-mvc框架不支持,servlet的话get都支持"servlet的get请求及及支持参数放地址栏,也支持参数放请求体")
* 这种是 key-value 形式的参数,由于这里是 GET 请求,使用参数只能放在地址栏中(若为POST请求的话,则而外可以放在请求体中)
* @param username
* @param address
* @param age
*
* @RequestParam("name") String username 注解表示 username 这个变量绑定了前端传来的 name 参数
*
* 如果家里@RequestParam 注解,默认情况下,这个参数是必填的
*
* @RequestParam(value = "name",defaultValue = "java") defaultValue 表示如果前端请求的时候,没有传递 name 参数,则这里的 username 变量默认值为java
*
*/
@GetMapping("/hello7")
@ResponseBody
public void addUser(@RequestParam(value = "name",defaultValue = "java") String username, String address,@RequestParam Integer age){
System.out.println("username = " + username);
System.out.println("address = " + address);
System.out.println("age = " + age);
}
/**
* 请求路径中的参数,也可以用一个对象去接收
* @param user
*/
@GetMapping("/hello8")
@ResponseBody
public void addUser2(User user){
System.out.println("user = " + user);
}
/**
* 根据 id 去查询用户对象
*
* http://localhost8080/m02/hello9/7
*
* @PathVariable 表示这个参数来自路径中,要求参数的变量名要和路径中的占位符名称保持一致,(如果两者不一致,则需要在@PathVariable注解中单独进行指定)
*
* @param aaa
*/
@GetMapping("/hello9/{userId}")
@ResponseBody
public void getUserById(@PathVariable("userId") Long aaa){
System.out.println("aaa = " + aaa);
}
//http:localhost:8080/m02/hello10/100/zhangsan
@GetMapping("/hello10/{userId}/{username}")
@ResponseBody
public void getUserById2(@PathVariable("userId") Long aaa,@PathVariable String username){
System.out.println("username = " + username);
System.out.println("aaa = " + aaa);
}
@PostMapping("/hello11")
@ResponseBody
public void hello11(String username,String address,Integer age /*,HttpServletRequest request*/){
/*request.setCharacterEncoding("UTF-8"); 以前:必须在获取参数之前设置编码格式
现在因为用spring 导致设置编码格式前,就已经提取出来了,所以这个方法现在不适用了*/
System.out.println("username = " + username);
System.out.println("address = " + address);
System.out.println("age = " + age);
}
@PostMapping("/hello12")
@ResponseBody
public void hello12(User user){
System.out.println("user = " + user);
}
}
2. 实例 jsp
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>user.jsp</h1>
<h1>${hello}</h1>
</body>
</html>
四、存到 session 中
1. Controller 的 java 实例
package com.qf.mvc02.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
/**
* @author:lyz
* @since: jdk 1.8
**/
/**
* @Controller 注解表示当前类是一个处理器类
*/
@Controller
//这个注解表示当前类中的接口,如果在接口中,将参数存入到 Model 中,并且参数名称为 name,就自动将这个参数存入到 session 中
@SessionAttributes({"name"})
public class HelloController {
/**
* @RequestMapping 注解用来标记接口,即将来如果用户访问了 http://localhost:8080/mvc02/hello 接口,就会交给当前的方法去处理
* @return
*/
@RequestMapping("/hello")
public ModelAndView hello(){
ModelAndView mv = new ModelAndView("hello");
mv.addObject("msg","hello mvc02");
return mv;
}
/**
* 假如用户在调用这个接口的时候,传递了一个 name 参数,现在我们需要将这个 name 参数存入到 session 中
*/
@GetMapping("/hello2")
@ResponseBody
public void hello2(String name, Model model){
model.addAttribute("name",name);
System.out.println("name = " + name);
}
/**
* @SessionAttribute("name") 表示将之前通过 @SessionAttributes({"name"}) 注解存入到 session 中的数据,现在将里边的 name 属性提取出来,赋值给 name 变量
* @param name
*/
@GetMapping("/hello3")
@ResponseBody
public void hello3(@SessionAttribute("name") String name){
System.out.println("name = " + name);
}
@GetMapping("/hello4")
@ResponseBody
public void hello4(SessionStatus status){
//清空通过 @SessionAttributes({"name"}) 注解存入到 session 中的数据
//如果是开发者手动调用 session.setAttribute 方法存入到 session 中的数据,那么这个 setComplete 方法并不会将只清除
status.setComplete();
}
}
2.实例 jsp
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>${msg}</h1>
</body>
</html>
五、静态资源访问 的三种策略
1. 第一种:改接口名字
优点:相对访问高效(因为以 .do 结尾的都是 DispatcherServlet 的请求)
缺点:麻烦,需要每个接口都改名字
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- <url-pattern>/</url-pattern>-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2. 第二种:配置一个默认的处理器
这里额外添加一个默认的处理器,如果 HandlerMapping 找不到请求路径所对应的处理器,那么使用这个默认的处理器,这个默认的处理器会去本地文件夹中查找是否存在对应的资源
本质:运用的是重定向
优点:不需要像第一种方法哪有去改每个接口名
缺点:每个请求都要经过处理器,所以相对而言效率会下降
<mvc:default-servlet-handler/>
3. 第三种:配置资源
没用用处理器,只是进入 DispatcherServlet 中,去区分以下是静态资源还是动态资源
优点:简单快捷
缺点:(我还不知道)
<!--
mapping 指的是请求的地址(这里怎么写,带时候就怎么去写访问路径)
location 指的是资源的位置
Ant 风格的路径匹配符号
? 可以匹配单个字符
* 可以匹配单个文件名
** 可以匹配多层路径和文件
-->
<mvc:resources mapping="/**" location="/"/>
六、jackson
添加依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.0</version> </dependency>
1. 返回json的情况
(1). 核心关键 HttpMessageConverter
* HttpMessageConverter
* 1.将接口方法返回值的对象转为 JSON/XML
* 2.将请求提交的 JSON 或者 XML 字符串转为一个 Java 对象
*
* 无论我们使用哪种 JSON 解析器,都需要配置 HttpMessageConverter
*
* 但是,对于 jackson 和 gson,系统默认(springframework)已经给我们配置好了,所以我们现在不配置也可以使用
*
(2). 参用注解
//这些地方,注解的作用,都是两方面的,对象 -> JSON 和 JSON ->对象都会用到这些注解配置
(序列化和反序列化都会用到所配置的注解,即在两过程中功能需求都会生效)
@JsonIgnore
生成 JSON 的时候,忽略被注解属性@JsonProperty(……)
生成 JSON 的时候,该字段名称为 ……@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")
生成 JSON 的时候,要求转换为特定形式的时间,同时配置时区
public class Book {
//生成 JSON 的时候,忽略 ID
//这些地方,注解的作用,都是两方面的,对象 -> JSON 和 JSON ->对象都会用到这些注解配置
@JsonIgnore
private Integer id;
//生成 JSON 的时候,该字段名称为 bookName
@JsonProperty("bookName")
private String name;
private String author;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "Asia/Shanghai")
private Date publishDate;
}
(3). jackson的 自定义信息转换器(MappingJackson2HttpMessageConverter)
(时间属性配置全局注解)
在 spring-servlet.xml 内配置该内容,
通过该配置,可以使得全体实体类的时间类型属性,在序列化后都显示为
yyyy-MM-dd HH:mm:ss
该样式,但是同样的,在传参发送请求体的时候,也要按照该样式提交数据,不然反序列化过程中就会报错(报400错误“参数转换出错”)
<!--配置 HttpMessageConverter
需要修改 annotation-driven (注解驱动)
来加载 所自定义的 MappingJackson2HttpMessageConverter
(映射 Jackson 2 Http消息转换器)-->
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" id="httpMessageConverter">
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="dateFormat">
<bean class="java.text.SimpleDateFormat">
<constructor-arg name="pattern" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
</bean>
</property>
</bean>
2. 提交的参数时json的情况
给方法中的形参类型前加上
@RequestBody
* @RequestBody 表示将请求体中的内容,通过 HttpMessageConverter 自动转为参数对象 * (将请求体中的内容,映射到形参的对象中,形参对象的类型按情况选择, * ①如果请求体中的内容可以映射成实体对象,那类型就用实体对象的, * ②如果请求体中的内容,只是简简单单的 String,那就用 String 类型 * ③如果请求体中的内容,是 实体对象数组,那参数类型就用实体对象数组[] 去接收)
/实例如下/
@PostMapping("/book") @ResponseBody public void addBook(@RequestBody String book){ System.out.println("book = " + book); }
七、gson
添加依赖
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.10</version> </dependency>
1. json 解析器顺序
默认情况 jackson->gson->jsonb (前提是都导入了相关依赖)
若有自己手动配置的 HttpMessageConverter ,则手动配置的优先
* 如果当前项目中同时存在多个 json 解析器,默认情况下,选择顺序依次是 jackson->gson->jsonb
* 当然,如果自己手动配置了 HttpMessageConverter,则按照自己配置的来
2. gson的 自定义信息转换器(GsonHttpMessageConverter)
(时间属性配置全局注解)
这里调用到 GsonFactoryBean,来生成一个 Gson 对象,用于在 xml 中配置
<!--配置 HttpMessageConverter
需要修改 annotation-driven (注解驱动)
来加载 所自定义的 GsonHttpMessageConverter
(映射 Gson Http消息转换器)-->
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.http.converter.json.GsonHttpMessageConverter" id="httpMessageConverter">
<property name="gson">
<bean class="org.springframework.http.converter.json.GsonFactoryBean">
<property name="dateFormatPattern" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
</bean>
八、fastjson
添加依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.21</version> </dependency>
1. fastjson的 自定义信息转换器(FastJsonHttpMessageConverter)
-
配置 FastJsonHttpMessageConverter
-
配置 FastJsonConfig
(解决日期时间戳格式化为)属性dateFormat
-
配置属性defaultCharset,同样是给Charset的子类UTF_8
(解决中文乱码:属性Charset 不能通过new获取,但是我们可以给它配置一个Charset的子类UTF_8)
-
<!--配置 HttpMessageConverter
需要修改 annotation-driven (注解驱动)
来加载 所自定义的 GsonHttpMessageConverter
(映射 Gson Http消息转换器)-->
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter" id="httpMessageConverter">
<property name="fastJsonConfig">
<bean class="com.alibaba.fastjson2.support.config.FastJsonConfig">
<property name="dateFormat" value="yyyy-MM-dd HH:mm:ss"/>
</bean>
</property>
<property name="defaultCharset">
<bean class="sun.nio.cs.UTF_8"/>
</property>
<property name="supportedMediaTypes">
<value>application/json;charset=utf-8</value>
</property>
</bean>
自定义参数类型转换
json 格式的参数不需要手动配置 DateConverter(自定义参数类型转换器)
key-value 格式的参数需要手动配置相应的 DateConverter
0. 举例的业务请求
举例的:业务方法
@PostMapping("/book2") @ResponseBody public void addBook2(Date publishDate){ System.out.println("publishDate = " + publishDate); }
1. 自定义参数类型转换的类
举例:自定义 DateConverter 用于转换,前端传递过来的 Date 数据类型对象
package com.qf.mvc03.converter; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * @author:lyz * @since: jdk 1.8 * * 这是一个自定义的参数类型转换器,可以将前端传递过来的字符串参数自动的转为一个 Date 日期类型 **/ public class DateConverter implements Converter<String, Date> { private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); /** * 这就是具体的转换方法 * @param source 这个是源参数,即字符串参数 * @return 返回值就是转换之后的日期对象 */ @Override public Date convert(String source) { try { return sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } return null; } }
2. 注册到spring容器中
举例:将 DateConverter 注册到 Spring 容器中
<bean class="com.qf.mvc03.converter.DateConverter" id="dateConverter"/>
3. 配置参数类型转换Bean(FormattingConversionServiceFactoryBean)
配置
FormattingConversionServiceFactoryBean
<!--在 annotation-driven 引入自定义配置的参数类型转换工厂-->
<mvc:annotation-driven conversion-service="formattingConversionServiceFactoryBean">
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean class="org.springframework.format.support.FormattingConversionServiceFactoryBean" id="formattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="dateConverter"/>
</set>
</property>
</bean>
4. 同时需要在 mvc:annotation-driven 中引入
同时需要在
mvc:annotation-driven
中引入
<mvc:annotation-driven conversion-service="formattingConversionServiceFactoryBean">
<mvc:message-converters>
<ref bean="httpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
九、全局异常处理
导入依赖
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
1. 第一种:实现接口
(1). (创建 全局异常处理类 实现) HandlerExceptionResolver 接口
(接口方法,都会被封装成一个 HandlerMethod )
(返回的是 ModelAndView )
package com.qf.mvc03.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author:lyz
* @since: jdk 1.8
*
* 全局异常处理类
**/
public class GlobalException implements HandlerExceptionResolver {
/**
* 当系统抛出异常后,这个方法就会被触发
* @param request
* @param response
* @param handler 这个 Handler 其实就是发生异常的处理器,跟发生异常的接口方法
* @param ex
* @return 返回的视图和数据模型
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("error",ex.getMessage());
return mv;
}
}
(2). 将实现类注册到 spring 容器中,或是加上 @Component 注解 同时修改包扫描范围
<bean class="com.qf.mvc03.exception.GlobalException"/>
(3). 配置视图解析器
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
2. 第二种:自己定义类(实际开发中常用的)
(创建 全局异常处理类 )
@ControllerAdvice “增强的 Controller,它凌驾于普通的 Controller 之上”
在类里边去定义各种不同的异常处理方法
(注意修改注解扫描空间)
package com.qf.mvc03.exception;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* @author:lyz
* @since: jdk 1.8
*
* 全局异常处理类 2
*
* @ControllerAdvice 表示当前类是一个增强的 Controller,当前 Controller 可以监听到所有 Controller 的异常信息
* 这个类中的方法定义,和普通的 Controller 基本上一模一样
**/
@ControllerAdvice
public class GlobalException2 {
/**
* 通过 @ExceptionHandler 注解可以指定异常的类型,将来发生 ArithmeticException 异常就会进入到当前方法中进行处理
* @return
*/
@ExceptionHandler(ArithmeticException.class)
public String arithmeticException(ArithmeticException e, Model model){
model.addAttribute("error",e.getMessage());
return "error";
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public Map<String,String> arithmeticException(ArithmeticException e){
Map<String ,String> map = new HashMap<>();
map.put("status", "500");
map.put("message", e.getMessage());
return map;
}
}
3. 第三种:直接在 xml 文件里面配置
SimpleMappingExceptionResolver
(发生什么异常,找什么页面)
其中配置的01表示01.jsp
其中配置的02表示02.jsp
<!--全局异常处理第三种,xml里配置-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArithmeticException">01</prop>
<prop key="java.lang.NullPointerException">02</prop>
</props>
</property>
</bean>
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>01.jsp</h1>
</body>
</html>
<%--
Created by IntelliJ IDEA.
User: lyz
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>02.jsp</h1>
</body>
</html>
十、拦截器
作用:抽取 handler 中冗余的功能
1. 自定义拦截器类
package com.qf.mvc03.interceptor;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author:lyz
* @since: jdk 1.8
*
* 这是自定义的拦截器
*
* 拦截器是拦截对某一个处理器的调用
**/
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 在处理器方法执行之前,会触发该方法的执行
* @param request
* @param response
* @param handler 这个就是处理器本身,即 HandlerMethod
* @return 这个方法如果返回 true,表示让请求继续向下执行,那么此时处理器就会被调用;这个方法如果返回 false,就表示处理器的执行被拦截下来了,那么接下来的处理器就不会被执行了
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
/**
* 这个方法是在处理器执行完毕之后触发
* @param request
* @param response
* @param handler
* @param modelAndView 这个方法实际上就是处理器的返回值
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
/**
* 完成页面渲染之后,会触发的方法
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
}
2. 在 spring.xml 中的配置
<!--配置自定义拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<ref bean="myInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
十一、文件上传
- 两种文件上传方式:新版的不用加依赖,旧版的要加依赖
- 但两种上传方式的接口都是一样的
- 新版的要在 spring.xml 配置 bean,但 bean 内不需要配置属性(它的属性在 web.xml 中进行配置)
- 旧版也要在 spring.xml 配置 bean,同时 bean 内要配置属性
0. 相同的接口类
package com.qf.mvc03.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
/**
* @author:lyz
* @since: jdk 1.8
**/
@Controller
public class FileUploadController {
private SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");
/**
* SpringMVC 将上传的文件对象封装成一个 MultipartFile 对象,无论我们使用的是新版还是旧版的文件上传方案,这个接口中的代码都是一样的
* @param file
* @param username
* @return
*/
@PostMapping("/upload")
@ResponseBody
public String uploadFile(MultipartFile file,String username){
System.out.println("username = " + username);
String folderPath = new File("D:\\temp\\img") + sdf.format(new Date());
File folder = new File(folderPath);
if (!folder.exists()){
folder.mkdirs();
}
//获取原始的文件名
String oldName = file.getOriginalFilename();
//获取上传文件的后缀
String suffix = oldName.substring(oldName.lastIndexOf("."));
//生成新的文件名
String newName = UUID.randomUUID().toString() + suffix;
try {
file.transferTo(new File(folder,newName));
return "success";
} catch (IOException e) {
e.printStackTrace();
}
return "fail";
}
}
1. 新版文件上传方式
配置上传文件的解析器 StandardServletMultipartResolver
注意,这个地方的配置,
必须给 Bean 取名字,且名字必须为 multipartResolver
(1). 在 spring.xml 中配置 bean
<!--上传文件的解析器(新版方式)-->
<!--
注意,这个地方的配置,必须给 Bean 取名字,且名字必须为 multipartResolver
-->
<bean class="org.springframework.web.multipart.support.StandardServletMultipartResolver" id="multipartResolver">
</bean>
(2). 在 web.xml 中配置属性
<multipart-config>
<location>D:\temp\temp2</location><!--上传的临时目录-->
<max-file-size>10000000000</max-file-size><!--上传请求的,最大上传大小-->
<max-request-size>10000000000</max-request-size><!--每个文件最大上传大小-->
<file-size-threshold>10000</file-size-threshold><!--上传文件在内存中的,最大内存大小-->
</multipart-config>
2. 旧版文件上传方式
(1). 添加依赖
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
(2). 在 spring.xml 中配置 bean
配置上传文件的解析器 CommonsMultipartResolver
注意,这个地方的配置,
必须给 Bean 取名字,且名字必须为 multipartResolver
<!--上传文件的解析器(新版方式)-->
<!--
注意,这个地方的配置,必须给 Bean 取名字,且名字必须为 multipartResolver
-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<!--上传的临时目录(这里要的是项目资源下的目录,这里也可以不配置(不配置就会默认使用系统的临时文件存储地址)-->
<property name="uploadTempDir" value="classpath:/temp"/>
<property name="maxUploadSize" value="10000000000"/><!--上传请求的,最大上传大小-->
<property name="maxUploadSizePerFile" value="10000000000"/><!--每个文件最大上传大小-->
<property name="maxInMemorySize" value="10000"/><!--上传文件在内存中的,最大内存大小-->
</bean>
十二、文件下载
1. 单读取功能
//这里是只有读取功能
@GetMapping("/views/{year}/{month}/{day}/{imageName}")
@ResponseBody
public ResponseEntity<byte[]> downloadImg(@PathVariable String imageName, @PathVariable String year,@PathVariable String month,@PathVariable String day) throws IOException {
FileInputStream fis =new FileInputStream("D:\\temp\\img\\"+year+"\\"+month+"\\"+day+"\\"+imageName);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int len=0;
byte[] buf=new byte[1024];
while ((len=fis.read(buf))!=-1){
baos.write(buf,0,len);
}
HttpHeaders header = new HttpHeaders();
//这是是配置 HttpHeaders 传入文件(图片)的类型
header.setContentType(MediaType.IMAGE_JPEG);
return new ResponseEntity<>(baos.toByteArray(),header, HttpStatus.CREATED);
}
2.1 读取 + 下载( SpringMVC 中的下载)
//读取+下载
@GetMapping("/views/{year}/{month}/{day}/{imageName}")
@ResponseBody
public ResponseEntity<byte[]> downloadImg(@PathVariable String imageName, @PathVariable String year,@PathVariable String month,@PathVariable String day) throws IOException {
FileInputStream fis =new FileInputStream("D:\\temp\\img\\"+year+"\\"+month+"\\"+day+"\\"+imageName);
ByteArrayOutputStream baos=new ByteArrayOutputStream();
int len=0;
byte[] buf=new byte[1024];
while ((len=fis.read(buf))!=-1){
baos.write(buf,0,len);
}
HttpHeaders header = new HttpHeaders();
//这是是配置 HttpHeaders 传入文件(图片)的类型
header.setContentType(MediaType.IMAGE_JPEG);
//这里是配置编码解析,解决地址传参出现的中文乱码问题下
header.setContentDispositionFormData("attachment",new String(imageName.getBytes("UTF-8"),"ISO-8859-1"));
return new ResponseEntity<>(baos.toByteArray(),header, HttpStatus.CREATED);
}
2.2 读取 + 下载( Servlet 中的下载方式)
//Servlet 中的下载方式
@RequestMapping("/{name}")
public void downloadImg(@PathVariable String name, HttpSession session, HttpServletResponse response) throws IOException {
System.out.println("name:"+name);
//获取要下载文件的绝对路径
String path = session.getServletContext().getRealPath("/upload_file");
//文件的完整路径
String real_path = path+"\\"+name;
//配置响应头 告知浏览器,要以附件的形式保存内容 filename=浏览器显示的下载文件名
response.setHeader("content-disposition","attachment;filename="+name);
//读取目标文件,写出给客户端
IOUtils.copy(new FileInputStream(real_path),response.getOutputStream());
//上一步,已经是响应了,所以此 handler 直接是 void
}
十三、REST
1. 开发风格
是一种开发风格,遵从此风格开发软件,符合REST风格,则RESTful。
两个核心要求:
- 每个资源都有唯一的标识(URL)
- 不同的行为,使用对应的http-method
访问标识 | 资源 |
---|---|
http://localhost:8989/xxx/users | 所有用户 |
http://localhost:8989/xxx/users/1 | 用户1 |
http://localhost:8989/xxx/users/1/orders | 用户1的所有订单 |
请求方式 | 标识 | 意图 |
---|---|---|
GET(查询) | http://localhost:8989/xxx/users | 查询所有用户 |
POST(添加) | http://localhost:8989/xxx/users | 在所有用户中增加一个 |
PUT(更新) | http://localhost:8989/xxx/users | 在所有用户中修改一个 |
DELETE(删除) | http://localhost:8989/xxx/users/1 | 删除用户1 |
GET | http://localhost:8989/xxx/users/1 | 查询用户1 |
GET | http://localhost:8989/xxx/users/1/orders | 查询用户1的所有订单 |
POST | http://localhost:8989/xxx/users/1/orders | 在用户1的所有订单中增加一个 |
2. 优点
- **输出json:
3. 使用
(1). 定义Rest风格的 Controller
@RequestMapping(value=“/users”,method = RequestMethod.GET)
等价
@GetMapping(“/users”)
@RestController
public class RestController {
@GetMapping("/users")
public List<User> queryAllUsers(){
System.out.println("get");
List<User> users = ....
return users;
}
@PostMapping("/users")
public String addUser(@RequestBody User user){
System.out.println("Post user :"+user);
return "{status:1}";
}
@PutMapping("/users")
public String updateUser(@RequestBody User user){
System.out.println("Put user" user:"+user);
return "{status:1}";
}
@GetMapping("/users/{id}")
public String queryOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
System.out.println("Get user id:"+id);
return "{status:1}";
}
@DeleteMapping("/users/{id}")
public String deleteOneUser(@PathVariable Integer id){//@PathVariable 接收路径中的值
System.out.println("delete user id:"+id);
return "{status:1}";
}
}
(2). Ajax请求
<script>
function putUser(){ // 发送更新请求 (增加请求发送方式也是如此)
var xhr = new XMLHttpRequest();
//定义 put,delete,get,post方式 即可,不用定义_method
xhr.open("put","${pageContext.request.contextPath}/rest04/users");
// 设置请求头
xhr.setRequestHeader("content-type","application/json");
// 设置请求参数
var user = {id:1,NAME:"shine",city:"bj","birth":"2020/12/12","salary":100.5};
xhr.send(JSON.stringify(user));
xhr.onreadystatechange=function(){
if(xhr.readyState==4 && xhr.status==200){
var ret = xhr.responseText;
// 解析json,并输出
console.log(JSON.parse(ret));
}
}
/*$.ajax({
url:'${pageContext.request.contextPath}/rest04/users',
type:'put',
contentType:"application/json",//声明请求参数类型为 json
data:JSON.stringify(user),// 转换js对象成json
success:function(ret){
console.log(JSON.parse(ret));
}
});*/
}
function delUser(){ // 发送删除请求
var xhr = new XMLHttpRequest();
//定义 put,delete,get,post方式 即可,不用定义_method
xhr.open("delete","${pageContext.request.contextPath}/rest04/users/1");
xhr.send();
xhr.onreadystatechange=function(){
if(xhr.readyState==4 && xhr.status==200){
var ret = xhr.responseText;
console.log(JSON.parse(ret));
}
}
}
</script>
十四、跨域请求
浏览器对 AJAX 有一定限制:ajax 请求只能发送给当前的网站,不能发给别的网站,就是不能跨域
这话域指的是三个东西一样:一个指的是请求协议(从 htpp ->https 也算跨域),第二个指的是请求域名要一样,第三个端口要一样。这三个东西都要一样就认为你没有跨域,要是这三个有其中一个不一样,就是跨域了。
1. 解决方法
-
允许其他域访问(在被访问方的Controller类上,添加注解)
@CrossOrigin("http://localhost:8080") //允许此域发请求访问
-
或是在被访问方的 spring.xml 上配置
<!--配置跨域请求--> <mvc:cors> <mvc:mapping path="/**" allowed-origins="http://localhost:8081" max-age="1800" allowed-methods="*"/> </mvc:cors>
-
-
携带对方cookie,使得session可用(在访问方,ajax中添加属性:withCredentials: true)
$.ajax({ type: "POST", url: "http://localhost:8989/web/sys/login", ..., xhrFields: { // 跨域携带cookie withCredentials: true } }); 或 var xhr = new XMLHttpRequest(); // 跨域携带cookie xhr.withCredentials=true;
···SSM整合
-
传统的:使用 xml 文件去配置 ssm 整合
-
第二种:纯 java 代码去配置 ssm 整合
-
/** * @author:lyz * @since: jdk 1.8 * * Spring+SpringMVC 整合的时候,系统中同时存在两个容器: * 1. Spring 容器 * 2. SpringMVC 容器 * 其中,Spring 容器是父容器,SpringMVC 容器则是子容器,子容器可以访问父容器中的 Bean,但是父容器无法访问子容器中的 Bean。即如果我们将一个 Bean 注册到 Spring 容器中,则我们在 SpringMVC 容器中可以访问到这个 Bean;但是如果我们将一个 Bean 注册到 SpringMVC 容器中,则在 Spring 容器中是无法访问该 Bean 的。 * * 我们项目中的 Bean,按照注解可以划分为四类:Controller、Service、Repository、Component: * * 首先有一个硬性要求:Controller 是必须要注册到 SpringMVC 容器中。 * * 1. 我们可以将这些 Bean 全部都注册到 SpringMVC 容器中。 * 2. 也可以将 Controller 放到 SpringMVC 容器中,其余的 Bean 放到 Spring 容器中。 * * 在 SSM 整合中,以上两种方案均可。 * 在以前的 SSH 整合中,则必须使用第二种方案。 * **/
一、传统的(.xml )
1. Spring + SpringMVC
(0). 基本配置
- 打包方式 war
- 配置web修改信息
- 配置Tomcat
(1). 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.24</version>
</dependency>
(2). 测试 controller
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}
(3). 测试 service
@Service
public class HelloService {
public String hello() {
return "hello springmvc";
}
}
(4). 配置 spring-servlet.xml
同时注册配置 SpringMVC 注解扫描 以及注解驱动
<!--
use-default-filters="false" 表示不适用默认的注解过滤器,此时一个 Bean 都不会被扫描到
-->
<context:component-scan base-package="com.qf.xml" use-default-filters="false">
<!--这个就表示只扫描 Controller -->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
(5). 配置 applicationContext.xml
通知注册配置 Spring 注解扫描
<context:component-scan base-package="com.qf.xml" use-default-filters="true">
<!-- 排除 Controller,即不扫描 Controller-->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
(6). 再 web.xml 中配置上述两个xml文件
<!--加载 Spring 配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--加载 SpringMVC 配置文件-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
2. + MyBatis
(1). 添加依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0</version>
</dependency>
(2). 添加数据库配置文件
db.driverClassName=com.mysql.cj.jdbc.Driver
db.username=root
db.password=root
db.url=jdbc:mysql:///mybatis_01?serverTimezone=Asia/Shanghai
db.maxWait=60000
db.initialSize=100
db.maxActive=200
db.minIdle=10
(3). 在 applicationContext.xml 中配置 MyBatis
在 Spring 配置文件中配置 MyBatis 的
- 数据源
- 两个Bean
- sqlSessionFactoryBean
- 接口扫描
<!--1. 读取 db.properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!--2. 配置数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
<property name="url" value="${db.url}"/>
<property name="maxActive" value="${db.maxActive}"/>
<property name="maxWait" value="${db.maxWait}"/>
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="initialSize" value="${db.initialSize}"/>
<property name="minIdle" value="${db.minIdle}"/>
</bean>
<!--3. MyBatis 只需要配置两个 Bean 即可-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.qf.xml.model"/>
<property name="mapperLocations">
<value>
classpath*:com/qf/xml/mapper/*.xml
</value>
</property>
</bean>
<!--4. 配置 Mapper 接口的扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer">
<property name="basePackage" value="com.qf.xml.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"/>
</bean>
(4). 相关 controller、service、mapping、mapping.xml、model
-
controller
@RestController public class UserController { @Autowired UserService userService; @GetMapping("/user") public List<User> selectAll(){ return userService.selectAll(); } }
-
service
@Service public class UserService { @Autowired UserMapper userMapper; public List<User> selectAll() { return userMapper.selectAll(); } }
-
mapping
public interface UserMapper { List<User> selectAll(); }
-
mapping.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="com.qf.xml.mapper.UserMapper"> <select id="selectAll" resultType="com.qf.xml.model.User"> select * from `user`; </select> </mapper>
-
model
public class User { private Integer uid; private String uname; private String upwd; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime udate; //此处省略标准Bean的空/满参构造方法、全体get/set、重写toString }
(5). 在 pom.xml 配置额外的 resources 资源文件路径
由于本人习惯将 mapping、mapping.xml 放在同一包(dao/mapping)下,所以需要在 pom.xml 额外配置一个 resources 资源文件路径
<!--配置额外的 resources 资源文件路径-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
3. 事务配置
- (1). 配置数据源事务管理器
- (2). 配置事务的切面
- (3). 配置事务 AOP
<!--事务配置,三步骤-->
<!--配置数据源事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--下边该代码相当于 `直接配置事务的切面` 以及 `事务的AOP` (两步并一步的简写)-->
<!-- <tx:annotation-driven/>-->
<!--配置事务的切面-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*"/>
<tx:method name="delete*"/>
<tx:method name="update*"/>
<tx:method name="select*"/>
</tx:attributes>
</tx:advice>
<!--配置事务 AOP-->
<aop:config>
<aop:pointcut id="pc1" expression="execution(* com.qf.xml.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
</aop:config>
二、纯 Java 代码
纯 Java 代码进行 SSM 的整合,甚至连 web.xml 都不需要
0. 添加依赖
<?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>com.qf</groupId>
<artifactId>java_ssm</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.24</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<!--配置事务要加的两个依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
</dependency>
<!--打印日志的依赖:spring 里边用来打日志 slf4j -->
<!--1.7.36版本-->
<!--我用于检查执行sql连接是否一致-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.36</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.36</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<!--配置额外的 resources 资源文件路径-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
</project>
1. 配置 SpringConfig.java
等效于 applicationContext.xml
- @Configuration //表示这是一个配置类
底层代码剖析,见下方代码块文档注解部分
- @ComponentScan(value = “com.qf.java”,useDefaultFilters = true,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)}) //扫描特定路径下包的注解,但是不扫描 @Controller 注解
- @PropertySource(“classpath:db.properties”) //表示读取 db.properties
- @EnableTransactionManagement //配置这个注解相当于
<tx:annotation-driven/>
- DataSource dataSource() //配置数据源
- SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) //配置Sql会话工厂Bean
- MapperScannerConfigurer mapperScannerConfigurer() //配置映射扫描器配置器
- setEnvironment(Environment environment) // implements EnvironmentAware(Aware的子接口)后重写的方法,用于设置让系统自动调用,给 environment 属性赋值。 目的是用来给 dataSource 数据源的属性注入值/赋值
- TransactionManager transactionManager(DataSource dataSource) //配置事务管理器
package com.qf.java.config;
/**
* @author:lyz
* @since: jdk 1.8
**/
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.*;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.io.IOException;
/**
* 一般来说,我们将 @Configuration 加在配置类上,但是实际上,配置类也可以加 @Component、@Service、@Repository、@Controller 都可以
*
* 但是,@Configuration 注解有一个区别于另外四个注解的特点:
* 在加了 @Configuration 注解的类中,如果发生了方法之间的调用,那么情况下,并不会直接执行目标方法,而是先去 Spring 容器中查找对应的 Bean,找到了,就直接使用,没找到,才会调用目标方法。
*
*/
/**
* 这个是 Spring 的配置类,作用类似于 applicationContext.xml
*/
@Configuration //表示这是一个配置类
@ComponentScan(value = "com.qf.java",useDefaultFilters = true,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})
@PropertySource("classpath:db.properties") //读取 db.properties
@EnableTransactionManagement //这个注解相当于 <tx:annotation-driven/>
public class SpringConfig implements EnvironmentAware {
private Environment environment;
@Bean //配置数据源
DataSource dataSource(){
DruidDataSource ds =new DruidDataSource();
ds.setUsername(environment.getProperty("db.username"));
ds.setPassword(environment.getProperty("db.password"));
ds.setUrl(environment.getProperty("db.url"));
ds.setDriverClassName(environment.getProperty("db.driverClassName"));
return ds;
}
@Bean //配置SqlSessionFactoryBean
SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setTypeAliasesPackage("com.qf.java.model");
bean.setDataSource(dataSource);
// bean.setMapperLocations(new ClassPathResource("com/qf/java/mapper/AccountMapper.xml")); 单个注入
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/qf/java/mapper/*.xml"));
return bean;
}
@Bean //配置MapperScannerConfigurer
MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.qf.java.mapper");
configurer.setSqlSessionFactoryBeanName("sqlSessionFactoryBean");
return configurer;
}
/**
* 这个 set 方法会被系统自动调用,该方法被调用之后,就给 environment 属性赋上值了
* @param environment
*/
@Override
public void setEnvironment(Environment environment) {
this.environment=environment;
}
@Bean
TransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
2. 配置 SpringMVCConfig.java
等效于 spring-servlet.xml
@Configuration //表示这是一个配置类
@ComponentScan(value = “com.qf.java”,useDefaultFilters = false,includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)}) //只扫描特定路径下包的 @Controller 注解
@EnableWebMvc //配置处理器映射器+处理器适配器;这个配置就类似于
<mvc:annotation-driven/>
<!-- 下面这个可以代替 RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter, 但是,下面这个默认的配置,大于RequestMappingHandlerMapping + RequestMappingHandlerAdapter (及<mvc:annotation-driven/>的功能大于两个bean的配置功能) --> <mvc:annotation-driven/><!--实际开发中,大多数都是用这个--> <!-- 2.配置处理器映射器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/> <!-- 3.处理器适配器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
implements WebMvcConfigurer //继承 Web Mvc配置器 接口,重写各种方法用于配置不同的内容
addResourceHandlers(ResourceHandlerRegistry registry) //静态资源放行的配置
addInterceptors(InterceptorRegistry registry) //拦截器的配置
(注意此处需要自己继承 HandlerInterceptor 自定义一个拦截器类,重写里面的 preHandle、postHandle、afterCompletion方法)
(我这里的是自定义拦截器类MyInterceptor implements HandlerInterceptor)
addCorsMappings(CorsRegistry registry) //这里用于设置跨域请求的
configureViewResolvers(ViewResolverRegistry registry) //配置视图解析器
configureMessageConverters(List<HttpMessageConverter<?>> converters) //第一种配置 HttpMessageConverter 的方式
extendMessageConverters(List<HttpMessageConverter<?>> converters) //第二种配置 HttpMessageConverter 的方式(差别见下方代码块文档注解部分)
package com.qf.java.config;
/**
* @author:lyz
* @since: jdk 1.8
**/
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.java.interceptor.MyInterceptor;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.*;
import java.text.SimpleDateFormat;
import java.util.List;
/**
* 这个是 SpringMVC 的配置类,作用类似于 spring-servlet.xml
*/
@Configuration
@ComponentScan(value = "com.qf.java",useDefaultFilters = false,includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})
@EnableWebMvc //这个配置就类似于 <mvc:annotation-driven/>
public class SpringMVCConfig implements WebMvcConfigurer {
/**
* 这个方法中进行静态资源放行的配置
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyInterceptor())
.addPathPatterns("/**");
}
//跨域请求
@Override
public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**");
}
/**
* 配置视图解析器
* @param registry
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.jsp().prefix("/WEB-INF/jsp/")
.suffix(".jsp");
}
/**
* 这个是配置 HttpMessageConverter,这个会覆盖掉已有的配置
* @param converters
*/
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converter.setObjectMapper(om);
converters.add(converter);
}
/**
* 这个是在系统已经配置的 HttpMessageConverter 的基础上继续进行配置
* 而在 SpringMVC 中,jackson 和 gson 实际上都已经配置好了
* 要想配置 fastjson ,需要将其调用序列号改为最先 //converters.add(0,converter);
* @param converters
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper om = new ObjectMapper();
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
converter.setObjectMapper(om);
converters.add(0,converter);
}
}
3. 配置 WebInit.java
等效于 web.xml
- implements WebApplicationInitializer //继承 WebApplicationInitializer 重写 onStartup(ServletContext servletContext) 方法
(其余内容看代码块)
package com.qf.java.config;
/**
* @author:lyz
* @since: jdk 1.8
**/
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
/**
* Web 初始化的类,作用类似于 web.xml
*/
public class WebInit implements WebApplicationInitializer {
/**
* 当系统启动的时候,这个方法会被自动触发,我们可以在这个方法中注册 Servlet、Filter、Listener 等
* 即之前写在 web.xml 中的内容,现在都可以写到这个方法中
* @param servletContext
* @throws ServletException
*/
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
//这个是创建容器
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
//将 ServletContext 设置给容器
ctx.setServletContext(servletContext);
//注册配置类
ctx.register(SpringMVCConfig.class,SpringConfig.class);
//刷新容器
ctx.refresh();
//注册 DispatcherServlet 到项目中
ServletRegistration.Dynamic springmvc = servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
//设置 Servlet 的拦截规则
springmvc.addMapping("/");
}
}
Tips:零碎知识点
1. 静态资源 于 动态资源
- 静态资源:java代码不需要进行任何运算的,就能返回去的这种资源,就叫静态资源
(静态资源就是你想要那种东西,我原封不动的给你返去,我不需要进行任何而外的运行,就是静态资源)
- 动态资源:得运行java代码才能返回、获取、操作的资源叫动态资源
2. 前端页面强制刷新
在浏览器页面:用Ctrl+F5,强制刷新(目的时为了防止前端显示结果用缓存)
3. 前端页面请求状态码为302时
前端页面请求状态码为302时,表示用的时缓存
4. Ant 风格的路径匹配符号
Ant 风格的路径匹配符号 ? 可以匹配单个字符 * 可以匹配单个文件名 ** 可以匹配多层路径和文件
5. @ResponseBody
表示(在该方法中)我要返回的东西呢,就是这个方法的返回值,不用去查找页面
6. 快速搜索
选定要搜索的内容,点击两下 Shift
7. 查看子类
选定内容,按 Ctrl + H
8. 前端页面错误400
参数转换出错
9. 前端页面错误406
①你想返回的对象,它没法给你转换;
②或者是其实它转换,但是浏览器它识别不出来,不知道返回个啥东西,不知道该如何给你展示出来(浏览器不知道该如何把这个东西展示出来,使用它报406错误)
10. 拦截器和过滤器的区别:
- 执行时机:拦截器晚于过滤器
- 拦截器是一种 AOP 风格的过滤器
过滤器 拦截的是一个请求,请求到服务端之后呢,它可能由某个接口去处理
拦截器 (拦截的力度会更小一些)准确来说拦截器拦截的是处理器,当你去执行某个处理器方法的时候,它给你拦截下来