• 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中起到了重要的中间调度的作用。

  • 相关阅读:
    BZOJ2219数论之神——BSGS+中国剩余定理+原根与指标+欧拉定理+exgcd
    Luogu 3690 Link Cut Tree
    CF1009F Dominant Indices
    CF600E Lomsat gelral
    bzoj 4303 数列
    CF1114F Please, another Queries on Array?
    CF1114B Yet Another Array Partitioning Task
    bzoj 1858 序列操作
    bzoj 4852 炸弹攻击
    bzoj 3564 信号增幅仪
  • 原文地址:https://www.cnblogs.com/hayasi/p/5822169.html
Copyright © 2020-2023  润新知