当前位置: 首页 > news >正文

SpringMvc介绍。

目录

1、SpringMvc概述

1、基本介绍

2、工作流程 

3、bean加载控制

 二、请求

1、请求映射路径

2、请求方式

3、请求参数

4、请求参数(传递json数据)

5、日期类型参数传递

三、响应

四、REST风格

1、REST简介

2、RESTful入门案例

3、RESTful快速开发

五、拦截器

1、拦截器概念

2、拦截器开发

3、拦截器参数

4、拦截器链配置


1、SpringMvc概述

1、基本介绍

▶ 基本概念

  SpringMVC是一种基于Java实现MVC模型的轻量级Web框架

▷ 优点

  ● 使用简单、开发便捷(相比于Servlet)
  ● 灵活性强

▷ 当前WEB程序的工作流程:

● 三层架构

 ○ web程序通过浏览器访问前端页面,发送异步请求到后端服务器

 ○ 后台服务器采用三层架构进行功能开发
 ○表现层负责接收请求和数据然后将数据转交给业务层
 ○ 业务层负责调用数据层完成数据库表的增删改查,并将结果返给表现层
 ○ 表现层将数据转换成json格式返回给前端

 ○前端页面将数据进行解析最终展示给用户。

▷ 表现层与数据层的技术选型:

● 数据层采用Mybatis框架
● 变现层采用SpringMVC框架,SpringMVC主要负责的内容有:
  ○controller如何接收请求和数据
  ○ 如何将请求和数据转发给业务层
  ○ 如何将响应数据转换成json发回到前端

▶ 程序流程

1.浏览器发送请求到Tomcat服务器

2.Tomcat服务器接收到请求后,会将请求交给SpringMVC中的DispatcherServlet[前端控制器]来处理请求

3.DispatcherServlet不真正处理请求,只是按照对应的规则将请求分发到对应的Bean对象

4.Bean对象是有我们自己编写来处理不同的请求,每个Bean中可以处理一个或多个不同的请求url

5.DispatcherServlet和Bean对象都需要交给Spring容器来进行管理

▶ 知识点

@Controller

@RequestMapping

@ResponseBody

▶ 入门案例

● AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类

● AbstractDispatcherServletInitializer提供三个接口方法供用户实现

  ○ createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围

protected WebApplicationContext createServletApplicationContext() {        AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();  ctx.register(SpringMvcConfig.class);     return ctx; 
}

  ○ createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对的bean,使用当前方法进行,使用方式同createServletApplicationContext()

protected WebApplicationContext createRootApplicationContext() {     return null; 
}

○ getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所请求,任意请求都将转入到SpringMVC进行处理

protected String[] getServletMappings() {return new String[]{"/"};
}

2、工作流程 

▶ 启动服务器初始化过程

1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器

2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象

   ● 该方法加载SpringMVC的配置类SpringMvcConfig来初始化SpringMVC的容器

3. 加载SpringMvcConfig配置类

4. 执行@ComponentScan加载对应的bean

   ● 扫描指定包下所有类上的注解,如Controller类上的@Controller注解

5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法

   ● 此时就建立了 `/save` 和 save方法的对应关系

6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

   ● `/`代表所拦截请求的路径规则,只有被拦截后才能交给SpringMVC来处理请求

▶ 单次请求过程

1. 发送请求localhost/save
2. web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
   ● 因为符合上面第六步设置的请求路径,所以该请求会交给SpringMVC来处理
3. 解析请求路径/save
4. 由/save匹配执行对应的方法save()
   ● 上面的第五步已经将请求路径和方法建立了对应关系,通过/save就能找到对应的save方法
5. 执行save()
6. 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

3、bean加载控制

▶ Controller加载控制与业务bean加载控制

● SpringMVC相关bean(表现层bean)

● Spring控制的bean

  ○ 业务bean(Service)

  ○ 功能bean(DataSource等)

●SpringMVC相关bean加载控制

  ○ SpringMVC加载的bean对应的包均在com.itheima.controller包内

● Spring相关bean加载控制

  ○ 方式一:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean

  ○ 方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等

  ○ 方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

▶ 知识点

▶ bean的加载格式

public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {     protected WebApplicationContext createServletApplicationContext() {         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();        ctx.register(SpringMvcConfig.class);        return ctx;    }    protected WebApplicationContext createRootApplicationContext() {         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();        ctx.register(SpringConfig.class);        return ctx;    }    protected String[] getServletMappings() {        return new String[]{"/"};    }}

▶ 简化开发

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{    protected Class<?>[] getServletConfigClasses() {        return new Class[]{SpringMvcConfig.class};    }    protected String[] getServletMappings() {        return new String[]{"/"};    }    protected Class<?>[] getRootConfigClasses() {        return new Class[]{SpringConfig.class};    }}

 二、请求

1、请求映射路径

2、请求方式

▶ Get请求

  ● 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数

@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name ,int age){    System.out.println("普通参数传递 name ==> "+name);    System.out.println("普通参数传递 age ==> "+age);    return "{'module':'common param'}";}

▶ post请求

 ● 普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数

@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name ,int age){    System.out.println("普通参数传递 name ==> "+name);    System.out.println("普通参数传递 age ==> "+age);    return "{'module':'common param'}";}

▶ Post请求中文乱码处理

 ● 为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器

public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{    // 配字符编码过滤器    protected Filter[] getServletFilters() {        CharacterEncodingFilter filter = new CharacterEncodingFilter();        filter.setEncoding("utf-8");        return new Filter[]{filter};    }}

3、请求参数

▶ 普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数

@RequestMapping("/commonParam")@ResponseBody public String commonParam(String name ,int age){     System.out.println("普通参数传递 name ==> "+name);    System.out.println("普通参数传递 age ==> "+age);    return "{'module':'common param'}";}

▶ 普通参数:请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

@RequestMapping("/commonParamDifferentName")@ResponseBodypublic String commonParamDifferentName(@RequestParam("name")String userName , int age){    System.out.println("普通参数传递 userName ==> "+userName);    System.out.println("普通参数传递 age ==> "+age);    return "{'module':'common param different name'}";}

▶ @RequestParam

▶ POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

@RequestMapping("/pojoParam")@ResponseBodypublic String pojoParam(User user){    System.out.println("pojo参数传递 user ==> "+user);    return "{'module':'pojo param'}";}

▶ 嵌套POJO参数:POJO对象中包含POJO对象

▶ 嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

@RequestMapping("/pojoContainPojoParam")@ResponseBodypublic String pojoContainPojoParam(User user){    System.out.println("pojo嵌套pojo参数传递 user ==> "+user);    return "{'module':'pojo contain pojo param'}";}

▶ 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

@RequestMapping("/arrayParam")@ResponseBodypublic String arrayParam(String[] likes){    System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));    return "{'module':'array param'}";}

▶ 集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

@RequestMapping("/listParam")@ResponseBodypublic String listParam(@RequestParam List<String> likes){    System.out.println("集合参数传递 likes ==> "+ likes);    return "{'module':'list param'}";}

4、请求参数(传递json数据)

▶ 接收请求中的json数据

▷ ①:添加json数据转换相关坐标

<dependency>  <groupId>com.fasterxml.jackson.core</groupId>  <artifactId>jackson-databind</artifactId>  <version>2.9.0</version>
</dependency>

▷ ②:设置发送json数据(请求body中添加json数据)

▷ ③:开启自动转换json数据的支持 

@Configuration
@ComponentScan("com.itheima.controller")
@EnableWebMvc
public class SpringMvcConfig {
}

   注意事项:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换。

▷ ④:设置接收json数据

@RequestMapping("/listParamForJson")@ResponseBodypublic String listParamForJson(@RequestBody List<String> likes){                 System.out.println("list common(json)参数传递 list ==> "+likes);    return "{'module':'list common for json param'}";}

▶ @EnableWebMvc

▶ @RequestBody

▶ POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数

@RequestMapping("/pojoParamForJson")@ResponseBodypublic String pojoParamForJson(@RequestBody User user){     System.out.println("pojo(json)参数传递 user ==> "+user);    return "{'module':'pojo for json param'}";}

▶ POJO集合参数:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数

@RequestMapping("/listPojoParamForJson")@ResponseBodypublic String listPojoParamForJson(@RequestBody List<User> list){     System.out.println("list pojo(json)参数传递 list ==> "+list);    return "{'module':'list pojo for json param'}";}

▶ @RequestBody与@RequestParam区别

● 区别

 ○ @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】

 ○ @RequestBody用于接收json数据【application/json】

● 应用

 ○ 后期开发中,发送json格式数据为主,@RequestBody应用较广

 ○ 如果发送非json格式数据,选用@RequestParam接收请求参数

5、日期类型参数传递

▶ 参数传递

 ● 日期类型数据基于系统不同格式也不尽相同

  ○ 2088-08-18

  ○ 2088/08/18

  ○ 08/18/2088

 ● 接收形参时,根据不同的日期格式设置不同的接收方式

@RequestMapping("/dataParam")@ResponseBodypublic String dataParam(Date date,@DateTimeFormat(pattern = "yyyy-MM-dd") Date date1,                                         @DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss")Date date2){   System.out.println("参数传递 date ==> "+date);    System.out.println("参数传递 date(yyyy-MM-dd) ==> "+date1);System.out.println("参数传递 date(yyyy/MM/dd HH:mm:ss) ==> "+date2);    return "{'module':'data param'}";}
http://localhost/dataParam?date=2088/08/08&date1=2088-08-18&date2=2088/08/28 8:08:08

▶ @DateTimeFormat

▶ 类型转换器

● Converter接口

public interface Converter<S, T> {    @Nullable    T convert(S var1);}

  ○ 请求参数年龄数据(String→Integer)

  ○ json数据转对象(json → POJO)

  ○ 日期格式转换(String → Date)

● @EnableWebMvc功能之一:根据类型匹配对应的类型转换器

三、响应

▶ 响应页面(了解)

@RequestMapping("/toPage")public String toPage(){    return "page.jsp";}

▶ 响应文本数据(了解)

@RequestMapping("/toText")@ResponseBodypublic String toText(){    return "response text";}

▶ 响应json数据(对象转json)

@RequestMapping("/toJsonPOJO")@ResponseBodypublic User toJsonPOJO(){    User user = new User();    user.setName("赵云");    user.setAge(41);    return user;}

▶ 响应json数据(对象集合转json数组)

@RequestMapping("/toJsonList")@ResponseBodypublic List<User> toJsonList(){    User user1 = new User();    user1.setName("赵云");    user1.setAge(41);    User user2 = new User();    user2.setName("master 赵云");    user2.setAge(40);    List<User> userList = new ArrayList<User>();    userList.add(user1);    userList.add(user2);    return userList;}

▶ @ResponseBody

▶ HttpMessageConverter接口

public interface HttpMessageConverter<T> {    boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);    List<MediaType> getSupportedMediaTypes();    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)          throws IOException, HttpMessageNotReadableException;    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)          throws IOException, HttpMessageNotWritableException; 
}

四、REST风格

 1、REST简介

▶ 基本介绍

  REST(Representational State Transfer),表现形式状态转换

● 传统风格资源描述形式       

  ○ http://localhost/user/getById?id=1     

  ○ http://localhost/user/saveUser

● REST风格描述形式     

  ○ http://localhost/user/1     

  ○ http://localhost/user

● 优点:

  ○ 隐藏资源的访问行为,无法通过地址得知对资源是何种操作

  ○ 书写简化

▶ 风格简介

   上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范 描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts…… 

2、RESTful入门案例

▶ ①:设定http请求动作(动词)

@RequestMapping(value = "/users", method = RequestMethod.POST)@ResponseBodypublic String save(@RequestBody User user){    System.out.println("user save..." + user);    return "{'module':'user save'}";}@RequestMapping(value = "/users" ,method = RequestMethod.PUT)@ResponseBodypublic String update(@RequestBody User user){    System.out.println("user update..."+user);    return "{'module':'user update'}";}

▶ ②:设定请求参数(路径变量)

@RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE) @ResponseBodypublic String delete(@PathVariable Integer id){    System.out.println("user delete..." + id);    return "{'module':'user delete'}";}

▶ @RequestMapping

▶ @PathVariable

▶ @RequestBody,@RequestParam,@PathVariable 三者区别

● 区别

  ○ @RequestParam用于接收url地址传参或表单传参

  ○ @RequestBody用于接收json数据

  ○ @PathVariable用于接收路径参数,使用{参数名称}描述路径参数

● 应用

  ○ 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广

  ○ 如果发送非json格式数据,选用@RequestParam接收请求参数

  ○ 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求径变量,通常用于传递id值

3、RESTful快速开发

▶ 快速开发

▶ @RestController

▶ @GetMapping  @PostMapping  @PutMapping  @DeleteMapping

▶ 基于RESTful页面数据交互

①:制作SpringMVC控制器,并通过PostMan测试接口功能

②:设置对静态资源的访问放行

③:前端页面通过异步提交访问后台控制器

五、拦截器

 1、拦截器概念

▶ 基本介绍

(1)浏览器发送一个请求会先到Tomcat的web服务器

(2)Tomcat服务器接收到请求以后,会去判断请求的是静态资源还是动态资源

(3)如果是静态资源,会直接到Tomcat的项目部署目录下去直接访问

(4)如果是动态资源,就需要交给项目的后台代码进行处理

(5)在找到具体的方法之前,我们可以去配置过滤器(可以配置多个),按照顺序进行执行

(6)然后进入到到中央处理器(SpringMVC中的内容),SpringMVC会根据配置的规则进行拦截

(7)如果满足规则,则进行处理,找到其对应的controller类中的方法进行执行,完成后返回结果

(8)如果不满足规则,则不进行处理

(9)这个时候,如果我们需要在每个Controller方法执行的前后添加业务,具体该如何来实现?

这个就是拦截器要做的事。

▷ 拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行
● 作用:
  ○ 在指定的方法调用前后执行预先设定的代码
  ○ 阻止原始方法的执行
  ○ 总结:拦截器就是用来做增强

▶ 拦截器和过滤器之间的区别是什么?

 ● 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
 ● 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

 2、拦截器开发

▶ 步骤1 : 创建拦截器类

  让类实现HandlerInterceptor接口,重写接口中的三个方法。

@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {@Override//原始方法调用前执行的内容public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle...");return true;}@Override//原始方法调用后执行的内容public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...");}@Override//原始方法调用完成后执行的内容public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...");}
}

注意: 拦截器类要被SpringMVC容器扫描到。

▶步骤2 : 配置拦截器类

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");}@Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books" );}
}

▶步骤3 : SpringMVC添加SpringMvcSupport包扫描

@Configuration
@ComponentScan({"com.itheima.controller","com.itheima.config"})
@EnableWebMvc
public class SpringMvcConfig{}

▶ 步骤4 : 运行程序测试

使用PostMan发送`http://localhost/books`

如果发送`http://localhost/books/100`会发现拦截器没有被执行,原因是拦截器的`addPathPatterns`方法配置的拦截路径是`/books`,我们现在发送的是`/books/100`,所以没有匹配上,因此没有拦截,拦截器就不会执行。

▶ 步骤5 : 修改拦截器拦截规则

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");}@Overrideprotected void addInterceptors(InterceptorRegistry registry) {//配置拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*" );}
}

这个时候,如果再次访问`http://localhost/books/100`,拦截器就会被执行。拦截器中的`preHandler`方法,如果返回true,则代表放行,会执行原始Controller类中要请求的方法,如果返回false,则代表拦截,后面的就不会再执行了。

▶ 步骤6 : 简化SpringMvcSupport的编写

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");}
}

▶ 拦截器的执行流程

当有拦截器后,请求会先进入preHandle方法,

​    如果方法返回true,则放行继续执行后面的handle[controller的方法]和后面的方法

​    如果返回false,则直接跳过后面方法的执行。


3、拦截器参数

▶ 前置处理方法

原始方法之前运行preHandle

public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {System.out.println("preHandle");return true;
}

 ● request:请求对象
 ● response:响应对象
 ● handler:被调用的处理器对象,本质上是一个方法对象,对反射中的Method对象进行了再包装

使用request对象可以获取请求数据中的内容,如获取请求头的`Content-Type`

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String contentType = request.getHeader("Content-Type");System.out.println("preHandle..."+contentType);return true;
}

使用handler参数,可以获取方法的相关信息

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod hm = (HandlerMethod)handler;String methodName = hm.getMethod().getName();//可以获取方法的名称System.out.println("preHandle..."+methodName);return true;
}

▶ 后置处理方法

原始方法运行后运行,如果原始方法被拦截,则不执行  

public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {System.out.println("postHandle");
}

前三个参数和上面的是一致的。

modelAndView : 如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。因为现在都是返回json数据,所以该参数的使用率不高。

▶ 完成处理方法

拦截器最后执行的方法,无论原始方法是否执行

public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception {System.out.println("afterCompletion");
}

前三个参数与上面的是一致的。

ex : 如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理,因为我们现在已经有全局异常处理器类,所以该参数的使用率也不高。

这三个方法中,最常用的是preHandle,在这个方法中可以通过返回值来决定是否要进行放行,我们可以把业务逻辑放在该方法中,如果满足业务则返回true放行,不满足则返回false拦截。

4、拦截器链配置

▶ 配置多个拦截器

▷ 步骤1 : 创建拦截器类

实现接口,并重写接口中的方法

@Component
public class ProjectInterceptor2 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle...222");return false;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle...222");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion...222");}
}

▷ 步骤2 : 配置拦截器类

@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Autowiredprivate ProjectInterceptor2 projectInterceptor2;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//配置多拦截器registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");}
}

▷ 步骤3 : 运行程序,观察顺序


 

拦截器执行的顺序是和配置顺序有关。先进后出。

● 当配置多个拦截器时,形成拦截器链
● 拦截器链的运行顺序参照拦截器添加顺序为准
● 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
● 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作

 ● preHandle :与配置顺序相同,必定运行

 ● postHandle : 与配置顺序相反,可能不运行

 ● afterCompletion : 与配置顺序相反,可能不运行。

相关文章:

SpringMvc介绍。

目录 1、SpringMvc概述 1、基本介绍 2、工作流程 3、bean加载控制 二、请求 1、请求映射路径 2、请求方式 3、请求参数 4、请求参数&#xff08;传递json数据&#xff09; 5、日期类型参数传递 三、响应 四、REST风格 1、REST简介 2、RESTful入门案例 3、RESTfu…...

华为OD机试 - 最小传递延迟(JS)

最小传递延迟 题目 通讯网络中有N个网络节点 用1 ~ N进行标识 网络通过一个有向无环图进行表示 其中图的边的值,表示节点之间的消息传递延迟 现给定相连节点之间的延时列表times[i]={u,v,w} 其中u表示源节点,v表示目的节点,w表示u和v之间的消息传递延时 请计算给定源节点到…...

学生信息管理系统(通讯录)----------通俗易懂、附源码、C语言实现

绪论&#xff1a; 本篇文章使结构体章节后的习题&#xff0c;如果你对C语言有问题&#xff0c;或者结构体有什么问题不妨看看我之前所写的文章&#xff08;章回体&#xff09;,对于文件管理和内存分配问题我将在后面补上&#xff0c;对于这个学生信息管理系统我用了多种方法和…...

Python抽奖系统

#免费源码见文末公众号# 抽奖系统① def choujiang1():def write():with open(d:\\抽奖系统\\抽奖1.1.pickle,rb) as file:lst1pickle.load(file)namevar1.get()if name not in lst1 and name!录入成功&#xff01; and name!录入失败&#xff01; and name!:lst1.append(name)…...

真实景观渲染技巧【Three.js】

受到一些很棒的 three.js 演示、与 covid 相关的旅行禁令以及可能在 pinterest 上花太多时间看美丽的旅行照片的启发——我开始看看我是否可以使用 three.js 和r3f在浏览器中渲染一个令人信服的风景场景。 推荐&#xff1a;将 NSDT场景编辑器 加入你的3D开发工具链。 在过去一个…...

MySQL知识汇总:MySQL函数CASE WHEN用法详解

Case When的两种简单用法 用法一&#xff1a; CASE seasonWHEN Spring THEN 春天 WHEN Summer THEN 夏天 WHEN autumn THEN 秋天 else 冬天 end 用法二&#xff1a; CASE WHEN season Spring THEN 春天WHEN season Summer THEN 夏天WHEN season autumn THEN 秋天 els…...

Python学习-----模块1.0(模块的简介、定义与使用)

目录 前言&#xff1a; 1.什么是模块 2.模块的分类 &#xff08;1&#xff09;内置模块 &#xff08;2&#xff09;第三方模块 &#xff08;3&#xff09;自定义模块 3.模块的使用 4.自定义模块 5.模块和执行文件的判断 前言&#xff1a; 今天就开始讲Python中的模块篇…...

Linux进程学习【二】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…...

我问chatGPT,在JavaScript中构造函数和类的区别

问&#xff1a;构造器函数和面向中的类是同样的东西吗|&#xff1f; 答&#xff1a;构造器函数和面向对象中的类并不是同样的东西&#xff0c;它们之间有些许不同。 在面向对象编程中&#xff0c;类是一种抽象的概念&#xff0c;它描述了一类具有相同属性和行为的对象。类可以…...

软考高级-信息系统管理师之沟通管理(最新版)

项目沟通管理 1、项目沟通管理基础项目沟通管理的重要性项目沟通管理相关理论2、规划沟通管理3、管理沟通4、控制沟通项目沟通管理的技术和工具1、项目沟通管理基础 项目沟通管理的重要性 1、与1T项目成功有关的最重要的四个因素是:主管层的支持、用户参与、有经验的项目经理…...

PyQt5 自定义富文本编辑器

介绍 一款使用PyQt5和网页端框架wangEditor集成的富文本编辑器 代码片段 PyQt5客户端 与网页端建立连接def create_connect(self):self.web_view QWebEngineView()self.bridge JSBridge(self.web_view.page())self.web_view.load(QUrl.fromLocalFile(self.editor_path))w…...

【高可用系统架构设计】SLA服务可用性4个9是什么意思?如何保证服务的高可用性 HA(High Availability)?...

如何保证服务的高可用性 HA&#xff08;High Availability&#xff09;?高可用HA&#xff08;High Availability&#xff09;是分布式系统架构设计中必须考虑的因素之一&#xff0c;它通常是指&#xff0c;通过设计减少系统不能提供服务的时间。方法论上&#xff0c;高可用是通…...

微服务架构设计模式-(14)面向生产环境

生产环境要求 应用安全 数据权限 服务可配置性 不同环境的配置不一样&#xff0c;不能写死代码&#xff0c;所以要配置 可观测性 需要日志系统 应用安全 四个方面 身份验证 验证主体的身份解决方案 单体 cookie 微服务中 API Gateway 访问令牌 不透明令牌透明令牌&#xff…...

HTML5新增用法

新增语义化标签 并无特殊含义&#xff0c;是语义&#xff01;语义&#xff01;语义&#xff01; <header> 头部区域 <nav> 导航区域 <main> 主体区域 <article> 内部标签 <section> 块级标签 <aside> 侧边栏标签 <footer> 尾部…...

富足金字塔:人的努力是为了扩大选择的范围

人的努力是为了扩大选择的范围&#xff0c;这是熵减的另一种表述。富足金字塔代表着人生的三重境界。第一层是温饱。人需要食物、水、住所。第二层是品质。能源、ICT、教育带来更有品质的生活&#xff0c;如智能门锁、智能马桶、扫地机、洗碗机、洗衣烘衣机。第三层是梦想。包括…...

C++类基础(十七)

类的继承——补充知识 ● public 与 private 继承&#xff08;C Public, Protected and Private Inheritance&#xff09; 改变了类所继承的成员的访问权限 //公有继承 struct Base { public:int x; private:int y; protected:int z; }; struct Derive : public Base //公有继承…...

LeetCode刷题复盘笔记—一文搞懂贪心算法之56. 合并区间(贪心算法系列第十四篇)

今日主要总结一下可以使用贪心算法解决的一道题目&#xff0c;56. 合并区间 题目&#xff1a;56. 合并区间 Leetcode题目地址 题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间…...

Andriod入门级开发

这学期有个课设&#xff0c;我们组我负责一个手机APP的开发&#xff0c;虽然刚开始说要实现什么智能导航&#xff0c;类似高德地图那种&#xff0c;但最后阉割的只剩一个Socket通信了&#xff0c;因为之前没有接触过&#xff08;可能之后也不会再接触&#xff09;&#xff0c;记…...

DCL 数据控制语言

1、简介 DCL英文全称是Data Control Language(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访问权限。 2、管理用户 2.1 查询用户 select * from mysql.user;查询的结果如下: 其中 Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问…...

全网超详细的下载与安装VMware虚拟机以及为什么要安装VMware虚拟机

文章目录1. 文章引言2. 下载VMware3. 安装VMware1. 文章引言 我们使用最多的系统是windows系统&#xff0c;因为&#xff0c;国内电脑厂商的操作系统(os)基本是windows系统&#xff0c;比如华为、联想、华硕等电脑。 但线上的服务器大多是Linux系统&#xff0c;而我们经常使用…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...