HTTP 特点: 无状态和基于文本
基于文本 于 强类型的技术匹配 引来大量的数据绑定工作—— 文本 跟 数据类型的矛盾 —— 核心:数据处理
servlet
将HTTP公开给java语言
提供了会话机制。
深入的需求研究 ——servlet不能解决的问题——由此引入的struct2的 功能
1. 数据验证(高级验证)
2. 访问 业务逻辑 及 数据层
struct2 遵循 MVC设计模式 —— 分离 重用
控制器 : 框架帮助完成所有控制器的任务,因此在本书,控制器的内容直接就被跳过了。
本书讲的是动作被控制器调用之后的内容。
模型: 即处理 对应struct的 动作action
有两个作用:
封装 业务逻辑
数据传输
---------------------------------------------
第二章
(声明性)架构 组件
是一种特殊的配置方式,它以描述
而不是编码的方式
创建应用程序的架构。
声明性架构 入口点 structs.xml (主要是包含其他的 xml,模块化)
它在classes目录下: /WEB-INF/classes/structs.xml
structs.xml:
1. 设置 框架属性:
<structs>
<constant name="structs.devMode" value="true" />
...
<include file="包名/xxx.xml" />
</structs>
2. 在默认包内(即应用的根目录)定义全局动作
<package name="default" namespace="/" extends="structs-default">
<action name="xxx">
<result>/xxx.jsp</result>
</action>
</package>
3. include 模块的xml
模块的xml:
除了action 和action里的result元素之外,仅有的 其他元素是 structs根元素和package元素。
structs元素 是所有Struct2 xml文件的 强制性的 文档根元素。
package是 容器元素,它声明了一个url 映射到动作时,用到的命名空间:
应用名 + package元素里的namespace + 动作名.action
注释: 即使 一个简单的不需要 动作处理 的jsp页面, 也要用空的动作组件来转到该jsp页面,
这样可以保持架构一致, 且可以隐藏资源的真正结构,而只显示动作的逻辑命名空间。
HelloWorld 动作 代码:
主要包括:
动作 的业务逻辑处理,并 返回 用来 选择结果 的 控制 字符串。
数据存储:
1. 领域数据 总是存储在 动作 中。—— 动作 组件保存数据,动作组件的java代码就能方便地访问这些数据。
2. 动作对象 自身 被放在ValueStack中, 框架在 ValueStack上 提供了这些 动作的属性。——框架的其他区域也可以访问这些数据。
------------------------------------
struct2 核心组件
第三章, 动作 action
动作完成请求的核心处理
动作 包含业务逻辑,承载数据
struct2 是一个动作的框架,动作是这个框架的核心
动作 的作用:
封装 业务逻辑—— 需要做的实际工作
承载数据
选择结果页面
动作 携带数据—— 由于数据保存在动作本地,因此在执行业务逻辑的过程中
可以很方便地访问,使得访问数据的代码变得简洁。
注释:Struct2 通过为每一个映射到这个动作的请求 创建一个新的动作实例,来存储数据。
返回控制字符串
返回的 字符串 指向 呈现视图的 结果组件的名字
包容器 像java包,—— 包 还提供了 一种继承 机制,能够 继承已经定义的组件
(继承)
组织包
<package name="xxxx" namesapce="xxx/xxx/xxx" extends="structs-default">
<action>
...
</action>
...
</package>
命名空间: 同c++命名空间
当一个url到达时,如 localhost/appName/aaa/test.action
框架会 到 aaa的命名空间里,去找叫test的动作。
注意:
如同c++ 一样, 可以为不同包 设置相同的命名空间(就像namespace std一样)
注意: 处于实用的原因我们把动作分在不同的包内,但是 没有理由 连命名空间也不同。
即: 把动作分在不同的包内,是因为功能和共享等原因
但是 命名的空间 是没有什么理由 需要为每一个包 起一个不同的命名空间,这是没必要的。
除非是出于,单纯的为了让用户能够看到url的变化。 如进入安全访问的页面时,在命名空间上多一个
secure,这样用户知道,自己在进行安全的访问: localhost/appName/func/secure/xxx.action
如果不设置namespace,默认命名空间 ???
extends structs-default 包
可以继承 和使用 这个包中定义的组件 —— 默认拦截器战
通常 必须 扩展 structs-default! 为了使用它提供的拦截器
核心内容:动作 —— 可选的Action接口(实现 execute()方法)
另外: Action接口提供了一些 返回控制字符串 的常量值
ActionSupport类 实现了Action接口 ,并且实现了一些另外的接口。—— 它 提供了 一些接口
来和 存在于默认拦截器栈中的 workflow拦截器 配合实现 基本验证工作。
workflow拦截器 就像所有拦截器一样,致力于将横切任务从动作的执行逻辑中
分离转移出去。
检验逻辑被 workflow 拦截器调用, 而这些拦截器都是在动作执行之前触发。——实现了分离
对象 数据
第四章 使用拦截器 追加 工作流
拦截器 完成 框架大部分的基础工作
动作 是框架的核心和灵魂
使框架达到高水平关注分离,拦截器起到至关重要的作用
拦截器消除了动作组件中的横切任务
如:日志记录、
横切任务 —— 它不是某一个动作所特有,它横向关注所有动作。 从而创建 清晰的 关注点的分离
预处理、和后加工 仍然属于 横切 —— 如 数据转移(通过params拦截器实现):
几乎所有动作都需要 将一些数据 从请求参数转移到属性上 —— 它必须在动作触发前完成。
可以视为仅是为动作的实际工作做准备工作。
拦截器机制—— 不是让控制器直接调用一个动作,而是有一个处在控制器和动作组件之间的组件。
框架创建了一个 ActionInvocation对象,该对象封装了动作和一系列在动作执行之前之后触发的拦截器。
拦截器的强大功能之一是改变 工作流
就像本章目录一样。
拦截器可能会 返回 控制字符串,动作不会被触发,甚至栈中的下一个拦截器也不会被调用。
分层 让软件整洁, 益处是可重用和可配置。
把 可重用的逻辑 分隔到 拦截器 后,可以把它 应用到 所有动作 上。
如:通过继承default-stack 获得 代码重用 的益处:我们 能够重用 Struct2 框架开发人员 编写的
数据转移代码、数据验证代码和国际化代码等。
拦截器分层的 另一个好处是,可以定制拦截器,可以追加删除拦截器等。
虽然拦截器很重要,但一般不会去编写很多拦截器。
在 开发动作 时,时刻注意 可以被转移到拦截器中的 任何 任务。
拦截器的工作原理:
框架 调用 --> ActionInvocation的 invoke()(每次invoke()方法被调用,ActionInvocation都会检测,调用接下来的拦截器,
如果所有拦截器都被调用后,invoke会执行动作):
Interceptor 接口 定义intercept方法(传参是ActionInvocation实例)是拦截器执行的入口点。
拦截器 触发时做的工作:
1.预处理
2.决定是否将控制权转移给剩余拦截器和动作: 调用 invoke()方法,将 控制 转移给后续拦截器,或者返回控制字符串中断执行
最终invoke返回,一个控制字符串,指示哪个结果被呈现。
3.后加工
1. 预处理阶段 拦截器可以操作数据
2. 如果拦截器决定请求处理不应该继续,它可以不调用invoke()方法。而是直接返回一个控制字符串。
通过这种方式,它可以停止后续执行,并且自行决定 哪个结果会被呈现。
3. invoke返回控制字符串之后,拦截器可以改变数据,进行后加工,但是,结果页面 已经被确定了,此时只能修改呈现的 数据 而已,页面已经确定。
研究 Struct2 内建的 拦截器
我们说过,好的框架能够自动化web应用中大部分的日常任务。 内建拦截器提供了这个自动化。
数据转移拦截器:
params拦截器:
把数据 转移到 ValueStack上第一个匹配的属性上。
static-params拦截器:
也将参数转移到ValueStack公开的属性上,不同的是参数的来源。 转移的参数定义在
声明性架构的动作元素中。
servlet-config 拦截器: (注意 来源自哪,转移到哪)
将来源于Servlet API的各种对象(比如HttpServletRequest, 会话等等),
它不是像前面那样转移到ValueStack
而是转移到 动作 实现的 接口 的方法。
如下一系列接口用来取得不同的Servlet API对象:
ServletContextAware 设置 ServletContext
ServletRequestAware设置HttpServletRequest
。。。
fileUpload拦截器:
将文件转换为常规请求参数,以便像普通参数一样转移到动作(实际通过params拦截器,转移到ValueStack)上。
面向 工作流 的拦截器:
workflow拦截器:(放在拦截器栈的最后,之前的拦截器都要通过它)
功能:与动作协作(动作需实现一些接口函数,可以通过继承ActionSupport),提供 数据验证以及验证错误发生时改变后续工作流的功能。(它只提供这些功能,可以查源码就明白)
只有动作实现了Validateable接口,才会进行数据验证。
如果动作实现了ValidationAware接口,拦截器通过调研hasErrors方法来确认 验证逻辑 是否生产错误信息,如果出现错误信息,
返回INPUT控制字符串。
为了避免不能通过验证(如预填充的表单),有了excludeMethods参数,来过滤。
还可以在 intercept-ref 中 使用 param来配置拦截器。
exception拦截器:
使用 exception-mapping 元素 —— 告诉exception拦截器 一个特定的异常呈现哪个页面:
<global-results>
<result name="error">/xxx/Errorxxx.jsp</result>
</global-results>
<exception-mapping exception="java.lang.Exception" result="error" />
综上:
拦截器的实现多种多样
像workflow拦截器需要 动作 实现某些接口来配合
像exception拦截器需要 xml文件 来建立
params拦截器,需要 属性 等等
因为: 拦截器做了框架的大部分工作,所有什么样的工作都有,也就没有一个统一的样子。
也无法统一。工作差别太大了。
第五章 数据转移 类型转换
数据转移和类型转换,这些工作都是准备工作而已
发生在请求处理周期的两端
第六章
OGNL 就像指针一样 指向ValueStack上的属性
实际上,OGNL可以指向一系列的对象,ValueStack只是默认的对象而已。 这一系列的对象叫做ActionContext。
它包含的对象主要有:ValueStack Request Session等等。
property标签的 value属性 告诉标签呈现到页面上的 属性。
即:property通过 value属性的值 作为OGNL表达式 来查找 要呈现的属性。 没有使用#操作符指明ActionContext中的特
定对象,所有会默认在ValueStack查找。 如果不存在 空值会被转换为空字符串。
struct2标签的属性(这里说的是标签自己的属性像value、default等等) 分为两类:如 例子中的value 和 default属性
字符串属性(被标签当做String值使用)可以强制使用 ${expression}来当OGNL解析
和非字符串属性(标签当做OGNL表达式,OGNL用来查找呈现页面上的属性值)
第七章 构建用户界面