Struts2源码学习——XWork中的元素

Posted DarkFuture

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2源码学习——XWork中的元素相关的知识,希望对你有一定的参考价值。

  上篇文章梳理了Struts2中的XWork的容器的实现,这篇文章,着重回顾XWork中的各个元素以及他们的作用和相互之间的关系。


  首先,大体感受一下XWork中的各个主要元素。

  

  主要分为三个部分:

  (1),核心分发器 Dispatcher : Dispatcher 不属于XWork框架的组成,但是是XWork框架的调用者和驱动执行者,在运行过程中起着重要的作用。

  (2),控制流体系 (ActionProxy, ActionInvocation, Inteceptor, Action, Result), 其中,Interceptor和Action以及Result作为事件处理节点,是处理事件逻辑的主要地方,而ActionProxy和ActionInvocation负责对这些事件处理节点进行调用。

  这里,ActionProxy主要是XWork框架和外部环境,为框架内部元素提供一个环境的同时也提供外部调用XWork内部元素的接口,即负责提供一个执行环境。而ActionInvocation作为核心调度器,主要负责的就是对事件处理节点进行调用。

  (3),数据流体系 (ActionContext, ValueStack),其中ActionContext负责数据的储存和数据的共享,而ValueStack负责数据的操作。


  以上就是对XWork框架内部元素的大致的认识,接下来的部分,通过从源码的方式,查看各个元素内部的组成。

  一,XWork中的数据流体系

  XWork中的数据流体系,包括了ActionContext和ValueStack两大部分,

 1 public class ActionContext implements Serializable {
 2     static ThreadLocal<ActionContext> actionContext = new ThreadLocal();
 3     public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
 4     public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
 5     public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
 6     public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
 7     public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
 8     public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
 9     public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter";
10     public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
11     public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
12     public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
13     private Map<String, Object> context;
14 
15     public void setValueStack(ValueStack stack) {
16         this.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", stack);
17     }
18 
19     // 省略了许多其他对象的getter,setter
20 
21     public ValueStack getValueStack() {
22         return (ValueStack)this.get("com.opensymphony.xwork2.util.ValueStack.ValueStack");
23     }
24 
25     public Object get(String key) {
26         return this.context.get(key);
27     }
28 
29     public void put(String key, Object value) {
30         this.context.put(key, value);
31     }
32 }

  通过源码可以发现,其实ValueStack是ActionContext的内部对象。正如之前提到的,ActionContext在框架内部提供一个数据储存和数据共享的环境,而ValueStack则是在ActionContext的基础之上,增加对数据的操作的功能。

  既然ValueStack是ActionContext的内部对象,让我们把重点放到ActionContext类中,查看源码看看内部的数据结构如何。

  1 public class ActionContext implements Serializable {
  2     static ThreadLocal<ActionContext> actionContext = new ThreadLocal();
  3     public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name";
  4     public static final String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
  5     public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session";
  6     public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application";
  7     public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters";
  8     public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale";
  9     public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter";
 10     public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation";
 11     public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors";
 12     public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container";
 13     private Map<String, Object> context;
 14 
 15     public ActionContext(Map<String, Object> context) {
 16         this.context = context;
 17     }
 18 
 19     public void setActionInvocation(ActionInvocation actionInvocation) {
 20         this.put("com.opensymphony.xwork2.ActionContext.actionInvocation", actionInvocation);
 21     }
 22 
 23     public ActionInvocation getActionInvocation() {
 24         return (ActionInvocation)this.get("com.opensymphony.xwork2.ActionContext.actionInvocation");
 25     }
 26 
 27     public void setApplication(Map<String, Object> application) {
 28         this.put("com.opensymphony.xwork2.ActionContext.application", application);
 29     }
 30 
 31     public Map<String, Object> getApplication() {
 32         return (Map)this.get("com.opensymphony.xwork2.ActionContext.application");
 33     }
 34 
 35     public static void setContext(ActionContext context) {
 36         actionContext.set(context);
 37     }
 38 
 39     public static ActionContext getContext() {
 40         return (ActionContext)actionContext.get();
 41     }
 42 
 43     public void setContextMap(Map<String, Object> contextMap) {
 44         getContext().context = contextMap;
 45     }
 46 
 47     public Map<String, Object> getContextMap() {
 48         return this.context;
 49     }
 50 
 51     public void setConversionErrors(Map<String, Object> conversionErrors) {
 52         this.put("com.opensymphony.xwork2.ActionContext.conversionErrors", conversionErrors);
 53     }
 54 
 55     public Map<String, Object> getConversionErrors() {
 56         Map<String, Object> errors = (Map)this.get("com.opensymphony.xwork2.ActionContext.conversionErrors");
 57         if (errors == null) {
 58             errors = new HashMap();
 59             this.setConversionErrors((Map)errors);
 60         }
 61 
 62         return (Map)errors;
 63     }
 64 
 65     public void setLocale(Locale locale) {
 66         this.put("com.opensymphony.xwork2.ActionContext.locale", locale);
 67     }
 68 
 69     public Locale getLocale() {
 70         Locale locale = (Locale)this.get("com.opensymphony.xwork2.ActionContext.locale");
 71         if (locale == null) {
 72             locale = Locale.getDefault();
 73             this.setLocale(locale);
 74         }
 75 
 76         return locale;
 77     }
 78 
 79     public void setName(String name) {
 80         this.put("com.opensymphony.xwork2.ActionContext.name", name);
 81     }
 82 
 83     public String getName() {
 84         return (String)this.get("com.opensymphony.xwork2.ActionContext.name");
 85     }
 86 
 87     public void setParameters(HttpParameters parameters) {
 88         this.put("com.opensymphony.xwork2.ActionContext.parameters", parameters);
 89     }
 90 
 91     public HttpParameters getParameters() {
 92         return (HttpParameters)this.get("com.opensymphony.xwork2.ActionContext.parameters");
 93     }
 94 
 95     public void setSession(Map<String, Object> session) {
 96         this.put("com.opensymphony.xwork2.ActionContext.session", session);
 97     }
 98 
 99     public Map<String, Object> getSession() {
100         return (Map)this.get("com.opensymphony.xwork2.ActionContext.session");
101     }
102 
103     public void setValueStack(ValueStack stack) {
104         this.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", stack);
105     }
106 
107     public ValueStack getValueStack() {
108         return (ValueStack)this.get("com.opensymphony.xwork2.util.ValueStack.ValueStack");
109     }
110 
111     public void setContainer(Container cont) {
112         this.put("com.opensymphony.xwork2.ActionContext.container", cont);
113     }
114 
115     public Container getContainer() {
116         return (Container)this.get("com.opensymphony.xwork2.ActionContext.container");
117     }
118 
119     public <T> T getInstance(Class<T> type) {
120         Container cont = this.getContainer();
121         if (cont != null) {
122             return cont.getInstance(type);
123         } else {
124             throw new XWorkException("Cannot find an initialized container for this request.");
125         }
126     }
127 
128     public Object get(String key) {
129         return this.context.get(key);
130     }
131 
132     public void put(String key, Object value) {
133         this.context.put(key, value);
134     }
135 }

  通过源码可以发现,在最后的get方法和put方法中,其实是对ActionContext中存放的Map对象进行操作,其中还存放这框架内部其他的元素,如Session, Application等,

  在ActionContext类中,我们还需要注意的地方,就是在Web环境中,对并发中对共享数据的线程安全的处理,在ActionContext中,使用一个ThreadLocal对ActionContext中的Map对象进行封装。

 1 public class ActionContext implements Serializable {
 2     static ThreadLocal<ActionContext> actionContext = new ThreadLocal();
 3 
 4     // 省略其他代码。。。
 5 
 6     public void setActionInvocation(ActionInvocation actionInvocation) {
 7         this.put("com.opensymphony.xwork2.ActionContext.actionInvocation", actionInvocation);
 8     }
 9 
10     public static void setContext(ActionContext context) {
11         actionContext.set(context);
12     }
13 }

  ActionContext中封装了许多对框架内部对象的访问接口,主要分成两种:

  (1),对XWork框架对象的访问,getContainer,getValueStack,getActionInvocation;

  (2),对数据对象的访问, getSession,getApplication,getParameters等;

 1   ActionContext内部对访问数据对象的封装都是以Map形式返回,这体现了XWork与Web容器解耦的思想,XWrok本身并不是一个Web容器框架。 

  XWork实现访问真正的web容器对象实在ActionContext的子类ServletActionContext中,查看源码

 1 public class ServletActionContext extends ActionContext implements StrutsStatics {
 2     private static final long serialVersionUID = -666854718275106687L;
 3     public static final String STRUTS_VALUESTACK_KEY = "struts.valueStack";
 4     public static final String ACTION_MAPPING = "struts.actionMapping";
 5 
 6     private ServletActionContext(Map context) {
 7         super(context);
 8     }
 9 
10     public static ActionContext getActionContext(HttpServletRequest req) {
11         ValueStack vs = getValueStack(req);
12         return vs != null ? new ActionContext(vs.getContext()) : null;
13     }
14 
15     public static ValueStack getValueStack(HttpServletRequest req) {
16         return (ValueStack)req.getAttribute("struts.valueStack");
17     }
18 
19     public static ActionMapping getActionMapping() {
20         return (ActionMapping)ActionContext.getContext().get("struts.actionMapping");
21     }
22 
23     public static PageContext getPageContext() {
24         return (PageContext)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.PageContext");
25     }
26 
27     public static void setRequest(HttpServletRequest request) {
28         ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.HttpServletRequest", request);
29     }
30 
31     public static HttpServletRequest getRequest() {
32         return (HttpServletRequest)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.HttpServletRequest");
33     }
34 
35     public static void setResponse(HttpServletResponse response) {
36         ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.HttpServletResponse", response);
37     }
38 
39     public static HttpServletResponse getResponse() {
40         return (HttpServletResponse)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.HttpServletResponse");
41     }
42 
43     public static ServletContext getServletContext() {
44         return (ServletContext)ActionContext.getContext().get("com.opensymphony.xwork2.dispatcher.ServletContext");
45     }
46 
47     public static void setServletContext(ServletContext servletContext) {
48         ActionContext.getContext().put("com.opensymphony.xwork2.dispatcher.ServletContext", servletContext);
49     }
50 }

  以上是对数据流体系中的ActionContext的内部实现进行的了解,接下来我们将进入数据流中的另一个对象——ValueStack;

  XWork中的ValueStack其实是对OGNL(Object Graph Navigation Language)进行的扩展,让ValueStack再进行OGNL计算时,可以将一组对象作为Root对象。

 1 public interface ValueStack {
 2     String VALUE_STACK = "com.opensymphony.xwork2.util.ValueStack.ValueStack";
 3     String REPORT_ERRORS_ON_NO_PROP = "com.opensymphony.xwork2.util.ValueStack.ReportErrorsOnNoProp";
 4 
 5     Map<String, Object> getContext();
 6 
 7     void setDefaultType(Class var1);
 8 
 9     void setExprOverrides(Map<Object, Object> var1);
10 
11     Map<Object, Object> getExprOverrides();
12 
13     // 获取包含栈内所有元素的容器结构
14     CompoundRoot getRoot();
15    // 根据传入的表达式和对象进行写值计算
16     void setValue(String var1, Object var2);
17 
18     void setParameter(String var1, Object var2);
19 
20     void setValue(String var1, Object var2, boolean var3);
21 
22     String findString(String var1);
23 
24     String findString(String var1, boolean var2);
25     // 根据传入的表达式进行求值计算
26     Object findValue(String var1);
27 
28     Object findValue(String var1, boolean var2);
29     
30     Object findValue(String var1, Class var2);
31 
32     Object findValue(String var1, Class var2, boolean var3);
33 
34     // 得到栈顶元素
35     Object peek();
36 
37     // 弹出栈顶
38     Object pop();
39 
40     // 压栈
41     void push(Object var1);
42 
43     void set(String var1, Object var2);
44 
45     int size();
46 }

  ValueStack作为一个接口,提供了一些我们所熟知的栈的操作方法。通过查看其子类,我们可以了解ValueStack的使用方法。

  1 public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {
  2     public static final String THROW_EXCEPTION_ON_FAILURE = OgnlValueStack.class.getName() + ".throwExceptionOnFailure";
  3     private static final Logger LOG = LogManager.getLogger(OgnlValueStack.class);
  4     private static final long serialVersionUID = 370737852934925530L;
  5     private static final String MAP_IDENTIFIER_KEY = "com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
  6     protected CompoundRoot root;     // 这里使用了装饰模式, 后面的源码分析中会说到,对这个对象的操作内部其实是对对象的迭代遍历操作
  7     protected transient Map<String, Object> context;
  8     protected Class defaultType;
  9     protected Map<Object, Object> overrides;
 10     protected transient OgnlUtil ognlUtil;
 11     protected transient SecurityMemberAccess securityMemberAccess;
 12     private transient XWorkConverter converter;
 13     private boolean devMode;
 14     private boolean logMissingProperties;
 15    // 省略了许多代码。。。。 
 16     protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
 17         this.setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
 18         this.push(prov);
 19     }
 20 
 21     protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticAccess) {
 22         this.setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticAccess);
 23     }
 24  
 25     protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
 26         this.root = compoundRoot;
 27         this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
 28         this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), this.securityMemberAccess);
 29         this.context.put("com.opensymphony.xwork2.util.ValueStack.ValueStack", this);
 30         Ognl.setClassResolver(this.context, accessor);
 31         ((OgnlContext)this.context).setTraceEvaluations(false);
 32         ((OgnlContext)this.context).setKeepLastEvaluation(false);
 33     }
 34 
 35 
 36     public Map<String, Object> getContext() {
 37         return this.context;
 38     }
 39 
 40     public void setDefaultType(Class defaultType) {
 41         this.defaultType = defaultType;
 42     }
 43 
 44     public void setExprOverrides(Map<Object, Object> overrides) {
 45         if (this.overrides == null) {
 46             this.overrides = overrides;
 47         } else {
 48             this.overrides.putAll(overrides);
 49         }
 50 
 51     }
 52 
 53     public Map<Object, Object> getExprOverrides() {
 54         return this.overrides;
 55     }
 56 
 57     public CompoundRoot getRoot() {
 58         return this.root;
 59     }
 60 
 61     public void setParameter(String expr, Object value) {
 62         this.setValue(expr, value, this.devMode);
 63     }
 64 
 65     public void setValue(String expr, Object value) {
 66         this.setValue(expr, value, this.devMode);
 67     }
 68 
 69     public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
 70         Map context = this.getContext();
 71 
 72         try {
 73             this.trySetValue(expr, value, throwExceptionOnFailure, context);
 74         } catch (OgnlException var10) {
 75             this.handleOgnlException(expr, value, throwExceptionOnFailure, var10);
 76         } 以上是关于Struts2源码学习——XWork中的元素的主要内容,如果未能解决你的问题,请参考以下文章

struts2学习struts2核心知识

(转)struts2:数据校验,通过XWork校验框架实现(validation.xml)

关于Struts 2.5中的xwork包的问题

[原创]java WEB学习笔记59:Struts2学习之路---OGNL,值栈,读取对象栈中的对象的属性,读取 Context Map 里的对象的属性,调用字段和方法,数组,list,map(代码片

struts2框架学习笔记6:拦截器

struts2学习值栈简介与OGNL引入