最近用struts2的时候发现一个小问题,当请求中包含多个相同的参数比如:http://localhost:8888/struts2/adduser.action?degree=1°ree=2°ree=3°ree=4
多个degree的情况,而在action中只是用的String来接收,会通过ognl转换成1, 2, 3, 4.中间会多一个空格. 我jsp中使用的是checkbox,在struts2中 会对应checkboxInterceptor拦截器来处理.当处理完后会调用ai.invoke().这个方法中会调用interceptors中的拦截器,继续往下执行下一个拦截器.看源码:
checkboxInterceptor:
public String intercept(ActionInvocation ai) throws Exception {
Map parameters = ai.getInvocationContext().getParameters();
Map<String, String> newParams = new HashMap<String, String>();
Set<String> keys = parameters.keySet();
for (Iterator<String> iterator = keys.iterator(); iterator.hasNext();) {
String key = iterator.next();
if (key.startsWith("__checkbox_")) {
String name = key.substring("__checkbox_".length());
iterator.remove();
// is this checkbox checked/submitted?
if (!parameters.containsKey(name)) {
// if not, let's be sure to default the value to false
newParams.put(name, uncheckedValue);
}
}
}
parameters.putAll(newParams);
return ai.invoke();
}
DefaultActionInvocation:
public String invoke
() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
if (executed) {
throw new IllegalStateException("Action has already executed");
}
if (interceptors
.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
UtilTimerStack.profile("interceptor: "+interceptor.getName(),
new UtilTimerStack.ProfilingBlock<String>() {
public String doProfiling() throws Exception {
resultCode = interceptor.getInterceptor().intercept
(DefaultActionInvocation.this);//执行拦截器
return null;
}
});
} else {
resultCode = invokeActionOnly();
}
// this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
if (preResultListeners != null) {
for (Iterator iterator = preResultListeners.iterator();
iterator.hasNext();) {
PreResultListener listener = (PreResultListener) iterator.next();
String _profileKey="preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
}
// now execute the result, if we're supposed to
if (proxy.getExecuteResult()) {
executeResult();
}
executed = true;
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
接下来调用parameterInterceptor:
public String doIntercept(ActionInvocation invocation) throws Exception {
Object action = invocation.getAction();
if (!(action instanceof NoParameters)) {
ActionContext ac = invocation.getInvocationContext();
final Map parameters = retrieveParametersFromContext(ac);
if (LOG.isDebugEnabled()) {
LOG.debug("Setting params " + getParameterLogMap(parameters));
}
if (parameters != null) {
Map contextMap = ac.getContextMap();
try {
ReflectionContextState.setCreatingNullObjects(contextMap, true);
ReflectionContextState.setDenyMethodExecution(contextMap, true);
ReflectionContextState.setReportingConversionErrors(contextMap, true);
ValueStack stack = ac.getValueStack();
setParameters(action, stack, parameters);
} finally {
ReflectionContextState.setCreatingNullObjects(contextMap, false);
ReflectionContextState.setDenyMethodExecution(contextMap, false);
ReflectionContextState.setReportingConversionErrors(contextMap, false);
}
}
}
return invocation.invoke();
}
会调用ognl中ValueStack的setParameter方法给action中parameter设值,通过setValue方法,
最后会调用 :
OgnlRunTime中的callappropriateMethod方法
public static Object callAppropriateMethod( OgnlContext context, Object source, Object target, String methodName, String propertyName, List methods, Object[] args ) throws MethodFailedException
{
Throwable reason = null;
Object[] actualArgs = objectArrayPool.create(args.length);
try {
Method method = getAppropriateMethod( context, source, target, methodName, propertyName, methods, args, actualArgs );
if ( (method == null) || !isMethodAccessible(context, source, method, propertyName) )
{
StringBuffer buffer = new StringBuffer();
if (args != null) {
for (int i = 0, ilast = args.length - 1; i <= ilast; i++) {
Object arg = args[i];
buffer.append((arg == null) ? NULL_STRING : arg.getClass().getName());
if (i < ilast) {
buffer.append(", "); //
主要原来自这里.在对args进行转换成strting的时候使用的是逗号加空格,
所以才导致转换后的集合中间会多一个空格.
}
}
}
throw new NoSuchMethodException( methodName + "(" + buffer + ")" );
}
return invokeMethod(target, method, actualArgs);
}
catch (NoSuchMethodException e)
{ reason = e; }
catch (IllegalAccessException e)
{ reason = e; }
catch (InvocationTargetException e)
{ reason = e.getTargetException(); }
finally {
objectArrayPool.recycle(actualArgs);
}
throw new MethodFailedException( source, methodName, reason );
}
终于找到问题的根源了.怎么来修改呢.我采取的方法是在setXXX方面中进行修改.
public void setDegree(String degree) {
//1, 2, 3, 4 converte to 1,2,3,4
String result = "";
if(null != degree && !"".equals(degree)){
String [] params = degree.split(",");
if(params.length > 1){
for(int i=0 ; i<params.length ; i++){
result += params[i].trim();
//通过trim()方法来处理.
if( i < params.length - 1){
result += ",";
}
}
}
}
this.degree = result;
}
总结:其实这样转换很麻烦,最好的解决办法是在属性中使用数组或者集合来接收请求中传递的多个相同的参数值.