• 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);的实现可以参考一下各类中的具体代码: 



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

  • 相关阅读:
    刷题柱 -- 暂封
    模板重搭建計劃
    思路与好题记录与小技巧
    错误记录
    随便记点东西……
    图床
    杂碎的小技巧
    hnsdfz -- 6.21 -- day7
    hsdf -- 6.21 -- day6
    hnsdfz -- 6.20 -- day5
  • 原文地址:https://www.cnblogs.com/SoniceryD/p/4208779.html
Copyright © 2020-2023  润新知