struts2初始化的探索(二)

这篇文章主要解决我们上篇文章遗留下来的问题

第一: dispatcher = init.initDispatcher(config); 这句代码到底做了什么?

源码还是贴出来,如下:

    public void init() {

    	if (configurationManager == null) {
    		configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}

        try {
            init_FileManager();                                  //  初始化FileManager类的供应者
            init_DefaultProperties();                            //  初始化default.properties信息的供应者
            init_TraditionalXmlConfigurations();                 //  初始化struts-default.xml,struts-plugin.xml,struts.xml信息的供应者
            init_LegacyStrutsProperties();                       //  初始化struts.properties信息的供应者
            init_CustomConfigurationProviders();                 //  初始化用户struts.xml信息的供应者
            init_FilterInitParameters() ;                        //  初始化用户传入的过滤器参数信息的供应者
            init_AliasStandardObjects() ;                        //  初始化struts-default.xml,struts-plugin.xml,struts.xml进行别名信息的供应者
            Container container = init_PreloadConfiguration();   //  初始化相关的配置信息,并加载以上供应者。
            container.inject(this);                              //  注入当前类依赖项的信息。
            init_CheckWebLogicWorkaround(container);

            if (!dispatcherListeners.isEmpty()) {
                for (DispatcherListener l : dispatcherListeners) {
                    l.dispatcherInitialized(this);
                }
            }
            errorHandler.init(servletContext);

        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Dispatcher initialization failed", ex);
            throw new StrutsException(ex);
        }
    }

以上我们提到供应者这个概念:什么叫供应者?

                struts2这边用到了一个概念就是IOC思想。即是控制反转(Inversion of Control)。简单点理解IOC就是有一个容器,里面有很多要用到的实例或是类的信息。当开发员要用到某个类的实例的时候,不在是NEW了。而是通过当前容器来获得实例。有一点类似于工厂模式。简单来说,控制反转就是将对象创建的权力移交给容器。 Container类的实例就是笔者所讲的容器。而前面的供应者是为Container容器提供对应的类的信息或实例。确切的讲Container容器创建的时候,就会去找所有供应者并让供应者提供数据。值得一提的是这里面还有涩及到一个重要的中间人配置管理类(ConfigurationManager)。让我们看一下下面的代码,就是知道是什么一回事了。如下

1.configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);

这段代码的主要作用就是初始化配置管理类。createConfigurationManager(String name)的具体实现如下


再看一下ConfigurationManager的构造函数,如下:


其实就是返回了一个ConfigurationManager对象,而参数DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME的定义如下

扫描二维码关注公众号,回复: 2335970 查看本文章


到这里我们就明白了,它就是创建了一个默认框架工作类名为“struts”的配置管理对象。另外等会儿在其他方法的源码中我们也会发现,configurationManager.addContainerProvider(...)这样的一个方法,所以初始化的操作都是先创建对应的供应者。并把供应者实列增加到ConfigurationManager实例中去。

2.init_FileManager()等等一些方法

在这里只贴这一个方法,大致原理是一样的。简单分析后,我们主要要知道这些方法做了什么

    private void init_FileManager() throws ClassNotFoundException {
        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER)) { 
            final String fileManagerClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER);
            final Class<FileManager> fileManagerClass = (Class<FileManager>) Class.forName(fileManagerClassName);
            if (LOG.isInfoEnabled()) {
                LOG.info("Custom FileManager specified: #0", fileManagerClassName);
            }
            configurationManager.addContainerProvider(new FileManagerProvider(fileManagerClass, fileManagerClass.getSimpleName()));
        } else {
            // add any other Struts 2 provided implementations of FileManager
             configurationManager.addContainerProvider(new FileManagerProvider(JBossFileManager.class, "jboss"));
        }
        if (initParams.containsKey(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY)) {
            final String fileManagerFactoryClassName = initParams.get(StrutsConstants.STRUTS_FILE_MANAGER_FACTORY);
            final Class<FileManagerFactory> fileManagerFactoryClass = (Class<FileManagerFactory>) Class.forName(fileManagerFactoryClassName);
            if (LOG.isInfoEnabled()) {
                LOG.info("Custom FileManagerFactory specified: #0", fileManagerFactoryClassName);
            }
            configurationManager.addContainerProvider(new FileManagerFactoryProvider(fileManagerFactoryClass));
        }
    }

从以上代码我们不难看出,所有初始化的操作都是先创建对应的供应者。并把供应者实列增加到ConfigurationManager实例中去。

下一个问题就是,到底什么是供应者呢?供应者到底长啥样?为了解决这个问题,我们继续看一段代码

public interface ContainerProvider {

    /**销毁
     * Called before removed from the configuration manager
     */
    public void destroy();
    
    /**初始化
     * Initializes with the configuration
     * @param configuration The configuration
     * @throws ConfigurationException If anything goes wrong
     */
    public void init(Configuration configuration) throws ConfigurationException;
    
    /**是否需要重新加载
     * Tells whether the ContainerProvider should reload its configuration
     *
     * @return <tt>true</tt>, whether the ContainerProvider should reload its configuration, <tt>false</tt>otherwise.
     */
    public boolean needsReload();
    
    /**为容器注册bean跟属性
     * Registers beans and properties for the Container
     * 
     * @param builder The builder to register beans with
     * @param props The properties to register constants with
     * @throws ConfigurationException If anything goes wrong
     */
    public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException;
    
}

上面代码里面的ContainerProvider就是所有供应者类的父类接口类之一。从上面代码就是可以看出来他的作用就是为Container容器服务的。其中register方法就是最明显的代表了。即是把信息注入到Container容器

总结:初始化的过程

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.初始化错误处理类



猜你喜欢

转载自blog.csdn.net/qq_41907991/article/details/80637665