Struts 运行流程以及值栈小记

一、Struts运行流程图先了解一下:
这里写图片描述

引用网上的总结:
一个请求在Struts2框架中的处理大概分为以下几个步骤:
1、客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3、接着FilterDispatcher(现已过时)被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6、ActionProxy创建一个ActionInvocation的实例。
7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。

二、值栈:简单的说,就是存放action的堆栈,当我们提交一个请求道服务器端 action时,就有个堆栈,如果action在服务器端进行跳转,所有action共用一个堆栈,当需要保存在action中的数据时,首先从栈顶开始 搜索,若找到相同的属性名(与要获得的数据的属性名相同)时,即将值取出。但这种情况可能出现找到的值不是我们想要的值,那么解决此问题需要用TOP语法 和N语法来进行解决。

三、值栈是怎样创建的以及值栈和ActionContext的关系

  • 1、Struts的值栈的核心类是ValueStack接口,主要是其实现类OgnlValueStack
// 忽略其它属性,只看下面两个
protected CompoundRoot root;
// 这个就是常说的contextMap
protected transient Map<String, Object> context;
// ... ...
  • 2、Struts的核核心过滤器StrutsPrepareAndExecuteFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest)req;
    HttpServletResponse response = (HttpServletResponse)res;

    try {
        String uri = RequestUtils.getUri(request);
        if (this.excludedPatterns != null && this.prepare.isUrlExcluded(request, this.excludedPatterns)) {
            LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
            chain.doFilter(request, response);
        } else {
            LOG.trace("Checking if {} is a static resource", uri);
            boolean handled = this.execute.executeStaticResourceRequest(request, response);
            if (!handled) {
                LOG.trace("Assuming uri {} as a normal action", uri);
                this.prepare.setEncodingAndLocale(request, response);
                // 1、值栈是伴随着ActionContext的创建而创建的
                this.prepare.createActionContext(request, response);
                this.prepare.assignDispatcherToThread();
                request = this.prepare.wrapRequest(request);
                ActionMapping mapping = this.prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                    chain.doFilter(request, response);
                } else {
                    LOG.trace("Found mapping {} for {}", mapping, uri);
                    this.execute.executeAction(request, response, mapping);
                }
            }
        }
    } finally {
        this.prepare.cleanupRequest(request);
    }

}
  • 3、ValueStack伴随ActionContext创建
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
    Integer counter = 1;
    Integer oldCounter = (Integer)request.getAttribute("__cleanup_recursion_counter");
    if (oldCounter != null) {
        counter = oldCounter + 1;
    }

    ActionContext oldContext = ActionContext.getContext();
    ActionContext ctx;
    if (oldContext != null) {
        ctx = new ActionContext(new HashMap(oldContext.getContextMap()));
    } else {
     // 2、创建值栈
        ValueStack stack = ((ValueStackFactory)this.dispatcher.getContainer().getInstance(ValueStackFactory.class)).createValueStack();
        // 3、创建contextMap,同时也将其压入值栈的context中,毕竟这样才能称其为contextMap
        stack.getContext().putAll(this.dispatcher.createContextMap(request, response, (ActionMapping)null));
        // 4、将值栈的context作为构造参数传给ActionContext
        ctx = new ActionContext(stack.getContext());
    }

    request.setAttribute("__cleanup_recursion_counter", counter);
    // 5、将ActionContext与ThreadLocal绑定
    ActionContext.setContext(ctx);
    return ctx;
}

/** 第二步这里展开:*/
public ValueStack createValueStack() {
    // 创建一个新的值栈
     ValueStack stack = new OgnlValueStack(this.xworkConverter, this.compoundRootAccessor, this.textProvider, this.allowStaticMethodAccess);
     this.container.inject(stack);
     stack.getContext().put("com.opensymphony.xwork2.ActionContext.container", this.container);
     return stack;
 }

// 如何创建新的值值栈
protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
    // root赋值
    this.setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
    this.push(prov);
}

protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
    // 初始化root
    this.root = compoundRoot;
    this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
    // 创建默认的Context
    this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), this.securityMemberAccess);
    // 这里可以看到contexMap维护了一个值栈本身的引用
    this.context.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this);
    Ognl.setClassResolver(this.context, accessor);
    ((OgnlContext)this.context).setTraceEvaluations(false);
    ((OgnlContext)this.context).setKeepLastEvaluation(false);
}

// 
public static Map addDefaultContext(Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context) {
// 值栈的contextMap的实际数据结构其实就是OgnlContext
    OgnlContext result;

    // ... ...

    // 这里可以看到contexMap也维护了一个root引用
    result.setRoot(root);
    return result;
}
  • 4、contextMap的创建
public Map<String, Object> createContextMap(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) {
    Map requestMap = new RequestMap(request);
    HttpParameters params = HttpParameters.create(request.getParameterMap()).build();
    Map session = new SessionMap(request);
    Map application = new ApplicationMap(this.servletContext);
    // 创建contextMap
    Map<String, Object> extraContext = this.createContextMap(requestMap, params, session, application, request, response);
    if (mapping != null) {
        extraContext.put("struts.actionMapping", mapping);
    }

    return extraContext;
}

// 在contextMap中添加各种域对象的引用
public HashMap<String, Object> createContextMap(Map requestMap, HttpParameters parameters, Map sessionMap, Map applicationMap, HttpServletRequest request, HttpServletResponse response) {
    HashMap<String, Object> extraContext = new HashMap();
    extraContext.put("com.opensymphony.xwork2.ActionContext.parameters", parameters);
    extraContext.put("com.opensymphony.xwork2.ActionContext.session", sessionMap);
    extraContext.put("com.opensymphony.xwork2.ActionContext.application", applicationMap);
    extraContext.put("com.opensymphony.xwork2.ActionContext.locale", this.getLocale(request));
    extraContext.put("com.opensymphony.xwork2.dispatcher.HttpServletRequest", request);
    extraContext.put("com.opensymphony.xwork2.dispatcher.HttpServletResponse", response);
    extraContext.put("com.opensymphony.xwork2.dispatcher.ServletContext", this.servletContext);
    extraContext.put("request", requestMap);
    extraContext.put("session", sessionMap);
    extraContext.put("application", applicationMap);
    extraContext.put("parameters", parameters);
    AttributeMap attrMap = new AttributeMap(extraContext);
    extraContext.put("attr", attrMap);
    return extraContext;
}
  • 5、将值栈的context作为构造参数传给ActionContext
private Map<String, Object> context;

public ActionContext(Map<String, Object> context) {
    // 也就是说将值栈(OgnlValueStack)的contextMap属性中的所有东西都给了ActionContext
    this.context = context;
}

// 这里说明从ActionContext中获取东西基本也都是从contexMap中获取
public String getName() {
    return (String)this.get("com.opensymphony.xwork2.ActionContext.name");
}

public Object get(String key) {
    return this.context.get(key);
}
  • 6、将ActionContext与ThreadLocal绑定
// 新创建的ActionContext绑定到了ThreadLocal上。
// ThreadLocal的set方法是将ThreadLocal对象和数据对象作为键值对存入线程对象内部的一个Map类型的数据结构里
// 因此,由于ActionContext被绑定在ThreadLocal对象上,所以ActionContext是线程安全的。
static ThreadLocal<ActionContext> actionContext = new ThreadLocal();

public static void setContext(ActionContext context) {
    actionContext.set(context);
}

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

四、为什么请求过来的时候Action类中的属性可能直接获取到值呢?

  • 1、当Action接收到请求之后,会先创建一个Action实例,但是这个时候并不会马上调用Action中的方法。
  • 2、而是会将Action类中属性先放到值栈(ValueStack)中,这个时候所有的属性没有值或者只有对应类型的默认值。
  • 3、这个时候呢,Struts会先调用拦截器链中的各种拦截器(这里可对ValueStack中值做操作,最后在Action中得到操作后的值),调用完拦截器之后;会将值栈(ValueStack)中的属性赋值给Action类中相应的属性。
  • 4、然后才会调用Action中的相应方法,这个时候我们就能直接获取到这些值了。

五、总结

  • 1、值栈和ActionContext是一起创建的,一次请求创建一次。
  • 2、值栈(OgnlValueStack)包含两块:CompoundRoot(ArrayList) root和 Map context(常说的contextMap)。
  • 3、contextMap 中 维护了值栈本身和root两个引用。
  • 4、因为ActionContext 中维护了contextMap引用,contextMap中以维护了值栈本身的引用,所以ActionContext是间接引用了值栈(直接说ActionContext中维护了值栈的引用并不合适)。
  • 5、Action类中的属性可能直接获取到值是因为在请求到达Action之前,Struts已经将值提前存放到值栈中了,并在调用方法之前将这些值赋值给对应的属性。

猜你喜欢

转载自blog.csdn.net/qq_34560242/article/details/80963360
今日推荐