这篇文章主要解决我们上篇文章遗留下来的问题
第一: 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的定义如下
到这里我们就明白了,它就是创建了一个默认框架工作类名为“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.初始化错误处理类