• 背景:

从一个Member的增删改查,来了解Struts2的运行原理及学习ModelDriven拦截器、Preparable拦截器。

  • 新建项目实现列表的展示及删除功能:

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 02</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>

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.ognl.allowStaticMethodAccess" value="true" />
<constant name="struts.devMode" value="false" /> <package name="default" namespace="/" extends="struts-default">
<action name="member-*" class="com.dx.struts.actions.MemberAction" method="{1}">
<result name="{1}">/member-{1}.jsp</result>
<result name="delete" type="redirectAction">member-list</result>
</action>
</package>
</struts>

Member.java

/**
* @author Administrator
*
*/
package com.dx.struts.entity; public class Member{
private Long id;
private String name;
private Integer age;
private String gender; public Member() {
} public Member(Long id, String name, Integer age, String gender) {
super();
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
} public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
} }

MemberAction.java(暂时实现删除、列表功能)

 package com.dx.struts.actions;

 import java.util.Date;
import java.util.List;
import java.util.Map; import org.apache.struts2.interceptor.RequestAware; import com.dx.struts.dao.MemberDao;
import com.dx.struts.entity.Member; public class MemberAction implements RequestAware {
private MemberDao memberDao = new MemberDao(); private Long id; public void setId(Long id) {
this.id = id;
} public String list() {
List<Member> members = memberDao.getMembers();
request.put("members", members);
return "list";
} public String delete() {
memberDao.remove(this.id);
// 返回结果的类型应该为:redirectAction
// 也可以是chain:实际上chain是没有必要的,因为不需要在下一个action中保留啊当前action的状态
// 若使用chain,则到达目标页面后,地址栏显示的依然是删除的那个链接,则刷新时会重复提交。
return "delete";
} private Map<String, Object> request; @Override
public void setRequest(Map<String, Object> request) {
this.request = request;
} }

注意:这里边根据id删除Member接收id参数是通过:在MemberAction类中添加了id属性,之后实现了id的set方法才实现了参数接收。

MemberDao.java

package com.dx.struts.dao;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import com.dx.struts.entity.Member; public class MemberDao {
private static HashMap<Long, Member> members = new HashMap<Long, Member>(); static {
members.put(1001L, new Member(1001L, "member1", 20, "male"));
members.put(1002L, new Member(1002L, "member2", 20, "female"));
members.put(1003L, new Member(1003L, "member3", 20, "male"));
members.put(1004L, new Member(1004L, "member4", 20, "male"));
} public List<Member> getMembers() {
return new ArrayList<Member>(members.values());
} public Member get(Long id) {
return members.get(id);
} public void add(Member member) {
members.put(member.getId(), member);
} public void remove(Long id){
members.remove(id);
}
}

member-list.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:a href="/Struts_02/member-add.jsp" >add</s:a><br>
<table style="margin:0 auto;width:60%;" cellpadding="10" cellspacing="0" border="1">
<tr style="">
<td style="width:10%;">id</td>
<td style="width:40%;">name</td>
<td style="width:20%;">age</td>
<td style="width:20%;">gender</td>
<td style="width:10%;">view</td>
<td style="width:10%;">edit</td>
<td style="width:10%;">delete</td>
</tr>
<s:iterator value="#request.members">
<tr>
<td>${id}</td>
<td>${name}</td>
<td>${age}</td>
<td>${gender}</td>
<td><s:a href="member-view.action?id=%{id}" >view</s:a></td>
<td><s:a href="member-edit.action?id=%{id}" >edit</s:a></td>
<td><s:a href="member-delete.action?id=%{id}" >delete</s:a></td>
</tr>
</s:iterator>
</table>
</body>
</html>

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>
<a href="member-list.action">index</a>
</body>
</html>
  • 通过断点调试了解Action的运行原理:

把断点设置到delete方法内,断点调试代码的调用栈跟踪如下:

Daemon Thread [http-bio-8080-exec-7] (Suspended (breakpoint at line 52 in MemberAction))
owns: Method (id=91)
owns: SocketWrapper<E> (id=86)
NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]
NativeMethodAccessorImpl.invoke(Object, Object[]) line: 62
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 43
Method.invoke(Object, Object...) line: 498
OgnlRuntime.invokeMethod(Object, Method, Object[]) line: 871
OgnlRuntime.callAppropriateMethod(OgnlContext, Object, Object, String, String, List, Object[]) line: 1294
XWorkMethodAccessor(ObjectMethodAccessor).callMethod(Map, Object, String, Object[]) line: 68
XWorkMethodAccessor.callMethodWithDebugInfo(Map, Object, String, Object[]) line: 117
XWorkMethodAccessor.callMethod(Map, Object, String, Object[]) line: 108
OgnlRuntime.callMethod(OgnlContext, Object, String, Object[]) line: 1370
ASTMethod.getValueBody(OgnlContext, Object) line: 91
ASTMethod(SimpleNode).evaluateGetValueBody(OgnlContext, Object) line: 212
ASTMethod(SimpleNode).getValue(OgnlContext, Object) line: 258
Ognl.getValue(Object, Map, Object, Class) line: 467
Ognl.getValue(Object, Map, Object) line: 431
OgnlUtil$3.execute(Object) line: 352
OgnlUtil.compileAndExecuteMethod(String, Map<String,Object>, OgnlTask<T>) line: 404
OgnlUtil.callMethod(String, Map<String,Object>, Object) line: 350
DefaultActionInvocation.invokeAction(Object, ActionConfig) line: 430
DefaultActionInvocation.invokeActionOnly() line: 290
DefaultActionInvocation.invoke() line: 251
DeprecationInterceptor.intercept(ActionInvocation) line: 41
DefaultActionInvocation.invoke() line: 245
DebuggingInterceptor.intercept(ActionInvocation) line: 256
DefaultActionInvocation.invoke() line: 245
DefaultWorkflowInterceptor.doIntercept(ActionInvocation) line: 168
DefaultWorkflowInterceptor(MethodFilterInterceptor).intercept(ActionInvocation) line: 98
DefaultActionInvocation.invoke() line: 245
AnnotationValidationInterceptor(ValidationInterceptor).doIntercept(ActionInvocation) line: 265
AnnotationValidationInterceptor.doIntercept(ActionInvocation) line: 76
AnnotationValidationInterceptor(MethodFilterInterceptor).intercept(ActionInvocation) line: 98
DefaultActionInvocation.invoke() line: 245
StrutsConversionErrorInterceptor(ConversionErrorInterceptor).intercept(ActionInvocation) line: 138
DefaultActionInvocation.invoke() line: 245
ParametersInterceptor.doIntercept(ActionInvocation) line: 229
ParametersInterceptor(MethodFilterInterceptor).intercept(ActionInvocation) line: 98
DefaultActionInvocation.invoke() line: 245
ActionMappingParametersInteceptor(ParametersInterceptor).doIntercept(ActionInvocation) line: 229
ActionMappingParametersInteceptor(MethodFilterInterceptor).intercept(ActionInvocation) line: 98
DefaultActionInvocation.invoke() line: 245
StaticParametersInterceptor.intercept(ActionInvocation) line: 191
DefaultActionInvocation.invoke() line: 245
MultiselectInterceptor.intercept(ActionInvocation) line: 73
DefaultActionInvocation.invoke() line: 245
DateTextFieldInterceptor.intercept(ActionInvocation) line: 125
DefaultActionInvocation.invoke() line: 245
CheckboxInterceptor.intercept(ActionInvocation) line: 91
DefaultActionInvocation.invoke() line: 245
FileUploadInterceptor.intercept(ActionInvocation) line: 253
DefaultActionInvocation.invoke() line: 245
ModelDrivenInterceptor.intercept(ActionInvocation) line: 100
DefaultActionInvocation.invoke() line: 245
ScopedModelDrivenInterceptor.intercept(ActionInvocation) line: 141
DefaultActionInvocation.invoke() line: 245
ChainingInterceptor.intercept(ActionInvocation) line: 145
DefaultActionInvocation.invoke() line: 245
PrepareInterceptor.doIntercept(ActionInvocation) line: 171
PrepareInterceptor(MethodFilterInterceptor).intercept(ActionInvocation) line: 98
DefaultActionInvocation.invoke() line: 245
I18nInterceptor.intercept(ActionInvocation) line: 140
DefaultActionInvocation.invoke() line: 245
ServletConfigInterceptor.intercept(ActionInvocation) line: 164
DefaultActionInvocation.invoke() line: 245
AliasInterceptor.intercept(ActionInvocation) line: 193
DefaultActionInvocation.invoke() line: 245
ExceptionMappingInterceptor.intercept(ActionInvocation) line: 189
DefaultActionInvocation.invoke() line: 245
StrutsActionProxy.execute() line: 54
Dispatcher.serviceAction(HttpServletRequest, HttpServletResponse, ActionMapping) line: 575
ExecuteOperations.executeAction(HttpServletRequest, HttpServletResponse, ActionMapping) line: 81
StrutsPrepareAndExecuteFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 99
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 241
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 208
StandardWrapperValve.invoke(Request, Response) line: 218
StandardContextValve.invoke(Request, Response) line: 110
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 506
StandardHostValve.invoke(Request, Response) line: 169
ErrorReportValve.invoke(Request, Response) line: 103
AccessLogValve.invoke(Request, Response) line: 962
StandardEngineValve.invoke(Request, Response) line: 116
CoyoteAdapter.service(Request, Response) line: 452
Http11Processor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1087
Http11Protocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 637
JIoEndpoint$SocketProcessor.run() line: 318
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1142
ThreadPoolExecutor$Worker.run() line: 617

在这里介绍下上边UML是使用的http://plantuml.com/在线UML来实现的:

http://plantuml.com/

@startuml
"浏览器" -> StrutsPrepareAndExecuteFilter : dofilter()
StrutsPrepareAndExecuteFilter -> StrutsActionProxy :execute()
StrutsActionProxy -> DefaultActionInvocation :invoke()
DefaultActionInvocation -> ExceptionMappingInterceptor : interceptor()
ExceptionMappingInterceptor -> DefaultActionInvocation : invoke()
DefaultActionInvocation -> XxxxInterceptor : interceptor()
XxxxInterceptor -> DefaultActionInvocation : invoke()
DefaultActionInvocation -> DebuggingInterceptor : interceptor()
DebuggingInterceptor -> DefaultActionInvocation : invoke()
DefaultActionInvocation -> DefaultActionInvocation : invokeAction()
DefaultActionInvocation -> MemberAction : delete()
@enduml

StrutsActionProxy
ActionProxy是Action的一个代理类,也就是说Action的调用是通过ActionProxy实现的,
其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法,而该方法又调用了ActionInvocation

DefaultActionInvocation
ActionInvocation就是一个Action的调用者。
ActionInvocation在Action的执行过程中,负责Interceptor/Action/Result等一系列元素的调度。

具体代码请参考DefaultActionInvocation:

 /*
* 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.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.ognl.OgnlUtil;
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 ognl.MethodFailedException;
import ognl.NoSuchPropertyException;
import ognl.OgnlException; 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$ $Id$
* @see com.opensymphony.xwork2.DefaultActionProxy
*/
public class DefaultActionInvocation implements ActionInvocation { private static final Logger LOG = LoggerFactory.getLogger(DefaultActionInvocation.class); 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;
protected UnknownHandlerManager unknownHandlerManager;
protected OgnlUtil ognlUtil; public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
this.extraContext = extraContext;
this.pushAction = pushAction;
} @Inject
public void setUnknownHandlerManager(UnknownHandlerManager unknownHandlerManager) {
this.unknownHandlerManager = unknownHandlerManager;
} @Inject
public void setValueStackFactory(ValueStackFactory fac) {
this.valueStackFactory = fac;
} @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;
} @Inject
public void setOgnlUtil(OgnlUtil ognlUtil) {
this.ognlUtil = ognlUtil;
} 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 to register
*/
public void addPreResultListener(PreResultListener listener) {
if (preResultListeners == null) {
preResultListeners = new ArrayList<PreResultListener>(1);
} preResultListeners.add(listener);
} public Result createResult() throws Exception {
LOG.trace("Creating result related to resultCode [#0]", resultCode); 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) {
if (LOG.isDebugEnabled()) {
LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode);
}
} 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) {
if (LOG.isErrorEnabled()) {
LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName());
}
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 = 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) {
LOG.trace("Executing PreResultListeners for result [#0]", result); 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()); createInterceptors(proxy);
} protected void createInterceptors(ActionProxy proxy) {
// 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 = #0", methodName);
} String timerKey = "invokeAction: " + proxy.getActionName();
try {
UtilTimerStack.push(timerKey); Object methodResult;
try {
methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);
} catch (MethodFailedException e) {
// if reason is missing method, try find version with "do" prefix
if (e.getReason() instanceof NoSuchMethodException) {
try {
String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()";
methodResult = ognlUtil.callMethod(altMethodName, getStack().getContext(), action);
} catch (MethodFailedException e1) {
// if still method doesn't exist, try checking UnknownHandlers
if (e1.getReason() instanceof NoSuchMethodException) {
if (unknownHandlerManager.hasUnknownHandlers()) {
try {
methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
} catch (NoSuchMethodException e2) {
// throw the original one
throw e;
}
} else {
// throw the original one
throw e;
}
// throw the original exception as UnknownHandlers weren't able to handle invocation as well
if (methodResult == null) {
throw e;
}
} else {
// exception isn't related to missing action method, throw it
throw e1;
}
}
} else {
// exception isn't related to missing action method, throw it
throw e;
}
}
return saveResult(actionConfig, methodResult);
} catch (NoSuchPropertyException e) {
throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
} catch (MethodFailedException e) {
// We try to return the source exception.
Throwable t = e.getCause(); 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 current 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;
}
} /**
* Version ready to be serialize
*
* @return instance without reference to {@link Container}
*/
public ActionInvocation serialize() {
DefaultActionInvocation that = this;
that.container = null;
return that;
} /**
* Restoring Container
*
* @param actionContext current {@link ActionContext}
* @return instance which can be used to invoke action
*/
public ActionInvocation deserialize(ActionContext actionContext) {
DefaultActionInvocation that = this;
that.container = actionContext.getContainer();
return that;
} }
  • 通过新增用户、修改用户、查看用户及删除用户功能来学习ModelDriven拦截器

从上边实现删除代码中我们知道,我们可以通过在MemberAction类中添加一个id属性,并实现set方法,就可以实现在删除用户功能中接收传递到后台的id参数。不过,现在我们尝试学习一种新的方式:通过ModelDriven

修改struts.xml

        <action name="member-*" class="com.dx.struts.actions.MemberAction" method="{1}">
<result name="{1}">/member-{1}.jsp</result>
<result name="delete" type="redirectAction">member-list</result>
<result name="modify" type="redirectAction">member-list</result>
<result name="create" type="redirectAction">member-list</result>
</action>

修改MemberAction.java

package com.dx.struts.actions;

import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.struts2.interceptor.RequestAware;
import com.opensymphony.xwork2.ModelDriven;
import com.dx.struts.dao.MemberDao;
import com.dx.struts.entity.Member; public class MemberAction implements RequestAware, ModelDriven<Member> {
private MemberDao memberDao = new MemberDao();
private Member member; public String list() {
List<Member> members = memberDao.getMembers();
request.put("members", members);
return "list";
} public String view() {
Member member_ = memberDao.get(this.member.getId());
this.member.setAge(member_.getAge());
this.member.setName(member_.getName());
this.member.setGender(member_.getGender());
return "view";
} public String delete() {
memberDao.remove(this.member.getId());
// 返回结果的类型应该为:redirectAction
// 也可以是chain:实际上chain是没有必要的,因为不需要在下一个action中保留啊当前action的状态
// 若使用chain,则到达目标页面后,地址栏显示的依然是删除的那个链接,则刷新时会重复提交。
return "delete";
} public String edit() {
Member member_ = memberDao.get(this.member.getId());
this.member.setAge(member_.getAge());
this.member.setName(member_.getName());
this.member.setGender(member_.getGender());
return "edit";
} public String modify() {
Member member_ = memberDao.get(this.member.getId());
member_.setAge(this.member.getAge());
member_.setName(this.member.getName());
member_.setGender(this.member.getGender()); return "modify";
} public String create() {
member.setId(new Date().getTime());
memberDao.add(member); return "create";
} private Map<String, Object> request; @Override
public void setRequest(Map<String, Object> request) {
this.request = request;
} @Override
public Member getModel() {
this.member = new Member();
return this
.member;
}

}

注意:

这里是如果把member的所有属性都copy一份到MemberAction其实也是可以实现接收表单提交的参数的;

不过,实现了ModelDriven<Member>接口之后,不光是可以实现form表单提交的参数,也可以接收url提交的参数等。

member-view.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:debug></s:debug>
<s:form>
<s:textfield name="id" label="ID"></s:textfield>
<s:textfield name="name" label="Name"></s:textfield>
<s:textfield name="age" label="Age"></s:textfield>
<s:radio list="#{'male':'male','female':'female' }" name="gender" label="Gender"></s:radio>
</s:form>
</body>
</html>

member-add.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:debug></s:debug>
<s:form action="member-create.action">
<s:textfield name="name" label="Name"></s:textfield>
<s:textfield name="age" label="Age"></s:textfield>
<s:radio list="#{'male':'male','female':'female' }" name="gender" label="Gender"></s:radio>
<s:submit name="submit" label="提交"></s:submit>
</s:form>
</body>
</html>

member-edit.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:debug></s:debug>
<s:form action="member-modify.action">
<s:textfield name="id" label="ID"></s:textfield>
<s:textfield name="name" label="Name"></s:textfield>
<s:textfield name="age" label="Age"></s:textfield>
<s:radio list="#{'male':'male','female':'female' }" name="gender" label="Gender"></s:radio>
<s:submit name="submit" label="提交"></s:submit>
</s:form>
</body>
</html>

Struts(十六):通过CURD来学习Struts流程及ModelDriven的用法的更多相关文章

  1. Struts(十七):通过CURD来学习paramsPrepareParams拦截器栈

    背景: 通过上一章节<Struts(十六):通过CURD来学习Struts流程及ModelDriven的用法>学习了ModelDriven拦截器的用法,上章节中讲到了edit功能. 要修改 ...

  2. 二十六个月Android学习工作总结【转】

    原文:二十六个月Android学习工作总结 1.客户端的功能逻辑不难,UI界面也不难,但写UI花的时间是写功能逻辑的两倍.     2.写代码前的思考过程非常重要,即使在简单的功能,也需要在本子上把该 ...

  3. 二十六个月Android学习工作总结

    1.客户端的功能逻辑不难,UI界面也不难,但写UI花的时间是写功能逻辑的两倍. 2.写代码前的思考过程非常重要,即使在简单的功能,也需要在本子上把该功能的运行过程写出来. 3.要有自己的知识库,可以是 ...

  4. 流畅的python第十六章协程学习记录

    从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数.可是,在协程中,yield 通常出现在表达式的右边(例如,datum = yield),可以产出值,也可以不产出——如果 yi ...

  5. Struts(十八):通过CURD来学习PrepareInterceptor拦截器

    PrepareInterceptor拦截器的用法: 1.若Action实现了Preparable接口,则Action方法需实现prepare()方法: 2.PrepareInterceptor拦截器S ...

  6. Struts(二十六):文件上传

    表单的准备 想要使用html表单上传一个或多个文件 1.须把html表单的enctype属性设置为multipart/form-data 2.须把html表单的method属性设置为post 3.须添 ...

  7. 一脸懵逼学习Struts数据校验以及数据回显,模型驱动,防止表单重复提交的应用。

    1:Struts2表单数据校验: (1)前台校验,也称之为客户端校验,主要是通过Javascript编程的方式进行数据的验证. (2)后台校验,也称之为服务器校验,这里指的是使用Struts2通过xm ...

  8. 我的MYSQL学习心得(十六) 优化

    我的MYSQL学习心得(十六) 优化 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  9. 菜鸟学习Struts——简易计算器

    这是学习Struts的一个简单的例子文件结构如下: 1.配置Struts环境 2.新建input.jsp,success.jsp,error.jsp input.jsp代码如下: <%@ pag ...

随机推荐

  1. 用JAVA进行Json数据解析(对象数组的相互嵌套)

    这段时间我们在做一个英语翻译软件的小小小APP,涉及到了对Json数据的解析,所以特地来总结一下! 假设我们要对如下数据进行解析,其实在平时,返回的Json数据是很乱的,很难分清数据的关系,这是经过相 ...

  2. 关于html文档的规范

    1. <!DOCTYPE html> 告诉浏览器该文档使用哪种html或xhtml的规范 2. 元数据中的X-UA-Compatible <meta http-equiv=" ...

  3. 【itchat】用Python玩耍微信

    [itchat] itchat是个基于网页版微信的python微信API.功能目前做到基本可以满足正常的消息收发,信息的获取等等.不过对于红包之类网页版微信不支持的功能,这个模块自然也就无法支持了. ...

  4. [css 揭秘]:CSS揭秘 技巧(四):边框内圆角

    我的github地址:https://github.com/FannieGirl/ifannie/ 源码都在这上面哦! 喜欢的给我一个星吧 边框内圆角 问题:有时候我们需要一个容器,只在内侧有圆角,而 ...

  5. WEB端线上偶现问题如何复现?

    1.抓取出现问题的日志,还原操作过程,分析 每个过程中数据是否正常?是否有重复请求 2.询问当时操作员执行了哪些操作,尽可能多的了解事发经过 3.通过查看日志,数据库等信息,找到发生问题的节点, 比如 ...

  6. python全栈学习--day5

    字典 特点:字典是python中唯一的映射类型,采用键值对(key-value) 的形式存数据. 存储大量的数据,是关系型数据,查询数据快. 字典初始说明: 遍历字典从列表开始,列表是从头便利到尾的. ...

  7. String s=new String("abc")创建了几个对象?

    String str=new String("abc");   紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢? 答案应该是1个或者2个. 1个 ...

  8. Struts2学习笔记四 OGNL

    OGNL,全称为Object-Graph Navigation Language(对象图表达语言),它是一个功能强大的表达式语言,用来获取和设置Java对象的属性,调用java对象的方法,同时能够自动 ...

  9. p-value

    p-value p-value翻译为假定值,假设几率.我们在生物信息中通常使用p值方法(P-Value, Probability, Pr)来做检验.那么p-value是什么呢?其实P-value就是一 ...

  10. C语言的第一次作业总结

    PTA实验作业 题目一:温度转换 本题要求编写程序,计算华氏温度150°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1.实验代码: ...