• 引言

在我们开发过程中,往往会使用一个对像传递到一个具体的action中,之后到跳转页面中访问对应对象的具体的参数。

比如:我们搭建一个struts2项目:

回顾下如何搭建strut2:

1、下载的struts2开发包(struts-2.3.31-all.zip);

2、解压struts-2.3.31-all.zip,并把\apps\struts2-blank.war解压;

3、取出\struts2-blank\WEB-INF\lib下的所有jar包到Struts_01\WebContent\WEB-INF\lib下;

4、\struts2-blank\WEB-INF\src\java下的struts.xml、log4j2.xml、velocity.properties拷贝到Struts_01\WebContent\src;

5、拷贝\struts2-blank\WEB-INF\web.xml到Struts_01\WebContent\WEB-INF\web.xml下;

6、创建包com.dx.struts2.valuestack,并在包下创建Product.java。

package com.dx.struts2.valuestack;

public class Product {
private Integer productId;
private String productName;
private String productDesc;
private Double productPrice; public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getProductDesc() {
return productDesc;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public Double getProductPrice() {
return productPrice;
}
public void setProductPrice(Double productPrice) {
this.productPrice = productPrice;
} public String save(){
System.out.println("save");
return "success";
}
}

7、修改web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>Struts 01</display-name> <filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter> <filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> <welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list> <!-- Restricts access to pure JSP files - access available only via Struts
action <security-constraint> <display-name>No direct JSP access</display-name>
<web-resource-collection> <web-resource-name>No-JSP</web-resource-name> <url-pattern>*.jsp</url-pattern>
</web-resource-collection> <auth-constraint> <role-name>no-users</role-name>
</auth-constraint> </security-constraint> <security-role> <description>Don't
assign users to this role</description> <role-name>no-users</role-name> </security-role> -->
</web-app>

8、修改struts.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd"> <struts>
<!-- <constant name="struts.action.extension" value="action" /> -->
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="false" /> <package name="default" namespace="/" extends="struts-default">
<action name="product-save" class="com.dx.struts2.valuestack.Product"
method="save">
<result>/details.jsp</result>
</action>
</package> <!-- Add packages here --> </struts>

9、添加页面index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<s:form action="product-save">
product name:
<input name="productName" />
<br />
product description:
<input name="productDesc" />
<br />
product price:
<input name="productPrice" />
<br />
<s:submit name="method:save.do" value="提交"></s:submit>
</s:form>
</body>
</html>

10、添加details.jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
${productName}<br/>
request.getAttribute("productName"):<%=request.getAttribute("productName") %><br/>
${productDesc}<br/>
${productPrice}<br/> <%=request %>
</body>
</html>

运行发现request对象不是HttpRequestWrapper。

  • 跟踪调试:

从“引言”的代码运行结果中发现request是org.apache.struts2.dispatcher.StrutsRequestWrapper类型。那就说明一个问题,这里边的${productName}和<%=request.getAttribute("productName")%>都是通过调用StrutsRequestWrapper(Ctrl+T会弹出窗口,输入StrutsRequestWrapper会自动索引到源代码)的getAttribute(String key)函数:

/**
* Gets the object, looking in the value stack if not found
*
* @param key The attribute key
*/
public Object getAttribute(String key) {
if (key == null) {
throw new NullPointerException("You must specify a key value");
} if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) {
// don't bother with the standard javax.servlet attributes, we can short-circuit this
// see WW-953 and the forums post linked in that issue for more info
return super.getAttribute(key);
} ActionContext ctx = ActionContext.getContext();
Object attribute = super.getAttribute(key); //supper->HttpRequestWrapper if (ctx != null && attribute == null) {
boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE)); // note: we don't let # come through or else a request for
// #attr.foo or #request.foo could cause an endless loop
if (!alreadyIn && !key.contains("#")) {
try {
// If not found, then try the ValueStack
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE);
ValueStack stack = ctx.getValueStack();
if (stack != null) {
attribute = stack.findValue(key);
}
} finally {
ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE);
}
}
}
return attribute;
}

如果debug的话,可以从代码跟踪中找到上边的Product对象的参数是存储到哪里。

付截图:

从上边截图中我们发现Product对象是存储到ValueStack(值栈)中,而并不是在请求域中真的存在这样的一个值。

查看ActionContext源码,发现里边存储了Application/Seesion/Parameter等。

 /*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2; import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.util.ValueStack; import java.io.Serializable;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map; /**
* The ActionContext is the context in which an {@link Action} is executed. Each context is basically a
* container of objects an action needs for execution like the session, parameters, locale, etc. <p>
* <p/>
* The ActionContext is thread local which means that values stored in the ActionContext are
* unique per thread. See the {@link ThreadLocal} class for more information. The benefit of
* this is you don't need to worry about a user specific action context, you just get it:
* <p/>
* <ul><code>ActionContext context = ActionContext.getContext();</code></ul>
* <p/>
* Finally, because of the thread local usage you don't need to worry about making your actions thread safe.
*
* @author Patrick Lightbody
* @author Bill Lynch (docs)
*/
public class ActionContext implements Serializable { static ThreadLocal<ActionContext> actionContext = new ThreadLocal<ActionContext>(); /**
* Constant for the name of the action being executed.
*/
public static final String ACTION_NAME = "com.opensymphony.xwork2.ActionContext.name"; /**
* Constant for the {@link com.opensymphony.xwork2.util.ValueStack OGNL value stack}.
*/
public static final String VALUE_STACK = ValueStack.VALUE_STACK; /**
* Constant for the action's session.
*/
public static final String SESSION = "com.opensymphony.xwork2.ActionContext.session"; /**
* Constant for the action's application context.
*/
public static final String APPLICATION = "com.opensymphony.xwork2.ActionContext.application"; /**
* Constant for the action's parameters.
*/
public static final String PARAMETERS = "com.opensymphony.xwork2.ActionContext.parameters"; /**
* Constant for the action's locale.
*/
public static final String LOCALE = "com.opensymphony.xwork2.ActionContext.locale"; /**
* Constant for the action's type converter.
*/
public static final String TYPE_CONVERTER = "com.opensymphony.xwork2.ActionContext.typeConverter"; /**
* Constant for the action's {@link com.opensymphony.xwork2.ActionInvocation invocation} context.
*/
public static final String ACTION_INVOCATION = "com.opensymphony.xwork2.ActionContext.actionInvocation"; /**
* Constant for the map of type conversion errors.
*/
public static final String CONVERSION_ERRORS = "com.opensymphony.xwork2.ActionContext.conversionErrors"; /**
* Constant for the container
*/
public static final String CONTAINER = "com.opensymphony.xwork2.ActionContext.container"; private Map<String, Object> context; /**
* Creates a new ActionContext initialized with another context.
*
* @param context a context map.
*/
public ActionContext(Map<String, Object> context) {
this.context = context;
} /**
* Sets the action invocation (the execution state).
*
* @param actionInvocation the action execution state.
*/
public void setActionInvocation(ActionInvocation actionInvocation) {
put(ACTION_INVOCATION, actionInvocation);
} /**
* Gets the action invocation (the execution state).
*
* @return the action invocation (the execution state).
*/
public ActionInvocation getActionInvocation() {
return (ActionInvocation) get(ACTION_INVOCATION);
} /**
* Sets the action's application context.
*
* @param application the action's application context.
*/
public void setApplication(Map<String, Object> application) {
put(APPLICATION, application);
} /**
* Returns a Map of the ServletContext when in a servlet environment or a generic application level Map otherwise.
*
* @return a Map of ServletContext or generic application level Map
*/
public Map<String, Object> getApplication() {
return (Map<String, Object>) get(APPLICATION);
} /**
* Sets the action context for the current thread.
*
* @param context the action context.
*/
public static void setContext(ActionContext context) {
actionContext.set(context);
} /**
* Returns the ActionContext specific to the current thread.
*
* @return the ActionContext for the current thread, is never <tt>null</tt>.
*/
public static ActionContext getContext() {
return actionContext.get();
} /**
* Sets the action's context map.
*
* @param contextMap the context map.
*/
public void setContextMap(Map<String, Object> contextMap) {
getContext().context = contextMap;
} /**
* Gets the context map.
*
* @return the context map.
*/
public Map<String, Object> getContextMap() {
return context;
} /**
* Sets conversion errors which occurred when executing the action.
*
* @param conversionErrors a Map of errors which occurred when executing the action.
*/
public void setConversionErrors(Map<String, Object> conversionErrors) {
put(CONVERSION_ERRORS, conversionErrors);
} /**
* Gets the map of conversion errors which occurred when executing the action.
*
* @return the map of conversion errors which occurred when executing the action or an empty map if
* there were no errors.
*/
public Map<String, Object> getConversionErrors() {
Map<String, Object> errors = (Map) get(CONVERSION_ERRORS); if (errors == null) {
errors = new HashMap<String, Object>();
setConversionErrors(errors);
} return errors;
} /**
* Sets the Locale for the current action.
*
* @param locale the Locale for the current action.
*/
public void setLocale(Locale locale) {
put(LOCALE, locale);
} /**
* Gets the Locale of the current action. If no locale was ever specified the platform's
* {@link java.util.Locale#getDefault() default locale} is used.
*
* @return the Locale of the current action.
*/
public Locale getLocale() {
Locale locale = (Locale) get(LOCALE); if (locale == null) {
locale = Locale.getDefault();
setLocale(locale);
} return locale;
} /**
* Sets the name of the current Action in the ActionContext.
*
* @param name the name of the current action.
*/
public void setName(String name) {
put(ACTION_NAME, name);
} /**
* Gets the name of the current Action.
*
* @return the name of the current action.
*/
public String getName() {
return (String) get(ACTION_NAME);
} /**
* Sets the action parameters.
*
* @param parameters the parameters for the current action.
*/
public void setParameters(Map<String, Object> parameters) {
put(PARAMETERS, parameters);
} /**
* Returns a Map of the HttpServletRequest parameters when in a servlet environment or a generic Map of
* parameters otherwise.
*
* @return a Map of HttpServletRequest parameters or a multipart map when in a servlet environment, or a
* generic Map of parameters otherwise.
*/
public Map<String, Object> getParameters() {
return (Map<String, Object>) get(PARAMETERS);
} /**
* Sets a map of action session values.
*
* @param session the session values.
*/
public void setSession(Map<String, Object> session) {
put(SESSION, session);
} /**
* Gets the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
*
* @return the Map of HttpSession values when in a servlet environment or a generic session map otherwise.
*/
public Map<String, Object> getSession() {
return (Map<String, Object>) get(SESSION);
} /**
* Sets the OGNL value stack.
*
* @param stack the OGNL value stack.
*/
public void setValueStack(ValueStack stack) {
put(VALUE_STACK, stack);
} /**
* Gets the OGNL value stack.
*
* @return the OGNL value stack.
*/
public ValueStack getValueStack() {
return (ValueStack) get(VALUE_STACK);
} /**
* Gets the container for this request
*
* @param cont The container
*/
public void setContainer(Container cont) {
put(CONTAINER, cont);
} /**
* Sets the container for this request
*
* @return The container
*/
public Container getContainer() {
return (Container) get(CONTAINER);
} public <T> T getInstance(Class<T> type) {
Container cont = getContainer();
if (cont != null) {
return cont.getInstance(type);
} else {
throw new XWorkException("Cannot find an initialized container for this request.");
}
} /**
* Returns a value that is stored in the current ActionContext by doing a lookup using the value's key.
*
* @param key the key used to find the value.
* @return the value that was found using the key or <tt>null</tt> if the key was not found.
*/
public Object get(String key) {
return context.get(key);
} /**
* Stores a value in the current ActionContext. The value can be looked up using the key.
*
* @param key the key of the value.
* @param value the value to be stored.
*/
public void put(String key, Object value) {
context.put(key, value);
}
}

继续调试,我来看下是ValueStack是怎么存储数据的。

我们发现ValueStack是:com.opensymphony.xwork2.ognl.OgnlValueStack,

 /*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.ognl; import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.TextProvider;
import com.opensymphony.xwork2.XWorkConstants;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.MemberAccessValueStack;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.logging.LoggerUtils;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.*; import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern; /**
* Ognl implementation of a value stack that allows for dynamic Ognl expressions to be evaluated against it. When evaluating an expression,
* the stack will be searched down the stack, from the latest objects pushed in to the earliest, looking for a bean with a getter or setter
* for the given property or a method of the given name (depending on the expression being evaluated).
*
* @author Patrick Lightbody
* @author tm_jee
* @version $Date$ $Id$
*/
public class OgnlValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack { public static final String THROW_EXCEPTION_ON_FAILURE = OgnlValueStack.class.getName() + ".throwExceptionOnFailure"; private static final long serialVersionUID = 370737852934925530L; private static final String MAP_IDENTIFIER_KEY = "com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
private static final Logger LOG = LoggerFactory.getLogger(OgnlValueStack.class); CompoundRoot root;
transient Map<String, Object> context;
Class defaultType;
Map<Object, Object> overrides;
transient OgnlUtil ognlUtil;
transient SecurityMemberAccess securityMemberAccess;
private transient XWorkConverter converter; private boolean devMode;
private boolean logMissingProperties; protected OgnlValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
push(prov);
} protected OgnlValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticAccess) {
setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticAccess);
} @Inject
public void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
securityMemberAccess.setExcludedClasses(ognlUtil.getExcludedClasses());
securityMemberAccess.setExcludedPackageNamePatterns(ognlUtil.getExcludedPackageNamePatterns());
securityMemberAccess.setExcludedPackageNames(ognlUtil.getExcludedPackageNames());
} protected void setRoot(XWorkConverter xworkConverter, CompoundRootAccessor accessor, CompoundRoot compoundRoot,
boolean allowStaticMethodAccess) {
this.root = compoundRoot;
this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
this.context = Ognl.createDefaultContext(this.root, accessor, new OgnlTypeConverterWrapper(xworkConverter), securityMemberAccess);
context.put(VALUE_STACK, this);
Ognl.setClassResolver(context, accessor);
((OgnlContext) context).setTraceEvaluations(false);
((OgnlContext) context).setKeepLastEvaluation(false);
} @Inject(XWorkConstants.DEV_MODE)
public void setDevMode(String mode) {
devMode = "true".equalsIgnoreCase(mode);
} @Inject(value = "logMissingProperties", required = false)
public void setLogMissingProperties(String logMissingProperties) {
this.logMissingProperties = "true".equalsIgnoreCase(logMissingProperties);
} /**
* @see com.opensymphony.xwork2.util.ValueStack#getContext()
*/
public Map<String, Object> getContext() {
return context;
} /**
* @see com.opensymphony.xwork2.util.ValueStack#setDefaultType(java.lang.Class)
*/
public void setDefaultType(Class defaultType) {
this.defaultType = defaultType;
} /**
* @see com.opensymphony.xwork2.util.ValueStack#setExprOverrides(java.util.Map)
*/
public void setExprOverrides(Map<Object, Object> overrides) {
if (this.overrides == null) {
this.overrides = overrides;
} else {
this.overrides.putAll(overrides);
}
} /**
* @see com.opensymphony.xwork2.util.ValueStack#getExprOverrides()
*/
public Map<Object, Object> getExprOverrides() {
return this.overrides;
} /**
* @see com.opensymphony.xwork2.util.ValueStack#getRoot()
*/
public CompoundRoot getRoot() {
return root;
} /**
* @see com.opensymphony.xwork2.util.ValueStack#setParameter(String, Object)
*/
public void setParameter(String expr, Object value) {
setValue(expr, value, devMode);
} /** /**
* @see com.opensymphony.xwork2.util.ValueStack#setValue(java.lang.String, java.lang.Object)
*/
public void setValue(String expr, Object value) {
setValue(expr, value, devMode);
} /**
* @see com.opensymphony.xwork2.util.ValueStack#setValue(java.lang.String, java.lang.Object, boolean)
*/
public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
Map<String, Object> context = getContext();
try {
trySetValue(expr, value, throwExceptionOnFailure, context);
} catch (OgnlException e) {
handleOgnlException(expr, value, throwExceptionOnFailure, e);
} catch (RuntimeException re) { //XW-281
handleRuntimeException(expr, value, throwExceptionOnFailure, re);
} finally {
cleanUpContext(context);
}
} private void trySetValue(String expr, Object value, boolean throwExceptionOnFailure, Map<String, Object> context) throws OgnlException {
context.put(XWorkConverter.CONVERSION_PROPERTY_FULLNAME, expr);
context.put(REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
ognlUtil.setValue(expr, context, root, value);
} private void cleanUpContext(Map<String, Object> context) {
ReflectionContextState.clear(context);
context.remove(XWorkConverter.CONVERSION_PROPERTY_FULLNAME);
context.remove(REPORT_ERRORS_ON_NO_PROP);
} private void handleRuntimeException(String expr, Object value, boolean throwExceptionOnFailure, RuntimeException re) {
if (throwExceptionOnFailure) {
String message = ErrorMessageBuilder.create()
.errorSettingExpressionWithValue(expr, value)
.build();
throw new XWorkException(message, re);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Error setting value [#0] with expression [#1]", re, value.toString(), expr);
}
}
} private void handleOgnlException(String expr, Object value, boolean throwExceptionOnFailure, OgnlException e) {
boolean shouldLog = shouldLogMissingPropertyWarning(e);
String msg = null;
if (throwExceptionOnFailure || shouldLog) {
msg = ErrorMessageBuilder.create()
.errorSettingExpressionWithValue(expr, value)
.build();
}
if (shouldLog) {
LOG.warn(msg, e);
} if (throwExceptionOnFailure) {
throw new XWorkException(msg, e);
}
} /**
* @see com.opensymphony.xwork2.util.ValueStack#findString(java.lang.String)
*/
public String findString(String expr) {
return (String) findValue(expr, String.class);
} public String findString(String expr, boolean throwExceptionOnFailure) {
return (String) findValue(expr, String.class, throwExceptionOnFailure);
} /**
* @see com.opensymphony.xwork2.util.ValueStack#findValue(java.lang.String)
*/
public Object findValue(String expr, boolean throwExceptionOnFailure) {
try {
setupExceptionOnFailure(throwExceptionOnFailure);
return tryFindValueWhenExpressionIsNotNull(expr);
} catch (OgnlException e) {
return handleOgnlException(expr, throwExceptionOnFailure, e);
} catch (Exception e) {
return handleOtherException(expr, throwExceptionOnFailure, e);
} finally {
ReflectionContextState.clear(context);
}
} private void setupExceptionOnFailure(boolean throwExceptionOnFailure) {
if (throwExceptionOnFailure) {
context.put(THROW_EXCEPTION_ON_FAILURE, true);
}
} private Object tryFindValueWhenExpressionIsNotNull(String expr) throws OgnlException {
if (expr == null) {
return null;
}
return tryFindValue(expr);
} private Object handleOtherException(String expr, boolean throwExceptionOnFailure, Exception e) {
logLookupFailure(expr, e); if (throwExceptionOnFailure)
throw new XWorkException(e); return findInContext(expr);
} private Object tryFindValue(String expr) throws OgnlException {
Object value;
expr = lookupForOverrides(expr);
if (defaultType != null) {
value = findValue(expr, defaultType);
} else {
value = getValueUsingOgnl(expr);
if (value == null) {
value = findInContext(expr);
}
}
return value;
} private String lookupForOverrides(String expr) {
if ((overrides != null) && overrides.containsKey(expr)) {
expr = (String) overrides.get(expr);
}
return expr;
} private Object getValueUsingOgnl(String expr) throws OgnlException {
try {
return ognlUtil.getValue(expr, context, root);
} finally {
context.remove(THROW_EXCEPTION_ON_FAILURE);
}
} public Object findValue(String expr) {
return findValue(expr, false);
} /**
* @see com.opensymphony.xwork2.util.ValueStack#findValue(java.lang.String, java.lang.Class)
*/
public Object findValue(String expr, Class asType, boolean throwExceptionOnFailure) {
try {
setupExceptionOnFailure(throwExceptionOnFailure);
return tryFindValueWhenExpressionIsNotNull(expr, asType);
} catch (OgnlException e) {
final Object value = handleOgnlException(expr, throwExceptionOnFailure, e);
return converter.convertValue(getContext(), value, asType);
} catch (Exception e) {
final Object value = handleOtherException(expr, throwExceptionOnFailure, e);
return converter.convertValue(getContext(), value, asType);
} finally {
ReflectionContextState.clear(context);
}
} private Object tryFindValueWhenExpressionIsNotNull(String expr, Class asType) throws OgnlException {
if (expr == null) {
return null;
}
return tryFindValue(expr, asType);
} private Object handleOgnlException(String expr, boolean throwExceptionOnFailure, OgnlException e) {
Object ret = findInContext(expr);
if (ret == null) {
if (shouldLogMissingPropertyWarning(e)) {
LOG.warn("Could not find property [#0]!", e, expr);
}
if (throwExceptionOnFailure) {
throw new XWorkException(e);
}
}
return ret;
} private boolean shouldLogMissingPropertyWarning(OgnlException e) {
return (e instanceof NoSuchPropertyException || e instanceof MethodFailedException)
&& devMode && logMissingProperties;
} private Object tryFindValue(String expr, Class asType) throws OgnlException {
Object value = null;
try {
expr = lookupForOverrides(expr);
value = getValue(expr, asType);
if (value == null) {
value = findInContext(expr);
return converter.convertValue(getContext(), value, asType);
}
} finally {
context.remove(THROW_EXCEPTION_ON_FAILURE);
}
return value;
} private Object getValue(String expr, Class asType) throws OgnlException {
return ognlUtil.getValue(expr, context, root, asType);
} private Object findInContext(String name) {
return getContext().get(name);
} public Object findValue(String expr, Class asType) {
return findValue(expr, asType, false);
} /**
* Log a failed lookup, being more verbose when devMode=true.
*
* @param expr The failed expression
* @param e The thrown exception.
*/
private void logLookupFailure(String expr, Exception e) {
String msg = LoggerUtils.format("Caught an exception while evaluating expression '#0' against value stack", expr);
if (devMode && LOG.isWarnEnabled()) {
LOG.warn(msg, e);
LOG.warn("NOTE: Previous warning message was issued due to devMode set to true.");
} else if (LOG.isDebugEnabled()) {
LOG.debug(msg, e);
}
} /**
* @see com.opensymphony.xwork2.util.ValueStack#peek()
*/
public Object peek() {
return root.peek();
} /**
* @see com.opensymphony.xwork2.util.ValueStack#pop()
*/
public Object pop() {
return root.pop();
} /**
* @see com.opensymphony.xwork2.util.ValueStack#push(java.lang.Object)
*/
public void push(Object o) {
root.push(o);
} /**
* @see com.opensymphony.xwork2.util.ValueStack#set(java.lang.String, java.lang.Object)
*/
public void set(String key, Object o) {
//set basically is backed by a Map pushed on the stack with a key being put on the map and the Object being the value
Map setMap = retrieveSetMap();
setMap.put(key, o);
} private Map retrieveSetMap() {
Map setMap;
Object topObj = peek();
if (shouldUseOldMap(topObj)) {
setMap = (Map) topObj;
} else {
setMap = new HashMap();
setMap.put(MAP_IDENTIFIER_KEY, "");
push(setMap);
}
return setMap;
} /**
* check if this is a Map put on the stack for setting if so just use the old map (reduces waste)
*/
private boolean shouldUseOldMap(Object topObj) {
return topObj instanceof Map && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null;
} /**
* @see com.opensymphony.xwork2.util.ValueStack#size()
*/
public int size() {
return root.size();
} private Object readResolve() {
// TODO: this should be done better
ActionContext ac = ActionContext.getContext();
Container cont = ac.getContainer();
XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
TextProvider prov = cont.getInstance(TextProvider.class, "system");
boolean allow = "true".equals(cont.getInstance(String.class, XWorkConstants.ALLOW_STATIC_METHOD_ACCESS));
OgnlValueStack aStack = new OgnlValueStack(xworkConverter, accessor, prov, allow);
aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
aStack.setRoot(xworkConverter, accessor, this.root, allow); return aStack;
} public void clearContextValues() {
//this is an OGNL ValueStack so the context will be an OgnlContext
//it would be better to make context of type OgnlContext
((OgnlContext) context).getValues().clear();
} public void setAcceptProperties(Set<Pattern> acceptedProperties) {
securityMemberAccess.setAcceptProperties(acceptedProperties);
} public void setExcludeProperties(Set<Pattern> excludeProperties) {
securityMemberAccess.setExcludeProperties(excludeProperties);
} @Inject
public void setXWorkConverter(final XWorkConverter converter) {
this.converter = converter;
}
}

继续调试查看ValueStack属性,context是ognl.OgnlContext也是一个Map类型,Root类型是一个ValueStack类型。

这里我们看以看出Root是一个CompoundRoot类型

 /*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.opensymphony.xwork2.util; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; /**
* A Stack that is implemented using a List.
*
* @author plightbo
* @version $Revision$
*/
public class CompoundRoot extends CopyOnWriteArrayList<Object> { private static final long serialVersionUID = 8563229069192473995L; public CompoundRoot() {
} public CompoundRoot(List<?> list) {
super(list);
} public CompoundRoot cutStack(int index) {
return new CompoundRoot(subList(index, size()));
} public Object peek() {
return get(0);
} public Object pop() {
return remove(0);
} public void push(Object o) {
add(0, o);
}
}

从源代码中我们可以看出CompoundRoot是一个通过List实现的栈,而结合测试我们可以知道Product对象就是存储到root对象中。

在数据查找时,如果都存储在值栈中的话,会先从值栈中第一个元素查找,如果第一个元素的属性不包含要找的对象,就从第二元素找。

  •  总结:

ValueStack(值栈):贯穿整个Action的生命周期(每个Action类的对象实例都拥有一个ValueStack对象),相当于一个数据的中转站,在其中保存当前Action对象和其他相关对象。

Struts框架把ValueStack对象保存在名为"struts.valueStack"的请求属性中。

在值栈对象的内部有两个逻辑部分:

1、ContextMap:Struts把各种各样的映射关系(一些Map类型的对象)压入ContextMap中。实际上就是对ActionContext的引用。Struts会把下面这些映射压入ContextMap对中:

parameters:该Map中包含当请求的请求参数

request:该Map中包含当前request对象中的所有属性

session:该Map中包含当前session对象中所有属性

application:该Map中包含当前application对象中的所有属性

attr:该Map中按如下顺序来检索某个属性:request,session,application

2、ObjectStack:Struts把Action和相关对象压入ObjectStack中。

Struts(九):值栈(OGNL)的更多相关文章

  1. struts框架值栈问题二之值栈的内部结构

    2. 问题二 : 值栈的内部结构 ? * 值栈由两部分组成 > root -- Struts把动作和相关对象压入 ObjectStack 中--List > context -- Stru ...

  2. struts框架值栈问题三之值栈的创建和ActionContext对象的关系

    3. 问题三 : 值栈对象的创建,ValueStack 和 ActionContext 是什么关系? * 值栈对象是请求时创建的 * ActionContext是绑定到当前的线程上(一个Action访 ...

  3. struts框架值栈问题七之EL表达式也会获取到值栈中的数据

    7. 问题七:为什么EL也能访问值栈中的数据? * StrutsPreparedAndExecuteFilter的doFilter代码中 request = prepare.wrapRequest(r ...

  4. Struts2的整体回顾(Action, 拦截器, 值栈, OGNL表示式, ModelDriven)

    ValueStack里有map(request, session, attr, parameters)和对象栈. Map调用的方法: ActionContext.getContext().put(k, ...

  5. 关于Struts2中的值栈与OGNL表达式

    1.1.1    OGNL概述: Object Graphic Navigation Language(对象图导航语言)的缩写 * EL     :OGNL比EL功能强大很多倍. 它是一个开源项目. ...

  6. Java中的值栈

    OGNL表示式使用 和 值栈 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,它是一个开源项目. Struts2框架使用OGNL作为默认的表达式 ...

  7. Ognl值栈对象及struts标签

    用户每次访问struts的action,都会创建一个Action对象.值栈对象.ActionContext对象:然后把Action对象放入值栈中: 最后再把值栈对象放入request中,传入jsp页面 ...

  8. Struts工作机制图+OGNL+EL+值栈(Map,对象栈)

    struts 值栈  通过get set方法 方便的获取,设置属性值      比如从jsp页面传来的參数...从Action设置jsp所要回显的内容 注意EL表达式,struts2对request进 ...

  9. 【Java EE 学习 36】【struts2】【struts2系统验证】【struts2 ognl值栈】【struts2 ongl标签】【struts2 UI标签】【struts2模型驱动和令牌机制】

    一.struts2系统验证 1.基于struts2系统验证的方式实际上就是通过配置xml文件的方式达到验证的目的. 2.实际上系统校验的方法和手工校验的方法在底层的基本实现是相同的.但是使用系统校验的 ...

随机推荐

  1. Mycat 介绍

    Mycat 是什么 Mycat是什么?从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了 MySQL协议的的Server,前端用户可以把它看作是一个数据库代理,用 MySQL客户端工具和命 ...

  2. MYSQL数据库学习十四 存储过程和函数的操作

    14.1 为什么使用存储过程和函数 一个完整的操作会包含多条SQL语句,在执行过程中需要根据前面SQL语句的执行结果有选择的执行后面的SQL语句. 存储过程和函数的优点: 允许标准组件式编程,提高了S ...

  3. Redis学习笔记(三)常用命令整理

    Redis 常用命令 1.DEL key 删除key2.EXISTS key 检查key是否存在3.KEYS * 查看所有的key4.EXPIRE key seconds 设置key的过期时间5.TT ...

  4. 网络通信 --> TCP三次握手和四次挥手

    TCP三次握手和四次挥手 建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 一.TCP报文格式 如下图: (1)序号:Seq序号,占32位,用来标识从TCP源端向目的端发 ...

  5. iOS 代码开发规范

    最近公司因为业务增多,又招了一个iOS小伙伴,内心无比开心呀.但是多了一个人,日常代码开发就要统一规范起来,方便日后维护和查找.下边列一下我日常用到一些代码规范. (一).SVN规范 1.代码一定要先 ...

  6. CSS速查列表-1-(background)背景

    CSS 背景 CSS 属性定义背景效果: background-color background-image background-repeat background-attachment backg ...

  7. c# 实时监控数据库 SqlDependency

    http://blog.csdn.net/idays021/article/details/49661855 class Program { private static string _connSt ...

  8. jsp pageEncoding属性详解

    Java pageEncoding原理详解 首先看下文章解释: 意思是jsp文件本身的编码 巨大的疑问:这里有一个很大的问题,既然你都已经从jsp中读到了这条属性,证明内容读取正确,那你还需要知道这条 ...

  9. js通过a链接控制多个DIV只显示其中一个其它隐藏

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. 记录python接口自动化测试--把操作excel文件的方法封装起来(第五目)

    前面补充了如何来操作excel文件,这次把如何获取excel文件的sheet对象.行数.单元格数据的方法进行封装,方便后面调用 handle_excel.py# coding:utf-8 import ...