• 背景:

从一个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>

View Code

struts.xml

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE struts PUBLIC
 3     "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
 4     "http://struts.apache.org/dtds/struts-2.3.dtd">
 5
 6 <struts>
 7     <constant name="struts.ognl.allowStaticMethodAccess" value="true" />
 8     <constant name="struts.devMode" value="false" />
 9
10     <package name="default" namespace="/" extends="struts-default">
11         <action name="member-*" class="com.dx.struts.actions.MemberAction" method="{1}">
12             <result name="{1}">/member-{1}.jsp</result>
13             <result name="delete" type="redirectAction">member-list</result>
14         </action>
15     </package>
16 </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;}}

View Code

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

 1 package com.dx.struts.actions;
 2
 3 import java.util.Date;
 4 import java.util.List;
 5 import java.util.Map;
 6
 7 import org.apache.struts2.interceptor.RequestAware;
 8
 9 import com.dx.struts.dao.MemberDao;
10 import com.dx.struts.entity.Member;
11
12 public class MemberAction implements RequestAware {
13     private MemberDao memberDao = new MemberDao();
14
15     private Long id;
16
17     public void setId(Long id) {
18         this.id = id;
19     }
20
21     public String list() {
22         List<Member> members = memberDao.getMembers();
23         request.put("members", members);
24         return "list";
25     }
26
27     public String delete() {
28         memberDao.remove(this.id);
29         // 返回结果的类型应该为:redirectAction
30         // 也可以是chain:实际上chain是没有必要的,因为不需要在下一个action中保留啊当前action的状态
31         // 若使用chain,则到达目标页面后,地址栏显示的依然是删除的那个链接,则刷新时会重复提交。
32         return "delete";
33     }
34
35
36     private Map<String, Object> request;
37
38     @Override
39     public void setRequest(Map<String, Object> request) {
40         this.request = request;
41     }
42
43 }

注意:这里边根据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);}
}

View Code

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>

View Code

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>

View Code

  • 通过断点调试了解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:

  1 /*
  2  * Copyright 2002-2006,2009 The Apache Software Foundation.
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 package com.opensymphony.xwork2;
 17
 18 import com.opensymphony.xwork2.config.ConfigurationException;
 19 import com.opensymphony.xwork2.config.entities.ActionConfig;
 20 import com.opensymphony.xwork2.config.entities.InterceptorMapping;
 21 import com.opensymphony.xwork2.config.entities.ResultConfig;
 22 import com.opensymphony.xwork2.inject.Container;
 23 import com.opensymphony.xwork2.inject.Inject;
 24 import com.opensymphony.xwork2.interceptor.PreResultListener;
 25 import com.opensymphony.xwork2.ognl.OgnlUtil;
 26 import com.opensymphony.xwork2.util.ValueStack;
 27 import com.opensymphony.xwork2.util.ValueStackFactory;
 28 import com.opensymphony.xwork2.util.logging.Logger;
 29 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 30 import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
 31 import ognl.MethodFailedException;
 32 import ognl.NoSuchPropertyException;
 33 import ognl.OgnlException;
 34
 35 import java.util.ArrayList;
 36 import java.util.Iterator;
 37 import java.util.List;
 38 import java.util.Map;
 39
 40
 41 /**
 42  * The Default ActionInvocation implementation
 43  *
 44  * @author Rainer Hermanns
 45  * @author tmjee
 46  * @version $Date$ $Id$
 47  * @see com.opensymphony.xwork2.DefaultActionProxy
 48  */
 49 public class DefaultActionInvocation implements ActionInvocation {
 50
 51     private static final Logger LOG = LoggerFactory.getLogger(DefaultActionInvocation.class);
 52
 53     protected Object action;
 54     protected ActionProxy proxy;
 55     protected List<PreResultListener> preResultListeners;
 56     protected Map<String, Object> extraContext;
 57     protected ActionContext invocationContext;
 58     protected Iterator<InterceptorMapping> interceptors;
 59     protected ValueStack stack;
 60     protected Result result;
 61     protected Result explicitResult;
 62     protected String resultCode;
 63     protected boolean executed = false;
 64     protected boolean pushAction = true;
 65     protected ObjectFactory objectFactory;
 66     protected ActionEventListener actionEventListener;
 67     protected ValueStackFactory valueStackFactory;
 68     protected Container container;
 69     protected UnknownHandlerManager unknownHandlerManager;
 70     protected OgnlUtil ognlUtil;
 71
 72     public DefaultActionInvocation(final Map<String, Object> extraContext, final boolean pushAction) {
 73         this.extraContext = extraContext;
 74         this.pushAction = pushAction;
 75     }
 76
 77     @Inject
 78     public void setUnknownHandlerManager(UnknownHandlerManager unknownHandlerManager) {
 79         this.unknownHandlerManager = unknownHandlerManager;
 80     }
 81
 82     @Inject
 83     public void setValueStackFactory(ValueStackFactory fac) {
 84         this.valueStackFactory = fac;
 85     }
 86
 87     @Inject
 88     public void setObjectFactory(ObjectFactory fac) {
 89         this.objectFactory = fac;
 90     }
 91
 92     @Inject
 93     public void setContainer(Container cont) {
 94         this.container = cont;
 95     }
 96
 97     @Inject(required=false)
 98     public void setActionEventListener(ActionEventListener listener) {
 99         this.actionEventListener = listener;
100     }
101
102     @Inject
103     public void setOgnlUtil(OgnlUtil ognlUtil) {
104         this.ognlUtil = ognlUtil;
105     }
106
107     public Object getAction() {
108         return action;
109     }
110
111     public boolean isExecuted() {
112         return executed;
113     }
114
115     public ActionContext getInvocationContext() {
116         return invocationContext;
117     }
118
119     public ActionProxy getProxy() {
120         return proxy;
121     }
122
123     /**
124      * If the DefaultActionInvocation has been executed before and the Result is an instance of ActionChainResult, this method
125      * will walk down the chain of ActionChainResults until it finds a non-chain result, which will be returned. If the
126      * DefaultActionInvocation's result has not been executed before, the Result instance will be created and populated with
127      * the result params.
128      *
129      * @return a Result instance
130      * @throws Exception
131      */
132     public Result getResult() throws Exception {
133         Result returnResult = result;
134
135         // If we've chained to other Actions, we need to find the last result
136         while (returnResult instanceof ActionChainResult) {
137             ActionProxy aProxy = ((ActionChainResult) returnResult).getProxy();
138
139             if (aProxy != null) {
140                 Result proxyResult = aProxy.getInvocation().getResult();
141
142                 if ((proxyResult != null) && (aProxy.getExecuteResult())) {
143                     returnResult = proxyResult;
144                 } else {
145                     break;
146                 }
147             } else {
148                 break;
149             }
150         }
151
152         return returnResult;
153     }
154
155     public String getResultCode() {
156         return resultCode;
157     }
158
159     public void setResultCode(String resultCode) {
160         if (isExecuted())
161             throw new IllegalStateException("Result has already been executed.");
162
163         this.resultCode = resultCode;
164     }
165
166
167     public ValueStack getStack() {
168         return stack;
169     }
170
171     /**
172      * Register a com.opensymphony.xwork2.interceptor.PreResultListener to be notified after the Action is executed and before the
173      * Result is executed. The ActionInvocation implementation must guarantee that listeners will be called in the order
174      * in which they are registered. Listener registration and execution does not need to be thread-safe.
175      *
176      * @param listener to register
177      */
178     public void addPreResultListener(PreResultListener listener) {
179         if (preResultListeners == null) {
180             preResultListeners = new ArrayList<PreResultListener>(1);
181         }
182
183         preResultListeners.add(listener);
184     }
185
186     public Result createResult() throws Exception {
187         LOG.trace("Creating result related to resultCode [#0]", resultCode);
188
189         if (explicitResult != null) {
190             Result ret = explicitResult;
191             explicitResult = null;
192
193             return ret;
194         }
195         ActionConfig config = proxy.getConfig();
196         Map<String, ResultConfig> results = config.getResults();
197
198         ResultConfig resultConfig = null;
199
200         try {
201             resultConfig = results.get(resultCode);
202         } catch (NullPointerException e) {
203             if (LOG.isDebugEnabled()) {
204                 LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode);
205             }
206         }
207
208         if (resultConfig == null) {
209             // If no result is found for the given resultCode, try to get a wildcard '*' match.
210             resultConfig = results.get("*");
211         }
212
213         if (resultConfig != null) {
214             try {
215                 return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());
216             } catch (Exception e) {
217                 if (LOG.isErrorEnabled()) {
218                     LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName());
219                 }
220                 throw new XWorkException(e, resultConfig);
221             }
222         } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {
223             return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);
224         }
225         return null;
226     }
227
228     /**
229      * @throws ConfigurationException If no result can be found with the returned code
230      */
231     public String invoke() throws Exception {
232         String profileKey = "invoke: ";
233         try {
234             UtilTimerStack.push(profileKey);
235
236             if (executed) {
237                 throw new IllegalStateException("Action has already executed");
238             }
239
240             if (interceptors.hasNext()) {
241                 final InterceptorMapping interceptor = interceptors.next();
242                 String interceptorMsg = "interceptor: " + interceptor.getName();
243                 UtilTimerStack.push(interceptorMsg);
244                 try {
245                                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
246                             }
247                 finally {
248                     UtilTimerStack.pop(interceptorMsg);
249                 }
250             } else {
251                 resultCode = invokeActionOnly();
252             }
253
254             // this is needed because the result will be executed, then control will return to the Interceptor, which will
255             // return above and flow through again
256             if (!executed) {
257                 if (preResultListeners != null) {
258                     LOG.trace("Executing PreResultListeners for result [#0]", result);
259
260                     for (Object preResultListener : preResultListeners) {
261                         PreResultListener listener = (PreResultListener) preResultListener;
262
263                         String _profileKey = "preResultListener: ";
264                         try {
265                             UtilTimerStack.push(_profileKey);
266                             listener.beforeResult(this, resultCode);
267                         }
268                         finally {
269                             UtilTimerStack.pop(_profileKey);
270                         }
271                     }
272                 }
273
274                 // now execute the result, if we're supposed to
275                 if (proxy.getExecuteResult()) {
276                     executeResult();
277                 }
278
279                 executed = true;
280             }
281
282             return resultCode;
283         }
284         finally {
285             UtilTimerStack.pop(profileKey);
286         }
287     }
288
289     public String invokeActionOnly() throws Exception {
290         return invokeAction(getAction(), proxy.getConfig());
291     }
292
293     protected void createAction(Map<String, Object> contextMap) {
294         // load action
295         String timerKey = "actionCreate: " + proxy.getActionName();
296         try {
297             UtilTimerStack.push(timerKey);
298             action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
299         } catch (InstantiationException e) {
300             throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());
301         } catch (IllegalAccessException e) {
302             throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
303         } catch (Exception e) {
304             String gripe;
305
306             if (proxy == null) {
307                 gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";
308             } else if (proxy.getConfig() == null) {
309                 gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";
310             } else if (proxy.getConfig().getClassName() == null) {
311                 gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
312             } else {
313                 gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";
314             }
315
316             gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
317             throw new XWorkException(gripe, e, proxy.getConfig());
318         } finally {
319             UtilTimerStack.pop(timerKey);
320         }
321
322         if (actionEventListener != null) {
323             action = actionEventListener.prepare(action, stack);
324         }
325     }
326
327     protected Map<String, Object> createContextMap() {
328         Map<String, Object> contextMap;
329
330         if ((extraContext != null) && (extraContext.containsKey(ActionContext.VALUE_STACK))) {
331             // In case the ValueStack was passed in
332             stack = (ValueStack) extraContext.get(ActionContext.VALUE_STACK);
333
334             if (stack == null) {
335                 throw new IllegalStateException("There was a null Stack set into the extra params.");
336             }
337
338             contextMap = stack.getContext();
339         } else {
340             // create the value stack
341             // this also adds the ValueStack to its context
342             stack = valueStackFactory.createValueStack();
343
344             // create the action context
345             contextMap = stack.getContext();
346         }
347
348         // put extraContext in
349         if (extraContext != null) {
350             contextMap.putAll(extraContext);
351         }
352
353         //put this DefaultActionInvocation into the context map
354         contextMap.put(ActionContext.ACTION_INVOCATION, this);
355         contextMap.put(ActionContext.CONTAINER, container);
356
357         return contextMap;
358     }
359
360     /**
361      * Uses getResult to get the final Result and executes it
362      *
363      * @throws ConfigurationException If not result can be found with the returned code
364      */
365     private void executeResult() throws Exception {
366         result = createResult();
367
368         String timerKey = "executeResult: " + getResultCode();
369         try {
370             UtilTimerStack.push(timerKey);
371             if (result != null) {
372                 result.execute(this);
373             } else if (resultCode != null && !Action.NONE.equals(resultCode)) {
374                 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()
375                         + " and result " + getResultCode(), proxy.getConfig());
376             } else {
377                 if (LOG.isDebugEnabled()) {
378                     LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());
379                 }
380             }
381         } finally {
382             UtilTimerStack.pop(timerKey);
383         }
384     }
385
386     public void init(ActionProxy proxy) {
387         this.proxy = proxy;
388         Map<String, Object> contextMap = createContextMap();
389
390         // Setting this so that other classes, like object factories, can use the ActionProxy and other
391         // contextual information to operate
392         ActionContext actionContext = ActionContext.getContext();
393
394         if (actionContext != null) {
395             actionContext.setActionInvocation(this);
396         }
397
398         createAction(contextMap);
399
400         if (pushAction) {
401             stack.push(action);
402             contextMap.put("action", action);
403         }
404
405         invocationContext = new ActionContext(contextMap);
406         invocationContext.setName(proxy.getActionName());
407
408         createInterceptors(proxy);
409     }
410
411     protected void createInterceptors(ActionProxy proxy) {
412         // get a new List so we don't get problems with the iterator if someone changes the list
413         List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
414         interceptors = interceptorList.iterator();
415     }
416
417     protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
418         String methodName = proxy.getMethod();
419
420         if (LOG.isDebugEnabled()) {
421             LOG.debug("Executing action method = #0", methodName);
422         }
423
424         String timerKey = "invokeAction: " + proxy.getActionName();
425         try {
426             UtilTimerStack.push(timerKey);
427
428             Object methodResult;
429             try {
430                 methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);
431             } catch (MethodFailedException e) {
432                 // if reason is missing method, try find version with "do" prefix
433                 if (e.getReason() instanceof NoSuchMethodException) {
434                     try {
435                         String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1) + "()";
436                         methodResult = ognlUtil.callMethod(altMethodName, getStack().getContext(), action);
437                     } catch (MethodFailedException e1) {
438                         // if still method doesn't exist, try checking UnknownHandlers
439                         if (e1.getReason() instanceof NoSuchMethodException) {
440                             if (unknownHandlerManager.hasUnknownHandlers()) {
441                                 try {
442                                     methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
443                                 } catch (NoSuchMethodException e2) {
444                                     // throw the original one
445                                     throw e;
446                                 }
447                             } else {
448                                 // throw the original one
449                                 throw e;
450                             }
451                             // throw the original exception as UnknownHandlers weren't able to handle invocation as well
452                             if (methodResult == null) {
453                                 throw e;
454                             }
455                         } else {
456                             // exception isn't related to missing action method, throw it
457                             throw e1;
458                         }
459                     }
460                 } else {
461                     // exception isn't related to missing action method, throw it
462                     throw e;
463                 }
464             }
465             return saveResult(actionConfig, methodResult);
466         } catch (NoSuchPropertyException e) {
467             throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
468         } catch (MethodFailedException e) {
469             // We try to return the source exception.
470             Throwable t = e.getCause();
471
472             if (actionEventListener != null) {
473                 String result = actionEventListener.handleException(t, getStack());
474                 if (result != null) {
475                     return result;
476                 }
477             }
478             if (t instanceof Exception) {
479                 throw (Exception) t;
480             } else {
481                 throw e;
482             }
483         } finally {
484             UtilTimerStack.pop(timerKey);
485         }
486     }
487
488     /**
489      * Save the result to be used later.
490      * @param actionConfig current ActionConfig
491      * @param methodResult the result of the action.
492      * @return the result code to process.
493      */
494     protected String saveResult(ActionConfig actionConfig, Object methodResult) {
495         if (methodResult instanceof Result) {
496             this.explicitResult = (Result) methodResult;
497
498             // Wire the result automatically
499             container.inject(explicitResult);
500             return null;
501         } else {
502             return (String) methodResult;
503         }
504     }
505
506     /**
507      * Version ready to be serialize
508      *
509      * @return instance without reference to {@link Container}
510      */
511     public ActionInvocation serialize() {
512         DefaultActionInvocation that = this;
513         that.container = null;
514         return that;
515     }
516
517     /**
518      * Restoring Container
519      *
520      * @param actionContext current {@link ActionContext}
521      * @return instance which can be used to invoke action
522      */
523     public ActionInvocation deserialize(ActionContext actionContext) {
524         DefaultActionInvocation that = this;
525         that.container = actionContext.getContainer();
526         return that;
527     }
528
529 }

View Code

  • 通过新增用户、修改用户、查看用户及删除用户功能来学习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;@Overridepublic void setRequest(Map<String, Object> request) {this.request = request;}  @Overridepublic 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>

View Code

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>

View Code

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>

View Code

转载于:https://www.cnblogs.com/yy3b2007com/p/6618890.html

Struts(十六):通过CURD来学习Struts流程及ModelDriven的用法相关推荐

  1. 无人驾驶汽车系统入门(二十六)——基于深度学习的实时激光雷达点云目标检测及ROS实现

    无人驾驶汽车系统入门(二十六)--基于深度学习的实时激光雷达点云目标检测及ROS实现 在前两篇文章中,我们使用PCL实现了在点云中对地面的过滤和点云的分割聚类,通常来说,在这两步以后我们将对分割出来的 ...

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

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

  3. 深度学习(三十六)异构计算CUDA学习笔记(1)

    异构计算CUDA学习笔记(1) 原文地址:http://blog.csdn.net/hjimce/article/details/51506207 作者:hjimce 近日因为感觉自己在深度学习工程化 ...

  4. 疲劳驾驶样本集_无人驾驶技术入门(十六)| 初识深度学习之交通标志分类

    前言 在上两期的<无人驾驶技术入门>中,我以车道线检测为例,介绍了计算机视觉领域一些基本的算法.应用图像处理算法和调试算法阈值,就能实现车道线的检测和跟踪. 车道线检测.跟踪的项目,主要是 ...

  5. 不知名大牛二十六个月Android学习工作总结(整理)

    原文的阅读笔记: 1.客户端功能,UI界面比逻辑功能更花时间,写代码前的思考可以事半功倍 Tips:思考时在本子上简单画画UI的草图,谢谢该功能的运行过程 2.沉淀知识库:储备工具类.各种UI组件等 ...

  6. 第二十六课.深度强化学习(一)

    目录 强化学习基本概念 数学基础回顾 强化学习中的专业术语 强化学习中的随机性 如何使用AI进行游戏 关于rewards和returns 价值函数Value Functions action-valu ...

  7. 黑马传智播客第三十六期前端最新学习视频分享

    链接:https://pan.baidu.com/s/1km4IZjpvCJbOxuMUiSVE2Q 密码:jzoa 视频和详细资料都在 非常适合刚刚开始学习前端的同学 ~~链接失效可以私信我下哦~~

  8. 四十六、MongoDB数据库学习

    @Author:Runsen 文章目录 MongoDB MongoDB数据模型 库级操作语句 集合操作语句 文档操作 添加文档(数据) 查询文档(数据) 条件查询 修改文档(数据) 删除文档(数据) ...

  9. 深度学习(四十六)Adversarial Autoencoders学习笔记

最新文章

  1. Android中的ViewDragHelper
  2. 每日两SQL(5),欢迎交流~
  3. 19-6/28作业:100以内偶数求和
  4. 计算机网络(十七)-局域网
  5. 关于https工程的nginx简单配置
  6. 神奇的datetime和datetime,一毫秒引发的血案
  7. Hobo 4: Total War
  8. php excel图表,简易的phpexcel导出柱状图
  9. Wheel Speed Sensor Bosch 文章
  10. 固体物理期末3套试题
  11. C++银行账户管理程序2
  12. springboot系列(二十七):如何实现word携带图片导出?这你得会|超级详细,建议收藏
  13. python写的一个王者荣耀刷金币脚本
  14. 怎么生成html链接,终于认识如何创建网页超链接
  15. Service(一、本地服务)
  16. 葵花宝典——放入word中
  17. DBeaver安装及使用
  18. 最大熵,三硬币模型的R语言代码
  19. Python序列类型及操作
  20. 深入理解python特性_笔记《深入理解Python特性》PDF+编程技巧

热门文章

  1. 常用的 css 样式 记录
  2. 递归和循环:斐波那契数列
  3. const char*, char const*, char*const 的区别
  4. springcloud学习计划
  5. luogu P1064 金明的预算方案
  6. python中 __name__及__main()__的妙处02
  7. TCP/IP协议族(一) HTTP简介、请求方法与响应状态码
  8. java实现Kafka生产者示例
  9. 数字万用表的四位半,三位半都是什么意思?
  10. Windows核心编程 第八章 用户方式中线程的同步(下)