南京网站设计费用网络整合营销策划书
目录
一:拦截器
1:拦截器作用
2:拦截器使用
(1):定义拦截器(给保安定义任务)
(2):注册配置拦截器(给保安分配拦截表)
二:DispatcherServlet
1:DispatcherServlet初始化
2:DispatcherServlet处理请求
三:总结
一:拦截器
1:拦截器作用
兄弟们今天讲一讲拦截器啊,也是好久没有讲过拦截器了,将军说过一句话啊,飞机一定要会飞,船一定要在水上开,握手一定要伸手
\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/\😭/
那顾名思义啊,拦截器就是要拦截东西的呀,那我们听拦截的最多场景那肯定就是导弹拦截啊
当然哈,导弹拦截我们可能接触不到,但是下面的这个拦截,你肯定是见过的
咱想想,假如有一个人姓王,他想找隔壁小区里面的寡妇翠花交流一下学习经验,如果这个小区没有保安,还不用进出刷卡,那让老王天天想学习就学习,真让老王考上了怎么办。
真让老王考上了那肯定不行,那我转头一想,在小区门口给你安一个保安他不就炸了吗
保安(拦截器):你停下干什么的(拦截请求)
老王(请求):我想找翠花交流下学习经验(进行的业务)
保安(拦截器):你等一下啊,让我查一下拦截表(配置的拦截路径),翠花是不能随便见的(访问路劲/“翠花”被拦截),但如果你有令牌的话还是能见的我查查如果正确(需要经过拦截器校验才能访问)
接下来会有两种情况
(1)老王拿出了令牌,保安检查没问题放行(校验通过),老王进小区和翠花交流经验
(2)老王没拿出令牌,被保安一拳打飞
总结一下:拦截器主要用于在请求处理中的拦截操作,在对请求处理之前,需要先用拦截器进行一次预处理,判断一下你有没有权限进行这个处理请求,就比如说,有人想往你的卡的取一个亿,结果人家没有登录就直接把你的钱全取走了,那肯定是不行的,必须用拦截器校验一下用户有没有登录,进行身份认证一下,才能够取钱
2:拦截器使用
(1):定义拦截器(给保安定义任务)
定义一个拦截器很简单,就只用实现HandlerInterceptor接口就行了
然后重写它里面的三个方法preHandle(),postHandle(),afterCompletion()
如下代码我只给prehandler方法重写了一下,加入了一些逻辑,大概意思是从请求头中获取一下令牌,然后对令牌解析一下,如果解析成功了放行,如果不成功那就直接拦截(一拳打飞),
就是上面保安查看老王令牌正确不正确的过程
至于postHandle(),我就没定义,因为人家都进小区了,再出来的时候再排查的意义就不是很大了,当然如果你想搞些什么事,可以根据业务场景自己写一些逻辑来处理。
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {/*
* 业务请求之前调用
* @param request 请求报文
* @param response 响应报文
* @param handler
* */@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头String token = request.getHeader("user_token");log.info("请求头:{}",request.getHeader("user_token"));log.info("获取路径:{}",request.getRequestURI());//令牌解析Claims claims = JWTUtil.parseJWT(token);if (null == claims){log.error("解析JWT令牌失败");response.setStatus(401);return false;}log.info("解析令牌成功,放行!");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}
}
(2):注册配置拦截器(给保安分配拦截表)
给保安已经安排好干什么了,现在保安就得明白,你干什么该查,干什么不该查了
如下事注册配置拦截器的代码,只需要实现WebMvcConfigurer这个接口,然后重写addInterceptors()这个方法就可以了
@Configuration
public class AppConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
private final List<String> excludes = Arrays.asList(//不用拦截哪些路径"/**/*.html","/css/**","/js/**","/pic/**","/*.jpg","/*.png","/favicon.ico","/**/login","/user/register","/user/verification-code/send","/winning-records/show"
);@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor)//加入哪个拦截器.addPathPatterns("/**").//拦截的路径excludePathPatterns(excludes);//不拦截的路径}
}
方法解释
registry.addInterceptor(loginInterceptor)//加入哪个配置好的拦截器,就是咱刚刚配好的那个拦截器
.addPathPatterns("/**").//拦截的路径,给保安发的拦截表,请求的哪些路径应该拦截
excludePathPatterns(excludes);不拦截的路径,有哪些路径是不用拦截的上面代码中配置的excludes链表就是说哪些请求是不需要拦截的
拦截路径的写法
拦截路径 | 含义 | 举例 |
/* | ⼀级路径 | 能匹配/user,/book,/login,不能匹配 /user/login |
/** | 任意级路径 | 能匹配/user,/user/login,/user/reg |
/book/* | /book下的⼀级路径 | 能匹配/book/addBook,不能匹 配/book/addBook/1,/book |
/book/** | /book下的任意级路径 | 能配/book,/book/addBook,/book/addBook/2,不 能匹配/user/login |
以上只要有某一个请求访问服务器的时候,就会经过拦截器校验一下了,不会出现什么请求都能进的情况了 ,那么你就不好奇它是怎么实现的吗?
二:DispatcherServlet
当我们配置好拦截器后,当我们在访问页面的时候,控制台中会出现下面三段日志,它们都是有关于DispatcherServlet的,那么它是什么呢?
先说结论:DispatcherServlet
是 Spring MVC 框架的核心组件,它充当了前端控制器(Front Controller)的角色,负责接收所有 HTTP 请求,并根据配置将请求分发给对应的处理组件(如 Controller)
它的核心作用有以下三种
统一入口:所有 HTTP 请求首先由 DispatcherServlet
接收,避免每个请求单独处理。
请求分发:根据请求的 URL、参数等信息,将请求路由到对应的 Controller 方法。
协调组件:整合 Spring MVC 的其他组件(如处理器映射、视图解析器等),完成请求处理的全流程
在HTTP请求被我们自己定义的方法处理之前,DispatcherServlet会统一的接收所有的HTTP请求,这不就给拦截器的实现提供了天然的支持了吗?
1:DispatcherServlet初始化
上面控制台中三条关于DispatcherServlet的信息,其实都是它初始化的信息,那么DispatcherServlet是怎么进行初始化的呢?
我们先捋清一下调用关系,
DispatcherServlet继承了FramworkServlet,
FramwtorkServlet继承了HttpServletBean,
HttpServletBean继承了一个HttpServlet(这个继承了HttpServlet就证明了Spring是基于Servlet开发的)
HttpServletBean中有一个init()方法,init方法中调用了一个initServletBean()方法,这个initServletBean()方法由HttpServletBean的子类FramworkServlet来实现,initServletBean()源代码如下图
这里打印的日志就是我们上面图中,初始化DispatcherServlet
时打印的日志,然后会在这个方法中调用initWebApplicationContext()方法,创建ApplicationContext容器。
在 initWebApplicationContext()方法中,又会调用onRefresh方法来初始化SpringMVC容器
onRefresh方法由FramwtorkServlet的子类DispatcherServlet来实现,在onRefresh()方法调用的initStrategies方法中,对九大组件进行初始化
这九大组件我们只说一下4和5
其他的我们不做过多的讨论,感兴趣的兄弟们可以自己去查一下, 到此为止DispatcherServlet的初始化完毕。
大致调用关系如下图所示,如果没看明白,可以按照这个图自己点点源码看一遍
2:DispatcherServlet处理请求
DispatcherServlet是交给doDispatch处理请求,doDispatch的源代码如下,处理请求就这一个方法,比较长,在关键的地方我加了注释方便理解大概流程如下
请求包装 → 2. 处理器匹配 → 3. 缓存验证 → 4. 拦截器前置处理 → 5. 执行处理器 → 6. 视图渲染 → 7. 拦截器后置处理 → 8. 异常/结果处理 → 9. 资源清理
/*** DispatcherServlet 的核心方法,负责处理所有HTTP请求的分发逻辑。*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest = request; // 实际处理的请求对象(可能是multipart请求的包装)HandlerExecutionChain mappedHandler = null; // 请求对应的处理器执行链(包含Handler和Interceptors)boolean multipartRequestParsed = false; // 标记是否为multipart请求WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); // 获取异步管理器try {try {ModelAndView mv = null; // 处理器返回的ModelAndView结果Exception dispatchException = null; // 处理过程中抛出的异常try {// 1. 检查是否为multipart请求(如文件上传),如果是则包装为MultipartHttpServletRequestprocessedRequest = this.checkMultipart(request);multipartRequestParsed = (processedRequest != request); // 标记是否已处理multipart// 2. 根据当前请求找到对应的处理器执行链(HandlerExecutionChain)mappedHandler = this.getHandler(processedRequest);if (mappedHandler == null) {// 找不到处理器,返回404错误this.noHandlerFound(processedRequest, response);return;}// 3. 获取支持该处理器的适配器(HandlerAdapter)HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());// 4. 处理HTTP缓存(Last-Modified头)String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());// 检查资源是否未修改,如果是则直接返回304状态码if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {return;}}// 5. 执行拦截器的preHandle()方法(若任一拦截器返回false,终止请求)if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 6. 实际调用处理器(Controller方法),返回ModelAndViewmv = ha.handle(processedRequest, response, mappedHandler.getHandler());// 7. 如果已开始异步处理,直接返回(响应将由异步线程处理)if (asyncManager.isConcurrentHandlingStarted()) {return;}// 8. 处理默认视图名(当返回的ModelAndView未设置视图名时,根据请求路径生成)this.applyDefaultViewName(processedRequest, mv);// 9. 执行拦截器的postHandle()方法mappedHandler.applyPostHandle(processedRequest, response, mv);} catch (Exception ex) {// 捕获处理器或拦截器抛出的异常dispatchException = ex;} catch (Throwable err) {// 将其他Throwable转换为ServletExceptiondispatchException = new ServletException("Handler dispatch failed: " + err, err);}// 10. 处理分发结果(渲染视图或处理异常)this.processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);} catch (Exception ex) {// 处理过程中发生异常,触发拦截器的afterCompletion()triggerAfterCompletion(processedRequest, response, mappedHandler, ex);} catch (Throwable err) {// 转换为ServletException后触发afterCompletion()triggerAfterCompletion(processedRequest, response, mappedHandler,new ServletException("Handler processing failed: " + err, err));}} finally {// 最终清理逻辑if (asyncManager.isConcurrentHandlingStarted()) {// 异步请求处理中if (mappedHandler != null) {// 调用拦截器的afterConcurrentHandlingStarted()mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}// 标记multipart请求已处理asyncManager.setMultipartRequestParsed(multipartRequestParsed);} else if (multipartRequestParsed || asyncManager.isMultipartRequestParsed()) {// 同步请求且处理过multipart:清理临时资源(如上传的临时文件)this.cleanupMultipart(processedRequest);}}
}
其中在applyPreHandle方法我们点进去就会看到,HandlerInterceptor,这不正是我们在实现拦截器时要实现的接口吗。
三:总结
这篇文章啥也没说,就是说了一些
拦截器的概念,
以及怎么实现和注册一个拦截器
然后就是说一下DispatcherServlet是什么,
以及初始化流程,
以及它在接收请求的时候会干什么