zoukankan      html  css  js  c++  java
  • Struts2 源码分析——调结者(Dispatcher)之准备工作

    章节简言

    上一章笔者讲到关于struts2过滤器(Filter)的知识。让我们了解到StrutsPrepareFilter和StrutsExecuteFilter的作用。特别是StrutsPrepareFilter做了重要的讲解。从其中我们了解到Dispatcher类的重要性。而本章就是专对Dispatcher类的工作进行讲解。从前面章节的机制图片中我们橙黄色区里面看到FilterDispatcher。在笔者理解这里的FilterDispatcher相当于Dispatcher类的工作。那么到底Dispatcher类做了哪一些的工作呢?本章就是笔者就会详细的进行讲解。那么在讲解之前笔者还是有想把一些必要的知识说一下。从上一章中我们可以明白StrutsPrepareFilter类的主要工作有俩点:一是为struts2执行做一些相关的准备。如加载相关的配置信息。二是为struts2的request请求处理相关的信息。如设置编码格式和找到对应的action映射类。而这二点都离不开Dispatcher类的作用。甚至可以讲大部分都要靠Dispatcher类来完成。笔者很想把Dispatcher类的源码全部都POST上来。可是想到这样子读起来有一点吃力。所以笔者打算将来部分部分的POST上来进行讲解。

    调结者的准备工作

    在执行struts2之前必然要加载一些相关信息。如配置文件struts.xml之类。没有错。StrutsPrepareFilter就是通过Dispatcher类来完成这一系列的工作的(下面代码的红色部分)。让我们看一下Dispatcher类的代码就是能够明白。如下

    StrutsPrepareFilte类:

     1  public void init(FilterConfig filterConfig) throws ServletException {
     2         InitOperations init = new InitOperations();//用于初始化相关的功能操作。你可以理解为工具类一样子。
     3         Dispatcher dispatcher = null;//这个类相当的重要。他的作用连接着StrutsExecuteFilter。这里可以命名为调结者。
     4         try {
     5             FilterHostConfig config = new FilterHostConfig(filterConfig);//这里可以理解为把filterConfig在进行封装FilterHostConfig更为主便操作和理解。
     6             init.initLogging(config);//获取名为loggerFactory的参数,并实例化这个类。一般为去用户自定义日志。
     7             dispatcher = init.initDispatcher(config);//初化调结者。这里是重要。
     8 
     9             prepare = new PrepareOperations(dispatcher);
    10             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);//加载排除在内的action的正则表达式
    11 
    12             postInit(dispatcher, filterConfig);
    13         } finally {
    14             if (dispatcher != null) {
    15                 dispatcher.cleanUpAfterInit();
    16             }
    17             init.cleanup();
    18         }
    19     }

    Dispatcher类:

     1 public void init() {
     2 
     3         if (configurationManager == null) {
     4             configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);// 初始化配置管理类
     5         }
     6 
     7         try {
     8             init_FileManager();// 初始化FileManager类的供应者
     9             init_DefaultProperties(); // [1]初始化default.properties信息的供应者
    10             init_TraditionalXmlConfigurations(); // [2]初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供应者
    11             init_LegacyStrutsProperties(); // [3]初始化struts.properties信息的供应者
    12                                             // 可能是以前版本留下的文件。在这里笔者一直没有找到
    13             init_CustomConfigurationProviders(); // [5]初始化用户struts.xml信息的供应者
    14             init_FilterInitParameters(); // [6]初始化用户传入的过滤器参数信息的供应者
    15             init_AliasStandardObjects(); // [7]初始化struts-default.xml,struts-plugin.xml,struts.xml进行别名信息的供应者
    16 
    17             Container container = init_PreloadConfiguration();// 初始化相关的配置信息,并加载以上供应者。
    18             container.inject(this);// 注入当前类依赖项的信息。
    19             init_CheckWebLogicWorkaround(container);
    20 
    21             if (!dispatcherListeners.isEmpty()) {
    22                 for (DispatcherListener l : dispatcherListeners) {
    23                     l.dispatcherInitialized(this);
    24                 }
    25             }
    26             errorHandler.init(servletContext);
    27 
    28         } catch (Exception ex) {
    29             LOG.error("Dispatcher initialization failed", ex);
    30             throw new StrutsException(ex);
    31         }
    32     }

    以上的源码就是Dispatcher类加载相关配置信息工作的代码。从上面的注解中我们可以看“供应者”这三个字样。为什么这边笔者要叫他们为供应者。主要是struts2这边用到了一个概念就是IOC思想。即是控制反转(Inversion of Control)。简单点理解IOC就是有一个容器,里面有很多要用到的实例或是类的信息。当开发员要用到某个类的实例的时候,不在是NEW了。而是通过当前容器来获得实例。即是有一点类似于工厂模式。如果还是不能理解的话,请读者自行去找一些相关IOC思想的文章。笔者这里不会细说IOC思想。 Container类的实例就是笔者所讲的容器。而前面的供应者是为Container容器提供对应的类的信息或实例。确切的讲Container容器创建的时候,就会去找所有供应者并让供应者提供数据。值得一提的是这里面还有涩及到一个重要的中间人配置管理类(ConfigurationManager)。让我们看一下下面的代码,就是知道是什么一回事了。如下

    /**
         * 初始化文件管理器供应者
         */
        private void init_FileManager() throws ClassNotFoundException {
            if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) {
                final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER);
                final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName);
                LOG.info("Custom FileManager specified: {}", fileManagerClassName);
                configurationManager
                        .addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));
            } else {
                // add any other Struts 2 provided implementations of FileManager
                configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));
            }
            if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {
                final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY);
                final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class
                        .forName(fileManagerFactoryClassName);
                LOG.info("Custom FileManagerFactory specified: {}", fileManagerFactoryClassName);
                configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass));
            }
        }
    
        /**
         * 初始化加载default.properties文件信息的供应者
         */
        private void init_DefaultProperties() {
            configurationManager.addContainerProvider(new DefaultPropertiesProvider());
        }
    
        private void init_LegacyStrutsProperties() {
            configurationManager.addContainerProvider(new PropertiesConfigurationProvider());
        }
    
        /**
         * 初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供应者
         */
        private void init_TraditionalXmlConfigurations() {
            String configPaths = initParams.get("config");
            if (configPaths == null) {
                configPaths = DEFAULT_CONFIGURATION_PATHS;
            }
            String[] files = configPaths.split("\s*[,]\s*");
            for (String file : files) {
                if (file.endsWith(".xml")) {
                    if ("xwork.xml".equals(file)) {
                        configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
                    } else {
                        configurationManager
                                .addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
                    }
                } else {
                    throw new IllegalArgumentException("Invalid configuration file name");
                }
            }
        }

    看到上面的红色代码了吗?没有错。从这一点我们就是看出来。所以初始化的操作都是先创建对应的供应者。并把供应者实列增加到ConfigurationManager实例中去。正如上面所讲的供应者是为Container容器提供数据的。那么到底供应者又长什么样子呢?这就不得不让我们在看另一段代码了。如下

     1 package com.opensymphony.xwork2.config;
     2 
     3 import com.opensymphony.xwork2.inject.ContainerBuilder;
     4 import com.opensymphony.xwork2.util.location.LocatableProperties;
     5 
     6 
     7 /**
     8  * 用于给Container容器提供对应的对象,常量和属性的供应者类
     9  * 
    10  * @since 2.1
    11  */
    12 public interface ContainerProvider {
    13 
    14     /**
    15      * 消毁当前的供应者
    16      */
    17     public void destroy();
    18     
    19     /**
    20      * 初始化供应者
    21      * @param configuration The configuration
    22      * @throws ConfigurationException If anything goes wrong
    23      */
    24     public void init(Configuration configuration) throws ConfigurationException;
    25     
    26     /**
    27      * 是否需要重新加载
    28      *
    29      * @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
    30      */
    31     public boolean needsReload();
    32     
    33     /**
    34      * 用于注入容器的方法
    35      * 
    36      * @param builder The builder to register beans with
    37      * @param props The properties to register constants with
    38      * @throws ConfigurationException If anything goes wrong
    39      */
    40     public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
    41     
    42 }

    上面代码里面的ContainerProvider就是所有供应者类的父类接口类之一。从上面代码就是可以看出来他的作用就是为Container容器服务的。其中register方法就是最明显的代表了。即是把信息注入到Container容器。好了。让笔者好好整理一下。struts2执行之前都做了一些什么工作。即是StrutsPrepareFilter类在Dispatcher类的帮助下做了些什么。如下。

    1.判断ConfigurationManager类是否存在,如果不存在就是创建。

    2.初始化文件管理类的供应者,并增加ConfigurationManager实例中。用于监督和管理加载的文件。

    3.初始化加载default.properties文件信息的供应者,并增加ConfigurationManager实例中。用于加载struts2包default.properties信息。

    4.初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供应者,并增加ConfigurationManager实例中。用于加载struts2包中的struts-default.xml,struts-plugin.xml,struts.xml信息。

    5.初始化struts.properties信息的供应者,并增加ConfigurationManager实例中。这里笔者也有一点奇怪。源码里面是去加载struts.properties信息。可是笔者一直没有找到对应的文件。

    6.初始化用户传入的过滤器参数信息的供应者,并增加ConfigurationManager实例中。即是把过滤器参数一块注入到Container容器里面。

    7.初始化struts-default.xml,struts-plugin.xml,struts.xml进行别名信息的供应者。用于加载struts2包中的struts-default.xml,struts-plugin.xml,struts.xml信息。只是这里用别名进行注入。

    8.创建Container容器。把以上所有的供应者所提供的信息全部注入到Container容器。即是对象,常量等信息。

    9.给Dispatcher类本身进行依赖注入。笔者相信读者会看Dispatcher类的方法上面有几个@Inject的关键字。没有错。这就是说明当前方法是用于依赖注入的。用Container类的inject方法就是注入的意思。

    10.判断是否存在Dispatcher监听。如果存在就是执行。

    11.初始化错误处理类。

    本章总结

    本章重点讲的是struts2启动的时候需要加载的相关工作。这些工作大部分是靠Dispatcher类来完成。所以StrutsPrepareFilter类的代码中有Dispatcher类的出现。最后会作为PrepareOperations类的构造参数存放起来。以备request请求处理的需要做准备。下一章笔者将会讲到。通过本章的讲解我们就会明白Dispatcher类在整个strtus2中起到了重要的中间调度的作用。

  • 相关阅读:
    .Net Core调用NodeJs
    ASP.NET Core中间件中渲染Razor视图
    ASP.NET Core自定义View查找路径,实现主题切换
    Core路由2-Endpoint终结点路由
    Core路由1
    .NET Core的本地化机制(多语言)【转】
    实现ASP.NET Core MVC的插件式开发(ApplicationPart)
    Gitflow工作流程
    Git-开发中遇到紧急任务如何处理
    多线程笔记-CancellationToken(取消令牌)
  • 原文地址:https://www.cnblogs.com/hayasi/p/5822169.html
Copyright © 2011-2022 走看看