1.访问ServletApi三种方式
1).在Action访问Servlet API:
在Action类中操作Servlet中相关的对象(request,repsonse,session,cookie等)
- 获取请求参数: request.getParameter(String ..)
- 设置/获取共享数据: 域对象.setAttribute(String name,Object val);
- 操作Cookie: response.addCookie(...);
2).Action访问Servlet API,有三种方式:
1.通过Action实现感知接口.
操作步骤:
- 1):实现对应的感知接口.
- 2):覆盖接口中对应的setter方法.
- 3):定义成员变量接受setter方法中的参数.
- 4):使用对象.
原理:拦截器.
常用的感知接口:
ServletRequestAware ---->感知request对象.
ServletResponseAware---->感知response对象.
ServletContextAware----->感知application对象.
2.使用ServletActionContext类.
好比是一个工具类,封装当前请求的对象和响应对象信息.
ServletActionContext类提供了很多静态方法,可以直接获取request/response等对象.
常用的方法:
static HttpServletRequest getRequest() :获取请求对象
static HttpServletResponse getResponse() :获取响应对象
static ServletContext getServletContext() :获取应用对象
3.使用ActionContext类.
ActionContext:Action的上下文对象(环境).封装了当前请求/响应的所有的数据.
获取ActionContext对象:
ActionContext ctx = ActionContext.getContext();
3).Action访问Servlet API,有三种方式对比和选择:
- 1.通过Action实现感知接口.
简单/操作麻烦/让Action和Servlet的API耦合在一起了. - 2.使用ServletActionContext类.
理解/使用都很简单/让Action和Servlet的API存在耦合. - 3.使用ActionContext类.
简单/让Action和Servlet的API没有耦合,但是不能操作Cookie.
在开发中,使用方式2和方式3比较多,Struts2官方建议使用方式3.先使用方式3,搞不定再使用方式2.
2.Action获取请求参数三种方式
J2EE的最佳实践:按照功能职责,分层开发:
表现层:
职责:
1.接受请求数据
2.把数据封装Model对象
3.调用业务逻辑方法处理请求
4.控制界面跳转
MVC思想:
M:数据对象.
V:JSP
C:StrutsPrepareAndExecuteFilter(前端控制器.)
问题:Action到底充当什么角色?
Action获取请求参数三种方式:
1).第一种:Action本身作为Model对象,通过setter方法封装(属性注入)
JSP(注意表单参数的名字):
Action:
注意:表单中的参数名称要和Action中的setter方法的属性名相同.
2).第二种:创建独立Model对象,页面通过ognl表达式封装(属性注入)
1):创建Model对象,封装参数数据信息.
2):JSP(注意表单参数的名字):
给当请求Action中的user属性对应的对象中,设置username和password值.
Action中的:user属性.--->对应一个对象.
user属性对应对象的:username/password属性
:先从Action中获取是否有user对象(getUser方法),
如果有,就直接在该对象中设置其他参数值.
如果没有:创建新的User对象.
User user = Struts2框架通过getUser方法来获取User对象.
if(uesr ==null){
user = new User();
}
把参数设置到user对象中去.
3):Action代码:
方式1:同时提供getter/setter.
方式2:在生命对象的时候,new出来,再提供getter方法即可.
3).第三种:使用ModelDriven接口,对请求数据进行封装(模型驱动)
1):JSP(注意表单参数的名字):
username和password对应:
Model中的属性:
2):Action类:
4).Action获取请求参数三种方式对比:
第一种:Action本身作为Model对象,通过setter方法封装(属性注入)
1:如果参数比较多,此时Action中将提供大量字段和setter方法,Action很臃肿.
2:我们还得手动把数据封装到对象中去.
第二种:创建独立Model对象,页面通过ognl表达式封装(属性注入)
1:表单参数的name属性值,不是很清晰.
第三种:使用ModelDriven接口,对请求数据进行封装(模型驱动)
1:不能为多个对象封装数据.
一般的,我们在开发中使用第二种较多.
但是,修改密码的操作:
在User对象中,只有密码这个属性.
有时候,我们是方式1+方式2或者方式1+方式3.
3.Interceptor(拦截器)的美
拦截器:Interceptor
拦截器:
Struts2拦截器是在访问某个Action的某个方法之前或之后实施拦截,并且Struts2拦截器是可插拔的,拦截器是AOP的一种实现.
AOP:
面向切面编程.其实现原理:动态代理模式--->留给Spring
WebWork中文文档解释:
拦截器是动态拦截Action调用的对象。它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的代码的方式。
拦截器栈(Interceptor Stack):
Struts2拦截器栈就是将拦截器按一定的顺序连接成一条链。在访问被拦截的方法或字段时,Struts2拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器的"美":
DRY原则:Dont't Repeat Yourself.
拦截器在设计和程序结构上的优点:
拦截器能把很多功能从Action中独立出来,分散到不同的拦截器里面,减少了Action的代码。如此,拦截器和Action本身的功能都更单一了。当通用的功能代码被封装在拦截器里面(代码模块化),就可以对不同的Action,根据功能需要,来配置相应功能的拦截器了。提高了拦截器所实现的功能的重用性,也变相实现了装配式和可插拔式的体系结构,使得整个系统结构变得更灵活。
1.简化Action的实现
2.功能更单一
3.通用代码模块化
4.提高重用性
4.Struts2执行流程
1).Struts2的执行步骤:
- ①.客户端发送请求;
- ②.该请求经过一系列的过滤器(Filter):其中可选过滤器ActionContextCleanUp,帮助Struts2和其他框架集成。例如:SiteMesh Plugin。
- ③.接着FilterDispatcher被调用,FilterDispatcher询问ActionMapper,来决定该请求是否需要调用某个Action。
- ④.若ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy。
- ⑤.ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类。
- ⑥.ActionProxy创建一个ActionInvocation的实例。
- ⑦.ActionInvocation实例调用Action的前后,涉及到相关拦截器(Intercepter)的调用。
- ⑧.一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果是一个JSP或其他页面(也可以是其他的Action链)。 JSP页面展现可使用Struts2框架中的标签(该过程会涉及ActionMapper)。
在上述过程中所有的对象(Action、Interceptors、Results等)都由xwork容器中的ObjectFactory创建。
2).Struts2中内置的拦截器:
在struts-core-2.3.x.jar--->struts-default.xml中
常见的拦截器:
- 1:params拦截器
这个拦截器偷偷的把请求参数设置到相应的Action的属性去的,并自动进行类型转换。 - 2.modelDriven拦截器
如果Action实现ModelDriven接口,它将getModel()取得的模型对象存入OgnlValueStack中。 - 3.execption拦截器
顾名思义,在抛出异常的时候,这个拦截器起作用。最好把它放在第一位,让它能捕获所有的异常。 - 4.validation拦截器
调用验证框架读取 *-validation.xml文件,并且应用在这些文件中声明的校验。 - 5.token拦截器
核对当前Action请求(request)的有效标识,防止重复提交Action请求。 - 6.fileUpload拦截器
用来处理文件上传 - 7.workflow拦截器
调用Action的validate方法,一旦有错误返回,重新定位到INPUT结果视图 - 8.servletConfig
通过感知接口,获取感应对象
5.自定义拦截器
1).需求:
做一个登陆拦截器(LoginCheckInterceptor),拦截Action访问.
如果强行访问某一个需要登陆之后才能访问的Action,直接跳转到登录页面.
2).操作步骤:
步骤1.定义拦截器类
方式1:实现com.opensymphony.xwork2.interceptor.Interceptor接口.
方式2:继承com.opensymphony.xwork2.interceptor.AbstractInterceptor类.
步骤2.在struts.xml中注册拦截器
第一步:先在<package>中声明拦截器LoginInterceptor.
第二步:在<action>中来引用LoginInterceptor拦截器.
上图的拦截器引用配置:
因为是在<action name="main">中引用了登录拦截器,所以只有当前Action才会做登录拦截功能.其他Action元素,没有这个功能.
如果其他多个action元素都做登录检查功能,那么所有action元素都得配置:
<interceptor-ref name="loginCheck" />
如此一来,重复配置.
3).可以通过全局配置拦截器:
如果不拦截某一个Action:
1.方式一:代码中放行:
2.方式二:默认拦截器
6.OGNL和ValueStack(值栈)
1).什么是OGNL
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目。
Struts2框架使用OGNL作为默认的表达式语言。
EL(表达式语言),OGNL就是EL的升级版.
作用:Action和视图(JSP)之间数据交互的桥梁.
讲解OGNL之前,先得学习ValueStack.
2).什么是ValueStack
值栈是对应每一个请求对象的轻量级的内存数据中心。
每一次请求的时候,都会创建一个新的ValueStack对象,该ValueStack对象封装了这一次请求相关的数据信息.
- 1).ValueStack实际是一个接口,在Struts2中利用OGNL时,实际上使用的是实现了该接口的OgnlValueStack类,这个类是Struts2利用OGNL的基础。
- 2).ValueStack贯穿整个Action的生命周期(一次请求):每个Action类的实例都拥有一个ValueStack对象。 ValueStack相当于数据的中转站,在其中保存该次请求和当前Action对象和其他相关对象信息。
每一次的请求: 一个新的Action对象,新的ActionContext对象,新的ValueStack对象.
- 3).Struts2框架把ValueStack对象保存在名为“struts.valueStack”的request属性中。
3).如何获取ValueStack.
- 方式1: 因为ValueStack在请求中,属性名为:struts.valueStack.
ValueStack vs = request.getAttribute("struts.valueStack");
ValueStack vs = ServletActionContext.getRequest().getAttribute("struts.valueStack");
ValueStack vs = ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
- 方式2: 通过ActionContext对象获取
ValueStack vs = ActionContext.getContext().getValueStack();
以上两种方式获取的是同一个ValueStack对象.
4).ValueStack内部结构
ValueStack对象中有两个很重要的属性,这两个属性就是ValueStack的主要内部结构:
属性 | 类型 | 数据储存类型 | 引用 | 获取数据 |
---|---|---|---|---|
root | CompoundRoot extends ArrayList : 栈的数据结构(后进先出) | 主要存储Action对象相关的数据信息. | - | 从root中获取数据: 直接使用属性名获取. ---><s:property value="属性名"/> |
context | Map 上下文 | 主要存储映射关系数据.(key-value). | 存在用root对象的引用(只要拿到context对象就可以获取到root对象);context中还存在request、session、application、attr、parameters对象的引用。 | 从context中获取数据: #key ---><s:property value="#key"/> |
5).把数据放入ValueStack.
1.把数据放入root中:(栈,ArrayList.每次都要压在栈顶)
- 方式1:ValueStack对象.getRoot().add(0, Obejct val);//把数据压入栈顶
- 方式2:ValueStack对象.getRoot().push(Object val):等价于valueStack对象.getRoot().add(0, Obejct val);
- 方式3:ValueStack对象.set(String propertyName,Object value);
- 方式4.在Action中提供一个可访问的属性(getter方法).
此时:Action在栈顶,往Action中存储属性和属性值.
2).把数据放入context中:
- 方式1:ValueStack对象.getContext().put(String key,Object value); 太长了.
- 方式2:ActionContext对象.put(String key,Object value);
一般的:把集合中的数据放入context中.
6).如何从JSP中取出ValueStack中的数据:
- 此时必须使用Struts的标签.--->先引入Struts2标签
<%@ taglib uri="/struts-tags" prefix="s"%>
Struts2的调试标签:<s:debug/> :主要用来观察数据在哪一个空间(root,context).
访问方式:<s:property value="OGNL表达式"/>.
1).获取root中数据:
若:放入root中的数据,没有属性名:<s:property value="[栈中的存储位置].top"/>:栈顶是0.
2).把context中数据:
<s:property value="#key"/>
EL可以访问ValueStack中的数据:
不建议这么做,为什么可以呢:
Struts2重新包装而来请求对象.${msg}---><%=pageContext.findAttribute("msg") %>
StrutsRequestWrapper:先从ValueStack中取出数据,再放入request中.