本章简言

上一章讲到关于action代理类的工作。即是如何去找对应的action配置信息,并执行action类的实例。而这一章笔者将讲到在执行action需要用到的拦截器。为什么要讲拦截器呢?可以这样子讲吧。拦截器的应用是sturts2核心的亮点之一。如果不明白拦截器是什么的话,那么你相当于没有学习过struts2。笔者本来想直接讲这一章的知识点。可是又怕读者可能对拦截器没有一个概念化的理解。为什么这么讲呢?struts2在设计拦截器这一个部分的内容。在笔者看来事实是以AOP为核心思想来设计的。所以就是必须先理解一下AOP思想到底是什么东东。只有这样子才能更好的去理解struts2的拦截器。

AOP思想

AOP思想的全名为Aspect Oriented Programming。即是面向切面编程。相信读者者听过OOP(Object-Oriented Programing,面向对象编程)。笔者也认AOP是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。为什么笔者这边用“也”这个字呢?笔者不是一个喜欢吹牛的人。会就是会,不会就是不会。网络上有很多关于AOP思想的资料,笔者就是通过这些资料学习的,也认同AOP是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。所以笔者这里也只是简章的讲解一下AOP思想。希望读者见谅。

什么叫做面向切面编程呢?如果用专业的角度来讲切面叫作Aspect。struts2拦截器相关的类就是切面(Aspect)。完了。什么东东啊!没事。笔者就土话来讲吧。对于传统面向对象编程来讲,执行业务代码一般都会在方法里面。这一点大家都知道。相信大家也知道在执行相关业务代码之前也会执行对应的验证代码。作为一个软件设计师在设计整个构架的时候,一定会理解业务并把业务划分为几个独立的模块。而每模块都一定会有相关的验证代码。如什么数据不能为空等相关验证代码。甚至有一些软件系统希望有对应的日志跟踪的时候,对应的日志代码也要写入进去。如图下。

可以说图上是从纵向角度来看。把业务划分出多个独立业务模块,每一个业务模块都有相应的数据验证和日志记录。笔者认为AOP思想则是要横向角度来看。什么说呢?笔者在上面的图片加入横向角度之后会是一个什么样子。如图下

当笔者把横向角度加入之后,就是会发现把业务模块相关代码和其他代码进行了分离独立起来了。如上面图片中的图1就是加入之后的状态了。那么笔者是什么知道要这样子分呢?主要是关注点。即是横向关注点。笔者希望把业务代码和其他代码进行分离独立起来。这就是笔者的关注点(这里的业务代码是总业务代码。即是把所有的业务代码看作一个整体)。而图片中的图2便是最后的结果。为什么三块验证代码最后变成一个块呢?举个简单的例子吧。笔者相信有工作经验的程序员都会经验过非空的代码或是非空并没有特殊的字符等相关的验证吧。难道你不觉得这块的代码逻辑是可以共通的吗?当然笔者也想过这样子的问题——新一个专门用来实现验证功能的类不就行了吗?没有错。是可以的。可是AOP思想的核心笔者认为不是在这里。他的目标是让业务模块去选择自己对应的验证代码。验证代码就是切面(Aspect)。什么意思呢?当业务模块相关代码和其他代码进行了分离独立的时候。其他代码这个部分事实上是可以进行重组和切分。比如:日志相关的代码变成一个日志切面(只是一个类)。性能相关代码变成一个性能切面。各个切面之间是相互独立的。最后就是变成了根据不同的业务模块去选择不同需要的切面。图片上的图2就是最后笔者得出来的结果。当然笔者也不敢说自己是对的。个人看法而以。

对于上面AOP理解也是笔者自己的个人看法而以。笔者也不敢说是对的。每一个人的理解是不一样子的。也不一定见得读者的理解是错的。显然笔者认为AOP真的很不错。纵向把业务划分出模块。横向把代码划出模块。

 拦截器的执行机制

struts2的拦截器笔者认为就是AOP思想的一种体现。在进入action类实例之前必须先执行相关拦截器。即是拦截器相当于AOP思想里面的切面。把用户action类和拦截器分离独立,就像笔者上面讲的横向关注点一样子。因为大部分的用户action类是跟业务有关系的。所以strust2里面有很多拦截器。不同的action类可能会选择不同的拦截器。当然也有一些默认必须有的拦截器。从《Struts2 源码分析——Action代理类的工作》章节里面我们知道执行action请求是在DefaultActionInvocation类的invoke方法。可以这样子讲吧。一切都从这个方法开始的。如下

DefaultActionInvocation类:

 public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey); if (executed) {
throw new IllegalStateException("Action has already executed");
} if (interceptors.hasNext()) {//获得一个拦截器
final InterceptorMapping interceptor = interceptors.next();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);//执行拦截器
} finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
} // this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
if (preResultListeners != null) {
LOG.trace("Executing PreResultListeners for result [{}]", result); for (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener; String _profileKey = "preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
} // now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
} executed = true;
} return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}

上面的红色的代码是这个方法的核心点之一。也是实现AOP思想的代码亮点。让我们看一下红色代码做什么?判断interceptors是否有拦截器。如果没有就直接执行invokeActionOnly方法。即是执行action类实例对应的方法。如果有就获得拦截器并执行拦截器(执行intercept方法)。好了。关键点就在这个执行拦截器身上。即是执行intercept方法。intercept方法有一个参数就是DefaultActionInvocation类的接口。这个参数让struts2的AOP思想能够进行。为什么这样子讲呢?不清楚读者有没有想过。为什么这边判断拦截器是用if而不是用for 或是 while呢?必竟拦截器不只一个。我们都清楚AOP的目标就是让业务模块选择对应的切面。那么就有可能存在多个拦截器。这也是为什么亮点的原因了。看一下拦截器的代码就知道了。如下

LoggingInterceptor类:

 public String intercept(ActionInvocation invocation) throws Exception {
logMessage(invocation, START_MESSAGE);
String result = invocation.invoke();
logMessage(invocation, FINISH_MESSAGE);
return result;
}

上面的源码是笔者从多个拦截器中选择一个比较简单的来看。不清楚你们明白了没有。红色的代码已经很明确说明了一件事情。拦截器开始的时候,执行相关的拦截器逻辑,然后又重新调用DefaultActionInvocation类的invoke方法。从而获得下一个拦截器。就是这样子下一个拦截器又开始执行自己的intercept方法。做了相关的拦截器逻辑之后。又一次重新调用DefaultActionInvocation类的invoke方法。又做了相似的工作。只到没有了拦截器,执行用户action类实例的方法并返回结果。有了结果之后,就开始续继执行当前上一个拦截器的后半部分代码。只到返回到最开始的拦截器执行后半部分的代码。如果硬要说一个相似的专业词语的话。笔者会想到方法叠带。

笔者心里面对这一部分的做法一直很喜欢。当然也不少人不会认同笔者的观念。可以看得出来AOP思想把个个切面和业务模块处理的非常好。切面和业务模块又是独立的互不影响。同时可以让开发人员更加关注对应的业务逻辑。

注意:学习这一部分最好结合《Struts2 源码分析——核心机制》的核心机制的图片。这样子会更好理解。

本章总结

本章主要是讲到关于拦截器的运行机制。知道了struts2是如果进行处理拦截器和action类实例之间关系。同时也了解了相关AOP思想。

Struts2 源码分析——拦截器的机制的更多相关文章

  1. Struts2 源码分析-----拦截器源码解析 --- ParametersInterceptor

    ParametersInterceptor拦截器其主要功能是把ActionContext中的请求参数设置到ValueStack中,如果栈顶是当前Action则把请求参数设置到了Action中,如果栈顶 ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. springMVC源码分析--拦截器HandlerExecutionChain(三)

    上一篇博客springMVC源码分析--HandlerInterceptor拦截器调用过程(二)中我们介绍了HandlerInterceptor的执行调用地方,最终HandlerInterceptor ...

  4. Nacos 2.0源码分析-拦截器机制

    温馨提示: 本文内容基于个人学习Nacos 2.0.1版本代码总结而来,因个人理解差异,不保证完全正确.如有理解错误之处欢迎各位拍砖指正,相互学习:转载请注明出处. Nacos服务端在处理健康检查和心 ...

  5. Struts2 源码分析——DefaultActionInvocation类的执行action

    本章简言 上一章讲到关于拦截器的机制的知识点,让我们对拦截器有了一定的认识.我们也清楚的知道在执行用户action类实例之前,struts2会先去执行当前action类对应的拦截器.而关于在哪里执行a ...

  6. Struts2 源码分析——Action代理类的工作

    章节简言 上一章笔者讲到关于如何加载配置文件里面的package元素节点信息.相信读者到这里心里面对struts2在启动的时候加载相关的信息有了一定的了解和认识.而本章将讲到关于struts2启动成功 ...

  7. Struts2 源码分析——配置管理之PackageProvider接口

    本章简言 上一章讲到关于ContainerProvider的知识.让我们知道struts2是如何注册相关的数据.也知道如何加载相关的配置信息.本章笔者将讲到如何加载配置文件里面的package元素节点 ...

  8. Struts2 源码分析——调结者(Dispatcher)之执行action

    章节简言 上一章笔者写关于Dispatcher类如何处理接受来的request请求.当然读者们也知道他并非正真的执行action操作.他只是在执行action操作之前的准备工作.那么谁才是正真的执行a ...

  9. Struts2 源码分析——过滤器(Filter)

    章节简言 上一章笔者试着建一个Hello world的例子.是一个空白的struts2例子.明白了运行struts2至少需要用到哪一些Jar包.而这一章笔者将根据前面章节(Struts2 源码分析—— ...

随机推荐

  1. SQL初步知识点

    varchar(n) 长度为 n 个字节的可变长度且非 Unicode 的字符数据.n 必须是一个介于 1 和 8,000 之间的数值.存储大小为输入数据的字节的实际长度,而不是 n 个字节. nva ...

  2. ASP.Net MVC3 图片上传详解(form.js,bootstrap)

    图片上传的插件很多,但很多时候还是不能切合我们的需求,我这里给大家分享个我用一个form,file实现上传四张图片的小demo.完全是用jquery前后交互,没有用插件. 最终效果图如下: 玩过花田人 ...

  3. .NET中STAThread和MTAThread

    本文讨论在.NET中使用进程内COM组件时的公寓模型,以一个示例直观演示STAThread和MTAThread的作用和区别. 1. COM中的公寓 1.1 基本规则 公寓是COM组件的运行环境,日常生 ...

  4. The Hacker's Guide To Python 单元测试

    The Hacker's Guide To Python 单元测试 基本方式 python中提供了非常简单的单元测试方式,利用nose包中的nosetests命令可以实现简单的批量测试. 安装nose ...

  5. 通过圆形载入View了解自定义View

    这是自定义View的第一篇文章,通过制作简单的自定义View来了解自定义View的流程. 自定义View是Android学习和开发中必不可少的一部分.通过自定义View我们可以制作丰富绚丽的控件,自定 ...

  6. Oracle编程脚本记录

    --命令窗口查询 exec 存储名.包名.视图; select 函数名 from dual; create or replace procedure PR_test is begin --存储过程的代 ...

  7. Viewport---响应式 Web 设计----在路上(13)

    什么是 Viewport? viewport 是用户网页的可视区域. viewport 翻译为中文可以叫做"视区". 手机浏览器是把页面放在一个虚拟的"窗口"( ...

  8. c#设计模式-适配器模式

    一. 适配器(Adapter)模式 适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作. 名称由来 这很像变压器(Adapter),变压 ...

  9. highchart导出图片

    http://www.cnblogs.com/jasondan/p/3504120.html 项目中需求导出报表为图片存到Excel中去,或供其它页面调用. 开始存到截屏,但由于用户电脑分辨率不一样, ...

  10. Angular $watch

    如果想在某个属性发生变化的时候执行某些操作,那么scope.$watch是最佳选择 https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$w ...