Struts 学习记录
- Struts2项目的目录组织结构
- apps——存放了所有的Struts2的示例项目
- docs——存放了所有Struts2与XWork的文档
- lib——存放了所有Struts2相关的JAR文件以及Struts2所依赖的JAR文件
- src——存放了所有Struts2的源码,以Maven所指定的项目结构目录存放
- 构成对象的基本要素
- 签名(Signature)——对象核心语义概括
- 属性(Property)——对象内部特征和状态的描述
- 方法(Method)——对象行为特征的描述
- 对象构成的三种模式
- 属性-行为模式:同时拥有属性定义和方法定义,大多数对象都是这种模式
- 属性模式:对象只拥有属性定义,辅之以响应的setter和gettet方法。JavaBeann、VO、DTO
- PO(Persistent Object)——持久化对象
- BO(Business Object)——业务对象
- VO(Value Object)——值对象
- DTO(Data Transfer Object)——数据传输对象
- FormBean——页面对象
- 行为模式:主体是一系列的方法定义,而不含有具体的属性定义,也是一些无状态的写作对象。Servlet、接口实现类。
- 方法签名(Signature)——行为动作的逻辑语义概括
- 参数(Parameter)——行为动作的逻辑请求输入
- 返回值(Return)——行为动作的处理响应结果输出
- 对象关系模型
- 从属关系——一个对象在逻辑语义上隶属于另外一个对象
- 归属:是由外部世界的逻辑关系映射到编程元素之上而带来的。在面向对象的编程语言中,主要以对象之间互相引用的形式存在
- 继承:多态的实现。
- 协作关系——对象之间通过写作来共同表达一个逻辑语义
- 一个对象在绝大多数情况下都不是孤立存在的,他总是需要通过与其他对象的写作来完成其自身的业务逻辑
- 当对象运作在“属性-行为”模式上时,我们能够最大程度地应用各种设计模式,单一的属性模式和行为模式对象,在设计模式的应用上很难施展拳脚。
- 当对象运作在“属性-行为”模式上时,我们能够最大程度地发挥对象之间的协作能力,对象之间的通过行为动作的逻辑调用是对象协作的本质内容。
- 对象建模要本着提高程序的可读性、可维护性、可扩展性为基本原则
- 框架的本质
- 框架知识一个JAR包而已,其本质是对JDK的功能扩展。
- 是一组程序的集合,包含了一系列的最佳实践,作用是解决某个领域的问题。
- 最佳实践:无数程序员在经过了无数次的尝试后,总结出来的处理特定问题的特定方法。它能够极大的解放生产力。
- 永远不要生搬硬套任何最佳实践,结合具体的业务需求因地制宜的解决问题。
- 始终保证程序的可读性、可维护性和可扩展性。
- 简单是美:消除重复,化繁入简。
- 尽可能使用面向对象的观点进行编程
- 减少依赖,消除耦合:加入的外部JAR越多,就意味着程序对外部环境的依赖越高
- 分层开发模式
- 表示层(Presentation Layer)——负责处理与界面交互相关的功能
- 业务层(Business Layer)——负责复杂的业务逻辑计算和判断
- 持久层(Persistent Layer)——负责将业务逻辑数据进行持久化储存
- MVC模式
- M(Model)——数据模型:程序的核心载体,贯穿程序运行的核心内容
- V(View)——视图展现:Jsp
- C(Control)——控制器:
- Struts2的宏观视图:
- 第一条主线——Struts2的初始化:init方法驱动执行
- 尽在web应用启动时执行一次
- init方法的执行失败叫导致整个Web应用启动失败
- 主线初始化的两大内容
- 框架元素的初始化工作:包含了对框架内部的许多内置对象的创建和缓存
- 控制框架运行的必要条件:运行期检查
- 第二条主线——Struts2处理Http请求:doFilter方法驱动执行
- 第一阶段——Http请求处理:依赖Web容器,并时时刻刻将与Web容器打交道为主要工作
- 第二阶段——XWork执行业务逻辑:程序执行的控制权被移交给了XWork,这一阶段的程序代码不依赖于Web容器,完全由XWork框架驱动整个执行的过程
- 设计理念的解耦
- 从代码上进行物理解耦
- 讲逻辑分配到不同的执行阶段
- 严格意义上来说Struts2,实际上是由两个不同的框架所组成。
- 将Web容器与MVC实现分离,是Struts2区别于其他Web框架的最重要的特征。
- Struts2的微观元素:
- 第一条主线——Struts2的初始化
- 第二条主线——第一阶段Http请求的预处理
- 第二条主线——第二阶段XWork执行业务逻辑
- Struts2配置详解
- 配置概览
Struts2中的设计模式
- ThreadLocal模式
- Servlet对象的运行特征
- Servlet是一个无状态的单例对象(Singleton)
- Servlet在不同的线程池中却共享同一个变量
- 传统的基于Servlet的开发模式中,Servlet对象内部的实例变量不是线程安全的
- ThreadLocalMap变量
- 属于线程的内部属性,不同的线程拥有完全不同的ThreadLocalMap变量
- 线程中的ThreadLocalMap变量的值是在ThreadLocal对象进行set或者隔天操作时创建的
- 在创建ThreadLocalMap之前会检查当前线程中的ThreadLocalMap
- 使用当前线程的ThreadLocalMap的关键在于是用当前的ThreadLocal实例作为Key进行储存
- 纵向隔离——线程与线程之间的数据访问隔离。
- 横向隔离——同一个线程中,不同的ThreadLocal实例操作的对象之间相互隔离。
- ThreadLocal与synchronized关键字
- ThreadLocal是一个Java类,通过对当前线程中的局部变量的操作来解决不同线程的变量访问的冲突问题,每个线程都有其副本(空间换时间)
- synchronized是一个保留字,依靠JVM的锁机制来实现临界区的函数或者变量在访问中的原子性,锁机制的变量时多个线程共享的
- 数据共享还是数据传递
- ThreadLocal模式的核心在于实现一个共享环境。
- 随处存在的共享环境使所有开发层次耦合在一起,无法独立测试
- 数据传递应该通过接口函数的签名显示声明,才能够从接口声明中表达接口所表达的真正含义
- ThreadLocal模式主要步骤
- 建立一个类,并在其中封装一个静态的ThreadLocal变量,使其成为一个共享数据环境
- 在类中实现访问静态ThreadLocal变量的静态方法(设值取值)
- 结论
- 可以使数据在不同的编程层次得到有效共享
- 可以对执行逻辑与执行数据进行有效解耦
- 继承的特性
- 现有对象行为的覆盖——通过覆写(Override)父类中的已有方法完成
- 添加新的行为职责——通过在子类中添加新的方法完成
- 装饰模式的目的与特性
- 目的——继续拧对象行为职责扩展
- 特性——动态(扩展特性在运行期自动获得)
- 装饰模式所涉及的角色
- 原始接口(Component)——定义了一个接口方法。
- 默认目标实现类(TargetComponent)——对于原始接口的默认实现方式。
- 装饰实现类(ComponentDecorator)——同样实现了原始接口,内部封装了一个原始接口的对象实例TargetComponent
- 具体装饰实现类(ComponentDecoratorA、ComponentDecoratorB)——继承自装饰类ComponentDecorator,我们可以在operation方法中调用原始接口的对象实例TargetComponent获取默认的目标实现类的行为方式并加以扩展
- 装饰的两点要义
- 默认目标实现(TargetComponent)类封装于具体的装饰实现类(ComponentDecorator)或者其子类的内部,从而形成对象之间的引用关系
- 具体装饰实现类(ComponentDecorator)同样实现了原始接口(Component)这样一来我们从外部调用者的角度来看待原始接口(Component)的默认目标实现(TargetComponent),就好像它被装饰了一番,以装饰实现类(ComponentDecorator)的形式呈现出来。
- 比继承更加灵活的应用场景
- 适合对默认目标实现(TargetComponent)中的多个接口进行排列组合调度
- 适合对默认目标实现(TargetComponent)进行选择性的扩展
- 适合默认目标实现(TargetComponent)未知或者不易扩展的情况
- 主要角色
- 环境(Context)角色——持有一个Strategy类的引用,决定调用哪种Strategy角色完成业务逻辑
- 抽象策略(Strategy)角色——它是所有策略算法的核心归纳。
- 具体策略(ConcreteStrategy)角色——封装了具体的侧罗算法或行为。
- 最终目的是吧使用算法的责任(环境)和算法的实现进行解耦。
- 策略模式的核心
- 抽象,所有核心算法的行为接口的抽象统一
- 选择,指的是在运行期不同的算法实现之间进行选择
- 优点
- 提供了管理一组算法族的方法——通过接口和多个算法实现之间的的契约接口来完成业务场景。
- 提供了可以替换通过继承进行对象行为扩展的方法——使用公共抽象接口的不同实现类而并非一个抽象类的集成连完成的行为的扩展。
- 提供了将算法的调动责任预算法逻辑进行解耦的方法——通过角色转移将调用环境角色与算法抽象之间分开,形成引用关系。
- 构造模式的四个角色
- 客户端(Client)——调用具体的构造器完成对象构建。这个角色只负责构造器的创建和选择,对于产品的具体信息并不知晓。
- 抽象构造器(Builder)——这是一个抽象的构造器角色,有一组接口方法扮演。
- 具体构造器(ProductBuilder)——具体的构造器实现。其中封装了一个目标的对象实例。
- 产品(Product)——整个构造模式的产物。
- 构造器的三个步骤
- 构造器(Builder)首先会创建一个目标的实例加以缓存
- 构造器依次调用构建目标对象特性的行为方法,其目的在于为目标对象搜集并设置其内在特性
- 构造器最终调用其核心方法build完成目标对象的创建并返回给客户端。
- 责任链模式(Chain Of Responsibility)模式
- 主要角色
- 请求(Request)角色——请求角色指的是击鼓传花中的花束
- 执行对象(ConcreteHandler)角色——这里的执行对象角色就是之所有的参与游戏的人
- 重要的特性
- 请求在这个链上传递,知道脸上的某一个执行对象决定处理此请求
- 链的存在状态有对象的依赖协作关系决定。因此链可以是直线,环,树。
- 负责调用责任链的客户端对于到底是哪一个执行对象来进行请求的处理并不知晓
容器
- 对象生命周期管理两个不同的方面
- 在程序的运行期,对象实例的创建和引用机制
- 对象与其关联对象的依赖关系的处理机制
- 控制反转(Inverse of Control)
- 以此为基础创造了一个更加熟悉的概念,依赖注入(Dependency)
- 对象通过其依赖对象的帮助,完成其业务逻辑的过程。
- 每个对象不得不依赖于其写作的对象(也就是它所依赖的对象)的引用和构建。
- 结论:每个对象自身对于逻辑的执行能力,被其所依赖的对象反向控制了,这也就是控制反转的本质含义。
- 容器(Container)的引入
- 交给程序去管理如何获取以来对象的弊端
- 对象将频繁创建,效率大大降低。
- 对象创建的逻辑与业务逻辑代码高度耦合,使得一个对象的逻辑关注度大大分散
- 程序的执行效率大大降低,由于无法区分明确的职责,很难针对对象实现的业务逻辑进行单元测试
- 容器的目的是为了解决对象生命周期管理中所遇到的问题,其要遵循一定的原则
- 容器应该被设计成一个全局的。统一的编程元素
- 在最大程度上降低容器对业务逻辑的入侵
- 容器应该提供简单而全面的对象操作接口
- 容器中的接口的分类
- 获取对象实例——getInstance、getInstanceName
- 处理对象依赖关系——inject
- 处理对象的作用范围策略——setScopeStrategy、removeScopeStrategy
- 容器是一个辅助的编程元素,他在整个系统中应该被实例化为一个全局的、单例的对象。
- 容器在系统初始化时进行自身的初始化。
- 容器实现的两个方面
- 容器的初始化需求——应该掌握好容器初始化的时机,并考虑如何对容器实例进行系统级别的缓存
- 系统与容器的通信机制——应该是提供一种有效的机制与这个全局的容器实力进行沟通
- 被容器接管的对象
- 在bean节点中声明的框架内部对象
- 在bean节点中声明的自定义对象
- 在constant节点和Properties文件中声明的系统运行参数
- 以上者三类对象只需要在Struts2/XWork的配置文件中进行声明即可
- 容器进行对象操作的几个要点
- 通过操作容器进行对象操作的基本前提是,当前的操作主体能够获得全局的容器实例。因而,全局的容器实例的获取在操作主体的初始化过程中完成
- 通过操作容器进行的对象操作都是运行期(Runtime)操作
- 通过操作容器所获取的对象实例,都是受到容器托管的对象实例
- 通过操作容器进行的依赖注入操作,可以针对任意对象进行,该操作可以建立起任意对象和容器托管对象之间的联系
- 在容器内部进行缓存的是对象实例的构建方法,而不是对象实例本身。这就让容器看起来像一个工厂的集合,能够根据不同的要求,制造出不同种类的对象实例
XWork
- XWork的宏观视图
- 核心分发器 不属于Xwork框架的组成部分,但在Struts2中有着非常重要的地位。
- XWork的控制流体系 ActionProxy、ActionInvocation、Interceptor、Action、Result组成
- XWork的数据流体系 ActionContext和ValueStack组成了XWork在进行请求响应时所依赖的一个数据环境。
- 调用关系 核心分发器驱动着XWork框架的调用执行
- 映衬关系 XWork框架的控制流体系的执行基础是数据流中的元素 数据流的元素因为控制流的执行才存在意义。
- 解耦和 XWork将数据流和控制流这两大驱动程序运行的基本力量进行了物理隔离,将它们分派到不同的执行元素上,但在运行期间两者又通过某种编程手段邮寄的联系在了一起,这种看似很松实际很紧的联系正式贯穿XWork框架总体设计的一个核心思想。
- XWork的微观视图
- 数据元素流
- ActionContext——数据环境 请求的参数、处理的返回值、甚至一些原生的Web容器对象都封装与ActionContext内部
- ValueStack——数据访问环境 其中主要作用是对OGNL计算进行扩展,其赋予了ActionContext数据计算的功能。
- 结论:XWork对于数据流设计的亮点在于数据流元素设计成独立的数据结构。
- 控制流元素
- XWork认为,一个事件处理的流程是有规律并可以被继续细化的。
- Action——核心处理器(属性——行为模式)
- Interceptor——拦截器 本身属于AOP的概念
- Result——执行结果 被定义为一个独立的逻辑执行层次,组要作用是是核心处理类Action能够更加关注核心业务流程的处理,而将程序的跳转控制逻辑交给Result来完成
- 总结出来就是:一个完整的事件处理流程可定义为:以ACtion为业务处理核心,Interceptor进行逻辑辅助,Result进行响应逻辑跳转的具有丰富的执行层次的事件处理体系。
- 以上三个对象在事件处理流程进行规范化中完成了前两个步骤,①划分事件处理流程步骤②定义事件处理节点对象。整个规范化流程中最为关键的步骤也就是③组织事件处理节点对象的执行次序
- ActionProxy——执行环境 其重要作用就在于对外屏蔽整个控制流核心元素的执行过程,对内则为Action、Intercep、Result等事件处理节点对象提供一个无干扰的执行环境
- ActionInvocation——核心调度器 其被封装于ActionProxy的内部,是XWork内部真正事件处理流程的总司令。
- XWork的设计理念始终是将逻辑职责分派的最合适的编程元素之上
- 小结
- XWork的基本设计原理的别具匠心体现在两个方面~
- XWork对于请求——响应模式才去的是颠覆传统Servlet模式的实现方式
- XWork对于事件处理流程的定义,突破了”单一元素“和”顺序执行“这两大壁垒
深入XWork宏观视图
- 数据流体系 数据流有两大特性:数据和流
- ”数据“强调的是数据作为一个载体
- ”流“强调的是一个动态的过程,也就是数据访问和数据传输
- ActionContext虽表现出来的空间的概念,恰好成为数据载体进行储存的天然基石,它既负责数据存储又负责数据共享
- ValueStack是一个具备表达式引擎计算能力的数据结构
- XWork将ValueStack至于ActionContext中的目的在于伪静态的数据添加动态计算的功能
- ActionContext无法脱离ValueStack单独存在,否则所有的数据载体就如一潭死水。失去了流动性的数据流,只能称之为数据载体而非数据流
- ValueStack无法脱离ActionContext而单独存在否则ValueStack就没有了数据计算的基础、失去了数据的数据流,只能称之为一个表达式计算工具而非数据流。
- 控制流
- 两层主要关系①事件处理节点与事件驱动元素之间的关系②核心事件处理节点与辅助事件处理节点之间的关系
- 事件处理节点(Action、Interceptor、Result构成)与驱动元素控制流元素(ActionProxy、ActionInvocation构成)的两类
- 在XWork的控制流中,事件处理驱动元素对事件处理节点元素形成调用关系
- Action和Interceptor之间的关系
- 在整个栈结构中,除了位于栈底的Action以外,栈中的其他元素都是Interceptor对象
- 由于Action位于栈的底部,试图执行Action时,我们必须把位于Action之上所有Interceptor依次拿出来执行
- 每个位于栈中的Interceptor,除了需要完成它自身的逻辑外,还需要指定下一步的执行对象
- XWork对于Action和Interceptor的设计,充分体现了三个核心设计理念
- 对于事件处理,能够进行进一步的规范化的流程细化
- 对于细化的流程,能够具备高度的可扩展性
- 流程细化后的事件处理节点之间表现出明显的执行层次
数据流体系——相互依存
- ActionContext——一个平行世界
- 数据存储空间 其中的Map类型的变量Context是其真正的存储空间
- 数据共享空间 满足线程安全的判别标准
- 进行数据共享的数据信息是否是类的内部实例变量
- 外部对共享数据的访问是否存在多线程环境
- 数据存储内容 从ActionContext对外提供的访问接口分为不同的类型
- 对XWork框架对象的访问——getContainer、getValueStack、getActionInvocation
- 对数据对象的访问——gerSession、getApplication、getParameter
- 被封装后的SessionMap等对象,能够进一步保证数据访问的线程安全性
- 保持所有存储对象的Map结构,可以统一数据访问方式
- ValueStack——对OGNL的扩展
- ValueStack针对OGNL计算的扩展,实际上是针对OGNL三要素中的Root对象进行扩展 分为两个部分
- ValueStack从数据结构的角度被定义为一组对象的合集
- ValueStack规定在自身这个集合中的所有对象,在进行OGNL计算式都被视作是Root对象
- ValueStack的数据结构 具有栈的数据结构
- 栈是一个容器,可以存放多个对象
- 栈是先进后出的链表结构
- ValueStack是一个被设计成”栈“的数据结构,并且还是一个具备表达式引擎计算能力的数据结构
- ValueStack成为了Struts2/XWork框架进行OGNL操作的一个窗口
- ValueStack是XWork框架进行OGNL计算的场所
- ValueStack是XWork框架进行数据访问的基础
- ValueStack对OGNL计算规则的影响——无差别化
- 从栈的顶端开始对每个栈内元素进行表达式匹配计算,并返回第一个成功匹配的表达式计算结果
- 支持多个”根对象“操作的基本流程是自上而下遍历其中的元素逐一进行表达式求值计算,返回第一个成功匹配的表达式的计算结果其本质就是栈内元素遍历
- 栈顶元素和子栈
- 一个大小为N的ValueStack,除了自身外,有N-1个子栈
- 每一个子栈自身也是一个ValueStack,相对于ValueStack形成了一个递归的数据结构
- 深入ValueStack的实现
- CompoundRoot调用cutStack方法返回一个子栈的结构
- CompoundRootAccessor
- 形影不离。相互依存的ActionContext与ValueStack的两个重要结论
- ActionContext的创建,总是伴随着ValueStack的创建
- Valuestack的上下文环境与ActionContext的数据存储空间是等同的
控制流体系——有条不紊
- Action——革命性突破
- Action的定义提供了一些额外的信息
- 方法主体——Action接口的具体实现类是XWork进行核心业务逻辑处理的场所
- 运行参数——都以Action中枢性变量的形式出现
- 返回值——起到流程控制的标志作用
- POJO模式与Servlet模式的比较
- 分歧的核心在于具体负责请求处理的核心响应类是否是一个有状态的对象
- 请求数据——参数,还是属性变量?
- 响应数据——参数、返回值,还是属性变量?
- 响应逻辑跳转——返回值还是参数?
- 对于请求数据,参数比属性变量更为直观,也更符合Java原生的语法含义
- 对于响应数据,最适合的表达方式是方法的返回值
- 使用有状态的POJO对象来进行请求响应,在数据访问上具有天然的优势
- 状态与动作的合体
- 突破了传统Servlet模式中相应对象对Web容器的依赖
- 突破了传统Servlet模式中响应对象无状态的限制,在响应类中将看不到任何对Web容器对象的引用
- 对于有状态的响应类,响应类的属性变量是描述自身状态的核心元素,也是响应方法进行逻辑处理的核心数据依赖
- 属性特性 对象的属性特征主要表述对象的三个不同特性
- 对象自身的状态——成为XWork进行数据访问的基础
- 对象与其他对象之间的从属关系
- 对象与其他对象的协作关系——成为Action与业务逻辑操作接口进行整合的基础
- 行为特征 就是指对特定请求进行响应的过程
- XWork的Action是一个状态与动作的合体
- Interceptor——腾飞的翅膀——Interceptor其本质是一个代码段
- AOP相关概念
- 切面(Aspect)——一个关注点的模块化,这一关注点的实现可能横切多个对象,而这个模块化的过程由Interceptor来实现
- 通知(Advice)——在特定的连接点,AOP框架执行的动作。类型包括Before通知、After通知、Around通知和Throw通知等。
- 切入点(Pointcut)——指定一个通知将被引发的一系列连接点的集合。
- 连接点(Joinpoint)——程序执行过程中明确的点,如方法的调用或特定的异常被抛出。
- Interceptor的概念与基本的AOP概念之间的对应关系
- 切面(Aspect)——Interceptor实现
- 通知(Advice)——环绕通知(Around通知)
- 切入点(Pointcut)——Action对象
- 连接点(Joinpoint)——Action的执行
- 结论在XWork中所定义的Interceptor,是一组环绕在切入点(Action对象)的执行切面,可以在Action调用之前或者之后执行,从而对Action对象起到拦截作用。
- Interceptor在框架中所表现的出的特点
- 群居——interceptor对象是一个群居对象,在同一时刻总有多个Interceptor对象同时存在并协同工作
- 栈结构——相互包裹形成了数据结构上的栈结构
- 栈内元素
- 执行栈
- Interceptor的定义
- 参数ActionInvocation是XWork控制流元素中的核心调度元素,使用其作为作为参数基于两点考虑
- 便于Interceptor随时与控制流和数据流的其他元素沟通—— ActionInvocation不仅包含对控制流元素的访问接口,同时也包含对数据流元素的访问接口
- 便于ActionInvocation在Interceptor的内部进行执行调度
- 调用ActionInvocation的invoke方法来指定对执行栈的进一步调度执行,invoke方法的调用触发了执行栈中剩余Interceptor对象和Action对象的执行完成并返回结果
- 直接返回一个String类型的ResultCode来中指执行栈的调度执行
- AOP还是IoC
- Spring是用IoC来实现AOP,Spring通过实现容器完成对象生命周期和关联关系的管理。
- Spring和XWork对于AOP实现之间的本质区别就在于两者对于切入点的定义完全不同,也就是拦截器所拦截的对象不同。Spring主要偏重的是方法级别的AOP拦截,而XWork则针对Action对象进行拦截。
- 拦截器自身的逻辑都离不开通过对拦截对象状态的改变而进行的行为扩展
- XWork中的Interceptor的主要职责,就是通过对Action状态的改变而对Action的行为进行扩展
- Action本身作为一个运行在属性-行为模式上的对象,其内部主要表现出一下三个方面的特质
- 构成Action这个响应类与外部环境的交互接口
- 构成Action这个响应类的请求数据和相应数据
- 构成Action的行为动作与业务逻辑处理对象之间的协作关系
- 结论XWork是用AOP来实现IoC
- Interceptor的逻辑意义
- Interceptor对象的引入实际上是对事件处理流程中主要职责和次要职责的有效划分,并让每一个执行层次都能够完成其必要的职责归属
- Interceptor对象的引入能够极大地丰富整个事件处理流程的执行层次,从而为实现AOP片成打下坚实的数据结构基础
- Interceptor对象的引入能够是的责任链模式在整个执行栈的调度过程中顺利实施,从而改变事件处理流程中的顺序处理逻辑,自然形成AOP模型的雏形
- ActionInvocation——核心调度
- ActionInvocation接口中的逻辑分类
- 对控制流元素和数据流元素的访问接口——getAction、getActionProxy、getStack等
- 对执行调度流程的扩展接口——addPreListener、setActionEvenListener
- 对执行栈进行调度执行的接口——invoke、invokeActionOnly
- ActionInvocation调度分析
- invoke方法是对Interceptor对象和Action对象共同构成的执行栈进行逻辑执行调度
- 如果执行栈中的下一个元素是Interceptor对象,那么执行该INterceptor
- 如果执行栈中的下一个元素是Action对象,那么执行该Action对象
- 如果执行栈中找不到下一个执行元素,那么执行中止,返回执行结果ResultCode
- XWork时间处理流程所支持的三种不同的拦截方式
- before拦截——invoke之前执行
- after拦截——invoke之后执行
- PreResultListen拦截——再Interceptor和Action执行完毕之后、Result执行之前执行
- 在拦截器的逻辑实现中,对ActionInvocation的Invoke方法实施了递归调用,invocation.invoke的调用成为逻辑上的分水岭
- 递归调用使得位于分水岭之后的的代码逻辑被暂时封存,等待完整的递归调用结束之后才能继续执行
- 递归调用使得在执行栈中的下一个元素将被拿出来执行。从表面上看形成了栈结构的顺序遍历,实际上却形成了递归调用的嵌套,这就意味着在分水岭之前的逻辑代码将随着栈结构的遍历顺序执行
- 当整个递归调用完成之后,情况刚刚好相反,原本最后一个执行的元素对象将首先执行在分水岭之后的代码逻辑,从而触发整个逆序的执行过程
- ActionProxy——执行窗口,XWork事件处理框架的总代理
- ActionProxy的首要职责是维护XWork的执行元素与请求对象之间的配置映射关系
- ActionProxy构建方法createActionProxy方法的参数主要分为两类
- 配置关系映射——namespace、actionName、methodName等
- 运行上下文环境——extraContext
- 真正的XWork控制流元素都被ActionProxy有效地隐藏在了背后
- 数据环境的生命周期
- 作为Xwork的数据环境,ActionContext的数据内容在整个XWork控制流的生命周期中共享
- 数据流的生命周期比控制流的生命周期更长,ActionContext是一个横跨了整个XWork控制流执行周期的元素
- ActionContext所进行的数据共享,不仅对整个XWork的控制流元素有效,他甚至对整个当前执行线程有效
- 作为数据环境,Actioncontext是隐藏于XWork控制流执行背后的重要元素,成为控制流元素与数据流元素的沟通桥梁
- 三军会师之地
- 控制流中的核心处理元素Action,被置于数据流元素ValueStack中。
- 在传统的请求——响应的实现模式中,无论是参数——返回值模式还是参数——参数模式,控制流元素对于数据流元素都是有绝对的掌控权的。
- Action交互体系 体现了Action交互体系的三个不同方面
- 外部环境交互体系——构成Action与外部环境的交互接口
- 是指Action与Web容器中HttpServletRequest、HttpServletResponse、HttpSession对象的交互过程
- ServletConfigInterceptor这个拦截器为我们打造了一个对Action进行扩展的范例,打造分为三个过程
- 定义一个借口
- 让Action实现该接口
- 在拦截器终将Action强制转化为接口,完成接口方法的逻辑调用
- 这三个过程的代码逻辑调用蕴含了两种不同的Action扩展结果
- 通过Action实现的接口,向Action传递外部信息
- 通过Action实现的接口,完成功能的扩展
- 这种交互方式主要依据实际上是面向对象概念中实现类与接口之间的关系
- 数据交互体系——构成Action的请求数据和响应数据
- 反映了Action与数据流元素进行交互的过程(由ParametersInterceptor拦截器完成)
- ParametersInterceptor是一个将外部环境数据与Action的属性变量进行融合的骨架,而其中的retrieveParameter方法,就是外部环境数据的提供者
- 实际上是提供了一个数据环境映射到Action对象的途径和方法
- 协作交互体系——构成Action与业务逻辑处理对象之间的协作关系
运行主线篇
包罗万象——Struts2初始化主线
- 配置元素与初始化主线
- 从入口程序开始(两大主线的入口都是StrutsPrepareAndExecuteFilter)
- init方法的主要内容是针对三个元素展开,而这三个元素也成为之后Http请求处理这一条逻辑主线的执行依据
- Dispatcher——核心分发器
- PrepareOperations——Http预处理类
- ExecuteOperations——Http处理执行类
- createDispatcher方法filterConfig中所包含的参数其实来自于Web.xml的初始化参数配置
- 初始化主线的核心驱动力
- 核心驱动力的逻辑——对于程序运行目的的描述
- 核心驱动力的形式——推动程序运行的编程元素
- 数据结构——框架的核心配置元素
- 算法——围绕着核心配置元素的初始化过程
- 框架的核心配置是一种贯穿始终的核心驱动力,他不仅能够以一定的形式表现出框架的构成元素互相之间的逻辑关系,同时能够将它们的执行逻辑串联起来
- Struts2初始化主线的核心驱动力,正式对各种配置形式所进行的一次统一的对象化处理
- Struts2在初始化的时候,将各种各样的配置元素,无论是XML形式还是Properties文件形式(甚至可能是其他自定义的配置形式)转化为Struts2所定义的Java对象或者Struts2运行时的参数的处理过程。
- 初始化主线的构成元素 对多种多样的配置进行管理
- 对配置元素进行操作的流程元素由三个不同的类别构成
- 配置元素的加载器——将纷繁复杂的配置表现形式转化为框架元素
- 配置元素的构造器——对框架元素进行初始化操作,使用的是构造模式
- 配置元素的管理类——在初始化主线运行的过程中,对配置元素的数据的存储和配置元素的初始化行为进行控制。
核心分发器——Dispatcher
- 核心分发器的核心驱动作用(起——承——转——合)
- 起——负责系统初始化
- 承——接收并预处理Http请求
- 转——将Struts2转入XWork,通过ServiceAction方法进行请求转发
- 合——垃圾清理,cleanup
- 对于在整个请求周期中定义了完整的生命周期的框架元素的清理
- 对于线程安全的ThreadLocal对象的清理
- 针对Dispatcher的重要结论
- 作为一个线程安全的对象,Dispatcher涵盖了Struts2的整个生命周期。无论是Struts2的初始化,还是处理Http请求,实际都在Dispatcher中完成
- Dispatcher是Struts2与XWork的分界点,也是将MVC实现与Web容器隔离的分界点
配置元素的加载器(Provider)
- 配置元素的加载器的作用
- 配置加载接口的各种实现类(Provider)架起了各种配置表现形式到Java世界的桥梁
- ConfigurationProvider为统一的操作接口,其使用了接口多重继承机制,好处在于能够使Struts2在进行配置加载时拥有比较统一的操作路径和操作方法
- 容器加载器——ContainerProvider
- 不同的ContainerProvider之间并没有依赖关系
- 进行配置加载的生命周期:先进行初始化(调用init方法),再进行元素注册(调用register方法)
- 事件映射加载器——PackageProvider
- PackageConfig所提供的两个构造函数都被定义成了Protected,无法通过正常的构造函数的途径直接构造一个Package
- 配置元素的构造器(Builder)
- 构造器自身是一系列操作方法的集合类
- 操作方法可分为两种不同的类型,非别对应于构造器进行对象构造的两个步骤
- 参数搜集操作
- 创建对象操作
- 容器构造器——ContainerBuild
- 搜集参数——factory方法、alias方法、constant方法
- 构造对象——create方法
- 事件映射构造器——PackageConfig.Builder
- 他不仅是一个内部类,还是一个具体的实现操作类而非一个接口
- ContainerBuilder进行初始化时并不关系容器自身的数据结构,而是强调容器中元素的搜集过程,所以定义成接口
- 由于PackageConfig所有构造函数都被设置成protected,从而使得针对PackageConfig的构造器也不得不定义成一个内部类
- PackageConfig.Builder作为”构造器“的特点
- Builder构造器中绝大多数操作的返回值都是这个构造器本身,这有助于程序员在构造PackageConfig时使用链式操作方式进行连续调用。
- 外部程序构造PackageConfig的唯一途径是使用这个PackageConfig.Builder构造器,因为PackageConfig自身的构造函数被定义成了protected(隔离性)
- Builder构造器的核心方法build在搜集属性值并创建真正的PackageConfig对象时,调用Collections中的unmodifiableList方法进行不可变处理(稳定性)
配置元素的管理类
- 初始化三大核心要素(Dispatcher、Provider、Builder)
- 初始化主线的核心驱动力
- 初始化主线的操作载体
- 初始化主线中元素的构建方式
- 初始化驱动元素
- ConfigurationManager是整个配置元素进行操作的代理接口类
- 对所有配置元素的初始化进行调度的是Configuration对象
- 配置管理元素——Configuration
- Configuration主要目的就是为了对初始化主线中的配置元素进行管理,也被成为配置管理元素
- 对配置元素进行管理实际上蕴含了两个不同的方面
- 提供框架级别对所有配置元素的访问接口操作
- 对所有配置元素进行初始化调度
- configuration对象对于配置元素的调度本质,是对框架配置元素的操作
- 所有的配置元素都被完整地封装在DefaultConfiguration之中,成为Configuration尽心配置元素管理的数据基础
- 配置操作接口——ConfigurationManager
Struts2初始化主线详解
- 核心分发器的初始化
- 容器的初始化
- 容器的创建过程——init_PreloadConfiguration()
- 容器的依赖注入——container.inject(this)
- 容器初始化的步骤为五步
- 定义容器构造器——将Properties文件中的键值对与ContainerBuilder关联
- 使用ContainerBuilder进行参数搜集——ContainerBuilder的各种实现的核心逻辑不过是ContainerBuilder搜集参数的过程
- 使用ContainerBuilder进行容器创建
- 初始化时间映射元素PackageConfig——填充Configuration对象中的事件映射配置对象
- PackageConfig进行运行期改造
与Http请求的战斗——Struts2是一个运行于Web容器的表示层框架,其核心作用是帮助我们处理Http请求
- 编程资源
- OGNL——表达式引擎,假期外部世界与Java世界
- XWork——请求处理器,将请求划分为若干处理步骤并分派到不同处理元素调度执行
- 请求处理主线主要可归纳为两个方面
- 代码上的物理解耦——两个阶段分属于不同的框架主导执行
- 逻辑职责上的解耦——两个阶段的主要职责完全不同
- Http请求的预处理阶段
- Dispatcher——核心分发器,执行Struts2处理逻辑的实际场所
- PrepareOperations——Http预处理类,对所有的Http请求进行预处理
- StrutsPrepareFilter
- 步骤1——设置Encoding和Locale
- 步骤2——创建ActionContext,为了之后XWork能够在非Web容器环境下接手Http请求的处理工作打下坚实的基础
- 步骤3——将核心分发器Dispatcher绑定至当前线程
- 步骤4——对HttpServletRequest进行一定的封装
- 步骤5——根据Http请求查找ActionMapping
- ExecuteOperations——Http处理执行类,执行Http请求的场所
- StrutsExcuteFilter
- XWork框架的相关逻辑实际上由Dispatcher创建并负责驱动执行
- Dispatcher负责Http请求不同处理阶段的数据转化工作,从而保证代码在不同执行阶段的无缝切换
- 在XWork框架的调用接口ActionProxy中,我们将不再看到任何与Web容器相关的对象
- Xwork处理阶段
- ActionProxy的数据源
- 配置关系映射——namespace、actionName、methodName
- 运行上下文环境——extraContext
- Action在创建时所依赖的上下文环境与ActionProxy初始化时所以依赖的上下文环境是等同的
- ActionInvocation的初始化
- 创建上下文环境
- ActionInvocation对象的共享
- 创建Action对象
- 将Action对象置入ValueStack中
- 创建ActionInvocation的上下文环境
- 将拦截器堆栈置于初始调度状态
- ActionInvocation调度的再分析
- 执行方法的参数——没有参数
- 执行方法的返回值——String或者Result对象
让视图放开手脚
- 视图概述
- Jsp技术
- 解决了页面视图的构建问题
- 解决了与服务器端进行数据沟通的问题
- Servlet技术
- 所有的JSP在运行期都被编译成Servlet在Web容器中运行
- HttpServletRequest对象主要用于处理整个Http生命周期中的数据
- getParameter系列的方法是服务器端程序获取浏览器所传递参数的主要接口
- HttpServletResponse对象主要用于处理Http的响应结果
- 视图的本质
- 浏览器是视图层的表现载体,所有视图层的交互职责都是通过浏览器进行的操作
- 沟通协议——Http协议
- 请求内容——Http请求:HttpServletRequest
- 响应内容——Http响应:HttpServletResponse
- 视图的本质是Web容器对象HttpServletRequest和HttpServletResponse对浏览器行为的控制
- 视图的职责
- 视图内容呈现——以HTML为基础进行的构建
- 数据和逻辑呈现——以Java语法为基础进行的构建
- 只要实现了试图的两大职责,它就能称为一个“合格”的视图表现技术。
- 模版技术
- Ajax技术
- Flex技术
Result机制
- Result的不同视角
- XWork视角——事件处理节点
- 从XWork来看,他只是我们对事件处理流程步骤的划分结果而已
- 他将告诉程序该何去何从,成为在整个XWork控制流元素中实施控制职责的重要体系元素
- Struts视角——视图的操作窗口
- Result对象那个在Struts2中看作是视图的操作窗口,相当于完成了Servlet对象中HttpServletResponse的职责
- Result职责分析
- Result最大的职责,就是架起Controller层到View层的桥梁
- 需要一个起到中转作用的对象,完成程序执行权的转移
- 需要一个接口与实现分离的机制,将XWork与Struts2进行解耦和
- 需要一个Web容器的代理对象,屏蔽底层对象的操作细节
- 实现上的三个不同方面
- 封装跳转逻辑
- 准备显示数据
- 控制输出行为
标签库
- 产生的初衷
- 消除在Html代码中的Java代码,以一种自定义的扩展标签来代替原本需要Java代码表达的业务逻辑
- 避免在JSP页面中使用Java代码,业务逻辑与显示分离的目的,提高JSP自身的关注度
- 将数据展示彻底抽象成为一个独立的功能模块,能够规范编程。
- 优势
- 标签库能够对HTML的许多标签进行功能扩展
- 在一定程度上解决了在JSP页面中避免频繁使用Java代码的情况
- 能够极大程度地封装成块的HTML代码,进而形成一套完整的页面组件
- 标签库有利于组件化开发
- 趋势
- 指定标准标签库JSTl,视图统一绝大多数的标签
- 借助IDE的帮助,打造更多更丰富的Web层组建,并加强AJAX等功能
- 将成熟的JavaScriptUI框架改造成标签的形式
- 随着EL的逐步推广,在JSP中使用EL也越来月能够被大家接受
- 改革
- 放弃Taglib,推崇模版技术
- 不实用Taglib来构造页面组建,而更多采用JavaScript技术丰富页面展示
- 直接放弃使用HTML为模版的页面展现方式,改用Flex等Flash表现方案
- 标签分类
- 逻辑控制类——控制页面输出逻辑
- c:if——分支判断
- c:forEach——循环
- c:choose/c:when/c:otherwise——分支判断
- c:catch——异常处理
- 数据输出类——与服务器端进行数据沟通并以一定格式进行输出
- c:out——输出表达式的值
- c:url——输出格式化Url
- c:set——设置表达式的值
- c:param——设置参数
- fmt:message——输出资源文件中的值
- fmt:formatDate——格式化输出日期
- fmt:formatNumber——格式化输出日期
- 页面组件类——将页面元素的功能进行组件化编程
数据访问哲学
- 从哪里去获取数据
- 取过来的数据如何显示
- HttpServletRequest对象主要用于处理整个HttpServletRequest生命周期中的数据
- 传统意义上的数据访问,就是针对HttpServletRequest对象的读取操作
- ValueStack
- ValueStack是真个Struts2进行数据访问的实际操作句柄,而Action作为ValueStack的Root对象,其中的属性变量自然而然成为了数据访问源
- 对于请求-响应模式实现机制上的分歧,是造成数据访问模式差异的根本原因
- 数据访问长什么样
- 数据访问的困境,主要还是因为数据模型在某些层次的展现缺乏足够的表现力。
- 视图层不仅应负责与服务器短的数据沟通,还应该将获得的数据进行格式化输出。
Struts2的扩展机制
- 可维护性——程序应具备简单有效的方法对软件的功能模块进行修改和维护
- 可扩展性——程序应具备简单有效的方法对软件的功能模块进行添加和扩展
- 始终保证程序的可读性。可维护性和可扩展性。
程序扩展机制——插件模式(Plugin)
- 插件——可以即插即用的组件
- 组件——体现了插件的功能性
- 即插即用——体现了插件的灵活性和独立性
- 软件功能模块化
- 尽量将程序中的双向依赖关系变成一种单项的集中依赖关系
- 核心程序与功能模块
- 抽取各个功能模块的公共逻辑成为一个独立的核心功能模块
- 为核心功能模块添加对其他功能模块的执行调度功能
- 核心程序对功能模块的调度
- 各个功能模块仅仅依赖于核心程序,功能模块与功能模块之间并没有依赖
- 功能模块保持独立不会互相影响
- 利弊分析
- 软件运行环境——可以运行基础功能模块的基础构件,俗称为程序骨架
- 功能模块——运行在特定环境中的软件功能分类
- 优点
- 软件模块的管理变得更加容易
- 软件功能的可塑性和扩展性更强
- 软件的产品化和定制化成为可能
- 缺点
- 软件运行环境质量的优劣,将直接影响整个软件的质量
- 软件环境的版本升级将导致依赖于该环境的所有插件失效
Struts2的插件模式
- 深入Struts2插件
- 表现形式
- 框架的本质就是JAR
- 插件所在的JAR包,必须位于Web应用程序的CLASSPATH下
- 在JAR包内的根目录位置,应该存放一个名为Struts-plugin.xml的文件
- struts-plugin.xml的文件与Struts2的基础配置文件struts.xml的格式相同
- 安装使用
- 插件是否位于CLASSPATH下
- 是否在JAR根目录中存在一个名为struts-plugin.xml的配置文件
- 依赖关系
- Struts的插件对Struts2的核心JAR构成依赖
- 各插件之间原则上不形成相互依赖的关系
- Struts2的各个插件在呗Struts2的核心程序加载时,其顺序是随机的。
- 扩展点
- struts-plugin.xml文件的名称和所处的位置成为了核心程序与插件的通信接口
- struts-plugin.xml文件的内容结构成为了插件对核心程序进行扩展的依据
- 扩展点分为三类
- 定义新的事件映射关系对象(Action。Interceptor、Result等等)
- 引入新的接口定义实现或者运行参数,将其纳入Struts2的容器进行管理
- 覆盖Struts2、XWork的默认接口实现机制
- 插件分类
- 框架整合类插件
- 简化配置类插件
- 功能扩展类插件
- Struts2的插件加载机制
- 插件加载概述,加载顺序
- struts-default.xml——位于Struts2核心JAR包的根目录下——最先被加载,用于指定Struts2的一些默认行为
- struts-plugin.xml——所有插件所在的JAR包的根目录下——紧接着被加载,,Struts2会搜索所有的CLASSPATH中的JAR文件,查找其中的struts-plugin.xml文件,这些文件的加载顺序是不确定的
- struts.xml——位于应用程序的CLASSPATH下——最后被加载,是应用程序中Struts2的基础配置文件,指定应用程序级别的Struts2行为
- 配置加载接口定义,这个过程由init_TraditionalXmlConfigurations方法完成
- 配置加载接口的使用StrutsXmlConfigurationProvider的使用过程
- Struts2的插件被设计成独立于应用程序的JAR包,使得安装盒使用变得极其简单
- Struts2对于核心程序、插件以及用用程序之间的关系处理的很有条理,使得这三者之间虽然具备了丰富的层次关系,也不至于造成混乱
- Struts2对于插件的构成要求很低,扩展点却很多,使得我们在模块化设计的过程中,有更多的余地。