一文了解Spring中 * 的原理与使用
作者:WX7251 发布时间:2023-06-30 13:28:05
1.Spring中的 *
在web开发中, * 是经常用到的功能。它可以帮我们预先设置数据以及统计方法的执行效率等等。
今天就来详细的谈一下spring中的 * 。spring中 * 主要分两种,一个是HandlerInterceptor,一个是MethodInterceptor。
1.1HandlerInterceptor *
HandlerInterceptor是springMVC项目中的 * ,它拦截的目标是请求的地址,比MethodInterceptor先执行。其工作原理是当请求来时先进性预处理,如下。
这里我们可以实现一个通过HandlerInterceptor实现打印请求开始和结束的日志,如下。
1.依赖引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.实现类
* 类
@Component
public class EasyLogControllerInterceptor implements HandlerInterceptor {
/**
* 在controller调用之前执行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println(request.getRequestURI()+"开始执行");
return true;
}
/**
* 在controller调用中执行
*/
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* 在controller调用后执行
*/
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println(request.getRequestURI()+"执行结束");
}
}
controller类
@RestController
public class TestController {
@GetMapping("/hello")
public Map<String,String> hello(){
Map<String,String> response=new HashMap<>();
response.put("msg","hello");
return response;
}
}
配置类
@Configuration
public class IntercepterConfig implements WebMvcConfigurer {
@Autowired
private EasyLogControllerInterceptor easyLogControllerInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//addPathPatterns用于添加拦截路径
//excludePathPatterns用于添加不拦截的路径
registry.addInterceptor(easyLogControllerInterceptor).addPathPatterns("/hello");
}
//此方法用于配置静态资源路径
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/my/");
}
}
3.运行效果
1.1.1HandlerInterceptor讲解
实现一个HandlerInterceptor * 可以直接实现HandlerInterceptor接口,也可以继承HandlerInterceptorAdapter类。这两种方法殊途同归,其实HandlerInterceptorAdapter也就是声明了HandlerInterceptor接口中所有方法的默认实现,而我们在继承他之后只需要重写必要的方法。
下面就是HandlerInterceptorAdapter的代码,可以看到一个方法只是默认返回true,另外两个是空方法:
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
这三个方法都是干什么的,有什么作用,什么时候调用,不同的 * 之间是怎样的调用顺序呢?
先补一张图:
这还得参考一下DispatcherServlet的doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
processedRequest = checkMultipart(request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// Apply preHandle methods of registered interceptors.
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// Do we need view name translation?
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// Apply postHandle methods of registered interceptors.
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
// Trigger after-completion for successful outcome.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
catch (Exception ex) {
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
catch (Error err) {
ServletException ex = new NestedServletException("Handler processing failed", err);
// Trigger after-completion for thrown exception.
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);
throw ex;
}
finally {
// Clean up any resources used by a multipart request.
if (processedRequest != request) {
cleanupMultipart(processedRequest);
}
}
}
代码有点长,但是它封装了springMVC处理请求的整个过程。首先根据请求找到对应的HandlerExecutionChain,它包含了处理请求的handler和所有的HandlerInterceptor * ;然后在调用hander之前分别调用每个HandlerInterceptor * 的preHandle方法,若有一个 * 返回false,则会调用triggerAfterCompletion方法,并且立即返回不再往下执行;若所有的 * 全部返回true并且没有出现异常,则调用handler返回ModelAndView对象;再然后分别调用每个 * 的postHandle方法;最后,即使是之前的步骤抛出了异常,也会执行triggerAfterCompletion方法。
1.2 MethodInterceptor *
MethodInterceptor是AOP项目中的 * ,它拦截的目标是方法,即使不是controller中的方法。具体使用方式可以参考SpringBoot中利用AOP和 * 实现自定义注解
2.二者的区别
上面的两种 * 都能起到拦截的效果,但是他们拦截的目标不一样,实现的机制不同,所以有的时候适用不同的场景。
HandlerInterceptoer拦截的是请求地址,所以针对请求地址做一些验证、预处理等操作比较合适。当你需要统计请求的响应时间时MethodInterceptor将不太容易做到,因为它可能跨越很多方法或者只涉及到已经定义好的方法中一部分代码。MethodInterceptor利用的是AOP的实现机制,在本文中只说明了使用方式,关于原理和机制方面介绍的比较少,因为要说清楚这些需要讲出AOP的相当一部分内容。在对一些普通的方法上的拦截HandlerInterceptoer就无能为力了,这时候只能利用AOP的MethodInterceptor。
另外,还有一个跟 * 类似的东西----Filter。Filter是Servlet规范规定的,不属于spring框架,也是用于请求的拦截。但是它适合更粗粒度的拦截,在请求前后做一些编解码处理、日志记录等。而 * 则可以提供更细粒度的,更加灵活的,针对某些请求、某些方法的组合的解决方案。
另外的另外,用过人人网的ROSE框架的人都会非常喜欢它的 * 功能。因为它实现了全注解的方式,只要在类的名字上加上 * 的注解即表示这是一个 * 。而使用这个 * 的方法或者controller也只需在方法或controller的上面加上这个 * 的注解。其实这是一个关注点的转变,spring的切面控制在配置文件中,配置文件关注哪些地方需要拦截。而在ROSE中,则是在需要拦截的地方关注我要被谁拦截。
来源:https://blog.csdn.net/WX5991/article/details/122230962


猜你喜欢
- 前言Activity可以获取运行中的应用信息,可以获取到servcie,process,app,memory,Task信息等。获取信息Act
- 本章是后续学习的基石,只有充分理解了分布式系统的概念和面临的问题,以及ZooKeeper内部的概念,才能懂得ZooKeeper是如何对分布式
- 优点1.观察者和被观察者是抽象耦合的。2.建立一套触发机制。缺点1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知
- 前言首次通过右滑来返回到上一个页面的操作是在 IOS7上出现。到目前android应用上支持这种操作的依然不多。分析其主要原因应该是andr
- 这篇文章主要介绍了Java模拟多线程实现抢票,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考
- Unity IPostprocessBuild技术文章Unity IPostprocessBuild是Unity引擎中的一个非常有用的功能,
- listview实现上拉加载以及下拉刷新的方式有很多。下面是我写的一种自定义的布局,复用性也比较的强。首先就是继承的listview的自定义
- 本篇文章是直接下载最新的APK安装的方法,并不是增量下载该APk。想要实现一个android应用,自动更新下载APK软件的方法,我采取的是以
- 前言想必大家对c语言的动态内存分配并不陌生,忘了的小伙伴也可以看看我的这篇文章C语言动态内存分配c语言的动态内存分配由于有些地方用起来比较麻
- C#动态webservice调用接口using System;using System.Collections;using System.I
- 前言之前写过一篇关于配置中心对配置内容加密解密的介绍:《Spring Cloud构建微服务架构:分布式配置中心(加密解密) 》。在这篇文章中
- 介绍前面的内容对Handler做了介绍,也讲解了如何使用handler,但是我们并不知道他的实现原理。本文从源码的角度来分析如何实现的。首先
- Android利用爬虫实现模拟登录的实现实例为了用手机登录校网时不用一遍一遍的输入账号密码,于是决定用爬虫抓取学校登录界面,然后模拟填写本次
- 一、案例场景遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样——public static
- 这篇文章主要介绍了Mybatis一对多关联关系映射实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,
- Ionic是一款流行的移动端开发框架,但是刚入门的同学会发现,Ionic在iOS和Android的底部tabs显示不一样。在安卓情况下底部t
- 本文实例为大家分享了Android实现闪屏页效果的具体代码,供大家参考,具体内容如下1.效果图2.闪屏页逻辑及布局2.1 activity_
- 使用了Android的系统API实现了多点触控功能,多点触控对设备的硬件有一定的要求,目前市面上的手机几乎都能实现多点触控了。实现多点触控最
- 在5.2.1节和5.2.2节介绍了<a>标签以及TextView自动识别的特殊文本(网址、电话号、Email等),这些都可以通过
- mybatis-plus框架功能很强大,把很多功能都集成了,比如自动生成代码结构,mybatis crud封装,分页,动态数据源等等,附上官