本章简言

上一章讲到关于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. Wine——在Linux上运行Windows软件

    官网:https://www.winehq.org/ 参考: wikipedia 教你使用Wine在Linux上运行Windows软件 如何安装和使用Wine,以便在Linux上运行Windows应用 ...

  2. ReactJS入门(一)—— 初步认识React

    React刚开始红的时候,由于对其不甚了解,觉得JSX的写法略非主流,故一直没打算将其应用在项目上,随着身边大神们的科普,才后知后觉是个好东西. 好在哪里呢?个人拙见,有俩点: 1. 虚拟DOM —— ...

  3. 剑指Offer面试题:8.斐波那契数列

    一.题目:斐波那契数列 题目:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项.斐波那契数列的定义如下: 二.效率很低的解法 很多C/C++/C#/Java语言教科书在讲述递归函数的时 ...

  4. 剑指Offer面试题:17.树的子结构

    一.题目:树的子结构 题目:输入两棵二叉树A和B,判断B是不是A的子结构.例如下图中的两棵二叉树,由于A中有一部分子树的结构和B是一样的,因此B是A的子结构. 该二叉树的节点定义如下,这里使用C#语言 ...

  5. [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git

    [.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...

  6. 《CLR.via.C#第三版》第二部分第12章节 泛型 读书笔记(六)

    终于讲到泛型了.当初看到这个书名,最想看的就是作者对泛型,委托,反射这些概念的理解.很多人对泛型的理解停留在泛型集合上,刚开始我也是,随着项目越做越多,对待泛型的认识也越来越深刻. 泛型的概念:泛型是 ...

  7. Uiautomator 2.0之Until类学习小记

    1. 状态条件-Uiobject2Condtion 1.1 一个UiObject2Condition代表UiObject2满足某个条件的特定状态,主要用于获取到组件释放处于某种状态. 1.2 简单示例 ...

  8. iOS点击状态栏回到顶部底层实现原理

    在iOS开发中,苹果其实已经帮你实现了点击状态栏回到顶部这个功能,但我们在开发中会遇到点击不能回到顶部.其实这都和 ScrollView中的一个属性scrollsToTop有关,我们先看看苹果关于这个 ...

  9. OpenCascade Eigenvalues and Eigenvectors of Square Matrix

    OpenCascade Eigenvalues and Eigenvectors of Square Matrix eryar@163.com Abstract. OpenCascade use th ...

  10. 【转】批量复制操作(SqlBulkCopy)的出错处理:事务提交、回滚

    原文地址:http://blog.csdn.net/westsource/article/details/6658109 默认情况下,批量复制操作作为独立的操作执行. 批量复制操作以非事务性方式发生, ...