webx扩展了spring的容器加载机制,首先在结构上扩展了component层,构造出不同应用模块bean之间的隔离机制。
1.解析web.xml
和spring web程序一样,容器的加载是通过在web.xml中配置的listener实现的。servlet容器在实例化servlet context的时候会触发contextInitialized方法从而开启实例化容器的流程。
根据servlet规范,listener按照观察者模式设计,一旦监听到某个事件就执行某个方法。而web.xml文件中有WebxContextLoaderListener继承了ServletContextListener接口,就会调用contextInitialized方法。
2.分析WebxContextLoaderListener
该类的功能:监听ServletContext事件,加载Spring容器;
类的继承结构:
@Override
protected final ContextLoader createContextLoader() {
return new WebxComponentsLoader() {
@Override
protected Class<? extends WebxComponentsContext> getDefaultContextClass() {
Class<? extends WebxComponentsContext> defaultContextClass = WebxContextLoaderListener.this
.getDefaultContextClass();
if (defaultContextClass == null) {
defaultContextClass = super.getDefaultContextClass();
}
return defaultContextClass;
}
};
}
ContextLoaderListener中调用上面创建的contextLoader进行初始化webx容器
public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
if (this.contextLoader == null) {
this.contextLoader = this;
}
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
3.应用上下文结构
相对于Spring上下文中以bean为单位进行加载,webx新增加了组件的结构。在容器加载的时候webx默认会扫描web-info目录下的webx.xml 文件和webx-*xml文件,进行资源加载,创建容器。
webx将一个web应用分解成多个小应用模块:app1,app2等。将一个大的应用变成若干个小应用模块,并且配置文件相互隔离。
- 所有小应用模块共享一个Spring Root Context( 根容器 ),对应 webx.xml ,根容器的bean可以被注入到子容器中;反过来不行。
- 每个小应用独享一个Spring Sub Context( 子容器 ),对应 webx-*.xml ,子容器之间相互隔离,不可相互注入bean。
需要注意的是,即使web应用很简单,也至少配置一个子容器。
如何实现刚才所说的组件容器加载的呢?WebxComponentsContext继承了AbstractApplicationContext并对 postProcessBeanFactory 方法和 finishRefresh方法进行了覆写,也正是这两个方法实现了webx独特的component。这一点非常重要!
在配置文件中,例如webx-*.xml中导入其他的xml加载bean的时候,从根上下文中是加载不到这些bean的,要通过获取该模块的component对应的上下文才能获取。
WebApplicationContext context = WebApplicationContextUtils .getWebApplicationContext(getServletContext());
WebxComponentsContext webxComponentsContext = (WebxComponentsContext)context;
WebxComponents components = webxComponentsContext.getWebxComponents();
WebxComponent component = components.getComponent("home");
playService = (PlayService) component.getApplicationContext().getBean("playServiceImpl");
扩展加载component的结构图:
1)覆写postProcessBeanFactory方法,实际上是实例化WebxComponentsCreator内部类,用于初始化root component和sub component,并且将所有的sub component的父容器设置成root component,形成级联结构。
/** 初始化components。 */
private WebxComponentsImpl createComponents(WebxConfiguration parentConfiguration,
ConfigurableListableBeanFactory beanFactory) {
ComponentsConfig componentsConfig = getComponentsConfig(parentConfiguration);
// 假如isAutoDiscoverComponents==true,试图自动发现components
Map<String, String> componentNamesAndLocations = findComponents(componentsConfig, getServletContext());
// 取得特别指定的components
Map<String, ComponentConfig> specifiedComponents = componentsConfig.getComponents();
// 实际要初始化的comonents,为上述两种来源的并集
Set<String> componentNames = createTreeSet();
componentNames.addAll(componentNamesAndLocations.keySet());
componentNames.addAll(specifiedComponents.keySet());
// 创建root controller
WebxRootController rootController = componentsConfig.getRootController();
if (rootController == null) {
rootController = (WebxRootController) BeanUtils.instantiateClass(componentsConfig.getRootControllerClass());
}
// 创建并将components对象置入resolvable dependencies,以便注入到需要的bean中
WebxComponentsImpl components = new WebxComponentsImpl(componentsContext,
componentsConfig.getDefaultComponent(), rootController, parentConfiguration);
beanFactory.registerResolvableDependency(WebxComponents.class, components);
// 初始化每个component
for (String componentName : componentNames) {
ComponentConfig componentConfig = specifiedComponents.get(componentName);
String componentPath = null;
WebxController controller = null;
if (componentConfig != null) {
componentPath = componentConfig.getPath();
controller = componentConfig.getController();
}
if (controller == null) {
controller = (WebxController) BeanUtils.instantiateClass(componentsConfig.getDefaultControllerClass());
}
WebxComponentImpl component = new WebxComponentImpl(components, componentName, componentPath,
componentName.equals(componentsConfig.getDefaultComponent()), controller,
getWebxConfigurationName());
components.addComponent(component);
prepareComponent(component, componentNamesAndLocations.get(componentName));
}
return components;
}
2)- 覆写finishRefresh方法,遍历地所有的sub component,通过wcc.refresh方法初始化每个sub component中的bean。
/** 初始化所有components。 */
public void finishRefresh() {
components.getWebxRootController().onFinishedProcessContext();
for (WebxComponent component : components) {
logInBothServletAndLoggingSystem("Initializing Spring sub WebApplicationContext: " + component.getName());
WebxComponentContext wcc = (WebxComponentContext) component.getApplicationContext();
WebxController controller = component.getWebxController();
wcc.refresh();
controller.onFinishedProcessContext();
}
logInBothServletAndLoggingSystem("WebxComponents: initialization completed");
}