Struts2
概述
web层框架
对servlet的封装
struts2做了什么
- 将任意请求与action类中的方法建立请求
核心过滤器:将请求分配到不同的类
映射文件(structs.xml):为核心过滤器提供类与请求的映射
映射文件的优化:
用method属性将类中的方法与请求建立关系
用通配符将任意请求与类中的方法建立关系
- 将action与servlet解耦,为拦截器的动态代理提供先决条件
- 在页面和Web层封装和解析JavaBean
核心功能
- POJO对象作为action,处理请求
- execute方法与servlet解耦
- 支持JSP,FreeMarker,Velocity
- 基于SpringAOP思想的拦截器机制
- 校验功能
- ajax支持
常见web层框架
- structs1
- structs2
- springMVC
- WEBWork
漏洞
- 远程执行服务器脚本
- 重定向漏洞
配置
- 配置文件按先后顺序覆盖同名配置
default.properties(内置)
- 位于struts-core.jar包的org.apache.struts2包下
- 作用
- 定义了一些常量(键值对)
- 功能开关
struts-default.xml(内置)
- struts-core.jar包下
- 默认包structs-default,包类型abstract
- 作用:
- 定义了Bean元素、结果集类型、拦截器
struts-plugin.xml(内置)
- 插件配置文件,用于扩展插件
struts.xml(自定义)
- 配置约束文件
- package标签
- name:任意不可重复(一般用模块名称)
- namespace:”/”,用于区分请求同名路径,默认名称空间”“,structs后台解析默认加/
- extends
- struts-default,继承默认配置中的拦截器等设置
- 自定义
- action的访问路径:namespace+action的name属性
- action标签
- name对应请求路径
- class对应action类
- result标签
- name: 结果集(逻辑视图)
- 内置视图:NONE,SUCCESS,ERROR,INPUT,LOGIN
- 标签内容:响应路径
- param标签:给结果集传递参数
struts.properties(自定义)
用于覆盖内置常量
常用常量配置
- struts.devMode激活xml文件自动重新加载
<!-- 开发者模式:好处自动加载核心配置文件,提供友好的错误页面。副作用:异步请求的错误无法处理会抛弃 --> <!-- <constant name="struts.devMode" value="true" /> --> <!-- 只自动加载核心配置文件 --> <constant name="struts.configuration.xml.reload" value="true"/> <!-- 简单样式 --> <constant name="struts.ui.theme" value="simple"/>
struts.i18n.encoding=UTF-8 解决post乱码
struts.configuration.xml.reload=true 自动重新加载structs.xml
struts.i18n.reload = true自动重新加载国际化文件
struts.action.extension=action,, 配置action请求的默认扩展名
struts.ui.theme=xhtml 设置页面标签显示样式
struts.objectFactory=struts负责创建Action对象类
struts.enable.DynamicMethodInvocation=true支持动态调用
struts.multipart.maxSize=2097152 上次文件大小
配置文件分离
<struts>
<include file="struts-part1.xml"/>
<include file="struts-part2.xml"/>
</struts>
web.xml
- 配置前段控制器(核心过滤器)
- url-pattern:/*
默认配置
若不指定名称空间namespace,默认为“/”(有争议)
若没有class属性,执行默认处理类ActionSupport,该默认处理类在struts-default.xml 文件中有定义。
默认会执行Action类中的execute方法,而ActionSupport的execute会默认返回 success逻辑视图。
若不指定结果集result的名字默认为“success”。
jar包
asm-*.jar:字节码操作类库 (.class文件)
commons-fileupload-1.2.2.jar:文件上传组件,2.1.6版本后需要加入此文件。
commons-io-2.0.1.jar:传文件依赖的jar包。
commons-lang-2.5.jar:对java.lang包的增强,主要是提供字符串等操作的公用方法。
commons-logging-1.1.x.jar:ASF出品的日志包,Struts 2框架使用这个日志包来支持Log4J和JDK 1.4+的日志记录。(JCL,java通用日志接口)
freemarker-2.3.*.jar:模版引擎,struts2标签库,依赖freemarker ,Struts 2的UI标签的模板使用FreeMarker编写。
javassist*.jar 生成代理的类库。
log4j*.jar:一个日志实现。
ognl-3.0.x.jar:OGNL对象图导航语言(Object Graph Navigation Language), struts2框架通过其读写对象的属性。(类似于EL但更强大)
struts2-core-2.3.x.jar:Struts 2框架的核心类库。
xwork-core-2.3.x.jar:Command命令模式框架, Struts2和WebWork都基于和依赖xwork。
Config Brower插件
- 复制到lib下
- http://ip地址:端口/上下文名字/config-browser/index.action查看配置情况
核心过滤器
- StrutsPrepareAndExecuteFilter
- 控制器模式
拦截器
- 封装复用代码
- 按照配置文件中上下顺序执行
拦截器原理
- 创建代理对象
- 注入方法执行者
- 初始化方法执行者
- 执行拦截器方法
- 执行完毕,invoke放行
- return,阻断action
与过滤器对比
过滤器(filter) javaweb学习,拦截服务器端所有资源的访问 (静态、 动态)。在web.xml拦截器(Interceptor) struts2 学习,在struts2框架内部,只对Action访问进行拦截 (默认拦截器 ,无法拦截静态web资源, 如果要拦截静态资源,比如html、jsp,可以将静态web资源放入WEB-INF\xxx, 通过Action间接访问)
拦截器实现
- 实现Interceptor 接口
- 继承AbstractInterceptorn,覆盖intercept方法
- 继承MethodFilterInterceptor类,覆盖doIntercept方法
拦截器栈(Interceptor Stack):
- 将拦截器按照一定的顺序连接成一条链后的一个称呼。
- 在访问被拦截的方法时,拦截器链的拦截器按照定义的顺序被依次调用
拦截器配置
在包下注册拦截器和拦截器栈
在包下注册拦截器和拦截器栈 <Interceptors> <Interceptor name="myInterceptor" class=""/> <!--自定义拦截器栈--> <Interceptor-stack> <!--添加拦截器--> <Interceptor-ref name="myInterceptor"/> <!--添加拦截器栈--> <Interceptor-ref name="defaultStack"/> </Interceptor-stack> </Interceptors>
全局拦截器
在包下配置<default-Interceptor-ref name="myStack">
局部拦截器
- 覆盖同名全局拦截器
在action中书写<Interceptor-ref name="myStack">
排除与包含
- 必须继承methodFilterInterceptor,abstractInterceptor不支持方法排除
在拦截器栈中配置 <param name="excludeMethods">method1,method2</param>
Action
- 用于代理servlet处理业务逻辑
三种书写方式
- 实现action接口
- 命令模式:设计模式,将“行为请求者”与“行为实现者”解耦
- 继承ActionSupport类(重写execute方法)
- POJO类
- 反射机制
- public String execute();
路径查找机制
- 如果访问的路径,没有定义action,会自动向上层路径寻找。
配置默认action
配置默认的action:访问的name:如果访问时action找不到,默认会调用errorPage这action <default-action-ref name="errorPage"> </default-action-ref> <!-- 配置一个action,给默认action用目的:只是跳转到一个页面--> <action name="errorPage"> <result>/errorPage.jsp</result> </action>
配置默认页面
struts2默认寻找路径下index页面,需要在web.xml中配置index页面 <default-action-ref name="user" />包下配置主入口,需要删除index页面
方法与请求的映射关系
一一对应
<action name="" class="" method="">
通配符
<action name="user_*" class="bean.User" method="{1}"> <action name="*_*" class="{1}" method="{2}">会引起安全问题
动态方法调用
书写访问路径href="product!add.action"有安全隐患 需要在structs.xml中配置常量 0配置(注解项目)中使用
访问servlet的API
- 获取与域对象对应 的Map
- 操作Map简介操作域对象
ActionContext访问
- 这是一个门面对象
接口注入
- 根据需要实现接口
- ServletRequestAware
- ServletResponseAware
- SessionAware
- ServletContextAware
- 在action初始化的时候,将接口实现类中持有的域对象注入Action中
ServletActionContext访问
- 静态get方法获取域对象
- get方法从ThreadLocal中获取一个ActionContext,其中持有一个域对象的map
结果页面的配置
全局结果页面
- 对包下所有Action有效
局部结果页面
- 局部覆盖全局
结果集类型
- chain
- 跳转(转发)到Action
- dispatcher
- 转发到页面
- redirect
- 重定向到页面
- redirectAction
- 重定向到Action
- 同一个包下一般省略路径
- json
- 用于回写json数据
- 需要导入Struts2-json-plugin-2.3.32包
- 需要action所在的包继承json-default
- 内部解决了死循环内存溢出问题
数据封装
参数的接收机制
- ParametersInterceptor 的doIntercept方法封装参数
- invoke执行action的业务逻辑
属性驱动
set方法
- 需要在action中定义字段和set方法
表达式方法
- 页面中以bean.age形式书写,属性与bean类属性一一对应
- 在Action中持有Bean实例,并提供Bean的get和set方法
- 封装数据时,struts会调用get方法获取Action中 的Bean,在拦截器的循环中逐个封装属性
- 如果没有get方法,每次的Bean都是new出来的,并覆盖之前的引用,因此只能封装一条 属性
模型驱动
- 实现ModelDriven接口
- Action中持有Bean引用且实例化
- ModelDriven的实现类中封装方法对Bean进行非空判断,为空则直接返回
- Action中重写getModel方法
- Action中同时有模型驱动与属性驱动时,模型驱动有限封装数据,剩下的数据由属性驱动处理
- 模型中有复杂类型时,在页面用ongl表达式书写
- model在值栈中存放的名字是”model”,取值用model.xxx
复杂类型封装
内置转换器
- boolean 和 Boolean
- char和 Character
- int 和 Integer
- long 和 Long
- float 和 Float
- double 和 Double
- Date 可以接收 yyyy-MM-dd格式字符串
- 数组 可以将多个同名参数,转换到数组中
list集合
- 属性驱动的表达式方法
- list[0].age形式书写,list[0]对应一个bean实例
- Action中或者model中持有list实例,并提供set和get方法
Set集合
- 属性驱动的表达式方法
- list.makeNew[0].age形式书写,list[0]对应一个bean实例
- Action中或者model中持有set实例,并提供set和get方法
map集合
- 属性驱动的表达式方法
- map[“key”].age形式书写map[“key”]对应一个”key”与 bean实例的键值对
- Action中或者model中持有map实例,并提供set和get方法
自定义转换器
- 实现 TypeConverter
- 直接实现 TypeConverter 接口
- 继承 DefaultTypeConverter 类
- 继承 StrutsTypeConverter 类
- 提供页面到Bean的数据转换和Bean到页面的数据显示
- 注册转换器
类型转换错误处理机制
- ConversionErrorInterceptor收集并封装错误
- 将错误封装进actionErrors,actionMessages,fieldErrors集合中
- 集合被ValidationAwareSupport持有,ValidationAwareSupport被ActionSupport持有
- 集合为Map
值栈
概述
- 挂载在request上的一个对象
- request.getAttribute(“struts.valueStack”)
- 实现类OnglValueStack
- 生命周期与request相同
内部结构
root对象
- CompoundRoot root;
- extends CopyOnWriteArrayList,是一个ArrayList集合
- ONGLValueStack中持有引用
- ONGLValueStack的构造方法中调用setRoot方法时创建这个对象
- 持有Action的引用
Context对象
- OgnlContext context;
- 是Map的一个实现类
- ONGLValueStack中持有引用
- ONGLValueStack的构造方法中调用setRoot方法时创建这个对象
- 持有一个HashMap用于存取值
- 持有root对象的引用
ValueStack与ActionContext的关系
- ActionContext内置了一个map,ValueStack存放在这个map中
- ActionContext是一个门面,持有Action类所有相关数据的引用
- ActionContext的put/get,其实是对其中持有的Map
存放数据机制
- 直接调用值栈的set方法存值
- 创建了一个map
- 这个map调用了push方法被压入root栈
- root栈存放数据遵循先进后出原则
- map栈按键值对存取数据
- Action提供成员变量,并提供get方法
- Action就在root栈中,Action属性可以被搜索
创建过程分析
StrutsPrepareAndExecuteFilter的dofilter方法
prepare.createActionContext方法创建了ActionContext
- 创建了ActionContext的线程绑定的实例
- 创建了值栈 ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
- 将servlet相关对象放入了map栈中stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
- 此时root栈为空
prepare.wrapRequest
- 加强了request
- 装饰类的引用放入ActionContext
execute.executeAction
getContainer().getInstance(ActionProxyFactory.class).createActionProxy创建了Action的动态代理
注入了方法执行者的引用
ActionInvocation inv = createActionInvocation(extraContext, true); container.inject(inv); 持有所有的拦截器,invoke方法中迭代拦截器,拦截器执行拦截方法并回调
注入代理并加载参数
container.inject(proxy); proxy.prepare(); invocation.init(this); 初始化了方法执行者
初始化方法执行者
createAction(contextMap); 生成了Action(定义的Action实例) stack.push(action); 将action压入root栈顶 contextMap.put("action", action); 将action放入map栈 createInterceptors(proxy); List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping(proxy.getConfig().getInterceptors()); 根据proxy之前加载的参数获取所有拦截器并注入方法执行者(现在可以执行拦截器方法并回调了) 好处:将拦截的控制权交给拦截器 责任链模式
执行拦截器方法
如果实现了modelDriven接口,拦截器将model压入root栈栈顶 param拦截器执行intercept方法时,将参数放入root栈,如果 栈中有model,就会先放入model中,剩下的作为字段存入root栈
prepare.cleanupRequest
ONGL
- Object-Graph Navigation Language
作用
- 对象方法调用
- ’itcast’.toUpperCase()
- 类静态方法调用和值访问
- 格式:@java.lang.Math@max(10,20)
- @cn.itcast.MyConstant@APP_NAME
- struts.ognl.allowStaticMethodAccess=true开启功能
- 赋值操作和表达式串联
- 访问ONGL上文
- 操作集合对象
- 构造
- {“green”, “red”, “blue”}构造一个List,#{“key1” : “value1”, “key2” : “value2”, “key3” : “value3”}构造一个Map
- 投影操作
- collection.{XXX} ,将每个元素的xxx属性组成新集合
表达式
- 格式
- 访问root栈
- set放入的值,通过名字(引用)访问
- 压入值栈的值,[0].top
- Map栈内容 (如request、response、session、servletContext、attr、parameters)
- 需要#key来引用访问,例如 #request.name 相当于 request.getAttribute(“name” )
- 搜索机制
- 不加#,直接写名字(引用)
- 执行值栈的findValue方法
- (在Context中)搜索root栈,之后搜索Map栈
- 强制解析/强制不解析
- %{‘key’}强制不解析
- %{key}强制解析
- 标签默认不解析,此时从值栈中取值需要强制解析
- 配置文件访问值栈
- ${}
EL表达式对ONGL的解析
- 加强了request的getAttribute方法
- 装饰模式,持有request实例
- getAttribute方法先执行request的默认方法,如果搜索不到值,获取值栈的引用,并调用值栈的findValue方法
- 使用#获取值会报错
- 调用了findValue方法,而findValue方法把#作为字符串的一部分解析
标签库
控制标签
<s:if> <s:elseif> <s:else>标签
test属性可以接受ONGL表达式
<s:a>标签
<!-- action是action的名字 -->
<s:a action="product_find" namespace="/">
<!-- name:是参数名,value:参数值
原因:value:是个ognl表达式
-->
<s:param name="name" value="'苹果'"/>
我是新的超链接
</s:a>
数据标签
<s:property/>
value属性,接收OGNL表达式
default 属性, 如果OGNL表达式,取不到值,default设置显示默认值
escapeHtml 属性, 是否对HTML标签转义输出 (默认是转义,可以关闭)
<s:iterator>
value:迭代的集合,支持OGNL表达式,如果没有设置该属性,则默认使用值栈栈顶的集合来迭代
var:引用变量的名称,该变量是集合迭代时的子元素
会被压入root栈,且在map栈建立副本
status:引用迭代时的状态对象IteraterStatus实例,
int getCount(),返回当前迭代了几个元素;
int getIndex(),返回当前迭代元素的索引;
Boolean isEvent(),
boolean isOdd(),
boolean isFirst(),
boolean isLast(),
<s:fielderror/>
<s:actionmessage/>
<s:i18n>
<s:param>
模板和主题
Struts2 模板文件,支持两种Freemarker生成 (.ftl模板文件) , Velocity生成 (.vm 模板文件) — struts2默认采用 Freemarker
提供四种主题 :
- Simple 没有任何修饰效果,最简单主题
- Xhtml 通过 布局表格 自动排版 (默认主题 )
- css_xhtml 通过CSS进行排版布局
- ajax 以Xhtml模板为基础,增加ajax功能
配置默认布局
<constant name="struts.ui.theme" value="simple"/>
表单标签
action属性,对应 struts.xml <action>元素name属性;
namespace属性,对象 struts.xml <package>元素 namespace属性
<s:textfield>, <s:password>, <s:hidden>, <s:textarea>
<s:textfield> 文本域 ,生成 <input type=”text” >
<s:password> 密码域 ,生成<input type=”password” >
<s:hidden> 隐藏域 , 生成 <input type=”hidden” >
<s:radio>、<s:checkboxlist>、<s:select>
<s:radio> 接收list或者map 生成一组单选按钮
<s:select> 接收list或者map ,生成一组下拉列表
<s:checkboxlist> 接收list或者map ,生成一组复选框
list="{'nan','nv'}"
list="#{'nan':'男','nv':'女'}"
数据回显的区别
- 表单标签:name支持ognl表达式,value不支持,直接显示值
- 其他所有通用标签,value支持ognl表达式的