struts2源码分析(二)(初始化)

时间:2021-07-27 19:38:15

2、Dispatcher dispatcher = init.initDispatcher(config);
 这行代码是struts2初始化的核心部分(该方法是InitOperations中的方法)方法的具体内容如下:
 1:Dispatcher dispatcher = createDipatcher(filterConfig);

 /**
* 创建一个dispatcher实例,通过给定的servlet容器和配置项
* @param servletContext servlet容器
* @param initParams T 配置项
*/
public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {
//初始化参数
this.servletContext = servletContext;
this.initParams = initParams;
}

 改代码的主要作用是初始化一个Dispatcher对象,其createDispatcher(FilterConfig filterConfig)为InitOperations中的方法,该方法的具体内容如下:

/**
* 创建一个Dispatcher
*/
private Dispatcher createDispatcher( HostConfig filterConfig ) {
//把filterConfig中的配置参数以键值对的Map形式组织起来
Map<String, String> params = new HashMap<String, String>();
for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
String name = (String) e.next();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
//返回一个Dispatcher对象,并把servlet容器,和组织的Map传递过去
return new Dispatcher(filterConfig.getServletContext(), params);
}

 其中调用了Dispatcher的构造方法

 在这个构造方法中初始化了两个参数;分别为:

private ServletContext servletContext;//servlet容器
private Map<String, String> initParams;//配置项
 

 至此Dispatcher的初始化完成;需要注意的是初始化过程中对两个参数的设定
 2:dispatcher.init();改代码是核心中的核心,其是Dispatcher(org.apache.struts2.dispatcher)中的方法,该方法的具体内容如下:

 /**
* 加载配置项,包括xml和零配置策略还有更新选项设置,包括是否需要重新加载配置和资源文件
*/
public void init() {
//如果configurationManager为空,则创建一个该对象
if (configurationManager == null) {
//初始化configurationManager对象
configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}

try {
//初始化default.properties配置文件
①init_DefaultProperties(); // [1],第一步加载的是default.properties
②init_TraditionalXmlConfigurations(); // [2] 加载struts-defaults.xml,struts-plugin.xml,struts.xml这三个文件的provider
③init_LegacyStrutsProperties(); // [3] 加载LegacyPropertiesConfigurationProviders
④init_CustomConfigurationProviders(); // [5] 加载用户自定义的配置解析文件
⑤init_FilterInitParameters() ; // [6] 加载filter中的参数
⑥init_AliasStandardObjects() ; // [7] 加载BeanSelectionProvider

//进行加载前进行的配置初始化
1:Container container = init_PreloadConfiguration();
一:container.inject(this);
二:init_CheckConfigurationReloading(container);
三:init_CheckWebLogicWorkaround(container);

if (!dispatcherListeners.isEmpty()) {
for (DispatcherListener l : dispatcherListeners) {
l.dispatcherInitialized(this);
}
}
} catch (Exception ex) {
if (LOG.isErrorEnabled())
LOG.error("Dispatcher initialization failed", ex);
throw new StrutsException(ex);
}
}

如果configurationManager为空,则创建一个该对象

 if (configurationManager == null) {
//初始化configurationManager对象
ConfigurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}


这段代码的主要作用是初始化一个configurationManager对象;这个对象是Dispatcher中的属性

 private ConfigurationManager configurationManager;

 createConfigurationManager(String name)是Dispatcher中的方法,该方法的具体内容如下:

/**
* 创建一个configurationManager对象,并设置其默认框架名
* @param name
* @return
*/
protected ConfigurationManager createConfigurationManager(String name) {
return new ConfigurationManager(name);
}


调用了ConfigurationManager(com.opensymphony.xwork2.config)的构造方法,其构造方法具体内容如下

/**
* 可以设定默认框架名
* @param name 此时为struts
*/
public ConfigurationManager(String name) {
this.defaultFrameworkBeanName = name;
}


该构造方法的具体作用有两个,初始化默认FrameWork的名称该参数是ConfigurationManager中的

protected String defaultFrameworkBeanName;

 而BeanSelectionProvider.DEFAULT_BEAN_NAME这个静态常量的具体值为

 public static final String DEFAULT_BEAN_NAME = "struts";

 BeanSelectionProvider(org.apache.struts2.config)这个类的具体作用将会在后面讲到
 自此初始化ConfigurationManager对象结束

①init_DefaultProperties(); // [1],第一步加载的是default.properties
 该方法属于Dispatcher类,该方法的具体内容如下:

/**
* 把DefaultPropertiesProvider放入configurationManager对象的containerProvider中
*/
private void init_DefaultProperties() {
configurationManager.addContainerProvider(new DefaultPropertiesProvider());
}


其中调用了ConfigurationManager的addContainerProvider方法,该方法的具体内容如下:

/**
* 添加一个配置provider到ConfigurationProviders列表中。一个给定的ConfigurationProvider可能被添加多次
*
* @param provider将要被注册的ConfigurationProvider
*/
public void addContainerProvider(ContainerProvider provider) {
/**
* 如果该provider存在,则不进行添加
*/
if (!containerProviders.contains(provider)) {
containerProviders.add(provider);
}
}


其中containerProviders是ConfigurationManager中的一个参数

 private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();

 DefaultPropertiesProvider(org.apache.struts2.config)这个方法继承自
 LegacyPropertiesConfigurationProvider(org.apache.struts2.config)(这个类在后面会进行介绍)而LegacyPropertiesConfigurationProvider实现了ConfigurationProvider接口
 而ConfigurationProvider(com.opensymphony.xwork2.config)的具体内容如下

/**
* 这个接口继承了ContainerProvider,和PackageProvider接口
*/
public interface ConfigurationProvider extends ContainerProvider, PackageProvider {
}


其中ContainerProvider(com.opensymphony.xwork2.config)的主要内容如下:

public interface ContainerProvider {

/**
* 当被从configuratinManager的时候调用
*/
public void destroy();

/**
* 初始化配置信息
* @param configuration The configuration
* @throws ConfigurationException If anything goes wrong
*/
public void init(Configuration configuration) throws ConfigurationException;

/**
* 这个ContainerProvider是否需要重新装载其配置
*/
public boolean needsReload();

/**
* 为Container注册bean、properties
*
* @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;

}

该接口主要定义了4个方法分别为:destory(),init(Configuration configuration),needsReload(),register(ContainerBuilder builder, LocatableProperties props)
 这是个方法的具体用处在后面会进行介绍
 而PackageProvider(com.opensymphony.xwork2.config)的具体内容如下

public interface PackageProvider {

/**
* 初始化Configuration
* @param configuration 配置
* @throws ConfigurationException If anything goes wrong
*/
public void init(Configuration configuration) throws ConfigurationException;

/**
* 这个PackageProvider是否需要重新被配置
*/
public boolean needsReload();

/**
* 为这个配置加载Package
* @throws ConfigurationException
*/
public void loadPackages() throws ConfigurationException;

}


因此只要实现ConfigurationProvider接口的类,都可被添加到ConfigurationManager的containerProviders这个属性中
 而DefaultPropertiesProvider这个类的主要作用是读取default.properties中的配置信息放入ContainerBuilder中,
 在这里调用了DefaultPropertiesProvider的默认构造方法,该构造方法只是创建了一个DefaultPropertiesProvider对象,未进行任何其他操作
 至于该类的其他作用将在后面进行介绍

②init_TraditionalXmlConfigurations(); // [2] 加载struts-defaults.xml,struts-plugin.xml,struts.xml这三个文件的provider
 该方法属于Dispatcher的方法,方法的具体内容如下:

/**
* 加载各类xml配置
*/
private void init_TraditionalXmlConfigurations() {
//查看web.xml的Filter中是否配置了config这个参数
String configPaths = initParams.get("config");
if (configPaths == null) {
//如果没有配置,则采用默认的设置
configPaths = DEFAULT_CONFIGURATION_PATHS;
}
//把configpaths分离成3个string;分别为struts-default.xml;struts-plugin.xml;struts.xml
String[] files = configPaths.split("\\s*[,]\\s*");
//对分离的files进行遍历:struts-default.xml
for (String file : files) {
if (file.endsWith(".xml")) {//如果这些文件是以.xml结尾的话
if ("xwork.xml".equals(file)) {
//如果文件名等于xwork.xml的话,
configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));
} else {
//向configurationManager的containerProvider中添加下面的strutsxmlconfigurationprovider
configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
}
} else {
//如果不是以.xml结尾的文件的话,抛出一个异常
throw new IllegalArgumentException("Invalid configuration file name");
}
}
}


上述代码中如果file和xwork.xml相等则执行

configurationManager.addContainerProvider(createXmlConfigurationProvider(file, false));

 否则执行

configurationManager.addContainerProvider(createStrutsXmlConfigurationProvider(file, false, servletContext)); 

 方法createXmlConfigurationProvider(file, false)的具体内容如下

/**
* 创建一个xmlconfigurationProvider对象,用来解析xwork.xml
* @param filename 文件名
* @param errorIfMissing 如果找不到该文件的话,的处理办法,是否报错,是否抛出异常
* @return
*/
protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
return new XmlConfigurationProvider(filename, errorIfMissing);
}


这里调用了XmlConfigurationProvider(com.opensymphony.xwork.config.providers)的构造方法,该构造方法的具体内容如下

/**
* 默认的构造方法,前两个构造方法在内部都调用该构造方法来作为初始化改对象的选项
* @param filename struts-default.xml
* @param errorIfMissing
*/
public XmlConfigurationProvider(String filename, boolean errorIfMissing) {
//设定文件名
this.configFileName = filename;
//出错信息
this.errorIfMissing = errorIfMissing;

//dtd约束配置
Map<String, String> mappings = new HashMap<String, String>();
mappings.put("-//Apache Struts//XWork 2.3//EN", "xwork-2.3.dtd");
mappings.put("-//Apache Struts//XWork 2.1.3//EN", "xwork-2.1.3.dtd");
mappings.put("-//Apache Struts//XWork 2.1//EN", "xwork-2.1.dtd");
mappings.put("-//Apache Struts//XWork 2.0//EN", "xwork-2.0.dtd");
mappings.put("-//Apache Struts//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
mappings.put("-//Apache Struts//XWork 1.1//EN", "xwork-1.1.dtd");
mappings.put("-//Apache Struts//XWork 1.0//EN", "xwork-1.0.dtd");
setDtdMappings(mappings);
}


在这个构造方法里面主要进行了如下操作:
 1)初始化了文件名this.configFileName = filename;
 2)初始化错误处理方法this.errorIfMissing = errorIfMissing;
 3)添加了dtd约束文件
 setDtdMappings(mappings);方法的具体代码如下:

 //设定DTD约束
public void setDtdMappings(Map<String, String> mappings) {
this.dtdMappings = Collections.unmodifiableMap(mappings);
}


其中包括XmlConfigurationProvider的属性

 private Map<String, String> dtdMappings;

 该类负责解析xml文件
 方法createStrutsXmlConfigurationProvider(file, false, servletContext)的具体内容如下

/**
* 创建一个strutsxmlconfigurationprovider对象
* @param filename 配置文件名
* @param errorIfMissing 错误处理方式
* @param ctx servlet容器
* @return
*/
protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
}


这里调用了StrutsXmlConfigurationProvider(org.apache.struts2.config)的默认构造方法

/**
* 创建一个配置provider
* @param filename 文件名
* @param errorIfMissing 如果该文件未找到,我们是否需要抛出异常
* @param ctx Servlet容器
*/
public StrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
super(filename, errorIfMissing);//调用父类的默认构造方法
this.servletContext = ctx;//初始化servlet容器
this.filename = filename;//设置文件名 struts-default.xml
reloadKey = "configurationReload-"+filename;//?这里是什么意思,目前不了解
Map<String,String> dtdMappings = new HashMap<String,String>(getDtdMappings());//获取父类的dtdmapping,并添加struts2的DTD约束
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.0//EN", "struts-2.0.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1//EN", "struts-2.1.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN", "struts-2.1.7.dtd");
dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd");
setDtdMappings(dtdMappings);//设置新添加struts2的DTD约束
/**
* 以下这段代码,不懂具体含义是什么
*/
File file = new File(filename);
if (file.getParent() != null) {
this.baseDir = file.getParentFile();
}
}


在该构造方法里进行了如下操作
 1)首先调用父类的构造方法,设置文件名,设置错误处理方式
 2)初始化 this.servletContext = ctx;
 3)设置文件名  this.filename = filename;
 4)这行代码的主要作用不了解 reloadKey = "configurationReload-"+filename;
 5)获取父类配置的dtd约束文件
 6)在dtd中添加struts2的dtd约束文件
 7)调用父类的方法,设置新的dtd约束文件
 至此,struts-default.xml,struts-plugin.xml,struts.xml三个文件的解析类都放入ConfigurationManager的containerProviders中。具体解析方法在后面会进行介绍

③init_LegacyStrutsProperties(); // [3] 加载LegacyPropertiesConfigurationProviders

该方法属于Dispatcher的方法,方法的具体内容如下:

/**
* 把LegacyPropertiesConfigurationProvider添加到configurationManager对象的containerProvider中去;
* 具体这个provider解析的是什么,目前不了解
*/
private void init_LegacyStrutsProperties() {
configurationManager.addContainerProvider(new LegacyPropertiesConfigurationProvider());
}


这里的LegacyPropertiesConfigurationProvider(org.apache.struts2.config)被DefaultPropertiesProvider继承,目前该类的主要作用不是很清楚
 同时,这里只是创建了一个该类的实例,未进行任何其他操作,因此在这里不做过多介绍,该类的其他具体作用将在后面进行介绍

④init_CustomConfigurationProviders(); // [5] 加载用户自定义的配置解析文件
 该方法属于Dispatcher的方法,方法的具体内容如下:

/**
* 加载用户自定义的configProviders文件
*/
private void init_CustomConfigurationProviders() {
//从web.xml的Filter的配置信息里面查找configProviders的配置
String configProvs = initParams.get("configProviders");
if (configProvs != null) {
//吧用户配置的参数用,分割成一个string数组;这些类必须实现ConfigurationProvider接口
String[] classes = configProvs.split("\\s*[,]\\s*");
for (String cname : classes) {//对用户配置的文件进行遍历,添加到containerProvider中
try {
//通过java反射机制生成实体对象
Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
//加载到configurationManager的containerProvider中
configurationManager.addContainerProvider(prov);
} catch (InstantiationException e) {
throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
} catch (IllegalAccessException e) {
throw new ConfigurationException("Unable to access provider: "+cname, e);
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Unable to locate provider class: "+cname, e);
}
}
}
}


该方法的组要作用是,检索web.xml中的Filter的配置信息,查看用户是否配置了ConfigProvider选项,如果配置了该选项,首先对该选项的内容进行解析
 同时,利用java的反射机制生成一个该类的实例,添加到ConfigurationManager的containerProviders中

⑤init_FilterInitParameters() ; // [6] 加载filter中的参数
 该方法属于Dispatcher的方法,方法的具体内容如下:

/**
* 把web.xml中配置的参数放入configurationManager的cotainerprovider中去
*/
private void init_FilterInitParameters() {
configurationManager.addContainerProvider(new ConfigurationProvider() {
public void destroy() {
}

public void init(Configuration configuration) throws ConfigurationException {
}

public void loadPackages() throws ConfigurationException {
}

public boolean needsReload() {
return false;
}
//把web.xml中配置的fileter中的参数放入props中
public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
props.putAll(initParams);
}
});
}


该方法的具体作用是在该方法内部调用configurationManager的addContainerProvider方法,添加了一个匿名实现了ConfigurationProvider接口的类
 该匿名内部类的主要作用是加载web.xml配置文件在Filter中配置的参数

⑥init_AliasStandardObjects() ; // [7] 加载BeanSelectionProvider
 该方法是Dispatcher中的方法,该方法的具体内容如下:

/**
* 加载BeanSelectionProvider到configurationManager的containerprovider中去
*/
private void init_AliasStandardObjects() {
configurationManager.addContainerProvider(new BeanSelectionProvider());
}


该方法的主要作用是装载BeanSelectionProvider(org.apache.struts2.config)在这里只是创建了一个新的BeanSelectionProvider对象的实例,未进行其他操作
 至于该类的作用和具体含义将在后面进行介绍