webx 容器初始化

时间:2021-06-26 19:44:52
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容器;
类的继承结构:
webx 容器初始化
WebxContextLoaderListener中创建contextLoader

@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.应用上下文结构

webx 容器初始化

相对于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的结构图:

webx 容器初始化

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");
}