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:数据校验,通过XWork校验框架实现(validation.xml)
[原创]java WEB学习笔记59:Struts2学习之路---OGNL,值栈,读取对象栈中的对象的属性,读取 Context Map 里的对象的属性,调用字段和方法,数组,list,map(代码片