自己打断点走的struts流程&拦截器工作原理

 

①. 请求发送给 StrutsPrepareAndExecuteFilter

②. StrutsPrepareAndExecuteFilter 判定该请求是否是一个 Struts2 请 求(ActionMapping判断),不是就放行。

  (根据路径的后缀是 .action或者.doj进行判断)

③. 若该请求是一个 Struts2 请求,则 StrutsPrepareAndExecuteFilter 把请求的处理交给 ActionProxy

④. ActionProxy 创建一个 ActionInvocation 的实例,并进行初始化

⑤. ActionInvocation 实例在调用 Action 的过程前后,涉及到相关拦截 器(Intercepter)的调用。

⑥. Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置 找到对应的返回结果。调用结果的 execute 方法,渲染结果。

⑦. 执行各个拦截器 invocation.invoke() 之后的代码

⑧. 把结果发送到客户端

 

 

 

1.页面访问Action:

 

 2.StrutsPrepareAndExecuteFilter过滤器查看:(ActionMapping提取路径判断是否是Struts路径)

StrutsPrepareAndExecuteFilter.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
 * $Id: DefaultActionSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.struts2.dispatcher.ng.filter;
 
import org.apache.struts2.StrutsStatics;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
import org.apache.struts2.dispatcher.ng.ExecuteOperations;
import org.apache.struts2.dispatcher.ng.InitOperations;
import org.apache.struts2.dispatcher.ng.PrepareOperations;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.regex.Pattern;
 
/**
 * Handles both the preparation and execution phases of the Struts dispatching process.  This filter is better to use
 * when you don't have another filter that needs access to action context information, such as Sitemesh.
 */
public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
    protected PrepareOperations prepare;
    protected ExecuteOperations execute;
    protected List<Pattern> excludedPatterns = null;
 
    public void init(FilterConfig filterConfig) throws ServletException {
        InitOperations init = new InitOperations();
        Dispatcher dispatcher = null;
        try {
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);
            dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);
 
            prepare = new PrepareOperations(dispatcher);
            execute = new ExecuteOperations(dispatcher);
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
 
            postInit(dispatcher, filterConfig);
        } finally {
            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();
        }
    }
 
    /**
     * Callback for post initialization
     */
    protected void postInit(Dispatcher dispatcher, FilterConfig filterConfig) {
    }
 
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
 
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
 
        try {
            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
                prepare.setEncodingAndLocale(request, response);
                prepare.createActionContext(request, response);
                prepare.assignDispatcherToThread();
                request = prepare.wrapRequest(request);
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
                } else {
                    execute.executeAction(request, response, mapping);
                }
            }
        } finally {
            prepare.cleanupRequest(request);
        }
    }
 
    public void destroy() {
        prepare.cleanupDispatcher();
    }
 
}

  

  解释:

(1)首先获取原生request和response。

(2)对request增强:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
/*
 * $Id: DefaultActionSupport.java 651946 2008-04-27 13:41:38Z apetrelli $
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.struts2.dispatcher.ng;
 
import com.opensymphony.xwork2.ActionContext;
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 org.apache.struts2.RequestUtils;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsException;
import org.apache.struts2.dispatcher.Dispatcher;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.mapper.ActionMapping;
 
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
 
/**
 * Contains preparation operations for a request before execution
 */
public class PrepareOperations {
 
    private static final Logger LOG = LoggerFactory.getLogger(PrepareOperations.class);
 
    private Dispatcher dispatcher;
    private static final String STRUTS_ACTION_MAPPING_KEY = "struts.actionMapping";
    public static final String CLEANUP_RECURSION_COUNTER = "__cleanup_recursion_counter";
    private Logger log = LoggerFactory.getLogger(PrepareOperations.class);
 
    @Deprecated
    public PrepareOperations(ServletContext servletContext, Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }
 
    public PrepareOperations(Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }
 
    /**
     * Creates the action context and initializes the thread local
     */
    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
        ActionContext ctx;
        Integer counter = 1;
        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (oldCounter != null) {
            counter = oldCounter + 1;
        }
         
        ActionContext oldContext = ActionContext.getContext();
        if (oldContext != null) {
            // detected existing context, so we are probably in a forward
            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
        } else {
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
            ctx = new ActionContext(stack.getContext());
        }
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
        ActionContext.setContext(ctx);
        return ctx;
    }
 
    /**
     * Cleans up a request of thread locals
     */
    public void cleanupRequest(HttpServletRequest request) {
        Integer counterVal = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
        if (counterVal != null) {
            counterVal -= 1;
            request.setAttribute(CLEANUP_RECURSION_COUNTER, counterVal);
            if (counterVal > 0 ) {
                if (log.isDebugEnabled()) {
                    log.debug("skipping cleanup counter="+counterVal);
                }
                return;
            }
        }
        // always clean up the thread request, even if an action hasn't been executed
        try {
            dispatcher.cleanUpRequest(request);
        } finally {
            ActionContext.setContext(null);
            Dispatcher.setInstance(null);
        }
    }
 
    /**
     * Assigns the dispatcher to the dispatcher thread local
     */
    public void assignDispatcherToThread() {
        Dispatcher.setInstance(dispatcher);
    }
 
    /**
     * Sets the request encoding and locale on the response
     */
    public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) {
        dispatcher.prepare(request, response);
    }
 
    /**
     * Wraps the request with the Struts wrapper that handles multipart requests better
     * @return The new request, if there is one
     * @throws ServletException
     */
    public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {
        HttpServletRequest request = oldRequest;
        try {
            // Wrap request first, just in case it is multipart/form-data
            // parameters might not be accessible through before encoding (ww-1278)
            request = dispatcher.wrapRequest(request);
            ServletActionContext.setRequest(request);
        } catch (IOException e) {
            throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);
        }
        return request;
    }
 
    /**
     *   Finds and optionally creates an {@link ActionMapping}.  It first looks in the current request to see if one
     * has already been found, otherwise, it creates it and stores it in the request.  No mapping will be created in the
     * case of static resource requests or unidentifiable requests for other servlets, for example.
     */
    public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response) {
        return findActionMapping(request, response, false);
    }
 
    /**
     * Finds and optionally creates an {@link ActionMapping}.  if forceLookup is false, it first looks in the current request to see if one
     * has already been found, otherwise, it creates it and stores it in the request.  No mapping will be created in the
     * case of static resource requests or unidentifiable requests for other servlets, for example.
     * @param forceLookup if true, the action mapping will be looked up from the ActionMapper instance, ignoring if there is one
     * in the request or not
     */
    public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
        ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
        if (mapping == null || forceLookup) {
            try {
                mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
                if (mapping != null) {
                    request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
                }
            } catch (Exception ex) {
                if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
                    dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                }
            }
        }
 
        return mapping;
    }
 
    /**
     * Cleans up the dispatcher instance
     */
    public void cleanupDispatcher() {
        if (dispatcher == null) {
            throw new StrutsException("Something is seriously wrong, Dispatcher is not initialized (null) ");
        } else {
            try {
                dispatcher.cleanup();
            } finally {
                ActionContext.setContext(null);
            }
        }
    }
 
    /**
     * Check whether the request matches a list of exclude patterns.
     *
     * @param request          The request to check patterns against
     * @param excludedPatterns list of patterns for exclusion
     *
     * @return <tt>true</tt> if the request URI matches one of the given patterns
     */
    public boolean isUrlExcluded( HttpServletRequest request, List<Pattern> excludedPatterns ) {
        if (excludedPatterns != null) {
            String uri = RequestUtils.getUri(request);
            for ( Pattern pattern : excludedPatterns ) {
                if (pattern.matcher(uri).matches()) {
                    return true;
                }
            }
        }
        return false;
    }
 
}

  

具体的增强方法的实现在Dispatcher.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * Wrap and return the given request or return the original request object.
 * </p>
 * This method transparently handles multipart data as a wrapped class around the given request.
 * Override this method to handle multipart requests in a special way or to handle other types of requests.
 * Note, {@link org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper} is
 * flexible - look first to that object before overriding this method to handle multipart data.
 *
 * @param request the HttpServletRequest object.
 * @return a wrapped request or original request.
 * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper
 * @throws java.io.IOException on any error.
 *
 * @since 2.3.17
 */
public HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException {
    // don't wrap more than once
    if (request instanceof StrutsRequestWrapper) {
        return request;
    }
 
    String content_type = request.getContentType();
    if (content_type != null && content_type.contains("multipart/form-data")) {
        MultiPartRequest mpr = getMultiPartRequest();
        LocaleProvider provider = getContainer().getInstance(LocaleProvider.class);
        request = new MultiPartRequestWrapper(mpr, request, getSaveDir(), provider, disableRequestAttributeValueStackLookup);
    } else {
        request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);
    }
 
    return request;
}

  MultiPartRequestWrapper.java(增强后的request)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/*
 * $Id$
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.struts2.dispatcher.multipart;
 
import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.LocaleProvider;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.apache.struts2.dispatcher.StrutsRequestWrapper;
 
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Vector;
 
 
/**
 * Parse a multipart request and provide a wrapper around the request. The parsing implementation used
 * depends on the <tt>struts.multipart.parser</tt> setting. It should be set to a class which
 * extends {@link org.apache.struts2.dispatcher.multipart.MultiPartRequest}.
 * <p/>
 * The <tt>struts.multipart.parser</tt> property should be set to <tt>jakarta</tt> for the
 * Jakarta implementation, <tt>pell</tt> for the Pell implementation and <tt>cos</tt> for the Jason Hunter
 * implementation.
 * <p/>
 * The files are uploaded when the object is instantiated. If there are any errors they are logged using
 * {@link #addError(String)}. An action handling a multipart form should first check {@link #hasErrors()}
 * before doing any other processing.
 * <p/>
 * An alternate implementation, PellMultiPartRequest, is provided as a plugin.
 *
 */
public class MultiPartRequestWrapper extends StrutsRequestWrapper {
 
    protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
 
    private Collection<String> errors;
    private MultiPartRequest multi;
    private Locale defaultLocale = Locale.ENGLISH;
 
    /**
     * Process file downloads and log any errors.
     *
     * @param multiPartRequest Our MultiPartRequest object
     * @param request Our HttpServletRequest object
     * @param saveDir Target directory for any files that we save
     * @param provider
     */
    public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request,
                                   String saveDir, LocaleProvider provider,
                                   boolean disableRequestAttributeValueStackLookup) {
        super(request, disableRequestAttributeValueStackLookup);
        errors = new ArrayList<String>();
        multi = multiPartRequest;
        defaultLocale = provider.getLocale();
        setLocale(request);
        try {
            multi.parse(request, saveDir);
            for (String error : multi.getErrors()) {
                addError(error);
            }
        } catch (IOException e) {
            if (LOG.isWarnEnabled()) {
                LOG.warn(e.getMessage(), e);
            }
            addError(buildErrorMessage(e, new Object[] {e.getMessage()}));
        }
    }
 
    public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir, LocaleProvider provider) {
        this(multiPartRequest, request, saveDir, provider, false);
    }
 
    protected void setLocale(HttpServletRequest request) {
        if (defaultLocale == null) {
            defaultLocale = request.getLocale();
        }
    }
 
    protected String buildErrorMessage(Throwable e, Object[] args) {
        String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName();
        if (LOG.isDebugEnabled()) {
            LOG.debug("Preparing error message for key: [#0]", errorKey);
        }
        if (LocalizedTextUtil.findText(this.getClass(), errorKey, getLocale(), null, new Object[0]) == null) {
            return LocalizedTextUtil.findText(this.getClass(), "struts.messages.error.uploading", defaultLocale, null, new Object[] { e.getMessage() });
        } else {
            return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, null, args);
        }
    }
 
    /**
     * Get an enumeration of the parameter names for uploaded files
     *
     * @return enumeration of parameter names for uploaded files
     */
    public Enumeration<String> getFileParameterNames() {
        if (multi == null) {
            return null;
        }
 
        return multi.getFileParameterNames();
    }
 
    /**
     * Get an array of content encoding for the specified input field name or <tt>null</tt> if
     * no content type was specified.
     *
     * @param name input field name
     * @return an array of content encoding for the specified input field name
     */
    public String[] getContentTypes(String name) {
        if (multi == null) {
            return null;
        }
 
        return multi.getContentType(name);
    }
 
    /**
     * Get a {@link java.io.File[]} for the given input field name.
     *
     * @param fieldName input field name
     * @return a File[] object for files associated with the specified input field name
     */
    public File[] getFiles(String fieldName) {
        if (multi == null) {
            return null;
        }
 
        return multi.getFile(fieldName);
    }
 
    /**
     * Get a String array of the file names for uploaded files
     *
     * @param fieldName Field to check for file names.
     * @return a String[] of file names for uploaded files
     */
    public String[] getFileNames(String fieldName) {
        if (multi == null) {
            return null;
        }
 
        return multi.getFileNames(fieldName);
    }
 
    /**
     * Get the filename(s) of the file(s) uploaded for the given input field name.
     * Returns <tt>null</tt> if the file is not found.
     *
     * @param fieldName input field name
     * @return the filename(s) of the file(s) uploaded for the given input field name or
     *         <tt>null</tt> if name not found.
     */
    public String[] getFileSystemNames(String fieldName) {
        if (multi == null) {
            return null;
        }
 
        return multi.getFilesystemName(fieldName);
    }
 
    /**
     * @see javax.servlet.http.HttpServletRequest#getParameter(String)
     */
    public String getParameter(String name) {
        return ((multi == null) || (multi.getParameter(name) == null)) ? super.getParameter(name) : multi.getParameter(name);
    }
 
    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterMap()
     */
    public Map getParameterMap() {
        Map<String, String[]> map = new HashMap<String, String[]>();
        Enumeration enumeration = getParameterNames();
 
        while (enumeration.hasMoreElements()) {
            String name = (String) enumeration.nextElement();
            map.put(name, this.getParameterValues(name));
        }
 
        return map;
    }
 
    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterNames()
     */
    public Enumeration getParameterNames() {
        if (multi == null) {
            return super.getParameterNames();
        } else {
            return mergeParams(multi.getParameterNames(), super.getParameterNames());
        }
    }
 
    /**
     * @see javax.servlet.http.HttpServletRequest#getParameterValues(String)
     */
    public String[] getParameterValues(String name) {
        return ((multi == null) || (multi.getParameterValues(name) == null)) ? super.getParameterValues(name) : multi.getParameterValues(name);
    }
 
    /**
     * Returns <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
     *
     * @return <tt>true</tt> if any errors occured when parsing the HTTP multipart request, <tt>false</tt> otherwise.
     */
    public boolean hasErrors() {
        return !errors.isEmpty();
    }
 
    /**
     * Returns a collection of any errors generated when parsing the multipart request.
     *
     * @return the error Collection.
     */
    public Collection<String> getErrors() {
        return errors;
    }
 
    /**
     * Adds an error message when it isn't already added.
     *
     * @param anErrorMessage the error message to report.
     */
    protected void addError(String anErrorMessage) {
        if (!errors.contains(anErrorMessage)) {
            errors.add(anErrorMessage);
        }
    }
 
    /**
     * Merges 2 enumeration of parameters as one.
     *
     * @param params1 the first enumeration.
     * @param params2 the second enumeration.
     * @return a single Enumeration of all elements from both Enumerations.
     */
    protected Enumeration mergeParams(Enumeration params1, Enumeration params2) {
        Vector temp = new Vector();
 
        while (params1.hasMoreElements()) {
            temp.add(params1.nextElement());
        }
 
        while (params2.hasMoreElements()) {
            temp.add(params2.nextElement());
        }
 
        return temp.elements();
    }
 
    public void cleanUp() {
        if (multi != null) {
            multi.cleanUp();
        }
    }
 
}

  

 

  (3)调用ActionMapping进行提取路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
    ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
    if (mapping == null || forceLookup) {
        try {
            mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
            if (mapping != null) {
                request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
            }
        } catch (Exception ex) {
            if (dispatcher.isHandleException() || dispatcher.isDevMode()) {
                dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
            }
        }
    }
 
    return mapping;
}

 ActionMapping封装的路径信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
 * $Id$
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.struts2.dispatcher.mapper;
 
import com.opensymphony.xwork2.Result;
 
import java.util.Map;
 
/**
 * Simple class that holds the action mapping information used to invoke a
 * Struts action. The name and namespace are required, but the params map
 * is optional, and as such may be null. If a params map is supplied,
 * it <b>must</b> be a mutable map, such as a HashMap.
 *
 */
public class ActionMapping {
 
    private String name;
    private String namespace;
    private String method;
    private String extension;
    private Map<String, Object> params;
    private Result result;
 
    /**
     * Constructs an ActionMapping
     */
    public ActionMapping() {}
 
    /**
     * Constructs an ActionMapping with a default result
     *
     * @param result The default result
     */
    public ActionMapping(Result result) {
        this.result = result;
    }
 
    /**
     * Constructs an ActionMapping with its values
     *
     * @param name The action name
     * @param namespace The action namespace
     * @param method The method
     * @param params The extra parameters
     */
    public ActionMapping(String name, String namespace, String method, Map<String, Object> params) {
        this.name = name;
        this.namespace = namespace;
        this.method = method;
        this.params = params;
    }
 
    /**
     * @return The action name
     */
    public String getName() {
        return name;
    }
 
    /**
     * @return The action namespace
     */
    public String getNamespace() {
        return namespace;
    }
 
    /**
     * @return The extra parameters
     */
    public Map<String, Object> getParams() {
        return params;
    }
 
    /**
     * @return The method
     */
    public String getMethod() {
        if (null != method && "".equals(method)) {
            return null;
        } else {
            return method;
        }
    }
 
    /**
     * @return The default result
     */
    public Result getResult() {
        return result;
    }
     
    /**
     * @return The extension used during this request
     */
    public String getExtension() {
        return extension;
    }
 
    /**
     * @param result The result
     */
    public void setResult(Result result) {
        this.result = result;
    }
 
    /**
     * @param name The action name
     */
    public void setName(String name) {
        this.name = name;
    }
 
    /**
     * @param namespace The action namespace
     */
    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }
 
    /**
     * @param method The method name to call on the action
     */
    public void setMethod(String method) {
        this.method = method;
    }
 
    /**
     * @param params The extra parameters for this mapping
     */
    public void setParams(Map<String, Object> params) {
        this.params = params;
    }
     
    /**
     * @param extension The extension used in the request
     */
    public void setExtension(String extension) {
        this.extension = extension;
    }
 
    @Override
    public String toString() {
        return "ActionMapping{" +
                "name='" + name + '\'' +
                ", namespace='" + namespace + '\'' +
                ", method='" + method + '\'' +
                ", extension='" + extension + '\'' +
                ", params=" + params +
                ", result=" + (result != null ? result.getClass().getName() : "null") +
                '}';
    }
 
}

  

  

 

3.  由Dispatcher调用ActionProxy处理请求

Dispatcher.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
        throws ServletException {
 
    Map<String, Object> extraContext = createContextMap(request, response, mapping);
 
    // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
    ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
    boolean nullStack = stack == null;
    if (nullStack) {
        ActionContext ctx = ActionContext.getContext();
        if (ctx != null) {
            stack = ctx.getValueStack();
        }
    }
    if (stack != null) {
        extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
    }
 
    String timerKey = "Handling request from Dispatcher";
    try {
        UtilTimerStack.push(timerKey);
        String namespace = mapping.getNamespace();
        String name = mapping.getName();
        String method = mapping.getMethod();
 
        ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                namespace, name, method, extraContext, true, false);
 
        request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
 
        // if the ActionMapping says to go straight to a result, do it!
        if (mapping.getResult() != null) {
            Result result = mapping.getResult();
            result.execute(proxy.getInvocation());
        } else {
            proxy.execute();
        }
 
        // If there was a previous value stack then set it back onto the request
        if (!nullStack) {
            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
        }
    } catch (ConfigurationException e) {
        logConfigurationException(request, e);
        sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
    } catch (Exception e) {
        if (handleException || devMode) {
            sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        } else {
            throw new ServletException(e);
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}

  

④. ActionProxy 创建一个 ActionInvocation 的实例,并进行初始化

DefaultActionProxy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/*
 * $Id$
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.opensymphony.xwork2;
 
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.LocalizedTextUtil;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.profiling.UtilTimerStack;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
 
import java.io.Serializable;
import java.util.Locale;
 
 
/**
 * The Default ActionProxy implementation
 *
 * @author Rainer Hermanns
 * @author Revised by <a href="mailto:hu_pengfei@yahoo.com.cn">Henry Hu</a>
 * @author tmjee
 * @version $Date$ $Id$
 * @since 2005-8-6
 */
public class DefaultActionProxy implements ActionProxy, Serializable {
 
    private static final long serialVersionUID = 3293074152487468527L;
 
    private static final Logger LOG = LoggerFactory.getLogger(DefaultActionProxy.class);
 
    protected Configuration configuration;
    protected ActionConfig config;
    protected ActionInvocation invocation;
    protected UnknownHandlerManager unknownHandlerManager;
    protected String actionName;
    protected String namespace;
    protected String method;
    protected boolean executeResult;
    protected boolean cleanupContext;
 
    protected ObjectFactory objectFactory;
 
    protected ActionEventListener actionEventListener;
 
    private boolean methodSpecified = true;
 
    /**
     * This constructor is private so the builder methods (create*) should be used to create an DefaultActionProxy.
     * <p/>
     * The reason for the builder methods is so that you can use a subclass to create your own DefaultActionProxy instance
     * (like a RMIActionProxy).
     */
    protected DefaultActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
 
        this.invocation = inv;
        this.cleanupContext = cleanupContext;
        if (LOG.isDebugEnabled()) {
            LOG.debug("Creating an DefaultActionProxy for namespace [#0] and action name [#1]", namespace, actionName);
        }
 
        this.actionName = StringEscapeUtils.escapeHtml4(actionName);
        this.namespace = namespace;
        this.executeResult = executeResult;
        this.method = StringEscapeUtils.escapeEcmaScript(StringEscapeUtils.escapeHtml4(methodName));
    }
 
    @Inject
    public void setObjectFactory(ObjectFactory factory) {
        this.objectFactory = factory;
    }
 
    @Inject
    public void setConfiguration(Configuration config) {
        this.configuration = config;
    }
 
    @Inject
    public void setUnknownHandler(UnknownHandlerManager unknownHandlerManager) {
        this.unknownHandlerManager = unknownHandlerManager;
    }
 
    @Inject(required = false)
    public void setActionEventListener(ActionEventListener listener) {
        this.actionEventListener = listener;
    }
 
    public Object getAction() {
        return invocation.getAction();
    }
 
    public String getActionName() {
        return actionName;
    }
 
    public ActionConfig getConfig() {
        return config;
    }
 
    public void setExecuteResult(boolean executeResult) {
        this.executeResult = executeResult;
    }
 
    public boolean getExecuteResult() {
        return executeResult;
    }
 
    public ActionInvocation getInvocation() {
        return invocation;
    }
 
    public String getNamespace() {
        return namespace;
    }
 
    public String execute() throws Exception {
        ActionContext nestedContext = ActionContext.getContext();
        ActionContext.setContext(invocation.getInvocationContext());
 
        String retCode = null;
 
        String profileKey = "execute: ";
        try {
            UtilTimerStack.push(profileKey);
 
            retCode = invocation.invoke();
        } finally {
            if (cleanupContext) {
                ActionContext.setContext(nestedContext);
            }
            UtilTimerStack.pop(profileKey);
        }
 
        return retCode;
    }
 
 
    public String getMethod() {
        return method;
    }
 
    private void resolveMethod() {
        // if the method is set to null, use the one from the configuration
        // if the one from the configuration is also null, use "execute"
        if (StringUtils.isEmpty(this.method)) {
            this.method = config.getMethodName();
            if (StringUtils.isEmpty(this.method)) {
                this.method = ActionConfig.DEFAULT_METHOD;
            }
            methodSpecified = false;
        }
    }
 
    protected void prepare() {
        String profileKey = "create DefaultActionProxy: ";
        try {
            UtilTimerStack.push(profileKey);
            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
 
            if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
                config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
            }
            if (config == null) {
                throw new ConfigurationException(getErrorMessage());
            }
 
            resolveMethod();
 
            if (!config.isAllowedMethod(method)) {
                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
            }
 
            invocation.init(this);
 
        } finally {
            UtilTimerStack.pop(profileKey);
        }
    }
 
    protected String getErrorMessage() {
        if ((namespace != null) && (namespace.trim().length() > 0)) {
            return LocalizedTextUtil.findDefaultText(
                    XWorkMessages.MISSING_PACKAGE_ACTION_EXCEPTION,
                    Locale.getDefault(),
                    new String[]{namespace, actionName});
        } else {
            return LocalizedTextUtil.findDefaultText(
                    XWorkMessages.MISSING_ACTION_EXCEPTION,
                    Locale.getDefault(),
                    new String[]{actionName});
        }
    }
 
    public boolean isMethodSpecified() {
        return methodSpecified;
    }
}

  

  

 ⑤. ActionInvocation 实例在调用 Action 的过程前后,涉及到相关拦截 器(Intercepter)的调用。

DefaultActionInvocation.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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);
    }
}

 

再看拦截器的调用原理:

  拦截器是一个继承了序列化接口的普通接口。其工作原理是讲需要被拦截的对象作为参数传到intercept()方法内,在方法内部对此对象进行处理之后再执行原方法。

拦截器接口:

1
Interceptor .java(是一个继承序列化接口的类)
复制代码
/*
 * 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.interceptor;

import com.opensymphony.xwork2.ActionInvocation;

import java.io.Serializable;


/**
 * <!-- START SNIPPET: introduction -->
 * <p/>
 * An interceptor is a stateless class that follows the interceptor pattern, as
 * found in {@link  javax.servlet.Filter} and in AOP languages.
 * <p/>
 * <p/>
 * <p/>
 * Interceptors are objects that dynamically intercept Action invocations.
 * They provide the developer with the opportunity to define code that can be executed
 * before and/or after the execution of an action. They also have the ability
 * to prevent an action from executing. Interceptors provide developers a way to
 * encapulate common functionality in a re-usable form that can be applied to
 * one or more Actions.
 * <p/>
 * <p/>
 * <p/>
 * Interceptors <b>must</b> be stateless and not assume that a new instance will be created for each request or Action.
 * Interceptors may choose to either short-circuit the {@link ActionInvocation} execution and return a return code
 * (such as {@link com.opensymphony.xwork2.Action#SUCCESS}), or it may choose to do some processing before
 * and/or after delegating the rest of the procesing using {@link ActionInvocation#invoke()}.
 * <p/>
 * <!-- END SNIPPET: introduction -->
 * <p/>
 * <p/>
 * <p/>
 * <!-- START SNIPPET: parameterOverriding -->
 * <p/>
 * Interceptor's parameter could be overriden through the following ways :-
 * <p/>
 * <p/>
 * <p/>
 * <b>Method 1:</b>
 * <pre>
 * &lt;action name="myAction" class="myActionClass"&gt;
 *     &lt;interceptor-ref name="exception"/&gt;
 *     &lt;interceptor-ref name="alias"/&gt;
 *     &lt;interceptor-ref name="params"/&gt;
 *     &lt;interceptor-ref name="servletConfig"/&gt;
 *     &lt;interceptor-ref name="prepare"/&gt;
 *     &lt;interceptor-ref name="i18n"/&gt;
 *     &lt;interceptor-ref name="chain"/&gt;
 *     &lt;interceptor-ref name="modelDriven"/&gt;
 *     &lt;interceptor-ref name="fileUpload"/&gt;
 *     &lt;interceptor-ref name="staticParams"/&gt;
 *     &lt;interceptor-ref name="params"/&gt;
 *     &lt;interceptor-ref name="conversionError"/&gt;
 *     &lt;interceptor-ref name="validation"&gt;
 *     &lt;param name="excludeMethods"&gt;myValidationExcudeMethod&lt;/param&gt;
 *     &lt;/interceptor-ref&gt;
 *     &lt;interceptor-ref name="workflow"&gt;
 *     &lt;param name="excludeMethods"&gt;myWorkflowExcludeMethod&lt;/param&gt;
 *     &lt;/interceptor-ref&gt;
 * &lt;/action&gt;
 * </pre>
 * <p/>
 * <b>Method 2:</b>
 * <pre>
 * &lt;action name="myAction" class="myActionClass"&gt;
 *   &lt;interceptor-ref name="defaultStack"&gt;
 *     &lt;param name="validation.excludeMethods"&gt;myValidationExcludeMethod&lt;/param&gt;
 *     &lt;param name="workflow.excludeMethods"&gt;myWorkflowExcludeMethod&lt;/param&gt;
 *   &lt;/interceptor-ref&gt;
 * &lt;/action&gt;
 * </pre>
 * <p/>
 * <p/>
 * <p/>
 * In the first method, the whole default stack is copied and the parameter then
 * changed accordingly.
 * <p/>
 * <p/>
 * <p/>
 * In the second method, the 'interceptor-ref' refer to an existing
 * interceptor-stack, namely defaultStack in this example, and override the validator
 * and workflow interceptor excludeMethods typically in this case. Note that in the
 * 'param' tag, the name attribute contains a dot (.) the word before the dot(.)
 * specifies the interceptor name whose parameter is to be overridden and the word after
 * the dot (.) specifies the parameter itself. Essetially it is as follows :-
 * <p/>
 * <pre>
 *    &lt;interceptor-name&gt;.&lt;parameter-name&gt;
 * </pre>
 * <p/>
 * <b>Note</b> also that in this case the 'interceptor-ref' name attribute
 * is used to indicate an interceptor stack which makes sense as if it is referring
 * to the interceptor itself it would be just using Method 1 describe above.
 * <p/>
 * <!-- END SNIPPET: parameterOverriding -->
 * <p/>
 * <p/>
 * <b>Nested Interceptor param overriding</b>
 * <p/>
 * <!-- START SNIPPET: nestedParameterOverriding -->
 * <p/>
 * Interceptor stack parameter overriding could be nested into as many level as possible, though it would
 * be advisable not to nest it too deep as to avoid confusion, For example,
 * <pre>
 * &lt;interceptor name="interceptor1" class="foo.bar.Interceptor1" /&gt;
 * &lt;interceptor name="interceptor2" class="foo.bar.Interceptor2" /&gt;
 * &lt;interceptor name="interceptor3" class="foo.bar.Interceptor3" /&gt;
 * &lt;interceptor name="interceptor4" class="foo.bar.Interceptor4" /&gt;
 * &lt;interceptor-stack name="stack1"&gt;
 *     &lt;interceptor-ref name="interceptor1" /&gt;
 * &lt;/interceptor-stack&gt;
 * &lt;interceptor-stack name="stack2"&gt;
 *     &lt;interceptor-ref name="intercetor2" /&gt;
 *     &lt;interceptor-ref name="stack1" /&gt;
 * &lt;/interceptor-stack&gt;
 * &lt;interceptor-stack name="stack3"&gt;
 *     &lt;interceptor-ref name="interceptor3" /&gt;
 *     &lt;interceptor-ref name="stack2" /&gt;
 * &lt;/interceptor-stack&gt;
 * &lt;interceptor-stack name="stack4"&gt;
 *     &lt;interceptor-ref name="interceptor4" /&gt;
 *     &lt;interceptor-ref name="stack3" /&gt;
 *  &lt;/interceptor-stack&gt;
 * </pre>
 * Assuming the interceptor has the following properties
 * <table border="1" width="100%">
 * <tr>
 * <td>Interceptor</td>
 * <td>property</td>
 * </tr>
 * <tr>
 * <td>Interceptor1</td>
 * <td>param1</td>
 * </tr>
 * <tr>
 * <td>Interceptor2</td>
 * <td>param2</td>
 * </tr>
 * <tr>
 * <td>Interceptor3</td>
 * <td>param3</td>
 * </tr>
 * <tr>
 * <td>Interceptor4</td>
 * <td>param4</td>
 * </tr>
 * </table>
 * We could override them as follows :-
 * <pre>
 *    &lt;action ... &gt;
 *        &lt;!-- to override parameters of interceptor located directly in the stack  --&gt;
 *        &lt;interceptor-ref name="stack4"&gt;
 *           &lt;param name="interceptor4.param4"&gt; ... &lt;/param&gt;
 *        &lt;/interceptor-ref&gt;
 *    &lt;/action&gt;
 * <p/>
 *    &lt;action ... &gt;
 *        &lt;!-- to override parameters of interceptor located under nested stack --&gt;
 *        &lt;interceptor-ref name="stack4"&gt;
 *            &lt;param name="stack3.interceptor3.param3"&gt; ... &lt;/param&gt;
 *            &lt;param name="stack3.stack2.interceptor2.param2"&gt; ... &lt;/param&gt;
 *            &lt;param name="stack3.stack2.stack1.interceptor1.param1"&gt; ... &lt;/param&gt;
 *        &lt;/interceptor-ref&gt;
 *    &lt;/action&gt;
 *  </pre>
 * <p/>
 * <!-- END SNIPPET: nestedParameterOverriding -->
 *
 * @author Jason Carreira
 * @author tmjee
 * @version $Date$ $Id$
 */
public interface Interceptor extends Serializable {

    /**
     * Called to let an interceptor clean up any resources it has allocated.
     */
    void destroy();

    /**
     * Called after an interceptor is created, but before any requests are processed using
     * {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving
     * the Interceptor a chance to initialize any needed resources.
     */
    void init();

    /**
     * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
     * request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code.
     *
     * @param invocation the action invocation
     * @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself.
     * @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
     */
    String intercept(ActionInvocation invocation) throws Exception;

}
复制代码

 

 具体的实现类:

ActionAutowiringInterceptor.java  intercept(ActionInvocation invocation)是拦截方法

复制代码
/*
 * 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.spring.interceptor;

import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import com.opensymphony.xwork2.spring.SpringObjectFactory;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.WebApplicationContext;

/**
 * <!-- START SNIPPET: description -->
 * TODO: Give a description of the Interceptor.
 * <!-- END SNIPPET: description -->
 *
 * <!-- START SNIPPET: parameters -->
 * TODO: Describe the paramters for this Interceptor.
 * <!-- END SNIPPET: parameters -->
 *
 * <!-- START SNIPPET: extending -->
 * TODO: Discuss some possible extension of the Interceptor.
 * <!-- END SNIPPET: extending -->
 *
 * <pre>
 * <!-- START SNIPPET: example -->
 * &lt;!-- TODO: Describe how the Interceptor reference will effect execution --&gt;
 * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
 *      TODO: fill in the interceptor reference.
 *     &lt;interceptor-ref name=""/&gt;
 *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
 * &lt;/action&gt;
 * <!-- END SNIPPET: example -->
 * </pre>
 * 
 * Autowires action classes to Spring beans.  The strategy for autowiring the beans can be configured
 * by setting the parameter on the interceptor.  Actions that need access to the <code>ActionContext</code>
 * can implements the <code>ApplicationContextAware</code> interface.  The context will also be placed on
 * the action context under the APPLICATION_CONTEXT attribute.
 *
 * @author Simon Stewart
 * @author Eric Hauser
 */
public class ActionAutowiringInterceptor extends AbstractInterceptor implements ApplicationContextAware {
    private static final Logger LOG = LoggerFactory.getLogger(ActionAutowiringInterceptor.class);

    public static final String APPLICATION_CONTEXT = "com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor.applicationContext";

    private boolean initialized = false;
    private ApplicationContext context;
    private SpringObjectFactory factory;
    private Integer autowireStrategy;

    /**
     * @param autowireStrategy
     */
    public void setAutowireStrategy(Integer autowireStrategy) {
        this.autowireStrategy = autowireStrategy;
    }

    /**
     * Looks for the <code>ApplicationContext</code> under the attribute that the Spring listener sets in
     * the servlet context.  The configuration is done the first time here instead of in init() since the
     * <code>ActionContext</code> is not available during <code>Interceptor</code> initialization.
     * <p/>
     * Autowires the action to Spring beans and places the <code>ApplicationContext</code>
     * on the <code>ActionContext</code>
     * <p/>
     * TODO Should this check to see if the <code>SpringObjectFactory</code> has already been configured
     * instead of instantiating a new one?  Or is there a good reason for the interceptor to have it's own
     * factory?
     *
     * @param invocation
     * @throws Exception
     */
    @Override public String intercept(ActionInvocation invocation) throws Exception {
        if (!initialized) {
            ApplicationContext applicationContext = (ApplicationContext) ActionContext.getContext().getApplication().get(
                    WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

            if (applicationContext == null) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("ApplicationContext could not be found.  Action classes will not be autowired.");
                }
            } else {
                setApplicationContext(applicationContext);
                factory = new SpringObjectFactory();
                factory.setApplicationContext(getApplicationContext());
                if (autowireStrategy != null) {
                    factory.setAutowireStrategy(autowireStrategy.intValue());
                }
            }
            initialized = true;
        }

        if (factory != null) {
            Object bean = invocation.getAction();
            factory.autoWireBean(bean);
    
            ActionContext.getContext().put(APPLICATION_CONTEXT, context);
        }
        return invocation.invoke();
    }

    /**
     * @param applicationContext
     * @throws BeansException
     */
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    /**
     * @return context
     */
    protected ApplicationContext getApplicationContext() {
        return context;
    }

}
复制代码

 

 

⑥. Action 执行完毕,ActionInvocation 负责根据 struts.xml 中的配置 找到对应的返回结果。调用结果的 execute 方法,渲染结果。

⑦. 执行各个拦截器 invocation.invoke() 之后的代码

⑧. 把结果发送到客户端

 

posted @   QiaoZhi  阅读(669)  评论(0编辑  收藏  举报
编辑推荐:
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示