Struts2.3.15.1源码浅析

Struts2 两大运行主线:
  • 1.初始化主线:初始化主线主要是为Struts2创建运行环境(此处的环境与Struts2身处的Web环境是有区别的),初始化入口StrutsPrepareAndExecuteFilter继承 Filter,遵循Filter规范,初始化只是在应用启动的时候运行一次,以后无论过来多少HttpServletRequest都不会再运行啦。

StrutsPrepareAndExecuteFilter.java

Java代码  收藏代码
  1. public void init(FilterConfig filterConfig) throws ServletException {  
  2.         InitOperations init = new InitOperations();//Http请求预处理工具类,ExecuteOperates  Http请求逻辑处理类  
  3.         Dispatcher dispatcher = null;  
  4.         try {  
  5.             FilterHostConfig config = new FilterHostConfig(filterConfig);  
  6.             init.initLogging(config);  
  7.             dispatcher = init.initDispatcher(config);//核心分发器Dispatcher初始化  
  8.             //初始化静态资源加载器  
  9.             init.initStaticContentLoader(config, dispatcher);  
  10.             //初始化进行http预处理操作类  
  11.             prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);  
  12.             //初始化进行http请求逻辑处理操作类  
  13.             execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);  
  14.             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);  
  15.   
  16.             postInit(dispatcher, filterConfig);  
  17.         } finally {  
  18.             if (dispatcher != null) {  
  19.                 dispatcher.cleanUpAfterInit();  
  20.             }  
  21.             init.cleanup();//初始化清除工作  
  22.         }  
  23.     }  

 首先我们来看看,Struts2核心分发器初始化过程:

InitOperations.java

Java代码  收藏代码
  1. /** 
  2.      * Creates and initializes the dispatcher 
  3.      */  
  4.     public Dispatcher initDispatcher( HostConfig filterConfig ) {  
  5.         Dispatcher dispatcher = createDispatcher(filterConfig);  
  6.         dispatcher.init();  
  7.         return dispatcher;  
  8.     }  
  9.       
  10.     /** 
  11.      * Create a {@link Dispatcher} 
  12.      */  
  13.     private Dispatcher createDispatcher( HostConfig filterConfig ) {  
  14.         Map<String, String> params = new HashMap<String, String>();  
  15.         for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {  
  16.             String name = (String) e.next();  
  17.             String value = filterConfig.getInitParameter(name);  
  18.             params.put(name, value);  
  19.         }  
  20.         return new Dispatcher(filterConfig.getServletContext(), params);  
  21.     }  

 顺着流程,接着探索dispatcher.init(),这里是加载配置文件的地方

Dispatcher.java

Java代码  收藏代码
  1. public void init() {  
  2.   
  3.         if (configurationManager == null) {  
  4.             configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);  
  5.         }  
  6.   
  7.         try {  
  8.             init_FileManager();  
  9.             init_DefaultProperties(); // [1]加载default.properties org/apache/struts2/default.properties  
  10.             init_TraditionalXmlConfigurations(); // [2]加载struts-default.xml,struts-plugin.xml,struts.xml  
  11.             init_LegacyStrutsProperties(); // [3]  
  12.             init_CustomConfigurationProviders(); // [5]初始化自定义的provider配置类全名和实现ConfigurationProvider接口,用逗号隔开即可  
  13.             init_FilterInitParameters() ; // [6]  
  14.             init_AliasStandardObjects() ; // [7]加载框架内部内置的对象struts-default.xml中<bean>节点  
  15.   
  16.             Container container = init_PreloadConfiguration();  
  17.             container.inject(this);  
  18.             init_CheckWebLogicWorkaround(container);  
  19.   
  20.             if (!dispatcherListeners.isEmpty()) {  
  21.                 for (DispatcherListener l : dispatcherListeners) {  
  22.                     l.dispatcherInitialized(this);  
  23.                 }  
  24.             }  
  25.         } catch (Exception ex) {  
  26.             if (LOG.isErrorEnabled())  
  27.                 LOG.error("Dispatcher initialization failed", ex);  
  28.             throw new StrutsException(ex);  
  29.         }  
  30.     }  

 让我们截取init_TraditionalXmlConfigurations();的加载过程来了解Struts2是如何来加载我们配置的配置文件的:

Java代码  收藏代码
  1. private void init_TraditionalXmlConfigurations() {  
  2.         //首先读取web.xml中的config初始参数 如果不存在,默认加载加载struts-default.xml,struts-plugin.xml,struts.xml  
  3.         //如果不想使用默认值,在web.xml中设置config参数即可  
  4.         String configPaths = initParams.get("config");  
  5.         if (configPaths == null) {  
  6.             configPaths = DEFAULT_CONFIGURATION_PATHS;  
  7.         }  
  8.         String[] files = configPaths.split("\s*[,]\s*");  
  9.         for (String file : files) {  
  10.             if (file.endsWith(".xml")) {  
  11.                 if ("xwork.xml".equals(file)) {//xwork.xml文件单独解析  
  12.                     configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));  
  13.                 } else {  
  14.                     configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));  
  15.                 }  
  16.             } else {  
  17.                 throw new IllegalArgumentException("Invalid configuration file name");  
  18.             }  
  19.         }  
  20.     }  

 createStrutsXmlConfigurationProvider方法创建StrutsXmlConfigurationProvider对象,其继承XmlConfigurationProvider对象,而XmlConfigurationProvider 实现了ConfigurationProvider接口,ConfigurationProvider使用java很少见到的多继承机制,继承了ContainerProvider和PackageProvider接口。 XmlConfigurationProvider负责读取和解析配置文件,

Java代码  收藏代码
  1. /** 
  2.     * Create a PackageConfig from an XML element representing it. 
  3.     */  
  4.    protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {  
  5.        String packageName = packageElement.getAttribute("name");  
  6.        PackageConfig packageConfig = configuration.getPackageConfig(packageName);  
  7.        if (packageConfig != null) {  
  8.            if (LOG.isDebugEnabled()) {  
  9.                LOG.debug("Package [#0] already loaded, skipping re-loading it and using existing PackageConfig [#1]", packageName, packageConfig);  
  10.            }  
  11.            return packageConfig;  
  12.        }  
  13.        PackageConfig.Builder newPackage = buildPackageContext(packageElement);  
  14.        if (newPackage.isNeedsRefresh()) {  
  15.            return newPackage.build();  
  16.        }  
  17.        if (LOG.isDebugEnabled()) {  
  18.            LOG.debug("Loaded " + newPackage);  
  19.        }  
  20.        // add result types (and default result) to this package  
  21.        addResultTypes(newPackage, packageElement);  
  22.        // load the interceptors and interceptor stacks for this package  
  23.        loadInterceptors(newPackage, packageElement);  
  24.        // load the default interceptor reference for this package  
  25.        loadDefaultInterceptorRef(newPackage, packageElement);  
  26.        // load the default class ref for this package  
  27.        loadDefaultClassRef(newPackage, packageElement);  
  28.        // load the global result list for this package  
  29.        loadGlobalResults(newPackage, packageElement);  
  30.        // load the global exception handler list for this package  
  31.        loadGobalExceptionMappings(newPackage, packageElement);  
  32.        // get actions  
  33.        NodeList actionList = packageElement.getElementsByTagName("action");  
  34.        for (int i = 0; i < actionList.getLength(); i++) {  
  35.            Element actionElement = (Element) actionList.item(i);  
  36.            addAction(actionElement, newPackage);  
  37.        }  
  38.        // load the default action reference for this package  
  39.        loadDefaultActionRef(newPackage, packageElement);  
  40.        PackageConfig cfg = newPackage.build();  
  41.        configuration.addPackageConfig(cfg.getName(), cfg);  
  42.        return cfg;  
  43.    }  

 其中

  • addAction()方法负责读取节点,并将数据保存在ActionConfig中;
  • addResultTypes()方法负责读取节点,并将数据保存在ResultTypeConfig中;ResultTypeConfig使用了构造器模式创建对象
  • loadInterceptors()方法负责读取节点,并将数据保存在InterceptorConfig中;
  • loadInterceptorStack()方法负责读取节点,并将数据保存在InterceptorStackConfig中;
  • loadInterceptorStacks()方法负责读取节点,并将数据保存在InterceptorStackConfig中;

而以上这些方法都将会被addPackage()invoke,并将数据汇集到PackageConfig中。

配置文件实际的加载流程: DefaultConfiguration.reloadContainer()时调用了containerProvider.init(this); 

XmlConfigurationProvider.java

Java代码  收藏代码
  1. public void init(Configuration configuration) {  
  2.         this.configuration = configuration;  
  3.         this.includedFileNames = configuration.getLoadedFileNames();  
  4.         loadDocuments(configFileName);  
  5.     }   

加载Documents:

Java代码  收藏代码
  1. private void loadDocuments(String configFileName) {  
  2.        try {  
  3.            loadedFileUrls.clear();  
  4.            documents = loadConfigurationFiles(configFileName, null);  
  5.        } catch (ConfigurationException e) {  
  6.            throw e;  
  7.        } catch (Exception e) {  
  8.            throw new ConfigurationException("Error loading configuration file " + configFileName, e);  
  9.        }  
  10.    }  
  11.      
  12.      
  13.    private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {  
  14.        List<Document> docs = new ArrayList<Document>();  
  15.        List<Document> finalDocs = new ArrayList<Document>();  
  16.        if (!includedFileNames.contains(fileName)) {  
  17.            if (LOG.isDebugEnabled()) {  
  18.                LOG.debug("Loading action configurations from: " + fileName);  
  19.            }  
  20.   
  21.            includedFileNames.add(fileName);  
  22.   
  23.            Iterator<URL> urls = null;  
  24.            InputStream is = null;  
  25.   
  26.            IOException ioException = null;  
  27.            try {  
  28.                urls = getConfigurationUrls(fileName);  
  29.            } catch (IOException ex) {  
  30.                ioException = ex;  
  31.            }  
  32.   
  33.            if (urls == null || !urls.hasNext()) {  
  34.                if (errorIfMissing) {  
  35.                    throw new ConfigurationException("Could not open files of the name " + fileName, ioException);  
  36.                } else {  
  37.                    if (LOG.isInfoEnabled()) {  
  38.                    LOG.info("Unable to locate configuration files of the name "  
  39.                            + fileName + ", skipping");  
  40.                    }  
  41.                    return docs;  
  42.                }  
  43.            }  
  44.   
  45.            URL url = null;  
  46.            while (urls.hasNext()) {  
  47.                try {  
  48.                    url = urls.next();  
  49.                    is = fileManager.loadFile(url);  
  50.   
  51.                    InputSource in = new InputSource(is);  
  52.   
  53.                    in.setSystemId(url.toString());  
  54.   
  55.                    docs.add(DomHelper.parse(in, dtdMappings));  
  56.                } catch (XWorkException e) {  
  57.                    if (includeElement != null) {  
  58.                        throw new ConfigurationException("Unable to load " + url, e, includeElement);  
  59.                    } else {  
  60.                        throw new ConfigurationException("Unable to load " + url, e);  
  61.                    }  
  62.                } catch (Exception e) {  
  63.                    throw new ConfigurationException("Caught exception while loading file " + fileName, e, includeElement);  
  64.                } finally {  
  65.                    if (is != null) {  
  66.                        try {  
  67.                            is.close();  
  68.                        } catch (IOException e) {  
  69.                            LOG.error("Unable to close input stream", e);  
  70.                        }  
  71.                    }  
  72.                }  
  73.            }  
  74.   
  75.            //sort the documents, according to the "order" attribute  
  76.            Collections.sort(docs, new Comparator<Document>() {  
  77.                public int compare(Document doc1, Document doc2) {  
  78.                    return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));  
  79.                }  
  80.            });  
  81.   
  82.            for (Document doc : docs) {  
  83.                Element rootElement = doc.getDocumentElement();  
  84.                NodeList children = rootElement.getChildNodes();  
  85.                int childSize = children.getLength();  
  86.   
  87.                for (int i = 0; i < childSize; i++) {  
  88.                    Node childNode = children.item(i);  
  89.   
  90.                    if (childNode instanceof Element) {  
  91.                        Element child = (Element) childNode;  
  92.   
  93.                        final String nodeName = child.getNodeName();  
  94.                     //解析每个action配置是,对于include文件可以使用通配符*来进行配置  
  95.                        //如Struts.xml中可配置成<include file="actions_*.xml"/>  
  96.                        if ("include".equals(nodeName)) {  
  97.                            String includeFileName = child.getAttribute("file");  
  98.                            if (includeFileName.indexOf('*') != -1) {  
  99.                                // handleWildCardIncludes(includeFileName, docs, child);  
  100.                                ClassPathFinder wildcardFinder = new ClassPathFinder();  
  101.                                wildcardFinder.setPattern(includeFileName);  
  102.                                Vector<String> wildcardMatches = wildcardFinder.findMatches();  
  103.                                for (String match : wildcardMatches) {  
  104.                                    finalDocs.addAll(loadConfigurationFiles(match, child));  
  105.                                }  
  106.                            } else {  
  107.                                finalDocs.addAll(loadConfigurationFiles(includeFileName, child));  
  108.                            }  
  109.                        }  
  110.                    }  
  111.                }  
  112.                finalDocs.add(doc);  
  113.                loadedFileUrls.add(url.toString());  
  114.            }  
  115.   
  116.            if (LOG.isDebugEnabled()) {  
  117.                LOG.debug("Loaded action configuration from: " + fileName);  
  118.            }  
  119.        }  
  120.        return finalDocs;  
  121.    }  

 init_CustomConfigurationProviders(); // [5]初始化自定义的provider配置类全名和实现ConfigurationProvider接口,用逗号隔开即可

Java代码  收藏代码
  1. private void init_CustomConfigurationProviders() {  
  2.         String configProvs = initParams.get("configProviders");  
  3.         if (configProvs != null) {  
  4.             String[] classes = configProvs.split("\s*[,]\s*");  
  5.             for (String cname : classes) {  
  6.                 try {  
  7.                     Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());  
  8.                     ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();  
  9.                     configurationManager.addContainerProvider(prov);  
  10.                 } catch (InstantiationException e) {  
  11.                     throw new ConfigurationException("Unable to instantiate provider: "+cname, e);  
  12.                 } catch (IllegalAccessException e) {  
  13.                     throw new ConfigurationException("Unable to access provider: "+cname, e);  
  14.                 } catch (ClassNotFoundException e) {  
  15.                     throw new ConfigurationException("Unable to locate provider class: "+cname, e);  
  16.                 }  
  17.             }  
  18.         }  
  19.     }  

 接下来我们把目光放到Container container = init_PreloadConfiguration();上,从代码的表面意义上可以看出是对容器进行初始化

Java代码  收藏代码
  1. private Container init_PreloadConfiguration() {  
  2.         Configuration config = configurationManager.getConfiguration();  
  3.         Container container = config.getContainer();  
  4.   
  5.         boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));  
  6.         LocalizedTextUtil.setReloadBundles(reloadi18n);  
  7.   
  8.         ContainerHolder.store(container);  
  9.   
  10.         return container;  
  11.     }  

 Configuration与ConfigurationManager作为Struts2初始化过程中的两大强力的辅助类,对于配置元素的管理启动了至关重要的作用。

Configuration,提供了框架所有配置元素访问的接口,而且对所有配置元素进行初始化调度,接下来我们就从 Configuration config = configurationManager.getConfiguration();一点一点地来揭示Configuration对配置元素初始化调度的本质。

Java代码  收藏代码
  1. /** 
  2.      * Get the current XWork configuration object.  By default an instance of DefaultConfiguration will be returned 
  3.      * 
  4.      * @see com.opensymphony.xwork2.config.impl.DefaultConfiguration 
  5.      */  
  6.     public synchronized Configuration getConfiguration() {  
  7.         if (configuration == null) {  
  8.             setConfiguration(createConfiguration(defaultFrameworkBeanName));  
  9.             try {  
  10.                 configuration.reloadContainer(getContainerProviders());  
  11.             } catch (ConfigurationException e) {  
  12.                 setConfiguration(null);  
  13.                 throw new ConfigurationException("Unable to load configuration.", e);  
  14.             }  
  15.         } else {  
  16.             conditionalReload();  
  17.         }  
  18.   
  19.         return configuration;  
  20.     }  

 关键在于configuration.reloadContainer(getContainerProviders());方法,我们接着看代码:

Java代码  收藏代码
  1. /** 
  2.      * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls 
  3.      * buildRuntimeConfiguration(). 
  4.      * 
  5.      * @throws ConfigurationException 
  6.      */  
  7.     public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {  
  8.         packageContexts.clear();  
  9.         loadedFileNames.clear();  
  10.         List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();  
  11.   
  12.         ContainerProperties props = new ContainerProperties();  
  13.         ContainerBuilder builder = new ContainerBuilder();  
  14.         Container bootstrap = createBootstrapContainer(providers);  
  15.         for (final ContainerProvider containerProvider : providers)  
  16.         {  
  17.             bootstrap.inject(containerProvider);  
  18.             containerProvider.init(this);  
  19.             containerProvider.register(builder, props);  
  20.         }  
  21.         props.setConstants(builder);  
  22.   
  23.         builder.factory(Configuration.class, new Factory<Configuration>() {  
  24.             public Configuration create(Context context) throws Exception {  
  25.                 return DefaultConfiguration.this;  
  26.             }  
  27.         });  
  28.   
  29.         ActionContext oldContext = ActionContext.getContext();  
  30.         try {  
  31.             // Set the bootstrap container for the purposes of factory creation  
  32.   
  33.             setContext(bootstrap);  
  34.             container = builder.create(false);  
  35.             setContext(container);  
  36.             objectFactory = container.getInstance(ObjectFactory.class);  
  37.   
  38.             // Process the configuration providers first  
  39.             for (final ContainerProvider containerProvider : providers)  
  40.             {  
  41.                 if (containerProvider instanceof PackageProvider) {  
  42.                     container.inject(containerProvider);  
  43.                     ((PackageProvider)containerProvider).loadPackages();  
  44.                     packageProviders.add((PackageProvider)containerProvider);  
  45.                 }  
  46.             }  
  47.   
  48.             // Then process any package providers from the plugins  
  49.             Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);  
  50.             for (String name : packageProviderNames) {  
  51.                 PackageProvider provider = container.getInstance(PackageProvider.class, name);  
  52.                 provider.init(this);  
  53.                 provider.loadPackages();  
  54.                 packageProviders.add(provider);  
  55.             }  
  56.   
  57.             rebuildRuntimeConfiguration();  
  58.         } finally {  
  59.             if (oldContext == null) {  
  60.                 ActionContext.setContext(null);  
  61.             }  
  62.         }  
  63.         return packageProviders;  
  64.     }  

很显然我们在这个方法中看到,方法的参数即是一组配置元素的加载器(ConfigurationProvider) Struts2的初始换主线自此全部结束。

 *****************************************华丽的分割线***************************************

下面我们再来看看Struts2的第二条主线:

######请求处理主线 我们回到StrutsPrepareAndExecuteFilter来看看,Filter标准中的doFilter()方法:

Java代码  收藏代码
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
  2.   
  3.        HttpServletRequest request = (HttpServletRequest) req;  
  4.        HttpServletResponse response = (HttpServletResponse) res;  
  5.   
  6.        try {  
  7.            prepare.setEncodingAndLocale(request, response);  
  8.            prepare.createActionContext(request, response);  
  9.            prepare.assignDispatcherToThread();  
  10.            if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {  
  11.                chain.doFilter(request, response);  
  12.            } else {  
  13.                request = prepare.wrapRequest(request);  
  14.                ActionMapping mapping = prepare.findActionMapping(request, response, true);  
  15.                if (mapping == null) {  
  16.                    boolean handled = execute.executeStaticResourceRequest(request, response);  
  17.                    if (!handled) {  
  18.                        chain.doFilter(request, response);  
  19.                    }  
  20.                } else {  
  21.                    execute.executeAction(request, response, mapping);  
  22.                }  
  23.            }  
  24.        } finally {  
  25.            prepare.cleanupRequest(request);  
  26.        }  
  27.    }  

 prepare.createActionContext(request, response);:创建上下文ActionContext并初始化其内部线程安全的ThreadLocal对象

Java代码  收藏代码
  1. /** 
  2.      * Creates the action context and initializes the thread local 
  3.      */  
  4.     public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {  
  5.         ActionContext ctx;  
  6.         Integer counter = 1;  
  7.         Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);  
  8.         if (oldCounter != null) {  
  9.             counter = oldCounter + 1;  
  10.         }  
  11.           
  12.         ActionContext oldContext = ActionContext.getContext();  
  13.         if (oldContext != null) {  
  14.             // detected existing context, so we are probably in a forward  
  15.             ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));  
  16.         } else {  
  17.             ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();  
  18.             stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext));  
  19.             ctx = new ActionContext(stack.getContext());  
  20.         }  
  21.         request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);  
  22.         ActionContext.setContext(ctx);  
  23.         return ctx;  
  24.     }  

 prepare.assignDispatcherToThread();:将dispatcher对象绑定到Dispatcher内部线程安全的instance对象中

request = prepare.wrapRequest(request);:对HttpServletRequest进行封装,传统的Web容器元素的数据是通过HttpServletRequest 的接口实现访问,但是在Struts2中数据的储存位置发生了变化,它们不在适合于web对象绑定在一起,而是以ActionContext的形式存在于当前 的线程中,故传统的访问方式无法访问到Struts2中的数据,因此Struts2针对这种情况作出了对HttpServletRequest装饰的扩展。

PrepareOperations.java

Java代码  收藏代码
  1. /** 
  2.     * Wraps the request with the Struts wrapper that handles multipart requests better 
  3.     * @return The new request, if there is one 
  4.     * @throws ServletException 
  5.     */  
  6.    public HttpServletRequest wrapRequest(HttpServletRequest oldRequest) throws ServletException {  
  7.        HttpServletRequest request = oldRequest;  
  8.        try {  
  9.            // Wrap request first, just in case it is multipart/form-data  
  10.            // parameters might not be accessible through before encoding (ww-1278)  
  11.            request = dispatcher.wrapRequest(request, servletContext);  
  12.        } catch (IOException e) {  
  13.            throw new ServletException("Could not wrap servlet request with MultipartRequestWrapper!", e);  
  14.        }  
  15.        return request;  
  16.    }  

 Dispatcher.java

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

 ActionMapping mapping = prepare.findActionMapping(request, response, true);ActionMapping是一个普通的java类,但是它将 URL形式的HTTP请求与Struts2中的Action建立起了联系。Struts2在进行Http请求处理时,由ActionMapper的实现类在运行期查找相应的 事件映射关系并生成ActionMapping对象。

Java代码  收藏代码
  1. /** 
  2.      * Finds and optionally creates an {@link ActionMapping}.  if forceLookup is false, it first looks in the current request to see if one 
  3.      * has already been found, otherwise, it creates it and stores it in the request.  No mapping will be created in the 
  4.      * case of static resource requests or unidentifiable requests for other servlets, for example. 
  5.      * @param forceLookup if true, the action mapping will be looked up from the ActionMapper instance, ignoring if there is one 
  6.      * in the request or not  
  7.      */  
  8.     public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {  
  9.         ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);  
  10.         if (mapping == null || forceLookup) {  
  11.             try {  
  12.                 //ActionMapper类才是Struts2进行URL Mapping关系查找的核心类  
  13.                 mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());  
  14.                 if (mapping != null) {  
  15.                     request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);  
  16.                 }  
  17.             } catch (Exception ex) {  
  18.                 dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);  
  19.             }  
  20.         }  
  21.   
  22.         return mapping;  
  23.     }   

 ActionMapper类具有多个默认的实现类,每个实现类具有不同的ActionMapping查找规则,所以这个地方给我们留下了无限的遐想,是个很好的扩展点。

execute.executeAction(request, response, mapping);:开始真正执行业务逻辑 ExecuteOperations.java

Java代码  收藏代码
  1. /** 
  2.      * Executes an action 
  3.      * @throws ServletException 
  4.      */  
  5.     public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {  
  6.         dispatcher.serviceAction(request, response, servletContext, mapping);  
  7.     }  

 Dispatcher.java

Java代码  收藏代码
  1. public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,  
  2.                               ActionMapping mapping) throws ServletException {  
  3.   
  4.         Map<String, Object> extraContext = createContextMap(request, response, mapping, context);  
  5.   
  6.         // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action  
  7.         ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);  
  8.         boolean nullStack = stack == null;  
  9.         if (nullStack) {  
  10.             ActionContext ctx = ActionContext.getContext();  
  11.             if (ctx != null) {  
  12.                 stack = ctx.getValueStack();  
  13.             }  
  14.         }  
  15.         if (stack != null) {  
  16.             extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));  
  17.         }  
  18.   
  19.         String timerKey = "Handling request from Dispatcher";  
  20.         try {  
  21.             UtilTimerStack.push(timerKey);  
  22.             String namespace = mapping.getNamespace();  
  23.             String name = mapping.getName();  
  24.             String method = mapping.getMethod();  
  25.   
  26.             Configuration config = configurationManager.getConfiguration();  
  27.             ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(  
  28.                     namespace, name, method, extraContext, true, false);  
  29.   
  30.             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());  
  31.             //如果ActionMapping对象中包含Result对象,则直接跳过Action而执行Result  
  32.             // if the ActionMapping says to go straight to a result, do it!  
  33.             if (mapping.getResult() != null) {  
  34.                 Result result = mapping.getResult();  
  35.                 result.execute(proxy.getInvocation());  
  36.             } else {  
  37.                 proxy.execute();  
  38.             }  
  39.   
  40.             // If there was a previous value stack then set it back onto the request  
  41.             if (!nullStack) {  
  42.                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);  
  43.             }  
  44.         } catch (ConfigurationException e) {  
  45.             // WW-2874 Only log error if in devMode  
  46.             if (devMode) {  
  47.                 String reqStr = request.getRequestURI();  
  48.                 if (request.getQueryString() != null) {  
  49.                     reqStr = reqStr + "?" + request.getQueryString();  
  50.                 }  
  51.                 LOG.error("Could not find action or result " + reqStr, e);  
  52.             } else {  
  53.                 if (LOG.isWarnEnabled()) {  
  54.                     LOG.warn("Could not find action or result", e);  
  55.                 }  
  56.             }  
  57.             sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);  
  58.         } catch (Exception e) {  
  59.             if (handleException || devMode) {  
  60.                 sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);  
  61.             } else {  
  62.                 throw new ServletException(e);  
  63.             }  
  64.         } finally {  
  65.             UtilTimerStack.pop(timerKey);  
  66.         }  
  67.     }  

 Map<String, Object> extraContext = createContextMap(request, response, mapping, context);: 该方法主要把Application、Session、Request的key value值拷贝到Map中,并放在HashMap<String,Object>中

Dispatcher.java

Java代码  收藏代码
  1. /** 
  2.      * Create a context map containing all the wrapped request objects 
  3.      * 
  4.      * @param request The servlet request 
  5.      * @param response The servlet response 
  6.      * @param mapping The action mapping 
  7.      * @param context The servlet context 
  8.      * @return A map of context objects 
  9.      */  
  10.     public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response,  
  11.             ActionMapping mapping, ServletContext context) {  
  12.   
  13.         // request map wrapping the http request objects  
  14.         Map requestMap = new RequestMap(request);  
  15.   
  16.         // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately  
  17.         Map params = new HashMap(request.getParameterMap());  
  18.   
  19.         // session map wrapping the http session  
  20.         Map session = new SessionMap(request);  
  21.   
  22.         // application map wrapping the ServletContext  
  23.         Map application = new ApplicationMap(context);  
  24.   
  25.         Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context);  
  26.           
  27.         if (mapping != null) {  
  28.             extraContext.put(ServletActionContext.ACTION_MAPPING, mapping);  
  29.         }  
  30.         return extraContext;  
  31.     }  

 接下来调用Configuration对象的reloadContainer()利用ContainerBuilder对象生成Container对象,为生成ActionProxy对象做好准备,

ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy( namespace, name, method, extraContext, true, false);创建ActionProxyFacotry的过程也完成了ActionInvocation对象的创建:

DefaultActionProxyFactory.java----------createActionProxy()

Java代码  收藏代码
  1. public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {  
  2.         DefaultActionProxy proxy = new DefaultActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);  
  3.         container.inject(proxy);  
  4.         proxy.prepare();//  
  5.         return proxy;  
  6.     }  

 DefaultActionProxy.java----------prepare()

Java代码  收藏代码
  1. protected void prepare() {  
  2.        String profileKey = "create DefaultActionProxy: ";  
  3.        try {  
  4.            UtilTimerStack.push(profileKey);  
  5.            config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);  
  6.   
  7.            if (config == null && unknownHandlerManager.hasUnknownHandlers()) {  
  8.                config = unknownHandlerManager.handleUnknownAction(namespace, actionName);  
  9.            }  
  10.            if (config == null) {  
  11.                throw new ConfigurationException(getErrorMessage());  
  12.            }  
  13.   
  14.            resolveMethod();  
  15.   
  16.            if (!config.isAllowedMethod(method)) {  
  17.                throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);  
  18.            }  
  19.   
  20.            invocation.init(this);  
  21.   
  22.        } finally {  
  23.            UtilTimerStack.pop(profileKey);  
  24.        }  
  25.    }  

 DefaultActionInvocation.java-------------init(ActionProxy proxy)

Java代码  收藏代码
  1. public void init(ActionProxy proxy) {  
  2.         this.proxy = proxy;  
  3.         Map<String, Object> contextMap = createContextMap();  
  4.   
  5.         // Setting this so that other classes, like object factories, can use the ActionProxy and other  
  6.         // contextual information to operate  
  7.         ActionContext actionContext = ActionContext.getContext();  
  8.   
  9.         if (actionContext != null) {  
  10.             actionContext.setActionInvocation(this);  
  11.         }  
  12.   
  13.         createAction(contextMap);  
  14.   
  15.         if (pushAction) {  
  16.             stack.push(action);//压入CompoundRoot 压栈操作  
  17.             contextMap.put("action", action);  
  18.         }  
  19.   
  20.         invocationContext = new ActionContext(contextMap);  
  21.         invocationContext.setName(proxy.getActionName());  
  22.   
  23.         // get a new List so we don't get problems with the iterator if someone changes the list  
  24.         List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());  
  25.         interceptors = interceptorList.iterator();  
  26.     }  

 DefaultActionInvocation.java---createAction(contextMap);

Java代码  收藏代码
  1. protected void createAction(Map<String, Object> contextMap) {  
  2.         // load action  
  3.         String timerKey = "actionCreate: " + proxy.getActionName();  
  4.         try {  
  5.             UtilTimerStack.push(timerKey);  
  6.             action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);  
  7.         } catch (InstantiationException e) {  
  8.             throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());  
  9.         } catch (IllegalAccessException e) {  
  10.             throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());  
  11.         } catch (Exception e) {  
  12.             String gripe;  
  13.   
  14.             if (proxy == null) {  
  15.                 gripe = "Whoa!  No ActionProxy instance found in current ActionInvocation.  This is bad ... very bad";  
  16.             } else if (proxy.getConfig() == null) {  
  17.                 gripe = "Sheesh.  Where'd that ActionProxy get to?  I can't find it in the current ActionInvocation!?";  
  18.             } else if (proxy.getConfig().getClassName() == null) {  
  19.                 gripe = "No Action defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";  
  20.             } else {  
  21.                 gripe = "Unable to instantiate Action, " + proxy.getConfig().getClassName() + ",  defined for '" + proxy.getActionName() + "' in namespace '" + proxy.getNamespace() + "'";  
  22.             }  
  23.   
  24.             gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");  
  25.             throw new XWorkException(gripe, e, proxy.getConfig());  
  26.         } finally {  
  27.             UtilTimerStack.pop(timerKey);  
  28.         }  
  29.   
  30.         if (actionEventListener != null) {  
  31.             action = actionEventListener.prepare(action, stack);  
  32.         }  
  33.     }  
  34.    

 继续回到Dispatcher中的proxy.execute();继续执行业务逻辑 DefaultActionProxy.java-------------execute()

Java代码  收藏代码
  1. public String execute() throws Exception {  
  2.        ActionContext nestedContext = ActionContext.getContext();  
  3.        ActionContext.setContext(invocation.getInvocationContext());  
  4.   
  5.        String retCode = null;  
  6.   
  7.        String profileKey = "execute: ";  
  8.        try {  
  9.            UtilTimerStack.push(profileKey);  
  10.   
  11.            retCode = invocation.invoke();  
  12.        } finally {  
  13.            if (cleanupContext) {  
  14.                ActionContext.setContext(nestedContext);  
  15.            }  
  16.            UtilTimerStack.pop(profileKey);  
  17.        }  
  18.   
  19.        return retCode;  
  20.    }  

 DefaultActionInvocation.java---invoke()

Java代码  收藏代码
  1. /** 
  2.      * @throws ConfigurationException If no result can be found with the returned code 
  3.      */  
  4.     public String invoke() throws Exception {  
  5.         String profileKey = "invoke: ";  
  6.         try {  
  7.             UtilTimerStack.push(profileKey);  
  8.   
  9.             if (executed) {  
  10.                 throw new IllegalStateException("Action has already executed");  
  11.             }  
  12.   
  13.             if (interceptors.hasNext()) {  
  14.                 final InterceptorMapping interceptor = interceptors.next();  
  15.                 String interceptorMsg = "interceptor: " + interceptor.getName();  
  16.                 UtilTimerStack.push(interceptorMsg);  
  17.                 try {  
  18.                                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  
  19.                             }  
  20.                 finally {  
  21.                     UtilTimerStack.pop(interceptorMsg);  
  22.                 }  
  23.             } else {  
  24.                 resultCode = invokeActionOnly();  
  25.             }  
  26.   
  27.             // this is needed because the result will be executed, then control will return to the Interceptor, which will  
  28.             // return above and flow through again  
  29.             if (!executed) {  
  30.                 if (preResultListeners != null) {  
  31.                     for (Object preResultListener : preResultListeners) {  
  32.                         PreResultListener listener = (PreResultListener) preResultListener;  
  33.   
  34.                         String _profileKey = "preResultListener: ";  
  35.                         try {  
  36.                             UtilTimerStack.push(_profileKey);  
  37.                             listener.beforeResult(this, resultCode);  
  38.                         }  
  39.                         finally {  
  40.                             UtilTimerStack.pop(_profileKey);  
  41.                         }  
  42.                     }  
  43.                 }  
  44.   
  45.                 // now execute the result, if we're supposed to  
  46.                 if (proxy.getExecuteResult()) {  
  47.                     executeResult();  
  48.                 }  
  49.   
  50.                 executed = true;  
  51.             }  
  52.   
  53.             return resultCode;  
  54.         }  
  55.         finally {  
  56.             UtilTimerStack.pop(profileKey);  
  57.         }  
  58.     }  

 resultCode = invokeActionOnly();:真正调用Action实际逻辑的地方

DefaultActionInvocation.java---invokeActionOnly()

Java代码  收藏代码
  1. public String invokeActionOnly() throws Exception {  
  2.         return invokeAction(getAction(), proxy.getConfig());  
  3.     }  
  4.       
  5.      protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {  
  6.         String methodName = proxy.getMethod();  
  7.   
  8.         if (LOG.isDebugEnabled()) {  
  9.             LOG.debug("Executing action method = " + actionConfig.getMethodName());  
  10.         }  
  11.   
  12.         String timerKey = "invokeAction: " + proxy.getActionName();  
  13.         try {  
  14.             UtilTimerStack.push(timerKey);  
  15.   
  16.             boolean methodCalled = false;  
  17.             Object methodResult = null;  
  18.             Method method = null;  
  19.             try {  
  20.                 method = getAction().getClass().getMethod(methodName, EMPTY_CLASS_ARRAY);  
  21.             } catch (NoSuchMethodException e) {  
  22.                 // hmm -- OK, try doXxx instead  
  23.                 try {  
  24.                     String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);  
  25.                     method = getAction().getClass().getMethod(altMethodName, EMPTY_CLASS_ARRAY);  
  26.                 } catch (NoSuchMethodException e1) {  
  27.                     // well, give the unknown handler a shot  
  28.                     if (unknownHandlerManager.hasUnknownHandlers()) {  
  29.                         try {  
  30.                             methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);  
  31.                             methodCalled = true;  
  32.                         } catch (NoSuchMethodException e2) {  
  33.                             // throw the original one  
  34.                             throw e;  
  35.                         }  
  36.                     } else {  
  37.                         throw e;  
  38.                     }  
  39.                 }  
  40.             }  
  41.   
  42.             if (!methodCalled) {  
  43.                 methodResult = method.invoke(action, EMPTY_OBJECT_ARRAY);  
  44.             }  
  45.   
  46.             return saveResult(actionConfig, methodResult);  
  47.         } catch (NoSuchMethodException e) {  
  48.             throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");  
  49.         } catch (InvocationTargetException e) {  
  50.             // We try to return the source exception.  
  51.             Throwable t = e.getTargetException();  
  52.   
  53.             if (actionEventListener != null) {  
  54.                 String result = actionEventListener.handleException(t, getStack());  
  55.                 if (result != null) {  
  56.                     return result;  
  57.                 }  
  58.             }  
  59.             if (t instanceof Exception) {  
  60.                 throw (Exception) t;  
  61.             } else {  
  62.                 throw e;  
  63.             }  
  64.         } finally {  
  65.             UtilTimerStack.pop(timerKey);  
  66.         }  
  67.     }  

 OK action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。

Java代码  收藏代码
  1. // now execute the result, if we're supposed to  
  2.                 if (proxy.getExecuteResult()) {  
  3.                     executeResult();  
  4.                 }  

 DefaultActionInvocation.java---executeResult()

Java代码  收藏代码
  1. /** 
  2.      * Uses getResult to get the final Result and executes it 
  3.      * 
  4.      * @throws ConfigurationException If not result can be found with the returned code 
  5.      */  
  6.     private void executeResult() throws Exception {  
  7.         result = createResult();  
  8.   
  9.         String timerKey = "executeResult: " + getResultCode();  
  10.         try {  
  11.             UtilTimerStack.push(timerKey);  
  12.             if (result != null) {  
  13.                 result.execute(this);  
  14.             } else if (resultCode != null && !Action.NONE.equals(resultCode)) {  
  15.                 throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()  
  16.                         + " and result " + getResultCode(), proxy.getConfig());  
  17.             } else {  
  18.                 if (LOG.isDebugEnabled()) {  
  19.                     LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());  
  20.                 }  
  21.             }  
  22.         } finally {  
  23.             UtilTimerStack.pop(timerKey);  
  24.         }  
  25.     }  

 DefaultActionInvocation.java---createResult()

Java代码  收藏代码
  1. public Result createResult() throws Exception {  
  2.   
  3.         if (explicitResult != null) {  
  4.             Result ret = explicitResult;  
  5.             explicitResult = null;  
  6.   
  7.             return ret;  
  8.         }  
  9.         ActionConfig config = proxy.getConfig();  
  10.         Map<String, ResultConfig> results = config.getResults();  
  11.   
  12.         ResultConfig resultConfig = null;  
  13.   
  14.         try {  
  15.             resultConfig = results.get(resultCode);  
  16.         } catch (NullPointerException e) {  
  17.             if (LOG.isDebugEnabled()) {  
  18.                 LOG.debug("Got NPE trying to read result configuration for resultCode [#0]", resultCode);  
  19.             }  
  20.         }  
  21.           
  22.         if (resultConfig == null) {  
  23.             // If no result is found for the given resultCode, try to get a wildcard '*' match.  
  24.             resultConfig = results.get("*");  
  25.         }  
  26.   
  27.         if (resultConfig != null) {  
  28.             try {  
  29.                 return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());  
  30.             } catch (Exception e) {  
  31.                 if (LOG.isErrorEnabled()) {  
  32.                     LOG.error("There was an exception while instantiating the result of type #0", e, resultConfig.getClassName());  
  33.                 }  
  34.                 throw new XWorkException(e, resultConfig);  
  35.             }  
  36.         } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {  
  37.             return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);  
  38.         }  
  39.         return null;  
  40.     }  

 具体result.execute(this);的实现可以参考一下各类中的具体代码: 



 时间匆忙,只是对整个执行流程做了代码层面的演示,待后续将详细说明附上 ^.^

原文地址:https://www.cnblogs.com/SoniceryD/p/4208779.html