• Struts(十六):通过CURD来学习Struts流程及ModelDriven的用法


    • 背景:

    从一个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;60%;" cellpadding="10" cellspacing="0" border="1">
            <tr style="">
                <td style="10%;">id</td>
                <td style="40%;">name</td>
                <td style="20%;">age</td>
                <td style="20%;">gender</td>
                <td style="10%;">view</td>
                <td style="10%;">edit</td>            
                <td style="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;
    
        @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>
    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
  • 相关阅读:
    c语言-何为编程?
    c语言-注释
    【转】使用DirectUI技术实现QQ界面
    c语言-error C2440: “static_cast”: 无法从“UINT (__thiscall CHyperLink::* )(CPoint)”转换为“LRESULT (__thiscall CWnd::* )(CPoint)”
    系统分析师【转】
    c语言-经验之谈
    开源托管站点大全
    c语言-扑克牌小魔术
    c语言-猜数字游戏
    世界语简介
  • 原文地址:https://www.cnblogs.com/yy3b2007com/p/6618890.html
Copyright © 2020-2023  润新知