Struts2的Action

Struts2的结构

Action是Strus2的核心逻辑之一。但这并不意味着Action是复杂的,实际上Action很简单,一个普通的java类就能够做为一个Action。咱们之因此能够很方便的使用Action,是由于Struts2的框架对Action作了最大的支持。就像是一个老大,手底下有一大群牛逼的小弟,因此能够成天无所事事。Action的逻辑简单,可是围绕着Action的各个组件的逻辑就复杂的不行了。上图展现了struts2中一个从请求的获取到响应的流程。java

一个请求被获取后,会通过层层的Filter到达ActionMapper。ActionMapper能够判断该是否须要为该请求调用一个Action。若是是的话,这个请求会被送给ActionProxy。ActionProxy会经过配置文件和请求生成一个ActionInvocation。ActionInvocation的主要任务是根据运行对应的Action方法,并获取Result。在ActionInvocation中会先运行Interceptor,若是没有从Interceptor中获取Result,则会运行Action的方法。获取result后,会检查配置文件中Action对应的结果,并根据结果访问页面或者请求另一个Action(Action链)。安全


Action的实现

Action能够经过三种方式实现:
session

1.一个类能够是普通的java类,可是必须有一个execute()app

2.实现了Action接口,Action接口提供了execute()方法框架

3.继承了ActionSupport类型,ActionSupport实现了Action和其余的一些接口,提供了许多经常使用方法。工具


Action的流程

proxy应当是Action流程的开端,因此这里从Proxy开始进行分析一直到获取Result为止。this

DefaultActionProxy的execute中首先会设置ActionContext,将当前请求的Context给予ActionContext。每一个ActionContext都是基于ThreadLocal且静态的。也就说每一个请求都会生成一个ActionContext,这个ActionContext在这次请求是能够全局调用的spa

接下来DefaultActionInvocation的invoke方法被调用了。在invoke方法中首先会查看拦截器,这些拦截器被存储在名为interceptors的列表中。遍历这个链表,并运行拦截器的interceptor方法,将invocation自己做为参数传递给interceptor方法。interceptor方法的最后会从新调用invocation的invoke方法,因此该方法是递归方法。拦截器会不断递归interceptor方法,直到全部的拦截器都被调用过或者在一个拦截器中得到了一个resultCode。线程

invoke方法代码:code

public String invoke() throws Exception {
        String profileKey = "invoke: ";
        try {
            ...
            if (interceptors.hasNext()) {
                final InterceptorMapping interceptor = interceptors.next();
                String interceptorMsg = "interceptor: " + interceptor.getName();
                UtilTimerStack.push(interceptorMsg);
                try {
                     resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
                            }
                finally {
                    UtilTimerStack.pop(interceptorMsg);
                }
            } else {
                resultCode = invokeActionOnly();
            }
            ...
            if (!executed) {
                if (preResultListeners != null) {
                    for (Object preResultListener : preResultListeners) {
                        PreResultListener listener = (PreResultListener) preResultListener;
                        ...
                        try {
                            UtilTimerStack.push(_profileKey);
                            listener.beforeResult(this, resultCode);
                        }
                        finally {
                            UtilTimerStack.pop(_profileKey);
                        }
                    }
                }
                if (proxy.getExecuteResult()) {
                    executeResult();
                }
                executed = true;
            }
            return resultCode;
        }
        finally {
            UtilTimerStack.pop(profileKey);
        }
    }

若是运行完全部拦截器都没有获取ResultCode,那么就会运行请求指定的Action的方法了。该方法是经过反射机制获取的。

经过反射机制获取方法代码:

protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
        String methodName = proxy.getMethod();
        ...
        try {
            UtilTimerStack.push(timerKey);

            boolean methodCalled = false;
            Object methodResult = null;
            Method method = null;
            try {
                method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
            } catch (NoSuchMethodException e) {
                ...
            }
            if (!methodCalled) {
                methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);
            }
            return saveResult(actionConfig, methodResult);
        } catch (NoSuchMethodException e) {
           ...
        } catch (InvocationTargetException e) {
           ...
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }

获取到一个ResultCode后会检查PreResultListener,PreResultListener一般也是拦截器,可是不会运行interceptor方法,而是beforeResult方法。

最后会调用DefaultActionInvocation的executeResult方法。这个方法的做用是根据咱们获取的resultcode,在配置中获取一个Result对象,并同过Result对象的execute方法将响应结果发送出去。


Action与ServletAPI

Action的请求参数通常能够经过“注入”的方式获取,这种方式避免了Action与ServletAPI的耦合,可是也有不少状况是须要直接访问ServletAPI获取的。Struts2也提供了许多访问ServletAPI的方法。

ActionContext

ActionContext类提供了HttpServletRequest,HttpSession,ServletContext的获取方法,它们分别对应JSP内置对象的request,session,application。可是ActionContext只能获取request内容,也就是只能从页面获取参数。

ActionContext的获取方式为 :

ActionContext actionContext = ActionContext.getContext();

ActionContext是action执行时的上下文,它存放着Action须要用到的各类对象.如:请求参数(Parameter),会话(Session),Servlet上下文(ServletContext),本地化(Locale)

每次执行Action的时候都会建立新的ActionContext,ActionContext是线程安全的,在同一个线程里ActionContext是惟一的。

ActionContext对Servlet的一些内容进行了封装,从而使Action与Serlvet解耦合。

ServletActionContext

ServletActionContext继承于ActionContext,提供了一些直接访问Serlvet的方法。SerlvetActionContext能够操做Cookie.ServletActionContext能够获取的对象有:

HttpServletRequest——请求对象

HttpServletResponse——响应对象

ServletContext——上下文信息

ApplicationContext——Http页面上下文

ActionContext与ServletActionContext都是基于WEB应用的,因此普通的java应用是没法获取ActionContext或ServletActionContext的。在运行的时候,它们的值将为NULL。ActionContext和ServletActionContext均可以获取WEB数据,可是ActionContext更偏向于值得操做而ServletActionContext更偏向Serlvet的操做。

Others

SessionAware,RequestAware,ApplicationAware,ParameterAware也能够访问页面获取的数据。要使用这些工具,Action就必须实现SessionAware,RequestAware,ApplicationAware,ParameterAware接口。并实现

Public void setSession(Map<String,Object> map);

Public void setRequest(Map<String,Object> map );

Public void setApplication(Map<String,Object> map);

Public void setParameter(Map<String,Object> map);

ServletRequestAware,ServletResponseAware能够用来获取HttpServletRequest和HttpServletResponse对象。要获取这些对象,就必须实现ServletRequestAware,ServletResponseAware接口。并实现:

Public  void setServletRequest(HttpServletRequest request);

Public void  setServletResponse(HttpServletResponse response);


参考了许多资料,也看了点代码,不过老实说仍是只知其一;不知其二,望高手出来指点迷津:)

相关文章
相关标签/搜索