TA的每日心情 | 开心 2021-12-13 21:45 |
---|
签到天数: 15 天 [LV.4]偶尔看看III
|
struts 2 是基于OpenSymphony的网络工程框架,所以struts的类基本都在OpenSymphony的子包下。
在Struts 2 中的模型(model),视图(view)和控制器(controller)分别用动作(action),结果(result)和过滤调度器(FilterDispatcher)来实现的。
控制器(controller)的任务是将用户的请求映射到合适的动作(action)。在Struts 2中,和过滤调度器(FilterDispatcher)做控制器(controller)同样的工作。
模型(model)包含数据和业务逻辑。在Struts 2中模型(model)是由动作(action)组件来体现的。
视图(view)是MVC模式中的描述组件。在Struts 2中视图(view)是用JSP, Velocity模板, Freemaker或一些其他描述层的技术实现的。

控制器(controller)接收到用户的请求,决定调用哪个Struct 2的动作(action)。
框架(Framework)创建一个动作(action)的实例并将它和动作调用器(ActionInvocation)的一个实例进行关联。
在Struts2中, 调用的动作(action)可以通过在应用XML文件中定义的一系列拦截器(interceptor)。
框架(Framework)调用动作调用器(ActionInvocation)的invoke()方法启动动作(action)的执行。
invoke()方法每次被调用的时候,动作调用器(ActionInvocation)都会询问自身的状态并执行下一步的拦截器(interceptor)。
动作调用器(ActionInvocation)通过调用拦截器(interceptor)的interceptor()方法来掌握控制在栈中的拦截器(interceptor)。
拦截器(interceptor)的interceptor()方法依次调用动作调用器(ActionInvocation)的invoke()方法,直到所有的拦截器(interceptor)被调用。最后,动作(action)被调用并将相应的结果返回给用户。
拦截器(interceptor)可以在动作(action)执行之前或之后,或是前后同时被调用。不是必须每次都调用它们。
首先拦截器(interceptor)按照在栈中定义的顺序执行,然后动作(action)被调用并将结果整合,之后再倒序调用在将栈中的拦截器(interceptor)。
抽象类com.opensymphony.xwork2.interceptor.AbstractInterceptor 继承了接口类Interceptor,下面就以国际化拦截器部分代码为例子:
public class I18nInterceptor extends AbstractInterceptor {
protected static final Logger LOG = LoggerFactory.getLogger(I18nInterceptor.class);
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("intercept ""
+ invocation.getProxy().getNamespace() + "/"
+ invocation.getProxy().getActionName() + "" { ");
}
//get requested locale
Map<String, Object> params = invocation.getInvocationContext().getParameters();
boolean storeInSession = true;
Object requested_locale = findLocaleParameter(params, parameterName);
if (requested_locale == null) {
requested_locale = findLocaleParameter(params, requestOnlyParameterName);
if (requested_locale != null) {
storeInSession = false;
}
}
//save it in session
Map<String, Object> session = invocation.getInvocationContext().getSession();
/*这里省略了保存session信息的代码,补充:invocation.getInvocationContext()返回一个ActionContext对象,ActionContext cd = invocation.getInvocationContext()*/
saveLocale(invocation, locale);
if (LOG.isDebugEnabled()) {
LOG.debug("before Locale=" + invocation.getStack().findValue("locale"));
}
/*拦截器(interceptor)的interceptor()方法依次调用动作调用器(ActionInvocation)的invoke()方法,直到所有的拦截器(interceptor)被调用*/
final String result = invocation.invoke();
if (LOG.isDebugEnabled()) {
LOG.debug("after Locale=" + invocation.getStack().findValue("locale"));
}
if (LOG.isDebugEnabled()) {
LOG.debug("intercept } ");
}
return result;
}
那么Interceptor何时调用呢?
框架(Framework)调用动作调用器(ActionInvocation)的invoke()方法启动动作(action)的执行。
invoke()方法每次被调用的时候,动作调用器(ActionInvocation)都会询问自身的状态并执行下一步的拦截器(interceptor)。
参考com.opensymphony.xwork2.DefaultActionInvocation
public class DefaultActionInvocation implements ActionInvocation {
/*其他方法略掉*/
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();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
/*invoke()方法每次被调用的时候,动作调用器(ActionInvocation)都会询问自身的状态并执行下一步的拦截器(interceptor)*/
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} else {
resultCode = invokeActionOnly();
}
/*首先拦截器(interceptor)按照在栈中定义的顺序执行,然后动作(action)被调用并将结果整合,之后再倒序调用在将栈中的拦截器(interceptor)*/
// 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 (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
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);
}
}
总结:通过上面的介绍,不难发现Interceptor的本质就是一个Action,它要达到的目的就是为了提高Action代码的复用性(比如国际化或者类型转换拦截器),降低Action的耦合性,但是Intercepetor又是一中特殊的Action,它在执行指定Action之前或者之后完成可复用的模块化业务逻辑,可以把通用的逻辑进行封装(比如国际化或者类型转换拦)。
下面提供DefaultActionInvocation的完整源代码struts2.3.1.2
/*
* 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.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
import com.opensymphony.xwork2.config.entities.ResultConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.PreResultListener;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* The Default ActionInvocation implementation
*
* @author Rainer Hermanns
* @author tmjee
* @version $Date: 2011-06-09 00:40:33 +0200 (Thu, 09 Jun 2011) $ $Id: DefaultActionInvocation.java 1133590 2011-06-08 22:40:33Z jafl $
* @see com.opensymphony.xwork2.DefaultActionProxy
*/
public class DefaultActionInvocation implements ActionInvocation {
private static final long serialVersionUID = -585293628862447329L;
//static {
// if (ObjectFactory.getContinuationPackage() != null) {
// continuationHandler = new ContinuationHandler();
// }
//}
private static final Logger LOG = LoggerFactory.getLogger(DefaultActionInvocation.class);
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
protected Object action;
protected ActionProxy proxy;
protected List<PreResultListener> preResultListeners;
protected Map<String, Object> extraContext;
protected ActionContext invocationContext;
protected Iterator<InterceptorMapping> interceptors;
protected ValueStack stack;
protected Result result;
protected Result explicitResult;
protected String resultCode;
protected boolean executed = false;
protected boolean pushAction = true;
protected ObjectFactory objectFactory;
protected ActionEventListener actionEventListener;
protected ValueStackFactory valueStackFactory;
protected Container container;
private Configuration configuration;
protected UnknownHandlerManager unknownHandlerManager;
public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
DefaultActionInvocation.this.extraContext = extraContext;
DefaultActionInvocation.this.pushAction = pushAction;
}
@Inject
public void setUnknownHandlerManager(UnknownHandlerManager unknownHandlerManager) {
this.unknownHandlerManager = unknownHandlerManager;
}
@Inject
public void setValueStackFactory(ValueStackFactory fac) {
this.valueStackFactory = fac;
}
@Inject
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
@Inject
public void setObjectFactory(ObjectFactory fac) {
this.objectFactory = fac;
}
@Inject
public void setContainer(Container cont) {
this.container = cont;
}
@Inject(required=false)
public void setActionEventListener(ActionEventListener listener) {
this.actionEventListener = listener;
}
public Object getAction() {
return action;
}
public boolean isExecuted() {
return executed;
}
public ActionContext getInvocationContext() {
return invocationContext;
}
public ActionProxy getProxy() {
return proxy;
}
/**
* If the DefaultActionInvocation has been executed before and the Result is an instance of ActionChainResult, this method
* will walk down the chain of ActionChainResults until it finds a non-chain result, which will be returned. If the
* DefaultActionInvocation"s result has not been executed before, the Result instance will be created and populated with
* the result params.
*
* @return a Result instance
* @throws Exception
*/
public Result getResult() throws Exception {
Result returnResult = result;
// If we"ve chained to other Actions, we need to find the last result
while (returnResult instanceof ActionChainResult) {
ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();
if (aProxy != null) {
Result proxyResult = aProxy.getInvocation().getResult();
if ((proxyResult != null) && (aProxy.getExecuteResult())) {
returnResult = proxyResult;
} else {
break;
}
} else {
break;
}
}
return returnResult;
}
public String getResultCode() {
return resultCode;
}
public void setResultCode(String resultCode) {
if (isExecuted())
throw new IllegalStateException("Result has already been executed.");
this.resultCode = resultCode;
}
public ValueStack getStack() {
return stack;
}
/**
* Register a com.opensymphony.xwork2.interceptor.PreResultListener to be notified after the Action is executed and before the
* Result is executed. The ActionInvocation implementation must guarantee that listeners will be called in the order
* in which they are registered. Listener registration and execution does not need to be thread-safe.
*
* @param listener
*/
public void addPreResultListener(PreResultListener listener) {
if (preResultListeners == null) {
preResultListeners = new ArrayList<PreResultListener>(1);
}
preResultListeners.add(listener);
}
public Result createResult() throws Exception {
if (explicitResult != null) {
Result ret = explicitResult;
explicitResult = null;
return ret;
}
ActionConfig config = proxy.getConfig();
Map<String, ResultConfig> results = config.getResults();
ResultConfig resultConfig = null;
try {
resultConfig = results.get(resultCode);
} catch (NullPointerException e) {
// swallow
}
if (resultConfig == null) {
// If no result is found for the given resultCode, try to get a wildcard "*" match.
resultConfig = results.get("*");
}
if (resultConfig != null) {
try {
return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
} catch (Exception e) {
LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
throw new XWorkException(e, resultConfig);
}
} else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
}
return null;
}
/**
* @throws ConfigurationException If no result can be found with the returned code
*/
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();
String interceptorMsg = "interceptor: " + interceptor.getName();
UtilTimerStack.push(interceptorMsg);
try {
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
}
finally {
UtilTimerStack.pop(interceptorMsg);
}
} 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 (Object preResultListener : preResultListeners) {
PreResultListener listener = (PreResultListener) preResultListener;
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);
}
}
public String invokeActionOnly() throws Exception {
return invokeAction(getAction(), proxy.getConfig());
}
protected void createAction(Map<String, Object> contextMap) {
// load action
String timerKey = "actionCreate: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch (InstantiationException e) {
throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
} catch (IllegalAccessException e) {
throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
} catch (Exception e) {
String gripe = "";
if (proxy == null) {
gripe = "Whoa! No ActionProxy instance found in current ActionInvocation. This is bad ... very bad";
} else if (proxy.getConfig() == null) {
gripe = "Sheesh. Where"d that ActionProxy get to? I can"t find it in the current ActionInvocation!?";
} else if (proxy.getConfig().getClassName() == null) {
gripe = "No Action defined for "" + proxy.getActionName() + "" in namespace "" + proxy.getNamespace() + """;
} else {
gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ", defined for "" + proxy.getActionName() + "" in namespace "" + proxy.getNamespace() + """;
}
gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
throw new XWorkException(gripe, e, proxy.getConfig());
} finally {
UtilTimerStack.pop(timerKey);
}
if (actionEventListener != null) {
action = actionEventListener.prepare(action, stack);
}
}
protected Map<String, Object> createContextMap() {
Map<String, Object> contextMap;
if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
// In case the ValueStack was passed in
stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
if (stack == null) {
throw new IllegalStateException("There was a null Stack set into the extra params.");
}
contextMap = stack.getContext();
} else {
// create the value stack
// this also adds the ValueStack to its context
stack = valueStackFactory.createValueStack();
// create the action context
contextMap = stack.getContext();
}
// put extraContext in
if (extraContext != null) {
contextMap.putAll(extraContext);
}
//put this DefaultActionInvocation into the context map
contextMap.put(ActionContext.ACTION_INVOCATION, this);
contextMap.put(ActionContext.CONTAINER, container);
return contextMap;
}
/**
* Uses getResult to get the final Result and executes it
*
* @throws ConfigurationException If not result can be found with the returned code
*/
private void executeResult() throws Exception {
result = createResult();
String timerKey = "executeResult: " + getResultCode();
try {
UtilTimerStack.push(timerKey);
if (result != null) {
result.execute(this);
} else if (resultCode != null && !Action.NONE.equals(resultCode)) {
throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
+ " and result " + getResultCode(), proxy.getConfig());
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
}
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
public void init(ActionProxy proxy) {
this.proxy = proxy;
Map<String, Object> contextMap = createContextMap();
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if (actionContext != null) {
actionContext.setActionInvocation(this);
}
createAction(contextMap);
if (pushAction) {
stack.push(action);
contextMap.put("action", action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don"t get problems with the iterator if someone changes the list
List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
interceptors = interceptorList.iterator();
}
protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
String methodName = proxy.getMethod();
if (LOG.isDebugEnabled()) {
LOG.debug("Executing action method = " + actionConfig.getMethodName());
}
String timerKey = "invokeAction: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
boolean methodCalled = false;
Object methodResult = null;
Method method = null;
try {
method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);
} catch (NoSuchMethodException e) {
// hmm -- OK, try doXxx instead
try {
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);
} catch (NoSuchMethodException e1) {
// well, give the unknown handler a shot
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
methodCalled = true;
} catch (NoSuchMethodException e2) {
// throw the original one
throw e;
}
} else {
throw e;
}
}
}
if (!methodCalled) {
methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);
}
return saveResult(actionConfig, methodResult);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
} catch (InvocationTargetException e) {
// We try to return the source exception.
Throwable t = e.getTargetException();
if (actionEventListener != null) {
String result = actionEventListener.handleException(t, getStack());
if (result != null) {
return result;
}
}
if (t instanceof Exception) {
throw (Exception) t;
} else {
throw e;
}
} finally {
UtilTimerStack.pop(timerKey);
}
}
/**
* Save the result to be used later.
* @param actionConfig
* @param methodResult the result of the action.
* @return the result code to process.
*/
protected String saveResult(ActionConfig actionConfig, Object methodResult) {
if (methodResult instanceof Result) {
this.explicitResult = (Result) methodResult;
// Wire the result automatically
container.inject(explicitResult);
return null;
} else {
return (String) methodResult;
}
}
}
|
|