1. 关于@RequestMapping注解

在控制器中,在处理请求的方法之前添加@RequestMapping注解,可以配置请求路径与处理请求的方法的映射关系!

@RequestMapping注解的源代码中有:

/*** The primary mapping expressed by this annotation.* <p>This is an alias for {@link #path}. For example,* {@code @RequestMapping("/foo")} is equivalent to* {@code @RequestMapping(path="/foo")}.* <p><b>Supported at the type level as well as at the method level!</b>* When used at the type level, all method-level mappings inherit* this primary mapping, narrowing it for a specific handler method.* <p><strong>NOTE</strong>: A handler method that is not mapped to any path* explicitly is effectively mapped to an empty path.*/
@AliasFor("path")
String[] value() default {};

由于value是默认的属性,所以,平时所使用到的@RequestMapping("reg.do")其实配置的就是这个value属性的值!

它的数据类型是String[],所以,在配置时,可以同时配置多个路径!例如:

// 【显示注册页面】
@RequestMapping({"reg.do", "register.do"})
public String reg() {System.out.println("UserController.reg()");return "register";
}

则通过reg.doregister.do均可使得reg()方法被执行,而方法的代码是不变的,最终的效果就是这2个路径都可以打开同一个页面!

在源代码中,还有:

/*** The path mapping URIs (e.g. {@code "/profile"}).* <p>Ant-style path patterns are also supported (e.g. {@code "/profile/**"}).* At the method level, relative paths (e.g. {@code "edit"}) are supported* within the primary mapping expressed at the type level.* Path mapping URIs may contain placeholders (e.g. <code>"/${profile_path}"</code>).* <p><b>Supported at the type level as well as at the method level!</b>* When used at the type level, all method-level mappings inherit* this primary mapping, narrowing it for a specific handler method.* <p><strong>NOTE</strong>: A handler method that is not mapped to any path* explicitly is effectively mapped to an empty path.* @since 4.2*/
@AliasFor("value")
String[] path() default {};

结合以上2段源代码,可以看到value属性与path属性是完全相同的!如果需要显式的指定属性名称时,使用path可以更好的表现语义,该属性名称是从4.2版本加入的!

在源代码还有:

/*** The HTTP request methods to map to, narrowing the primary mapping:* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.* <p><b>Supported at the type level as well as at the method level!</b>* When used at the type level, all method-level mappings inherit* this HTTP method restriction (i.e. the type-level restriction* gets checked before the handler method is even resolved).*/
RequestMethod[] method() default {};

以上源代码说明在使用@RequestMapping时,可以配置名为method的属性,该属性的值的类型是RequestMethod[]类型,默认值是无。

该属性的作用是“配置所映射的HTTP请求方式”,如果没有配置该属性,表示“可以通过任何请求方式访问该路径”!如果显式的配置了该属性,则只有配置值对应的请求方式才是允许的,而没有被配置值的请求方式将不被允许!

例如,将请求方式限制为POST类型:

@RequestMapping(value="handle_reg.do", method=RequestMethod.POST)

如果仍尝试使用GET或其它不被允许的请求方式,将会出现405错误:

HTTP Status 405 – Method Not Allowed

并且,还伴随具体的提示信息:

Request method 'GET' not supported

由于method属性的值类型是RequestMethod[],所以,可以设置为允许多种请求方式,例如:

@RequestMapping(path="login.do", method={RequestMethod.GET, RequestMethod.POST})

如果需要将请求方式限制为固定的某1种,还可以使用简化后的注解!例如使用@PostMapping注解,就可以将请求方式限制为POST类型,在使用时,只需要配置请求路径即可,例如:

// @RequestMapping(value="handle_reg.do", method=RequestMethod.POST)
@PostMapping("handle_reg.do")

@PostMapping注解的源代码中,关于该注解的声明是:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {}

在该注解的声明之前添加了@RequestMapping(method = RequestMethod.POST)就表示当前@PostMapping注解具有@RequestMapping的作用特点且已经将请求方式限制为POST了!

除此以外,还有@GetMapping@PutMapping@DeleteMapping@PatchMapping,均对应某1种请求方式!

小结:如果需要将请求方式限制为某1种,则应该使用以上这些简化的注解,如果需要同时允许多种不同的请求方式,应该使用@RequestMapping

关于@RequestMapping注解,除了添加在处理请求的方法之前,还可以添加在控制器类的声明之前!例如:

@Controller
@RequestMapping("user")
public class UserController {}

当控制器类的声明之前配置了@RequestMapping("user")后,当前类中映射的所有请求路径中都需要添加user这个层级,例如,在没有添加该配置之前时,访问路径是:

http://localhost:8080/springmvc02/reg.do
http://localhost:8080/springmvc02/login.do

添加了配置之后,访问路径是:

http://localhost:8080/springmvc02/user/reg.do
http://localhost:8080/springmvc02/user/login.do

通常,推荐为每一个控制器类的声明之前都添加该注解的配置!

可以看到 ,SpringMVC框架在处理配置的路径时,会把类之前的@RequestMapping配置值与方法之前的配置值拼接起来作为完整的访问路径,但是,开发人员并不需要考虑配置值的左右两侧的/符号,例如以下配置是等效的:

在控制器类之前的配置 在方法之前的配置
user reg.do
user /reg.do
user/ reg.do
user/ /reg.do
/user reg.do
/user /reg.do
/user/ reg.do
/user/ /reg.do

在实际使用时,推荐使用第1种即可!如果使用其它的配置风格也是可以的,但是,在同一个项目中,应该只使用1种风格的配置!

2. 拦截器

拦截器(Interceptor)在SpringMVC框架中可以作用于若干个不同的请求,且经过拦截器的处理后,可以选择对这些请求进行阻止(不允许继续向后续的流程中执行),或选择放行!

注意:拦截器的作用并不一定是”拦“下来就不允许执行了,可能某些拦截器的做法就是”拦“下来后全部放行!

当需要要使用拦截器时,首先,需要自定义类,实现HandlerInterceptor接口,并在重写的方法中添加输出语句,以便于观察方法的执行时间点及执行的先后顺序:

package cn.tedu.spring;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {System.out.println("LoginInterceptor.preHandle()");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {System.out.println("LoginInterceptor.postHandle()");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {System.out.println("LoginInterceptor.afterCompletion()");}}

然后,还需要对拦截器进行配置,关于该配置的写法要求:

  • 相关的配置需要写在某个类中,这个类需要实现WebMvcConfigurer接口,且在类的声明之前必须添加@Configuration@EnableWebMvc这2个注解,且这个类的对象必须是初始化类的getServletConfigClasses()方法的返回值;
  • 在以上类中重写addInterceptors()方法,以配置拦截器。

例如使用原本存在的SpringMvcConfig为作配置类:

package cn.tedu.spring;@EnableWebMvc
@Configuration
@ComponentScan("cn.tedu.spring")
public class SpringMvcConfig implements WebMvcConfigurer {private String characterEncoding = "utf-8";@Beanpublic ViewResolver viewResolver() {// 省略}@Overridepublic void addInterceptors(InterceptorRegistry registry) {HandlerInterceptor interceptor = new LoginInterceptor();// 注意:表示拦截器处理的路径时,各路径必须使用 / 作为第1个字符registry.addInterceptor(interceptor).addPathPatterns("/index.do");}}

在一个项目中,允许同时存在若干个拦截器,配置的先后顺序决定了执行顺序,如果某个请求会经过多个拦截器,只有这些拦截器全部都放行,才可以继续向后执行,只要其中任何一个拦截器的处理结果是阻止运行,该请求就不可以向后执行了!

通过运行效果可以观察到:

  • 当拦截器中的preHandle()方法返回true时,会行执行拦截器中的preHandle()方法,再执行需要请求的控制器中的方法,再执行拦截器中的postHandle()afterCompletion()方法;
  • 当拦截器中的preHandle()方法返回false时,只会执行拦截器中的preHandle()方法,且客户端的浏览器窗口将显示一片空白。
  • 只有拦截器中的preHandle()方法才是真正意义上的”拦截“方法!

回到LoginInterceptor类中,重写preHandle()方法以判断阻止或放行:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("LoginInterceptor.preHandle()");// 如果已经登录,则放行,如果未登录,则阻止且重定向到登录界面HttpSession session = request.getSession();if (session.getAttribute("username") == null) {String contextPath = request.getContextPath();response.sendRedirect(contextPath + "/user/login.do");return false;}return true;
}

关于拦截器的配置,首先,每个拦截器都可以配置若干个拦截的路径!关于配置拦截路径时调用的addPathPatterns()方法,其源代码是:

/*** Add URL patterns to which the registered interceptor should apply to.*/
public InterceptorRegistration addPathPatterns(String... patterns) {return addPathPatterns(Arrays.asList(patterns));
}/*** List-based variant of {@link #addPathPatterns(String...)}.* @since 5.0.3*/
public InterceptorRegistration addPathPatterns(List<String> patterns) {this.includePatterns.addAll(patterns);return this;
}

实际使用时,可以写成例如:

registry.addInterceptor(interceptor).addPathPatterns("/index.do", "/user/password.do");

或者写成:

List<String> pathPatterns = new ArrayList<>();
pathPatterns.add("/index.do");
pathPatterns.add("/user/password.do");
registry.addInterceptor(interceptor).addPathPatterns(pathPatterns);

在配置路径时,还可以使用星号(*)作为通配符,例如,可以将/blog/*配置到拦截路径中,则/blog/delete.do/blog/edit.do等路径都可以被匹配!

但是,需要注意的是:1个星号(*)只能表示某层级下的资源,不可以匹配到若干个层级!例如配置为/blog/*时,就无法匹配到/blog/2020/list.do!如果一定匹配若干个层级,必须使用2个连续的星号(**),例如配置为/blog/**,则可以匹配到/blog/list.do/blog/2020/list.do/blog/2020/07/list.do……

在注册拦截器之后,还可以调用excludePathPatterns()方法添加”排除“的路径,被”排除“的路径将不会被拦截器处理!所以,也可以理解为”例外“或”白名单“,例如:

List<String> pathPatterns = new ArrayList<>();
pathPatterns.add("/user/reg.do");
pathPatterns.add("/user/handle_reg.do");
pathPatterns.add("/user/login.do");
pathPatterns.add("/user/handle_login.do");
registry.addInterceptor(interceptor).addPathPatterns("/user/**").excludePathPatterns(pathPatterns);

注意:在调用方法时,必须先调用addPathPatterns()方法然后再调用excludePathPatterns()方法!

3. 拦截器与过滤器的区别

【相同/相似】

拦截器与过滤器都是可以作用于若干个不同的请求的,在处理请求之前将执行拦截器或过滤器中的代码,并且,都能够实现放行或阻止的效果,并且,都有”链“的概念,在同一个项目中允许存在若干个拦截器或过滤器,同一个请求需要经历多个拦截器或过滤器,只有这些拦截器或过滤器全部放行,才能向后执行!

【区别】

  • 过滤器Filter是Java EE中的组件,则任何Java EE项目都可以使用过滤器,而拦截器Interceptor是SpringMVC框架中的组件,只有使用了SpringMVC框架的Java EE项目才可以使用拦截器,并且,只有被SpringMVC框架处理的请求才可能被拦截器处理,例如将SpringMVC框架处理的路径设置为*.do时,直接访问HTML页面、图片等资源将不会被拦截器处理;
  • 过滤器Filter是执行在所有Servlet组件之前的,而拦截器Interceptor的第1次执行是在DispatcherServlet之后,且在Controller组件之前的(当然,使用过滤器时,也许是通过Servlet来处理请求的,使用拦截器时,是通过Controller来处理请求的,所以,这2者都是在处理请求之前执行,所以,一般情况下,差异并不明显);
  • 过滤器Filter只能配置过滤路径(黑名单),而拦截器Interceptor既可以配置拦截路径(黑名单),又可以配置排除路径(例外,白名单),后者的配置更加灵活!

【小结】

通过分析以上区别,可以发现:通过过滤器Filter实现的效果,改为使用拦截器Interceptor基本上都可以实现!同时,拦截器还具备”配置更加灵活“的特点,所以,在绝大部分情况下,应该优先使用拦截器!

当然,过滤器也具有拦截器无法取代的特点,就是”执行时间点“非常早,它是执行在所有Servlet组件之前的,所以,如果某个需要被”拦“下来执行的任务是非常早期就要执行,则必须使用过滤器!

例如,SpringMVC框架默认使用的编码是ISO-8859-1,是不支持中文的,所以,使用POST提交的请求参数中,只要存在非ASCII码字符,就会出现乱码,如果需要自定义编码,需要在项目的初始化类中重写getServletFilters()方法,并在该方法中返回SpringMVC框架自带的字符编码过滤器,且设置编码,例如:

@Override
protected Filter[] getServletFilters() {return new Filter[] { new CharacterEncodingFilter("UTF-8") };
}

4. SpringMVC阶段小结

  • 【理解】SpringMVC框架的作用:解决V-C交互的问题;
  • 【理解】SpringMVC框架的核心执行流程图;
  • 【掌握】通过SpringMVC框架接收并处理客户端的请求:
    • SpringMVC项目的搭建:添加spring-webmvc依赖,配置不使用web.xml,添加Tomcat环境,创建SpringMVC的配置类,创建初始化项目的类并加载SpringMVC的配置类、设置SpringMVC框架所处理的请求路径;
    • 创建控制器类:自定义名称,必须放在组件扫描的包或其子孙包中,必须添加@Controller注解;
    • 创建处理请求的方法:使用@RequestMapping系列注解配置请求路径,使用public访问权限,暂定使用String作为返回值类型,方法名称可以自定义,方法的参数列表可以按需设计。
  • 【掌握】通过SpringMVC框架接收客户端提交的请求参数:
    • 将请求参数逐一声明为处理请求的方法的参数;
    • 将多个请求参数封装到自定义对象中,并将自定义的数据类型声明为处理请求的方法的参数。
  • 【掌握】使用SpringMVC封装转发的数据到视图组件,并且在视图组件显示转发的数据;
  • 【理解】转发与重定向的区别;
  • 【掌握】Session的使用原则与使用方式;
  • 【掌握】拦截器的使用与配置;
  • 【理解】拦截器与过滤器的区别;
  • 【掌握】解决POST请求中文乱码的问题;
  • 【掌握】阅读简单的注解源代码,例如@RequestMapping@RequestParam等。

SpringMVC框架 学习DAY_03:@RequestMapping注解/拦截器与过滤器相关推荐

  1. 框架:Spring Aop、拦截器、过滤器的区别

    Spring Aop.拦截器.过滤器的区别 Filter过滤器:拦截web访问的url地址. Interceptor拦截器:拦截以.action结尾的url,拦截Action的访问. Spring A ...

  2. Spring MVC学习(8)—HandlerInterceptor处理器拦截器机制全解

    基于最新Spring 5.x,详细介绍了Spring MVC的HandlerInterceptor处理器拦截器机制,以及它的一系列拦截方法. 本次我们来学习Sring MVC的HandlerInter ...

  3. 【SSM框架系列】SpringMVC的文件上传、拦截器及异常处理

    SpringMVC的文件上传 服务器端实现文件上传的技术有很多种,Servlet3.0,FileUtils,框架等等,都可以实现文件上传,不管使用哪一种上传技术,都必须满足三要素: 表单项type=& ...

  4. SpringMVC框架--学习笔记(上)

    1.SpringMVC入门程序: (1)导入jar包:spring核心jar包.spring-webmvc整合Jar包 (2)配置前端控制器:web.xml文件中 <?xml version=& ...

  5. SpringMVC框架 学习DAY_01:框架概括 / 简易应用 / 核心执行流程图 /在框架下显示HTML模板页面/ 接受请求

    1. SpringMVC框架的作用 MVC = Model(数据模型) + View(视图) + Controller(控制器) SpringMVC框架主要解决了接收请求与处理响应的问题,也可以认为是 ...

  6. springboot 自定义注解拦截器

    springboot 自定义注解拦截器 最近在工作中,发现自定义注解拦截使用起来特别方便,现在来写出来给大家看看 环境springboot 首先写一个自定义注解 package com.study.c ...

  7. Springboot + redis + 注解 + 拦截器来实现接口幂等性校验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:wangzaiplus www.jianshu.com/p/ ...

  8. springboot + redis + 注解 + 拦截器 实现接口幂等性校验

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 来源:https://www.jianshu.com/p/6189275403ed 一.概念 ...

  9. @slf4j注解_SpringBoot + Redis + 注解 + 拦截器 实现接口幂等性校验

    一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多 ...

最新文章

  1. 【NIO】异步模型之Callback -- 封装NIO
  2. MySQL存储过程_创建-调用
  3. python3软件怎么使用_Python3时间戳如何应用于数学计算?
  4. 用DELPHI的RTTI实现对象的XML持久化 【转】
  5. 在Qt Designer中使用自定义Widgets小部件
  6. job history 的查看
  7. Go语言学习资料整理
  8. python学生管理系统类图_类图 python
  9. oracle函数大全-字符串处理函数
  10. 腾讯企业邮箱服务器地址imap端口号,腾讯企业邮箱如何设置IMAP、POP3/SMTP及其SSL加密方式...
  11. 放大电路的分析方法详解
  12. 信息化建设规划制定的难点及关键点分析
  13. 王道操作系统知识点总结
  14. R语言使用table1包绘制(生成)三线表、使用单变量分列构建三线表、编写自定义三线表结构(将因子变量细粒度化重新构建三线图)、自定义修改描述性统计参数输出自定义统计量
  15. 在线excel表格,支持协同编辑
  16. java面试题:重写和重载的区别——详解
  17. python去除重复单词_Python程序,用于删除给定句子中的所有重复单词。
  18. Chrome浏览器常用快捷键总结
  19. 5个海盗分100颗宝石
  20. 恢复系统自带的office软件

热门文章

  1. JavaWeb--MVC案例1-------(4)删除
  2. 一次SQLSERVER触发器编写感悟
  3. DNS Flusher
  4. vs2005格式化代码
  5. 解决MathType与Word 2016兼容性问题。
  6. Linux文件系统只读Read-only file system的解决方法
  7. 安装了低版本Jdk后eclipse无法打开的终极解决方法
  8. Spark SQL中出现 CROSS JOIN 问题解决
  9. spring mvc 异常统一处理方式
  10. SQL数据库常见故障及解决方法