拦截器 | 学习笔记

简介: 快速学习拦截器,介绍了拦截器系统机制, 以及在实际应用过程中如何使用。

开发者学堂课程【Java Web开发系列课程 - Struts2框架入门:拦截器】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/537/detail/7315


拦截器


内容介绍:

一、拦截器的定义

二、拦截器的作用

三、拦截器与过滤器的区别

四、拦截器的执行流程

五、拦截器的实现

 

一、拦截器的定义

1.STRUTS2框架内部流程图分析

拦截器与过滤器十分相似。过滤器是指过滤所有操作,在每次请求一个 Jsp 或 Servlet 之前,均会首先进入一次过滤器,执行结束后,然后二次通过过滤器返回。

拦截器与此极为相似,在执行一个 Action 之前,首先会经过一系列的拦截器,Action执行结束后,再次经过一系列拦截器返回。为了方便理解,我们结合 STRUTS2框架内部流程图进行分析。

其中 Interceptor 1、Interceptor 2、Interceptor 3即为拦截器,从该框架内部流程图可知,在执行 Action 之前,首先经过这一系列拦截器,执行结束后,再次通过这一系列拦截器。

STRUTS2框架内部流程图如下:

image.png

2. 拦截器与拦截器栈

Interceptor 拦截器类似于我们前面学过的过滤器,是可以在 action 执行前后执行的代码,是我们在做 web 开发时经常用到的技术,如权限控制、日志等。我们也可以将多个 Interceptor 连接在一起组成 Interceptor 栈。

Struts 拦截器,每个拦截器只有一个对象实例,因此,采用单例模式(多个过滤器组成过滤器链,而过滤器也属于单例模式),所有引用这个拦截器的 Action 都共享这一拦截器实例,因此,在拦截器中如果使用类变量,要注意同步问题,也就是要注意线程安全问题。

(1)拦截器

拦截器是在访问某个方法、字段之前或之后实施拦截;

拦截器是 AOP 的一种实现(AOP 在 Spring 中会进行详细介绍,可简单理解为其是通过动态代理实现的);

(2)拦截器栈

拦截器栈就是将拦截器按一定的顺序联结成一条链;

在访问拦截器的方法或字段时,拦截器链中的拦截器就会按照之前定义的顺序依次被调用;

(3)实现原理

Struts2拦截器的实现原理相对简单,当请求 Struts2的 action 时,Struts2会查找配置文件,并根据配置实例化相应拦截器对象,然后串成一个列表,最后一个一个地调用列表中的拦截器。

3. 总结

(1)拦截器

拦截器与过滤器很相似,在 action 执行的前后执行,Struts2的核心功能都是通过拦截器实现的。

前几节我们学习过 Struts2的设值、Struts2的类型转化、Struts2的结构、Struts2的验证,以及之前学习过的 model driven(模型驱动),和之后要学习到的文件的上传与下载,这些功能都要依靠拦截器来实现。

也可以自行编写拦截器以增强 Struts2的功能。这也体现了 Struts2深受开发者喜爱的原因,即虽然 Struts2的功能很多,但是我们可以自行去配置其功能,如不同的应用其应用方向不同,所需的配置也不同,我们根据个人需求,增加或去除相应的功能,以在一定程度上提高执行效率。

(2)拦截器栈

由多个拦截器组成组成。从Struts2的工作方式出发,Struts2首先读取配置文件 struts-default.xml,其中定义了很多拦截器,默认情况下使用的是名为 default Stack的拦截器栈,其中包含18个拦截器,如下:

image.png

且其中部分拦截器不会被使用,如若不使用 model driven,拦截器 ModelDriven 就不会被使用,再如不进行文件上传操作,那么拦截器 fileUpload 就不会被使用,再如不使用验证功能,则拦截器 validaction 就不会被使用,再如不使用类型转化功能,则拦截器 conversionError 就不会被使用,等等。

因此,在了解每个拦截器的功能之后,就可以挑选自己必须使用的拦截器进行配置。而各个拦截器组成了一个栈,即在执行 action 之前会按照顺序依次执行拦截器代码,再执行 action,因此,我们可以自行对其进行装配。

 

二、拦截器的作用

之前我们学过,公共的数据可以在 filter 中进行处理,对于 action 的一些公共处理代码可以放到拦截器中进行实行,代码可以重复利用,可以相应地提高效率。

如:权限控制、日志等公共的内容都可以放到拦截器中进行。前文中提到“拦截器是AOP 的一种实现”,其实 AOP 也会进行公共代码处理。

 

三、拦截器与过滤器的区别

两者的概念十分地相似,但也有一些不同之处:

1.两者的过滤内容不同。

过滤器属于 Web 容器,可以过滤一切请求(包括 action、servlet、jsp、html 等),而拦截器隶属于 Struts2框架,只能拦截 action,无法拦截对 jsp 的请求。

2. 两者的实现方式不同。

过滤器内部采用函数回调来实现,而拦截器采用动态代理来实现。

 

四、拦截器的执行流程

拦截器与过滤器有许多共同之处。在执行action之前,会依次经过多个拦截器,这多个拦截器之间的执行是通过多个连接是通过责任链的设计模式来实现的。

拦截器的执行流程图见下页:

根据图示可知,请求进入后,首先执行 filter,接下来依次执行第1个、第2个、…、第N 个拦截器,再执行 action,再执行 result 结果值,值再通过这N个拦截器,返回。整个过程采用的是责任链的方式进行的。

image.png

这个流程图十分重要,要求必须掌握对于设计模式的内容大家在课后可以自行了解,这部分内容也又很大的实际意义,但是一般在编写管理系统时,对设计模式的涉及量较少。

               

五、拦截器的实现步骤

1. 编写拦截器

包括两种方式,即实现 Interceptor 接口或继承 Abstract Interceptor 类。

2. 在 struts.xml 中配置拦截器

3. 在 action 中引用拦截器

以下为实现过程案例:

在已有的文件夹中 Copy Project,并命名 Project name 为13struts2_interceptor。

关闭原文件,点击新建的 Project,在其“type filter text”中点击“Web”,将其Web Context-root 改为/interceptor,将 src -- cn.sxt.action 中的文件删除。然后点击上方操作栏中的“New Java Class”,在 Name 中输入 HelloAction。并对弹出页面进行编辑:

package cn.sxt.action;

import com.opensymphony.xwork2.Action;

public class HelloAction {

public String execute () {

System.out.println("execute");

return Action.SUCCESS ;  

}

左侧目录中点击 struts.xml 进行配置(以下均展示部分配置文件),

image.png

编辑图中红色框线中的内容,对 action 进行配置,即改为如下内容:<action name=”hello”class=”cn.sxt.action.HelloAction”>

<result>/index.jsp</result>

<action>

左侧目录中点击 WebRoot,将其中的 register.jsp 删除。

左侧目录中点击 src,再在右侧点击 New Java Package,新建一个包,并命名为cn.sxt.interceptor。再点击 New Java Class 建立拦截器,此处我们尝试建立一个时间拦截器 TimeInterceptor,用来计算 action 的执行时间

但其实在实际应用中,时间拦截器并没有太大的实际意义,且时间拦截器在 Struts2中也执行了相关操作。

点击 Package Explorer,寻找 interceptor 中的 TimerInterceptor 查询源码,然后在右侧点击 TimerInterceptor.class 中的 Attach Source 进行关联,其 Location path 为 E:/resouces/150526/stru

ts2/xwork-assembly-2.1.6-all.zip.再点击 External File。

可以查询得知其操作,即写入日志等,如下:

 image.png

说明该拦截器主要在 invocation 中执行 invokeUnderTiming,双击invokeUnderTiming 可以发现其内包含的计算时间的操作。

其中包含了起始时间、执行 result 与重视时间。

以下为具体的时间拦截器配置过程:

(1)实现 Interceptor 接口

public class TimeInterceptor implements Interceptor {

//注意此处的Interceptor的包一定要用引用正确,

com.opensymphony.xwork2.interceptor

//且其中包括很多方法,核心的方法是 intercept

//与过滤器相近,过滤器的核心方法是 doFilter

//通过此处 intercept 方法可以得出 ActionInvocation(见 Struts2拦截器示意图中拦截器左侧的框图)

}

}

(2)继承 Abstract Interceptor 类

<!--编写拦截器-->

public class TimeInterceptor extends AbstractInterceptor {

//双击AbstractInterceptor发现其并未进行任何操作

//此处提供两种方法的原因:

“继承”可以节省代码编写;

“类”仅支持单继承,若一个软件需要其他的类实现某些功能,只能通过实现接口的方式进行,故而不能只提供类继承这一种方式。

也就是设计模式中的适配器模式,这种模式在 Spring 中使用较多,在做窗体应用程序时,有很多事件与实现。虽然并未系统学习过设计模式的内容,但其实我们在很多情况下都在使用它。

本节多次使用了设计模式,在 structs、interceptor、拦截器中就使用了设计模式,如拦截器的实现是一种 AOP 的方式,即代理设计模式,多个拦截器一同工作时,每个拦截器拦截后再进入另一个拦截器的过程是一个责任链的设计模式,包括多个设计模式,即组合设计模式。

@Override

public string intercept (ActionInvocation invocation) throws

Exception

long start =System.currentTimeMillis();

//long 类型,记录开始时间

//执行下一个拦截器,当拦截器执行结束后,执行 action

String result =invocation.invoke();

//返回结果类型为 string,且方法为 invocation.invoke

long end =System.currentTimeMillis();

//long 类型,记录当前执行结束的时间

System.out.println("执行 action 所用时间为:"+(end-start)

+"ms");

return result;

}

}

<!--配置拦截器-->

其实在刚刚的 struts-default.xml 中的<interceptors>中就配置了

很多拦截器。

具体配置方法如下:

在 struts.xml 中的 package 中进行配置:

<package name=”default”extends=”struts-default”name

space=”/”>

<interceptors>

//“interceptors”中的“s”可理解为英语中的复数,故指可配置多

个拦截器

<interceptor name=”time”class=”cn.sxt.interceptor.

TimeInterceptor”>

</interceptors>

<!--引用拦截器-->

<action name=”hello”class=”cn.sxt.interceptor.HelloAc

tion”>

<result>/index.jsp</result>

<interceptor-ref name=”time”/>

</action>

<package>

</struts>

运行代码,点击 Console 查看结果。

然后在地址为 http://localhost:8080/interceptor/hollo.action 的网页进行搜索,显示结果为“This is my JSP page”,返回后 console 界面显示“执行该 Action 所用时间为:1262ms”。

该次执行时间较长,再次执行,显示“执行该 Action 所用时间为:3ms”,而此时仅执行了上面编写的这一个拦截器。若电脑的 CPU 足够迅速,甚至会显示0ms。

相关文章
|
弹性计算 应用服务中间件
2024年阿里云便宜服务器优惠合集:61元、99元、199元、165元、30元、26元
2024年阿里云便宜服务器优惠合集:61元、99元、199元、165元、30元、26元
4286 2
神州数码无线AP关闭Guestwork网络
神州数码无线AP关闭Guestwork网络
259 0
|
关系型数据库 MySQL
MySQL查看连接数和进程信息
这篇文章介绍了如何在MySQL中查看连接数和进程信息,包括当前打开的连接数量、历史成功建立连接的次数、连接错误次数、连接超时设置,以及如何查看和终止正在执行的连接进程。
1750 10
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
596 3
|
人工智能 Cloud Native 架构师
CNCF 宣布 Dapr 毕业
Dapr 是一个可移植的分布式应用运行时,提供集成 API,帮助开发者构建可靠和安全的分布式应用,提升生产力 20-40%。Dapr 于 2019 年由微软发布,并于 2021 年 11 月正式加入 CNCF。截至 2024 年 11 月 13 日,Dapr 已正式从 CNCF 毕业。它支持多种云原生技术,广泛应用于 Grafana、FICO、HDFC 银行等企业。
359 2
|
缓存 网络协议 Ubuntu
netperf网卡测速ubuntu linux 环境下测硬件网卡速度
netperf网卡测速ubuntu linux 环境下测硬件网卡速度
493 1
|
NoSQL Go API
Redis Hset使用中的小坑
文章讨论了在使用Redis的HSet命令时的一个常见误区,即错误地根据命令的返回值(true/false)来判断数据是否成功插入,而实际上应通过检查错误对象(err)来判断操作是否成功。
188 0
|
人工智能 监控 Cloud Native
宜泊科技与阿里云联合打造基于云原生架构的停车系统解决方案,加速智慧停车产业数字化发展
近日,宜泊信息科技有限公司与阿里云联合发布基于云原生架构的智能停车系统解决方案,为停车场的管理者和使用者提供平台化的运营服务,围绕“智慧停车场景”与“云原生技术”的深度融合,为广大车主提供更加智慧、高效的出行服务。
2097 88
宜泊科技与阿里云联合打造基于云原生架构的停车系统解决方案,加速智慧停车产业数字化发展
|
存储 分布式计算 运维
大白话讲讲分布式存储系统的架构设计以及容错架构
分布式存储系统的架构设计旨在实现数据的分布式存储和负载均衡,通常采用数据分片和多节点存储的方式。容错架构则是为了提高系统的鲁棒性和可用性。在分布式存储系统中,容错架构常采用数据的冗余备份来应对节点故障或网络异常问题。通过复制数据到多个节点,即使某个节点发生故障,系统仍可以提供数据的可靠访问。此外,容错架构还包括故障检测和自动故障转移机制,用于及时检测节点故障,并将故障节点的任务转移给其他正常节点。这样可以保证系统在故障情况下仍能正常运行,并提供不间断的数据访问。通过合理的架构设计和有效的容错机制,分布式存储系统可以实现高可用性和数据可靠性,满足大规模数据存储和访问的需求。
2152 0
大白话讲讲分布式存储系统的架构设计以及容错架构