Struts2.0中ActionInvocation使用
- 格式:docx
- 大小:38.18 KB
- 文档页数:4
Struts2在WebWork基础上发展起来的,可扩展的JAVA EE web框架。
框架设计的目标贯穿整个开发周期,从开发到发布,包括维护的整个过程。
Action类:Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。
Struts2提供一个ActionSupport基类去实现常用的接口。
Action接口不是必须的,任何有execute标识的POJO 对象都可以用作Struts2的Action对象。
线程模式:Struts2Action对象为每一个请求产生一个实例,因此没有线程安全问题。
Servlet依赖:Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。
如果需要,Struts2 Action仍然可以访问初始的request和response。
但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
可测试性:Struts2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
捕获输入:Struts2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。
Action属性能够通过web页面上的taglibs访问。
表达式语言:Struts 2 使用"ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。
ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
Struts2不仅支持JSTL,Struts2 还可使用OGNL进行类型转换。
提供基本和常用对象的转换器。
校验:Struts2支持通过validate方法和XWork校验框架来进行校验。
XWork 校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性。
struts2核心工作流程与原理做为一名技术人员,听到太多关于.net和java的比较的话题。
我想对那些技术人员说,请先了解一下什么是java(或者.net)吧,其实你根本不了解。
这是Struts2官方站点提供的Struts 2 的整体结构。
一个请求在Struts2框架中的处理大概分为以下几个步骤1.客户端提起一个(HttpServletRequest)请求,如上文在浏览器中输入”http://localhost:8080/TestMvc/add.action”就是提起一个(HttpServletRequest)请求。
2.请求被提交到一系列(主要是三层)的过滤器(Filter),如(ActionContextCleanUp、其他过滤器(SiteMesh等)、 FilterDispatcher)。
注意这里是有顺序的,先ActionContextCleanUp,再其他过滤器(SiteMesh等)、最后到 FilterDispatcher。
3.FilterDispatcher是控制器的核心,就是mvc中c控制层的核心。
下面粗略的分析下我理解的FilterDispatcher工作流程和原理:FilterDispatcher进行初始化并启用核心doFilter其代码如下:public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain ) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;HttpServletResponse response = (HttpServletResponse) res;ServletContext servletContext = filterConfig.getServletContext();// 在这里处理了HttpServletRequest和HttpServletResponse。
Struts2基本流程及原理1.Struts 2的基本流程Struts 2框架由3个部分组成:核心控制器FilterDispatcher、业务控制器和用户实现的业务逻辑组件。
在这3个部分里,Struts 2框架提供了核心控制器FilterDispatcher,而用户需要实现业务控制器和业务逻辑组件。
2.核心控制器:FilterDispatcherFilterDispatcher是Struts 2框架的核心控制器,该控制器作为一个Filter运行在Web应用中,它负责拦截所有的用户请求,当用户请求到达时,该Filter会过滤用户请求。
如果用户请求以action结尾,该请求将被转入Struts 2框架处理。
Struts 2框架获得了*.action请求后,将根据*.action请求的前面部分决定调用哪个业务逻辑组件,例如,对于login.action请求,Struts 2调用名为login的Action来处理该请求。
Struts 2应用中的Action都被定义在struts.xml文件中,在该文件中定义Action时,定义了该Action的name属性和class属性,其中name属性决定了该Action处理哪个用户请求,而class属性决定了该Action的实现类。
Struts 2用于处理用户请求的Action实例,并不是用户实现的业务控制器,而是Action代理——因为用户实现的业务控制器并没有与Servlet API耦合,显然无法处理用户请求。
而Struts 2框架提供了系列拦截器,该系列拦截器负责将HttpServletRequest请求中的请求参数解析出来,传入到Action中,并回调Action 的execute方法来处理用户请求。
显然,上面的处理过程是典型的AOP(面向切面编程)处理方式。
用户实现的Action类仅仅是Struts 2的Action代理的代理目标。
用户实现的业务控制器(Action)则包含了对用户请求的处理。
Struts 2拦截器的研究与应用拦截器是一个一组过滤器的容器,每个过滤器可以在请求到达Action之前或之后,对请求进行一些处理。
每个拦截器可以有一个或多个配置参数,用于对拦截到的请求进行一些处理,比如对请求进行过滤,对请求参数进行解密等。
Struts2内置了一系列常用的拦截器,如TokenInterceptor、PrepareInterceptor、ModelDrivenInterceptor等,我们可以直接使用。
同时,我们也可以在项目中自定义拦截器,以满足我们的特定需求。
Struts2框架中,我们可以在多个地方使用拦截器,包括全局配置、action配置以及每个interceptor的参数配置。
2、Action配置:在Action的定义中,指定一个或多个使用的拦截器。
<action name="login" class="com.test.acton.LoginAction"><interceptor-ref name="validation"><param name="excludeMethods">input,back,cancel,browse</param></interceptor-ref><result name="success">/login.jsp</result></action>3、Interceptor参数配置:在自定义拦截器中,可以自定义一些参数,用于处理某些特定的请求,在配置文件中进行配置。
Struts2的拦截器可以组成一个独立的拦截器栈,每个拦截器的执行顺序是固定的,首先执行的是拦截器栈底部的拦截器,然后执行拦截器栈中间的拦截器,最后执行拦截器栈顶的拦截器。
即:先进后出(FILO)。
深入详解Struts2——struts2框架的调用流程struts2调用的时序图如下所示:1、当Servlet容器接收到一个请求后,将请求交给你在web.xml文件配置的过滤器FilterDispatcher,调用他的doFilter()方法2、FilterDispatcher询问ActionMapper,以便确定这个请求是否有对应的action调用。
3、ActionMapper返回一个描述action调用的ActionMapping对象4、FilterDispatcher调用Dispatcher类的serviceAction()方法5、Dispatcher调用ActionProxy的execute()方法6、ActionProxy设置ActionInvocation对象的执行上下文,然后调用其invoke方法7、ActionInvocation的invoke方法从拦截器映射中查找尚未执行的拦截器,调用他的intercept(invocation)方法,并将自身对象引用作为参数传递给拦截器。
8、拦截器完成某些预处理工作后,反过来调用ActionInvocation的invoke方法,ActionInvocation维护者自己的状态,所以他知道哪些拦截器已经被执行,如果还没有执行的拦截器,就继续执行他的intercept(invocation)方法。
9、如果所有的拦截器都已经执行过了,就调用action实例的execute方法10、ActionInvocation根据action执行返回的结果码,查找对应的Result,调用Result的execute(invocation)方法,将结果页面呈现给用户。
11、ActionInvocation的invoke方法将控制权返回给拦截器映射中的最后一个拦截器,该拦截器完成所有必须的后期处理工作,然后从intercept(invocation)方法返回,允许前一个拦截器执行他自己的后处理工作。
客户端初始化一个指向Servlet容器的请求;请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器)接着FiterDispatcher询问ActionMapper来决定这个请求是否要调用Action如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxyActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action ActonProxy创建一个ActionInvocation的实例ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关的拦截器的调用一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到相应的返回结果Package:处于模块化的考虑,可将各种动作分门别类的组织成不同的包必须有一个name属性,namespace属性是可选的以“/”作为默认值,调用该包里的动作,必须将命名空间的添加到有关的URI字符串中使用extends扩展某一个包<package name="default" namespace="/" extends="struts-default"> Result元素:是<action>的子元素,指出动作完成后把控制权交给谁对应着动作方法的返回值;同一个action可以有多个result;结果和方法返回值之间的映射关系有result的name属性负责建立,默认值是:success;如果某方法返回一个值,而该值没有与之匹配的result,structs 将尝试在global-results元素下为它匹配结果,再找不到就抛出异常可以在struts.xml文件里利用constant元素来覆盖default.properties 文件里的某个设置POJ O:plain and old Java Object动作类需要遵循的规则遵守JavaBeans属性名的命名规则;必须有一个不带任何参数的构造器;每个动作类至少有一个方法供struts在执行时调用(默认为execute)同一个动作类可以包含多个方法;编写动作类可以不考虑线程安全的问题Action的作用得到请求参数;调用逻辑处理类处理业务;返回String类型结果Action的一些重要元素ActionContext 数据环境需要为每个Action准备一个数据环境获取与Web容器相关的对象Interceptor 丰富的层次结构使用类似责任链的设计模式对Action的责任进行分类并连接起来Result 执行结果执行结果被抽象成一个层次,可以定义任意类型的View层的构造ActionProxy 执行环境需要一个类似调度器的产品将上面的元素整合起来ActionInvocation 调度者ActionProxy接口中有个比较特殊的变量:ActionInvocation,它就是Action的调度者,负责一些列的调度动作可以继承一下5个静态字段SUCCESS NONE ERROR INPUT LOGIN ActionSupport类ServletActionContext对象getRequest() 返回HttpServetRequest对象getSession()getResponse()getServletContext()result 的type属性默认为dispatcher,含义是把控制权转交给某个JSp页面type的其他常用类型:Redirect 重定向到另一个URLRedirectAction 重定向到另一个动作Session的创建和使用private Map session;public void setSession(Map session) {this.session = session;}public String execute() {this.session.put("USER_NAME", "Test User 1");return SUCCESS;}}Map session = invocation.getInvocationContext().getSession();OGNL:对象图形导航语言表达式:ONGL就是表达式,用简洁直观的语法表达想法。
1.1???????? 简单例子先做一个最简单的struts2的例子:在浏览器中请求一个action,然后返回一个字符串到jsp 页面上显示出来。
第一步:把struts2最低配置的jar包加入的项目中。
???????? commons-logging-1.0.4.jar??? freemarker-2.3.8.jar??? ognl-2.6.11.jar??? struts2-core-2.0.11.jar??? xwork-2.0.4.jar第二步:在web.xml中加入拦截器配置。
???????? <filter>??????? <filter-name>struts2</filter-name>?<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class></filter><filter-mapping>??????? <filter-name>struts2</filter-name>??????? <url-pattern>/*</url-pattern></filter-mapping>第三步:把空的struts.xml配置文件放到项目src下面。
???????? <struts></struts>第四部:编写自定义的action类。
package test;import com.opensymphony.xwork2.ActionSupport;public class HelloAction extends ActionSupport {??? private String str;??? public String hello() {?????? this.str = "hello!!!";?????? return "success";??? }??? public String getStr() {?????? return str;??? }??? public void setStr(String str) {?????? this.str = str;??? }第五步:编写struts.xml配置文件。
struts2工作流程解析1. 初始的哀求通过一条标准的过滤器链,到达 servlet 容器 ( 比如toat 容器,WebSphere 容器 )。
2. 过滤器链包括可选的ActionConttCleanUp 过滤器,用于系统整合技术,如 SiteMesh 插件。
3. 接着调用 FilterDiser(或者SutsPrepareAndExeeFilter),FilterDispatcher 查找 ActionMapper,以确定这个哀求是否需要调用某个 Action。
4. 假如 ActionMapper 确定需要调用某个 Action,FilterDispatcher 将控制权交给 ActionProxy。
5. ActionProxy 依照框架的配置文件(struts.xml),找到需要调用的 Action 类。
6.ActionProxy 创建一个 ActionInvoion 的实例。
ActionInvocation 先调用相关的拦截器(Action 调用之前的部分),最后调用 Action。
7.一旦 Action 调用返回结果,ActionInvocation 按照 struts.xml 配置文件,查找对应的转发路径。
返回结果通常是(但不总是,也可能是另外的一个 Action 链)JSP 技术或者 FreeMarker 的模版技术的网页展现。
Struts2 的标签和其他视图层组件,协助展现我们所需要的显示结果。
在此,我想说清晰一些,终于的显示结果一定是 HTML 标签。
标签库技术和其他视图层技术只是为了动态生成 HTML 标签。
8. 接着根据相反次序执行拦截器链 ( 执行 Action 调用之后的部分 )。
最后,响应通过滤器链返回(过滤器技术执行流程与拦截器一样,都是先执行前面部分,后执行后面部)。
假如过滤器链中存在ActionContextCleanUp,FilterDispatcher 不会清理线程局部的ActionContext。
简述struts2的执行流程Struts2的执行流程是一个基于MVC架构的框架,它将用户的请求转发给Action处理,并将处理结果返回给用户。
The execution process of Struts2 is based on MVC architecture, it forwards user requests to Action for processing and returns the processing results to the user.用户发送请求到Struts2的前端控制器DispatcherServlet,DispatcherServlet根据用户请求找到对应的Action类。
Users send requests to the front controller DispatcherServlet of Struts2, and DispatcherServlet finds the corresponding Action class based on the user's request.DispatcherServlet创建ActionContext,并将用户请求的数据封装到ActionContext中。
DispatcherServlet creates ActionContext and encapsulates the user's request data into the ActionContext.ActionContext将数据传递给ActionInvocation,ActionInvocation执行拦截器栈的拦截器。
The ActionContext passes the data to ActionInvocation, and ActionInvocation executes the interceptors in the interceptor stack.拦截器栈中的拦截器按照预先定义的顺序依次执行,对请求进行处理并可能修改Action的执行流程。
Struts2的使⽤注解配置Action(零配置)1.⾸先引⼊struts2注解的jar包:struts2-convention-plugin.jar------------------------------第⼀种⽅式-------------------------------------2.struts的配置<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""/dtds/struts-2.3.dtd"><struts><package name="lxyPackage" extends="json-default" namespace="/"><!-- S QLQ 增加的培训类别管理 --><global-results><result name="success" type="json"><param name="root">response</param></result></global-results></struts>3.TestAnnotationAction.java(测试JSON,跳转,重定向三种⽅式)package cn.xm.exam.action.trainContent;import java.util.HashMap;import java.util.Map;import org.apache.struts2.convention.annotation.Action;import space;import org.apache.struts2.convention.annotation.ParentPackage;import org.apache.struts2.convention.annotation.Result;import org.apache.struts2.convention.annotation.Results;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Controller;import com.opensymphony.xwork2.ActionSupport;@Controller@Scope("prototype") // 多例@Namespace("/")@Results({ @Result(name = "tiaozhuan", location = "/view/testPerson/outEmployeeAllot.jsp"),@Result(name = "redirect", type = "redirect", location = "/view/testPerson/outEmployeeAllot.jsp") })@ParentPackage("lxyPackage") // 属于哪个packagepublic class TestAnnotationAction extends ActionSupport {private Map response;@Action("/test1")public String test1() {response = new HashMap();response.put("hello", "hello");return SUCCESS;}@Action("/test2")public String test2() {return "tiaozhuan";}@Action("/test3")public String test3() {return "redirect";}public Map getResponse() {return response;}public void setResponse(Map response) {this.response = response;}}4.测试:(1)测试返回JSON(2)测试转发(3)测试重定向------------------------------第⼆种⽅式(注解返回JSON、跳转、重定向)-------------------------------------pom.xml依赖:<dependency><groupId>org.apache.struts</groupId><artifactId>struts2-convention-plugin</artifactId><version>2.3.24</version></dependency><dependency><groupId>org.apache.struts</groupId><artifactId>struts2-core</artifactId><version>2.3.24</version></dependency><dependency><groupId>org.apache.struts</groupId><artifactId>struts2-json-plugin</artifactId><version>2.3.24</version></dependency>struts.xml配置<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN""/dtds/struts-2.3.dtd"><struts><constant name="struts.i18n.encoding" value="utf-8"></constant><constant name="devMode" value="true"></constant><constant name="struts.enable.DynamicMethodInvocation" value="true"/><!-- 配置拦截的后缀 --><constant name="struts.action.extension" value="action,do"/><package name="default" extends="json-default"></package></struts>测试代码:package cn.qlq.action;import org.apache.struts2.convention.annotation.Action;import space;import org.apache.struts2.convention.annotation.ParentPackage;import org.apache.struts2.convention.annotation.Result;import com.opensymphony.xwork2.ActionSupport;import com.opensymphony.xwork2.Preparable;@Namespace("/")@ParentPackage("default")public class FirstAction extends ActionSupport implements Preparable {private static final long serialVersionUID = 1L;private String test;@Overridepublic void prepare() throws Exception {System.out.println("这是所有⽅法前的处理");}@Action(value = "test",results = { @Result(name = "success1", location = "/index2.jsp", type = "redirect") ,@Result(name = "error", location = "/index2.jsp") ,@Result(name = "success" ,type = "json" , params = {"root","test"})})@Overridepublic String execute() throws Exception {test = "test";return super.execute();}public String getTest() {return test;}public void setTest(String test) {this.test = test;}}注意: @Result的type值默认为dispatcher(转发)。
Interceptor说明
Interceptor的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法。
而它所依赖的参数ActionInvocation则是我们之前章节中曾经提到过的著名的Action调度者。
我在这里需要指出的是一个很重要的方法invocation.invoke()。
这是ActionInvocation中的方法,而ActionInvocation是Action调度者,所以这个方法具备以下2层含义(详细看DefaultActionInvocation源代码):
1. 如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。
2. 如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action 执行。
3.
DefaultActionInvocation部分源代码:
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
UtilTimerStack.profile("interceptor: "+interceptor.getName(),
new UtilTimerStack.ProfilingBlock<String>() {
public String doProfiling() throws Exception {
resultCode =
interceptor.getInterceptor().intercept(DefaultActionInvocation.this); //递归调用拦截器
return null;
}
});
} else {
resultCode = invokeActionOnly();
}
每个拦截器中的代码的执行顺序,在Action之前,拦截器的执行顺序与堆栈中定义的一致;而在Action和Result之后,拦截器的执行顺序与堆栈中定义的顺序相反。
Interceptor拦截类型
从上面的分析,我们知道,整个拦截器的核心部分是invocation.invoke()这个函数的调用位置。
事实上,我们也正式根据这句代码的调用位置,来进行拦截类型的区分的。
在Struts2中,Interceptor的拦截类型,分成以下三类:
1. before
before拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之前。
这些代码,将依照拦截器定义的顺序,顺序执行。
2. after
after拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之后。
这些代码,将一招拦截器定义的顺序,逆序执行。
PreResultListener
有的时候,before拦截和after拦截对我们来说是不够的,因为我们需要在Action执行完之后,但是还没有回到视图层之前,做一些事情。
Struts2同样支持这样的拦截,这种拦截方式,是通过在拦截器中注册一个PreResultListener 的接口来实现的。
如:在拦截器中使用如下代码,其中MyPreResultListener实现了PreResultListener接口并在beforeResult方法中做了一些事情然后在拦截器类中加入action.addPreResultListener(new MyPreResultListener());
从源码中,我们可以看到,我们之前提到的Struts2的Action层的4个不同的层次,在这个方法中都有体现,他们分别是:拦截器(Interceptor)、Action、PreResultListener和Result。
在这个方法中,保证了这些层次的有序调用和执行
问题
使用Struts2作为web框架,知道它的拦截器(Interceptor)机制,类似与Filter 和Spring的AOP,于是实现了一个为Action增加自定义前置(before)动作和后置动作(after)的拦截器(曰:WInterceptor),不过用一段时间发现,在WInterceptor的after中,对Action对象的属性修改在页面看不到,对请求对象的属性设置也无效。
为什么在调用了Action之后(invokeAction())之后,request就不能使用了呢,拦截器不能改变Action的Result么?
问题的关键在于,在调用actionInvocation.invoke()的之后,不仅执行类Action,也执行类Result。
因而,等退回到拦截器的调用代码时,Result已经生成,View已经确定,这时你再修改模型(Action的属性)或请求对象的属性,对视图不会有任何影响。
解决办法:
方法一:使用现成的PreResultListener监听器事件
搞清楚原因,卷起袖子干吧,只要让WInterpretor的after事件,放在Result 的生成之前就行了。
看看XWork的拦截器接口注入的actionInvocation,其实就提供增加Result执行的前置监听事件-PreResultListener:
Java代码
1./**
2. * Register a {@link PreResultListener} to be notified after th
e Action is executed and
3. * before the Result is executed.
4. * <p/>
5. * The ActionInvocation implementation must guarantee that list
eners will be called in
6. * the order in which they are registered.
7. * <p/>
8. * Listener registration and execution does not need to be thre
ad-safe.
9. *
10. * @param listener the listener to add.
11. */
12.void addPreResultListener(PreResultListener listener);
/** * Register a {@link PreResultListener} to be notified after the Action is executed and * before the Result is executed. * <p/> * The ActionInvocation implementation must guarantee that listeners will be called in * the order in which they are registered. * <p/> * Listener registration and execution does not need to be thread-safe. * * @param listener the listener to add. */ void
addPreResultListener(PreResultListener listener);
因此,让拦截器实现这个接口,就可以自然实现Action执行after事件了。
方法二,实现自己的 ActionInvocation ,手动分离Action和Result 的执行
本来前面的方法已经很好了,可是,可是啊,在addPreResultListener里的异常,不会被Struts的框架捕获,而且,addPreResultListener接口不能传递自己的上下文参数,难道动用ThreadLocal传参?
研究了一下XWork的ActionInvocation 接口默认实现类DefaultActionInvocation,写了一个包装类,将Action的执行和Result的生成完全分开,或许有人用的着,放上来,见附件(ActionInvocationWrapper),如有不妥之处请告知。
exeucteAction是执行Action,executeResult是执行Result。