您的位置:首页 > 其它

struct2源码解读(7)之搭建struct2运行环境

2015-11-10 11:26 274 查看
struct2源码解读(7)之搭建struct2运行环境 我们前面讨论过,struct2在tomcat启动的时候会自动运行过滤器StrutsPrepareAndExecuteFilter中的init()方法,在这个方法中搭建struct2运行环境完成了初始化。这个init()方法主要创建了一个核心对象Dispacher,实例化这个Dispacher对象之后,调用它的init()方法,解析配置文件。所谓的解析配置文件,其实就是把配置文件信息封装成不同的对象,如action标签信息封装到actionConfig对象,package标签信息封装到packageConfig对象等等.最后把这些对象放入到容器(container)中,当我们处理action请求时,就可以往这个container对象取配置信息数据,从而这个container就是我们所说的struct2运行环境。

一、container对象
container,俗称容器,里面存放了我们在配置文件中配置的对象。既然是容器,那就有存和取。container提供了两个方法inject()和getInstance()来实现存和取的功能container.inject()就是往容器存放对象,container.getInstance()就是往容器取出对象,具体实现我们下篇博文再做详细的探讨,这里大家知道这两个方法的作用即可。
前面我们已经探讨struct2是如何封装配置的信息到不同的对象中了,这篇博文我们来看下struct2是如何把这些对象放到容器中的.当然,首先得要获得container这个对象。

二、获得container对象
我们来看看dispacher.init()中调用的init_PreloadConfiguration()方法,这个方法返回的就是一个Container对象。
清单清单:dispacher.init()
public void init() {

try {
//部分代码略。
//解析配置文件,并封装到Container对象中
Container container = init_PreloadConfiguration();
//把dispacher对象也注入到容器中
container.inject(this);

}
在这个init_PreloadConfiguration()方法中,把配置信息都封装到了Configuration对象中,包括container这个容器对象,然后调用get()方法就可以得到container这个对象。
private Container init_PreloadConfiguration() {
//封装配置信息到Configuration 对象
Configuration config = configurationManager.getConfiguration();
//从配置信息到获得这个容器
Container container = config.getContainer();
return container;
}
Configuration,顾名思义,配置信息,这个对象包含了所有的配置信息。我们来看看这个Configuration对象。Configuration是一个接口,DefaultConfiguration是其默认实现类。
public class DefaultConfiguration implements Configuration {
// 属性
protected Map<String, PackageConfig> packageContexts = new LinkedHashMap<String, PackageConfig>();
protected RuntimeConfiguration runtimeConfiguration;
protected Container container;
protected String defaultFrameworkBeanName;
protected Set<String> loadedFileNames = new TreeSet<String>();
protected List<UnknownHandlerConfig> unknownHandlerStack;
ObjectFactory objectFactory;
//方法省略。

}
这个Configuration配置信息对象主要就是4个属性,一个就是packageConfig,一个就是runtimeConfiguration,还有就是Container和objectFactory.这个packageConfig对象封装了packageConfig,这个package都是我们开发的时候自己配的,而这个runtimeConfiguration是处理aciton请求时所需要的对象。那么,struct2是如何实例化这个Configuration对象的呢?
public synchronized Configuration getConfiguration() {
//如果Configuration,为空,就实例化一个
if (configuration == null) {
//setConfiguration其实就是this.configuration = configuration;这里就可以看到Configuration的默认实现类是DefaultConfiguration,实例化DefaultConfiguration时,也初始化了defaultFrameworkBeanName="struts",defaultFrameworkBeanName在初始化ConfigurationManager时就已经指定
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//getContainerProviders()获取解析器,这里调用reloadContainer解析配置文件,并完成Configuration 初始化,也就是设置上面属性(container)的值,这样就能通过configuration获得容器对象container了
configuration.reloadContainer(getContainerProviders());
}
//异常信息,略
} else {
conditionalReload();
}
//返回configuration对象
return configuration;
}
下面重点探讨下这个reloadContainer()方法,这里调用DefaultConfiguration.reloadContainer()方法。这个reloadContainer()就是设置了Configuration对象的所有属性值。

2.1.设置packageConfig
设置这个packageConfig属性,在解析package标签的时候已经解释过了,struct2把package标签里面的action,result等标签分别封装成actionConfig和resultConfig对象等,然后把这些对象封装到packageConfig对象,然后把packageConfig对象填加到Configuration对象的一个list集合中。

2.2.设置objectFactory
objectFactory = container.getInstance(ObjectFactory.class);
这个objectFactory是直接用getInstance(Class class)方法从容器中取出来的,也就是说前面肯定有inject()注入操作,我们来看下struct-default.xml配置文件
<bean class="com.opensymphony.xwork2.ObjectFactory" name="xwork" />
<bean type="com.opensymphony.xwork2.ObjectFactory" name="struts" class="org.apache.struts2.impl.StrutsObjectFactory" />
果然,配置文件中配置了objectFactory,那它肯定会在容器中,通过getInstance(Class class)这个ByClass的方法就可以取出相应的objectFactory。这里再一次体现了依赖注入的概念。通过配置配置文件,就能控制要实现的类型(这个bean会在解析配置文件的时候封装到containerBuilder对象中,然后创建container时,会把这个beans作为参数传进container)

2.3.设置runtimeConfiguration
通过rebuildRuntimeConfiguration()这个方法就能设置runtimeConfiguration这个属性
public void rebuildRuntimeConfiguration() {
runtimeConfiguration = buildRuntimeConfiguration();
}
这个runtimeConfiguration是指后面处理action请求时所需要的环境信息。那肯定有人问,处理action请求所需的信息,也就是我们配置的package内容不是已经封装到了packageConfig中了吗?为什么还要runtimeConfiguration这个对象。你们要清楚,每一个packageConfig对象都是对应一个package标签的信息,需要把他们放到一个list集合中了,但从集合中取出某个packageConfig的信息依然会很不方便,因此这就需要进行进一步的封装。
protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();
//循环遍历packageConfig
for (PackageConfig packageConfig : packageContexts.values()) {
//package没配置abstract的才会进行封装,因此配置了abstract的package是不会实例化的
if (!packageConfig.isAbstract()) {
//获得当前package的命名空间namespace
String namespace = packageConfig.getNamespace();
//判断
Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
if (configs == null) {
configs = new LinkedHashMap<String, ActionConfig>();
}
//把当前package的actionConfig包括父类的actionConfig对象都放到一个map中
Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();

for (Object o : actionConfigs.keySet()) {
String actionName = (String) o;
ActionConfig baseConfig = actionConfigs.get(actionName);
configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
}
//以namespace为key值,Map<String, ActionConfig>为vaule值存到一个map中
namespaceActionConfigs.put(namespace, configs);
//DefaultActionRef
if (packageConfig.getFullDefaultActionRef() != null) {
namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
}
}
}
//返回一个RuntimeConfiguration对象
return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
}
这个RuntimeConfiguration对象有两个方法,一个是getActionConfigs(),得到的Map<String, Map<String, ActionConfig>>,一个是getActionConfig()得到ActionConfigs。
代码清单:RuntimeConfigurationImpl构造函数
public RuntimeConfigurationImpl(Map<String, Map<String, ActionConfig>> namespaceActionConfigs, Map<String, String> namespaceConfigs) {
this.namespaceActionConfigs = namespaceActionConfigs;
this.namespaceConfigs = namespaceConfigs;
//正规表达式解析器
PatternMatcher<int[]> matcher = container.getInstance(PatternMatcher.class);
this.namespaceActionConfigMatchers = new LinkedHashMap<String, ActionConfigMatcher>();
this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet());

for (String ns : namespaceActionConfigs.keySet()) {
namespaceActionConfigMatchers.put(ns,
new ActionConfigMatcher(matcher,
namespaceActionConfigs.get(ns), true));
}
}
这里就明了了,RuntimeConfiguration其实就是把所有的actionConfig封装到了一起。下面再来探讨下设置Container属性,这样,configuration的属性就基本设置完了。

2.4.设置Container
public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
//解析配置文件。略
//把protperty value值封装到一个factory,然后把factory添加到builder的一个集合中
props.setConstants(builder);
//把Configuration封装到Factory,然后把Factory添加到builder的一个集合中
builder.factory(Configuration.class, new Factory<Configuration>() {
public Configuration create(Context context) throws Exception {
return DefaultConfiguration.this;
}
});
//从线程变量actionConext中取出ActionContext,刚开始是null的
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);
container = builder.create(false);
setContext(container);
//解析package标签,略
} finally {
//清空ActionContext
if (oldContext == null) {
ActionContext.setContext(null);
}
}
return packageProviders;
}
前几篇博文我们已经解析过struct2是如何封装各种配置信息到不同对象上的了,如大家不明白可以参考下前几篇博文,这里探讨下struct2是如何把这些对象注入到container中的。
struct2在循环遍历解析器解析配置文件时,把property文件和xml文件中的常量属性,以key-value的形式封装到ContainerProperties对象中,如<constant name="struts.devMode" value="true" />,封装后为(struts.devMode:true);而把<bean>标签的属性,封装到一个继承factory接口的实现类上,而把这个实现类封装到ContainerBuilder的一个list集合属性中。详细解析可参考前几篇博文。从设计上来说,ContainerProperties对象里面的属性能否也能封装到ContainerBuilder对象中呢 ?
props.setConstants(builder);
这个方法就是把ContainerProperties对象里面的属性封装到ContainerBuilder对象中
public void setConstants(ContainerBuilder builder) {
//keySet()获取hashtable上的所有key值,循环遍历
for (Object keyobj : keySet()) {
String key = (String)keyobj;
//这里又调用了builder的factory方法,把value值封装到了LocatableConstantFactory,然后把这个LocatableConstantFactory添加到builder的一个list集合中
builder.factory(String.class, key,
new LocatableConstantFactory<String>(getProperty(key), getPropertyLocation(key)));
}
}
把所有配置信息(除了package标签的属性)封装到ContainerBuilder对象中之后.把DefaultConfiguration也用工厂方法封装到ContainerBuilder中。这样,ContainerBuilder就拥有了配置文件中的所有对象的信息。
最后,调用了ContainerBuilder的ceate(false)方法,返回了一个container对象
container = builder.create(false);
这里也用到了上篇博文我们提到的构造者模式。在这个create(false方法)创建了一个Container对象实例。
public Container create(boolean loadSingletons) {
ensureNotCreated();
created = true;
final ContainerImpl container = new ContainerImpl(
new HashMap<Key<?>, InternalFactory<?>>(factories));
if (loadSingletons) {
//loadSingletons为false,这段代码不用管
}
container.injectStatics(staticInjections);
return container;
}
factorise封装了所有的配置信息(package除外),这里用factorise做参数,new了一个container对象,从而就把对象都放进了容器中。我们还可以通过inject()方法,手工地往容器中放入对象。

三、总结
至此,struct2初始化工作就全部完成了。struct2在创始化过程中,解析配置文件,把所有配置信息(package除外)封装到factory接口的实现类中,然后把这些factory设置到container容器对象中,package的配置信息,最后封装到runtimeConfiguration。这两个对像有点类似于系统属性和自定义属性,最后把这些对象封装到Configuration对象,Configuration对象又交给ConfigurationManager对象
,ConfigurationManager对象是Dispacher对象的一个属性,通过层层封装,最终把所有信息都交给了Dispacher.所以说,struct2的初始化,也就是生成一个包含配置信息的Dispacher对象,以后就是通过这个对象处理action请求的,这也是dispacher是struct2最核心的一个对象的原因。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  struct2 源码 初始化