首页 > 代码库 > structs2源码分析

structs2源码分析

一、到网上下载struts2的源代码:

http://mirrors.cnnic.cn/apache//struts/source/struts-2.3.16.3-src.zip

我把项目的源码路径定向到下载到的源代码:

这份是webwork的核心源码,读取配置文件的核心代码就在于此:

 

二、下面是struts本尊,基本实现的就是请求的分发:

二、从项目本身去做分析:

好多大牛大师傅都不断强调,去看源码分析源码,但是却从来很少人,告诉你怎么去看,经过多年的摸索我总算找到一个分析源代码的套路,特别对于java工程项目而已,一般来说这些类似于struts这类型的开源项目都会将源代码给开放出去,如果单单打开那些包去看源码哪怕你看个半天其他没啥实际效果,因为没有形成应用场景,例如,没有将请求到处理的执行流程都在脑中思考过一篇,去看人家的源码分析其实尼玛的就是把概念念一遍;

好啦,废话不多讲,上源码分析:

目前我本身的应用使用jetty作为应用服务器:

来看看这部分的代码:

protected void setupWebContext(WebAppContext webapp) {    webapp.addFilter(UrlRewriteFilter.class, "/*", FilterMapping.DEFAULT);    webapp.addFilter(StrutsPrepareAndExecuteFilter.class, "/*", FilterMapping.FORWARD |  FilterMapping.REQUEST);}
View Code

说明:webapp是接收请求上下文的对象,请求过来必须经过webappfilter

webapp.addFilter(StrutsPrepareAndExecuteFilter.class, "/*", FilterMapping.FORWARD |  FilterMapping.REQUEST);
View Code

这段代码实现拦截请求处理并转发的功能,也就是说请求必将经过

StrutsPrepareAndExecuteFilter类的处理:

StrutsPrepareAndExecuteFilter.java代码如下:

/* * $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(filterConfig.getServletContext(), dispatcher);            execute = new ExecuteOperations(filterConfig.getServletContext(), 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();    }}
View Code

可以得到的一些有用的信息,这个类是实现了:StrutsStatics, Filter这两个接口;

1init实现初始化配置信息;

2doFilter处理请求、过滤请求;

3destroy负责清空分发器;

我们再细心点观察思考,就会发现一个比较重要的对象:Dispatcher,在init的时候我们将其填充完成

我们来看看Dispather这个类:

/* * $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;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.ActionProxy;import com.opensymphony.xwork2.ActionProxyFactory;import com.opensymphony.xwork2.FileManager;import com.opensymphony.xwork2.FileManagerFactory;import com.opensymphony.xwork2.LocaleProvider;import com.opensymphony.xwork2.ObjectFactory;import com.opensymphony.xwork2.Result;import com.opensymphony.xwork2.config.Configuration;import com.opensymphony.xwork2.config.ConfigurationException;import com.opensymphony.xwork2.config.ConfigurationManager;import com.opensymphony.xwork2.config.ConfigurationProvider;import com.opensymphony.xwork2.config.FileManagerFactoryProvider;import com.opensymphony.xwork2.config.FileManagerProvider;import com.opensymphony.xwork2.config.entities.InterceptorMapping;import com.opensymphony.xwork2.config.entities.InterceptorStackConfig;import com.opensymphony.xwork2.config.entities.PackageConfig;import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;import com.opensymphony.xwork2.inject.Container;import com.opensymphony.xwork2.inject.ContainerBuilder;import com.opensymphony.xwork2.inject.Inject;import com.opensymphony.xwork2.interceptor.Interceptor;import com.opensymphony.xwork2.util.ClassLoaderUtil;import com.opensymphony.xwork2.util.LocalizedTextUtil;import com.opensymphony.xwork2.util.ValueStack;import com.opensymphony.xwork2.util.ValueStackFactory;import com.opensymphony.xwork2.util.location.LocatableProperties;import com.opensymphony.xwork2.util.location.Location;import com.opensymphony.xwork2.util.location.LocationUtils;import com.opensymphony.xwork2.util.logging.Logger;import com.opensymphony.xwork2.util.logging.LoggerFactory;import com.opensymphony.xwork2.util.profiling.UtilTimerStack;import freemarker.template.Template;import org.apache.struts2.ServletActionContext;import org.apache.struts2.StrutsConstants;import org.apache.struts2.StrutsException;import org.apache.struts2.StrutsStatics;import org.apache.struts2.config.DefaultBeanSelectionProvider;import org.apache.struts2.config.DefaultPropertiesProvider;import org.apache.struts2.config.PropertiesConfigurationProvider;import org.apache.struts2.config.StrutsXmlConfigurationProvider;import org.apache.struts2.dispatcher.mapper.ActionMapping;import org.apache.struts2.dispatcher.multipart.MultiPartRequest;import org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper;import org.apache.struts2.util.AttributeMap;import org.apache.struts2.util.ObjectFactoryDestroyable;import org.apache.struts2.util.fs.JBossFileManager;import org.apache.struts2.views.freemarker.FreemarkerManager;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.IOException;import java.io.StringWriter;import java.io.Writer;import java.util.ArrayList;import java.util.Collection;import java.util.HashMap;import java.util.HashSet;import java.util.List;import java.util.Locale;import java.util.Map;import java.util.Set;import java.util.concurrent.CopyOnWriteArrayList;/** * A utility class the actual dispatcher delegates most of its tasks to. Each instance * of the primary dispatcher holds an instance of this dispatcher to be shared for * all requests. * * @see org.apache.struts2.dispatcher.FilterDispatcher */public class Dispatcher {    /**     * Provide a logging instance.     */    private static final Logger LOG = LoggerFactory.getLogger(Dispatcher.class);    /**     * Provide a thread local instance.     */    private static ThreadLocal<Dispatcher> instance = new ThreadLocal<Dispatcher>();    /**     * Store list of DispatcherListeners.     */    private static List<DispatcherListener> dispatcherListeners =        new CopyOnWriteArrayList<DispatcherListener>();    /**     * Store ConfigurationManager instance, set on init.     */    private ConfigurationManager configurationManager;    /**     * Store state of StrutsConstants.STRUTS_DEVMODE setting.     */    private boolean devMode;    /**     * Store state of StrutsConstants.DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP setting.     */    private boolean disableRequestAttributeValueStackLookup;    /**     * Store state of StrutsConstants.STRUTS_I18N_ENCODING setting.     */    private String defaultEncoding;    /**     * Store state of StrutsConstants.STRUTS_LOCALE setting.     */    private String defaultLocale;    /**     * Store state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.     */    private String multipartSaveDir;    /**     * Stores the value of {@link StrutsConstants#STRUTS_MULTIPART_PARSER} setting     */    private String multipartHandlerName;    /**     * Provide list of default configuration files.     */    private static final String DEFAULT_CONFIGURATION_PATHS = "struts-default.xml,struts-plugin.xml,struts.xml";    /**     * Store state of STRUTS_DISPATCHER_PARAMETERSWORKAROUND.     * <p/>     * The workaround is for WebLogic.     * We try to autodect WebLogic on Dispatcher init.     * The workaround can also be enabled manually.     */    private boolean paramsWorkaroundEnabled = false;    /**     * Indicates if Dispatcher should handle exception and call sendError()     * Introduced to allow integration with other frameworks like Spring Security     */    private boolean handleException;    /**     * Provide the dispatcher instance for the current thread.     *     * @return The dispatcher instance     */    public static Dispatcher getInstance() {        return instance.get();    }    /**     * Store the dispatcher instance for this thread.     *     * @param instance The instance     */    public static void setInstance(Dispatcher instance) {        Dispatcher.instance.set(instance);    }    /**     * Add a dispatcher lifecycle listener.     *     * @param listener The listener to add     */    public static void addDispatcherListener(DispatcherListener listener) {        dispatcherListeners.add(listener);    }    /**     * Remove a specific dispatcher lifecycle listener.     *     * @param listener The listener     */    public static void removeDispatcherListener(DispatcherListener listener) {        dispatcherListeners.remove(listener);    }    private ServletContext servletContext;    private Map<String, String> initParams;    private ValueStackFactory valueStackFactory;    /**     * Create the Dispatcher instance for a given ServletContext and set of initialization parameters.     *     * @param servletContext Our servlet context     * @param initParams The set of initialization parameters     */    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {        this.servletContext = servletContext;        this.initParams = initParams;    }    /**     * Modify state of StrutsConstants.STRUTS_DEVMODE setting.     * @param mode New setting     */    @Inject(StrutsConstants.STRUTS_DEVMODE)    public void setDevMode(String mode) {        devMode = "true".equals(mode);    }    /**     * Modify state of StrutsConstants.DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP setting.     * @param disableRequestAttributeValueStackLookup New setting     */    @Inject(value=StrutsConstants.STRUTS_DISABLE_REQUEST_ATTRIBUTE_VALUE_STACK_LOOKUP, required=false)    public void setDisableRequestAttributeValueStackLookup(String disableRequestAttributeValueStackLookup) {        this.disableRequestAttributeValueStackLookup = "true".equalsIgnoreCase(disableRequestAttributeValueStackLookup);    }    /**     * Modify state of StrutsConstants.STRUTS_LOCALE setting.     * @param val New setting     */    @Inject(value=StrutsConstants.STRUTS_LOCALE, required=false)    public void setDefaultLocale(String val) {        defaultLocale = val;    }    /**     * Modify state of StrutsConstants.STRUTS_I18N_ENCODING setting.     * @param val New setting     */    @Inject(StrutsConstants.STRUTS_I18N_ENCODING)    public void setDefaultEncoding(String val) {        defaultEncoding = val;    }    /**     * Modify state of StrutsConstants.STRUTS_MULTIPART_SAVEDIR setting.     * @param val New setting     */    @Inject(StrutsConstants.STRUTS_MULTIPART_SAVEDIR)    public void setMultipartSaveDir(String val) {        multipartSaveDir = val;    }    @Inject(StrutsConstants.STRUTS_MULTIPART_PARSER)    public void setMultipartHandler(String val) {        multipartHandlerName = val;    }    @Inject    public void setValueStackFactory(ValueStackFactory valueStackFactory) {        this.valueStackFactory = valueStackFactory;    }    @Inject(StrutsConstants.STRUTS_HANDLE_EXCEPTION)    public void setHandleException(String handleException) {        this.handleException = Boolean.parseBoolean(handleException);    }    /**     * Releases all instances bound to this dispatcher instance.     */    public void cleanup() {        // clean up ObjectFactory        ObjectFactory objectFactory = getContainer().getInstance(ObjectFactory.class);        if (objectFactory == null) {            if (LOG.isWarnEnabled()) {            LOG.warn("Object Factory is null, something is seriously wrong, no clean up will be performed");            }        }        if (objectFactory instanceof ObjectFactoryDestroyable) {            try {                ((ObjectFactoryDestroyable)objectFactory).destroy();            }            catch(Exception e) {                // catch any exception that may occurred during destroy() and log it                LOG.error("exception occurred while destroying ObjectFactory [#0]", e, objectFactory.toString());            }        }        // clean up Dispatcher itself for this thread        instance.set(null);        // clean up DispatcherListeners        if (!dispatcherListeners.isEmpty()) {            for (DispatcherListener l : dispatcherListeners) {                l.dispatcherDestroyed(this);            }        }        // clean up all interceptors by calling their destroy() method        Set<Interceptor> interceptors = new HashSet<Interceptor>();        Collection<PackageConfig> packageConfigs = configurationManager.getConfiguration().getPackageConfigs().values();        for (PackageConfig packageConfig : packageConfigs) {            for (Object config : packageConfig.getAllInterceptorConfigs().values()) {                if (config instanceof InterceptorStackConfig) {                    for (InterceptorMapping interceptorMapping : ((InterceptorStackConfig) config).getInterceptors()) {                        interceptors.add(interceptorMapping.getInterceptor());                    }                }            }        }        for (Interceptor interceptor : interceptors) {            interceptor.destroy();        }        // Clear container holder when application is unloaded / server shutdown        ContainerHolder.clear();        //cleanup action context        ActionContext.setContext(null);        // clean up configuration        configurationManager.destroyConfiguration();        configurationManager = null;    }    private void init_FileManager() throws ClassNotFoundException {        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) {            final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER);            final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName);            if (LOG.isInfoEnabled()) {                LOG.info("Custom FileManager specified: #0", fileManagerClassName);            }            configurationManager.addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));        } else {            // add any other Struts 2 provided implementations of FileManager            configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));        }        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {            final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY);            final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class.forName(fileManagerFactoryClassName);            if (LOG.isInfoEnabled()) {                LOG.info("Custom FileManagerFactory specified: #0", fileManagerFactoryClassName);            }            configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass));        }    }    private void init_DefaultProperties() {        configurationManager.addContainerProvider(new DefaultPropertiesProvider());    }        private void init_LegacyStrutsProperties() {        configurationManager.addContainerProvider(new PropertiesConfigurationProvider());    }    private void init_TraditionalXmlConfigurations() {        String configPaths = initParams.get("config");        if (configPaths == null) {            configPaths = DEFAULT_CONFIGURATION_PATHS;        }        String[] files = configPaths.split("\\s*[,]\\s*");        for (String file : files) {            if (file.endsWith(".xml")) {                if ("xwork.xml".equals(file)) {                    configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));                } else {                    configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));                }            } else {                throw new IllegalArgumentException("Invalid configuration file name");            }        }    }    protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {        return new XmlConfigurationProvider(filename, errorIfMissing);    }    protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {        return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);    }    private void init_CustomConfigurationProviders() {        String configProvs = initParams.get("configProviders");        if (configProvs != null) {            String[] classes = configProvs.split("\\s*[,]\\s*");            for (String cname : classes) {                try {                    Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());                    ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();                    configurationManager.addContainerProvider(prov);                } catch (InstantiationException e) {                    throw new ConfigurationException("Unable to instantiate provider: "+cname, e);                } catch (IllegalAccessException e) {                    throw new ConfigurationException("Unable to access provider: "+cname, e);                } catch (ClassNotFoundException e) {                    throw new ConfigurationException("Unable to locate provider class: "+cname, e);                }            }        }    }    private void init_FilterInitParameters() {        configurationManager.addContainerProvider(new ConfigurationProvider() {            public void destroy() {            }            public void init(Configuration configuration) throws ConfigurationException {            }            public void loadPackages() throws ConfigurationException {            }            public boolean needsReload() {                return false;            }            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {                props.putAll(initParams);            }        });    }    private void init_AliasStandardObjects() {        configurationManager.addContainerProvider(new DefaultBeanSelectionProvider());    }    private Container init_PreloadConfiguration() {        Configuration config = configurationManager.getConfiguration();        Container container = config.getContainer();        boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));        LocalizedTextUtil.setReloadBundles(reloadi18n);        ContainerHolder.store(container);        return container;    }    private void init_CheckWebLogicWorkaround(Container container) {        // test whether param-access workaround needs to be enabled        if (servletContext != null && servletContext.getServerInfo() != null                && servletContext.getServerInfo().contains("WebLogic")) {            if (LOG.isInfoEnabled()) {            LOG.info("WebLogic server detected. Enabling Struts parameter access work-around.");            }            paramsWorkaroundEnabled = true;        } else {            paramsWorkaroundEnabled = "true".equals(container.getInstance(String.class,                    StrutsConstants.STRUTS_DISPATCHER_PARAMETERSWORKAROUND));        }    }    /**     * Load configurations, including both XML and zero-configuration strategies,     * and update optional settings, including whether to reload configurations and resource files.     */    public void init() {        if (configurationManager == null) {            configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);        }        try {            init_FileManager();            init_DefaultProperties(); // [1]            init_TraditionalXmlConfigurations(); // [2]            init_LegacyStrutsProperties(); // [3]            init_CustomConfigurationProviders(); // [5]            init_FilterInitParameters() ; // [6]            init_AliasStandardObjects() ; // [7]            Container container = init_PreloadConfiguration();            container.inject(this);            init_CheckWebLogicWorkaround(container);            if (!dispatcherListeners.isEmpty()) {                for (DispatcherListener l : dispatcherListeners) {                    l.dispatcherInitialized(this);                }            }        } catch (Exception ex) {            if (LOG.isErrorEnabled())                LOG.error("Dispatcher initialization failed", ex);            throw new StrutsException(ex);        }    }    protected ConfigurationManager createConfigurationManager(String name) {        return new ConfigurationManager(name);    }    /**     * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.     * <p/>     * This method first creates the action context from the given parameters,     * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.     * After that, the Action method is executed and output channels through the response object.     * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,     * using the 404 return code.     * All other errors are reported by throwing a ServletException.     *     * @param request  the HttpServletRequest object     * @param response the HttpServletResponse object     * @param mapping  the action mapping object     * @throws ServletException when an unknown error occurs (not a 404, but typically something that     *                          would end up as a 5xx by the servlet container)     * @param context Our ServletContext object     */    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,                              ActionMapping mapping) throws ServletException {        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);        // 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();            Configuration config = configurationManager.getConfiguration();            ActionProxy proxy = config.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, context, HttpServletResponse.SC_NOT_FOUND, e);        } catch (Exception e) {            if (handleException || devMode) {                sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);            } else {                throw new ServletException(e);            }        } finally {            UtilTimerStack.pop(timerKey);        }    }    /**     * Performs logging of missing action/result configuration exception     *     * @param request current {@link HttpServletRequest}     * @param e {@link ConfigurationException} that occurred     */    protected void logConfigurationException(HttpServletRequest request, ConfigurationException e) {        // WW-2874 Only log error if in devMode        String uri = request.getRequestURI();        if (request.getQueryString() != null) {            uri = uri + "?" + request.getQueryString();        }        if (devMode) {            LOG.error("Could not find action or result\n#0", e, uri);        } else if (LOG.isWarnEnabled()) {            LOG.warn("Could not find action or result: #0", e, uri);        }    }    /**     * Create a context map containing all the wrapped request objects     *     * @param request The servlet request     * @param response The servlet response     * @param mapping The action mapping     * @param context The servlet context     * @return A map of context objects     */    public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,            ActionMapping mapping, ServletContext context) {        // request map wrapping the http request objects        Map requestMap = new RequestMap(request);        // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately        Map params = new HashMap(request.getParameterMap());        // session map wrapping the http session        Map session = new SessionMap(request);        // application map wrapping the ServletContext        Map application = new ApplicationMap(context);        Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);        if (mapping != null) {            extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);        }        return extraContext;    }    /**     * Merge all application and servlet attributes into a single <tt>HashMap</tt> to represent the entire     * <tt>Action</tt> context.     *     * @param requestMap     a Map of all request attributes.     * @param parameterMap   a Map of all request parameters.     * @param sessionMap     a Map of all session attributes.     * @param applicationMap a Map of all servlet context attributes.     * @param request        the HttpServletRequest object.     * @param response       the HttpServletResponse object.     * @param servletContext the ServletContextmapping object.     * @return a HashMap representing the <tt>Action</tt> context.     */    public HashMap<String,Object> createContextMap(Map requestMap,                                    Map parameterMap,                                    Map sessionMap,                                    Map applicationMap,                                    HttpServletRequest request,                                    HttpServletResponse response,                                    ServletContext servletContext) {        HashMap<String,Object> extraContext = new HashMap<String,Object>();        extraContext.put(ActionContext.PARAMETERS, new HashMap(parameterMap));        extraContext.put(ActionContext.SESSION, sessionMap);        extraContext.put(ActionContext.APPLICATION, applicationMap);        Locale locale;        if (defaultLocale != null) {            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());        } else {            locale = request.getLocale();        }        extraContext.put(ActionContext.LOCALE, locale);        //extraContext.put(ActionContext.DEV_MODE, Boolean.valueOf(devMode));        extraContext.put(StrutsStatics.HTTP_REQUEST, request);        extraContext.put(StrutsStatics.HTTP_RESPONSE, response);        extraContext.put(StrutsStatics.SERVLET_CONTEXT, servletContext);        // helpers to get access to request/session/application scope        extraContext.put("request", requestMap);        extraContext.put("session", sessionMap);        extraContext.put("application", applicationMap);        extraContext.put("parameters", parameterMap);        AttributeMap attrMap = new AttributeMap(extraContext);        extraContext.put("attr", attrMap);        return extraContext;    }    /**     * Return the path to save uploaded files to (this is configurable).     *     * @return the path to save uploaded files to     * @param servletContext Our ServletContext     */    private String getSaveDir(ServletContext servletContext) {        String saveDir = multipartSaveDir.trim();        if (saveDir.equals("")) {            File tempdir = (File) servletContext.getAttribute("javax.servlet.context.tempdir");            if (LOG.isInfoEnabled()) {            LOG.info("Unable to find ‘struts.multipart.saveDir‘ property setting. Defaulting to javax.servlet.context.tempdir");            }            if (tempdir != null) {                saveDir = tempdir.toString();                setMultipartSaveDir(saveDir);            }        } else {            File multipartSaveDir = new File(saveDir);            if (!multipartSaveDir.exists()) {                if (!multipartSaveDir.mkdirs()) {                    String logMessage;                    try {                        logMessage = "Could not find create multipart save directory ‘" + multipartSaveDir.getCanonicalPath() + "‘.";                    } catch (IOException e) {                        logMessage = "Could not find create multipart save directory ‘" + multipartSaveDir.toString() + "‘.";                    }                    if (devMode) {                        LOG.error(logMessage);                    } else {                        if (LOG.isWarnEnabled()) {                            LOG.warn(logMessage);                        }                    }                }            }        }        if (LOG.isDebugEnabled()) {            LOG.debug("saveDir=" + saveDir);        }        return saveDir;    }    /**     * Prepare a request, including setting the encoding and locale.     *     * @param request The request     * @param response The response     */    public void prepare(HttpServletRequest request, HttpServletResponse response) {        String encoding = null;        if (defaultEncoding != null) {            encoding = defaultEncoding;        }        // check for Ajax request to use UTF-8 encoding strictly http://www.w3.org/TR/XMLHttpRequest/#the-send-method        if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {            encoding = "UTF-8";        }        Locale locale = null;        if (defaultLocale != null) {            locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale());        }        if (encoding != null) {            applyEncoding(request, encoding);        }        if (locale != null) {            response.setLocale(locale);        }        if (paramsWorkaroundEnabled) {            request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request        }    }    private void applyEncoding(HttpServletRequest request, String encoding) {        try {            if (!encoding.equals(request.getCharacterEncoding())) {                // if the encoding is already correctly set and the parameters have been already read                // do not try to set encoding because it is useless and will cause an error                request.setCharacterEncoding(encoding);            }        } catch (Exception e) {            LOG.error("Error setting character encoding to ‘" + encoding + "‘ - ignoring.", e);        }    }    /**     * 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.     * @param servletContext Our ServletContext object     * @return a wrapped request or original request.     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper     * @throws java.io.IOException on any error.     */    public HttpServletRequest wrapRequest(HttpServletRequest request, ServletContext servletContext) 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(servletContext), provider);        } else {            request = new StrutsRequestWrapper(request, disableRequestAttributeValueStackLookup);        }        return request;    }    /**     * On each request it must return a new instance as implementation could be not thread safe     * and thus ensure of resource clean up     *     * @return     */    protected MultiPartRequest getMultiPartRequest() {        MultiPartRequest mpr = null;        //check for alternate implementations of MultiPartRequest        Set<String> multiNames = getContainer().getInstanceNames(MultiPartRequest.class);        for (String multiName : multiNames) {            if (multiName.equals(multipartHandlerName)) {                mpr = getContainer().getInstance(MultiPartRequest.class, multiName);            }        }        if (mpr == null ) {            mpr = getContainer().getInstance(MultiPartRequest.class);        }        return mpr;    }    /**     * Removes all the files created by MultiPartRequestWrapper.     *     * @param request the HttpServletRequest object.     * @see org.apache.struts2.dispatcher.multipart.MultiPartRequestWrapper     */    public void cleanUpRequest(HttpServletRequest request) {        ContainerHolder.clear();        if (!(request instanceof MultiPartRequestWrapper)) {            return;        }        MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;        multiWrapper.cleanUp();    }    /**     * Send an HTTP error response code.     *     * @param request  the HttpServletRequest object.     * @param response the HttpServletResponse object.     * @param code     the HttpServletResponse error code (see {@link javax.servlet.http.HttpServletResponse} for possible error codes).     * @param e        the Exception that is reported.     * @param ctx      the ServletContext object.     */    public void sendError(HttpServletRequest request, HttpServletResponse response, ServletContext ctx, int code, Exception e) {        Boolean devModeOverride = FilterDispatcher.getDevModeOverride();        if (devModeOverride != null ? devModeOverride : devMode) {            if (LOG.isDebugEnabled()) {                LOG.debug("Exception occurred during processing request: #0", e, e.getMessage());            }            try {                FreemarkerManager mgr = getContainer().getInstance(FreemarkerManager.class);                freemarker.template.Configuration config = mgr.getConfiguration(ctx);                Template template = config.getTemplate("/org/apache/struts2/dispatcher/error.ftl");                List<Throwable> chain = new ArrayList<Throwable>();                Throwable cur = e;                chain.add(cur);                while ((cur = cur.getCause()) != null) {                    chain.add(cur);                }                HashMap<String,Object> data = http://www.mamicode.com/new HashMap<String,Object>();                data.put("exception", e);                data.put("unknown", Location.UNKNOWN);                data.put("chain", chain);                data.put("locator", new Locator());                Writer writer = new StringWriter();                template.process(data, writer);                response.setContentType("text/html");                response.getWriter().write(writer.toString());                response.getWriter().close();            } catch (Exception exp) {                try {                    if (LOG.isDebugEnabled()) {                        LOG.debug("Cannot show problem report!", exp);                    }                    response.sendError(code, "Unable to show problem report:\n" + exp + "\n\n" + LocationUtils.getLocation(exp));                } catch (IOException ex) {                    // we‘re already sending an error, not much else we can do if more stuff breaks                }            }        } else {            try {                // WW-1977: Only put errors in the request when code is a 500 error                if (code == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {                    // WW-4103: Only logs error when application error occurred, not Struts error                    if (LOG.isErrorEnabled()) {                        LOG.error("Exception occurred during processing request: #0", e, e.getMessage());                    }                    // send a http error response to use the servlet defined error handler                    // make the exception availible to the web.xml defined error page                    request.setAttribute("javax.servlet.error.exception", e);                    // for compatibility                    request.setAttribute("javax.servlet.jsp.jspException", e);                }                // send the error response                response.sendError(code, e.getMessage());            } catch (IOException e1) {                // we‘re already sending an error, not much else we can do if more stuff breaks            }        }    }    /**     * Cleanup any resources used to initialise Dispatcher     */    public void cleanUpAfterInit() {        if (LOG.isDebugEnabled()) {            LOG.debug("Cleaning up resources used to init Dispatcher");        }        ContainerHolder.clear();    }    /**     * Provide an accessor class for static XWork utility.     */    public static class Locator {        public Location getLocation(Object obj) {            Location loc = LocationUtils.getLocation(obj);            if (loc == null) {                return Location.UNKNOWN;            }            return loc;        }    }    /**     * Expose the ConfigurationManager instance.     *     * @return The instance     */    public ConfigurationManager getConfigurationManager() {        return configurationManager;    }    /**     * Modify the ConfigurationManager instance     *     * @param mgr The configuration manager     * @deprecated should be removed as is used only in tests     */    public void setConfigurationManager(ConfigurationManager mgr) {        ContainerHolder.clear();        this.configurationManager = mgr;    }    /**     * Expose the dependency injection container.     * @return Our dependency injection container     */    public Container getContainer() {        if (ContainerHolder.get() != null) {            return ContainerHolder.get();        }        ConfigurationManager mgr = getConfigurationManager();        if (mgr == null) {            throw new IllegalStateException("The configuration manager shouldn‘t be null");        } else {            Configuration config = mgr.getConfiguration();            if (config == null) {                throw new IllegalStateException("Unable to load configuration");            } else {                Container container = config.getContainer();                ContainerHolder.store(container);                return container;            }        }    }}
View Code

注意到下面的实现:

 /**     * Create the Dispatcher instance for a given ServletContext and set of initialization parameters.     *     * @param servletContext Our servlet context     * @param initParams The set of initialization parameters     */    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {        this.servletContext = servletContext;        this.initParams = initParams;}
View Code

说明:servletContext就是servlet请求上下文

   protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {        return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);}
View Code

这里面创建StrutsXmlConfigurationProvider

这个类做了些什么呢?

在看:

/* * $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.config;import java.io.File;import java.io.IOException;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import javax.servlet.ServletContext;import com.opensymphony.xwork2.ActionContext;import com.opensymphony.xwork2.config.ConfigurationException;import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;import com.opensymphony.xwork2.inject.ContainerBuilder;import com.opensymphony.xwork2.inject.Context;import com.opensymphony.xwork2.inject.Factory;import com.opensymphony.xwork2.util.location.LocatableProperties;import com.opensymphony.xwork2.util.logging.Logger;import com.opensymphony.xwork2.util.logging.LoggerFactory;/** * Override Xwork class so we can use an arbitrary config file */public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {    private static final Logger LOG = LoggerFactory.getLogger(StrutsXmlConfigurationProvider.class);    private File baseDir = null;    private String filename;    private String reloadKey;    private ServletContext servletContext;    /**     * Constructs the configuration provider     *     * @param errorIfMissing If we should throw an exception if the file can‘t be found     */    public StrutsXmlConfigurationProvider(boolean errorIfMissing) {        this("struts.xml", errorIfMissing, null);    }    /**     * Constructs the configuration provider     *     * @param filename The filename to look for     * @param errorIfMissing If we should throw an exception if the file can‘t be found     * @param ctx Our ServletContext     */    public StrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {        super(filename, errorIfMissing);        this.servletContext = ctx;        this.filename = filename;        reloadKey = "configurationReload-"+filename;        Map<String,String> dtdMappings = new HashMap<String,String>(getDtdMappings());        dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.0//EN", "struts-2.0.dtd");        dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1//EN", "struts-2.1.dtd");        dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN", "struts-2.1.7.dtd");        dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd");        setDtdMappings(dtdMappings);        File file = new File(filename);        if (file.getParent() != null) {            this.baseDir = file.getParentFile();        }    }        /* (non-Javadoc)     * @see com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#register(com.opensymphony.xwork2.inject.ContainerBuilder, java.util.Properties)     */    @Override    public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {        if (servletContext != null && !containerBuilder.contains(ServletContext.class)) {            containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() {                public ServletContext create(Context context) throws Exception {                    return servletContext;                }            });        }        super.register(containerBuilder, props);    }    /* (non-Javadoc)     * @see com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#init(com.opensymphony.xwork2.config.Configuration)     */    @Override    public void loadPackages() {        ActionContext ctx = ActionContext.getContext();        ctx.put(reloadKey, Boolean.TRUE);        super.loadPackages();    }    /**     * Look for the configuration file on the classpath and in the file system     *     * @param fileName The file name to retrieve     * @see com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#getConfigurationUrls     */    @Override    protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {        URL url = null;        if (baseDir != null) {            url = findInFileSystem(fileName);            if (url == null) {                return super.getConfigurationUrls(fileName);            }        }        if (url != null) {            List<URL> list = new ArrayList<URL>();            list.add(url);            return list.iterator();        } else {            return super.getConfigurationUrls(fileName);        }    }    protected URL findInFileSystem(String fileName) throws IOException {        URL url = null;        File file = new File(fileName);        if (LOG.isDebugEnabled()) {            LOG.debug("Trying to load file " + file);        }        // Trying relative path to original file        if (!file.exists()) {            file = new File(baseDir, fileName);        }        if (file.exists()) {            try {                url = file.toURI().toURL();            } catch (MalformedURLException e) {                throw new IOException("Unable to convert "+file+" to a URL");            }        }        return url;    }    /**     * Overrides needs reload to ensure it is only checked once per request     */    @Override    public boolean needsReload() {        ActionContext ctx = ActionContext.getContext();        if (ctx != null) {            return ctx.get(reloadKey) == null && super.needsReload();        } else {            return super.needsReload();        }    }        public String toString() {        return ("Struts XML configuration provider ("+filename+")");    }}
View Code

注意到里面的:

  public StrutsXmlConfigurationProvider(boolean errorIfMissing) {        this("struts.xml", errorIfMissing, null);    }
View Code

我相信大多数人看到这里应该已经明白读取struts配置的真是这个类;

再来注意下:

private ConfigurationManager configurationManager;
View Code

实际上ConfigurationManager 就是整个项目的配置管理类,struts读取到文件系统的东西都是放到ConfigurationManager 所管理的内存区内;

doFilter负责处理请求的过滤,

我们来看看其处理的逻辑:

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);                }
View Code

也就是说它是要找到符合findActionMapping的一个map路由,然后executeAction,然后里面在执行serviceAction

 Configuration config = configurationManager.getConfiguration();            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                    namespace, name, method, extraContext, true, false);
View Code

我们看到proxy.execute();就是说代理然后再执行对对应的action

网上有一张图,能够比较清晰的表达:

依照上图,我们可以看出一个请求在struts的处理大概有如下步骤:

  1、客户端初始化一个指向Servlet容器的请求;

  2、这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin);

  3、接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action;

  4、如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy;

  5、ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类;

  6、ActionProxy创建一个ActionInvocation的实例。

  7、ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。

8、一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper。

Struts2的包结构:

以下是包说明:

org.apache.struts2. components

该包封装视图组件,Struts2在视图组件上有了很大加强,不仅增加了组件的属性个数,更新增了几个非常有用的组件,如updownselect、doubleselect、datetimepicker、token、tree等。

 另外,Struts2可视化视图组件开始支持主题(theme),缺省情况下,使用自带的缺省主题,如果要自定义页面效果,需要将组件的theme属性设置为simple。

org.apache.struts2. config

该包定义与配置相关的接口和类。实际上,工程中的xml和properties文件的读取和解析都是由WebWork完成的,Struts只做了少量的工作。

org.apache.struts2.dispatcher

Struts2的核心包,最重要的类都放在该包中。

org.apache.struts2.impl

该包只定义了3个类,他们是StrutsActionProxy、StrutsActionProxyFactory、StrutsObjectFactory,这三个类都是对xwork的扩展。

org.apache.struts2.interceptor

定义内置的截拦器。

org.apache.struts2.servlet

用HttpServletRequest相关方法实现principalproxy接口。

org.apache.struts2.util

实用包。

org.apache.struts2.views

提供freemarker、jsp、velocity等不同类型的页面呈现。

 

 根目录下的5个文件说明:

StrutsStatics

Struts常数。常数可以用来获取或设置对象从行动中或其他集合。

RequestUtils

请求处理程序类。此类只有一个方法getServletPath,作用检索当前请求的servlet路径

ServletActionContext

网站的特定的上下文信息

StrutsConstants

该类提供了框架配置键的中心位置用于存储和检索配置设置。

StrutsException

通用运行时异常类

 

 

 

 

structs2源码分析