您的位置:首页 > 编程语言 > Java开发

Struts2框架教程笔记一

2012-04-08 01:24 204 查看
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;

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: