结合Spring IOC 看Webx & HSF & Spring MVC

写在前面

Spring IOC这个部分还是很难的,我基本有了一年多的使用经验才慢慢开始看源码,但都是头大无比,一定要静下心慢慢看,就像一杯苦茶,越品越有味道!

Webx是阿里开源的web框架:
http://www.openwebx.org/docs/Webx3_Guide_Book.html
HSF是阿里的

IOC大致流程

IOC容器的两个最主要的两个接口BeanFactoryApplicationContext相信大家都接触过,IOC整个初始化流程非常复杂,分为两个主要的过程:

  1. 注册BeanDefinition
  2. getBean

Spring在复杂的流程中也为开发者留有一些接口,开发人员可以利用这些接口完成自己一些项目功能,或者在其之上继续开发框架。这部分也经常会以Spring生命周期的形式出现在各类资料上,那我们来疏导一下:

BeanFactoryPostProcessor

public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

BeanFactoryPostProcessor容器级操作,会在整个beanFactory初始化之后调用用户定义的BeanFactoryPostProcessor,例如placeholder,就是在BeanFactory注册好了之后对其中的placeholder进行替换。

Aware

顾名思义,这是spring提供的用户可以对spring容器的一些感知,常见的有ApplicationContextAware

    public interface ApplicationContextAware extends Aware {


    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

Aware的调用是被ApplicationContextAwareProcessor这个BeanPostProcessor在postProcessBeforeInitialization时调用的。有点绕,你可以先看下面的,然后再回来。

BeanPostProcessor

先看代码
public interface BeanPostProcessor {

    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

BeanPostProcessor是在执行用户定义的init-method前后执行before和after的,上例中的Aware就是在before的时候执行的。

InitializingBean

InitializingBean是在用户的init-method之前执行

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

}

有点乱了是不是,简单屡一下ApplicationContext的refresh过程,也就是初始化过程,首先会注册BeanFactory,然后执行BeanFactoryPostProcessor之后注册ApplicationContextAwareProcessor也就是一些Aware,然后是执行所有的BeanPostProcessor,中间会执行InitializingBean。

贴一下refresh的代码

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // Reset 'active' flag.
            cancelRefresh(ex);

            // Propagate exception to caller.
            throw ex;
        }
    }
}

大部分的流程都是这里分析出来的,注意两个方法postProcessBeanFactoryfinishRefresh这两个方法在其子类都是没有实现的,也就是流出来的拓展点,我们的Webx就是利用这个点做的处理。其中postProcessBeanFactory是在BeanFactory初始化完成后finishRefresh在整个ApplicationContext初始化完成后。

HSF

首先说一下HSF

HSF是阿里的RPC框架,其实抛开HSF,我们的Spring RMI,HttpInvoker都是这样的RPC框架,其借助IOC去初始化框架的原理都是一样的!!

HSF 与 IOC

我之前写过一篇大致分析HSF泛化调用的流程文章,这里把基于Spring的不上来。我们在基于Spring的HSF开发中,会使用两个类HSFSpringProviderBean和HSFSpringConsumerBean。强烈建议在学习之前先对HSF的流程有个大致的感觉

HSFSpringProviderBean

我们只看类的定义

public final class HSFSpringProviderBean implements InitializingBean, ApplicationContextAware

我们看到了两个熟悉的生命周期接口InitializingBean和ApplicationContextAware,看看具体的干什么吧。

@Override
public void afterPropertiesSet() throws Exception {
    init();
}

也就是说在HSFSpringProviderBean注入了所有属性之后会调用HSF的init函数,这个函数就是负责provider的服务注册,线程池的分配等等。

public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (0 == AppInfoUtils.hsfSpringBeanCountDown) {
        AppInfoUtils.hsfSpringBeanCountDown = applicationContext.getBeanNamesForType(HSFSpringProviderBean.class).length;
    }
    if (applicationContext instanceof AbstractApplicationContext) {
        try {
            Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener",
                    new Class<?>[] { ApplicationListener.class }); // 兼容Spring2.0.1
            method.setAccessible(true);
            method.invoke(applicationContext, new Object[] { new HSFApplicationListener() });
            isInSpringContainer = true;
            LOGGER.info("接口[" + providerBean.getMetadata().getInterfaceName() + "]版本["
                    + providerBean.getMetadata().getVersion() + "]添加Listner成功");
        } catch (Throwable t) {
            LOGGER.error("", "接口[" + providerBean.getMetadata().getInterfaceName() + "]版本["
                    + providerBean.getMetadata().getVersion() + "]添加Listner成功失败", t);
        }
    }
}

这部分首先是利用反射加入一个ApplicationListener,这个ApplicationListener会在Spring容器的初始完成和销毁的时候发出通知,这样HSF就能够在这时发布和销毁了。

private final class HSFApplicationListener implements ApplicationListener {
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            HSFSpringProviderBean.this.providerBean.publish();
            setAppInitedStatus();
        } else if (event instanceof ContextClosedEvent) {
            if (AppInfoUtils.appInited.compareAndSet(true, false)) {
                LOGGER.info("Spring容器关闭,设置应用初始化状态为未初始化!");
            }
        }
    }

    private void setAppInitedStatus() {
        // 最后一个HsfspringBean发布完的时候才设置状态(providerBean都初始化完成了,但是可能会publish失败)
        if (0 == (--AppInfoUtils.hsfSpringBeanCountDown)) {
            if (AppInfoUtils.appInited.compareAndSet(false, true)) {
                LOGGER.info("所有hsfSpringBean初始化完成,Spring容器初始化完成,可以通过pandora查询应用启动状态。");
            }
        }
    }
}

HSFSpringConsumerBean

public class HSFSpringConsumerBean implements FactoryBean, InitializingBean

同样会有一个InitializingBean,同样的是在Bean注入完成之后调用init负责初始化及服务的订阅,最主要是使用反射生成一个服务的代理

FactoryBean这个是一个工厂方法用户获得我们服务的接口类等。不多介绍

webx

Webx我们都知道Webx存在一个父ApplicationContext和一堆子ApplicationContext,然后整个Webx的执行流程也是从WebxFrameworkFilter(filter)->WebxRootController(RequestContext)->WebxController(pipeline)。如果这些你感到不是很懂,那先看看官方文档哈

http://www.openwebx.org/docs/Webx3_Guide_Book.html#d0e4894

那接下来我们结合IOC来看看初始化

初始化ServletContextListener

首先需要知道web的项目都是由tomcat等加载之后再去加载Spring的环境的,也就是说我们需要从web的环境中感知,于是有了ServletContextListener

public interface ServletContextListener extends EventListener {
    /**
     ** Notification that the web application initialization
     ** process is starting.
     ** All ServletContextListeners are notified of context
     ** initialization before any filter or servlet in the web
     ** application is initialized.
     */

    public void contextInitialized ( ServletContextEvent sce );

    /**
     ** Notification that the servlet context is about to be shut down.
     ** All servlets and filters have been destroy()ed before any
     ** ServletContextListeners are notified of context
     ** destruction.
     */
    public void contextDestroyed ( ServletContextEvent sce );
}

那么我们是怎么声明的呢,或者说我们怎么样去让web项目区感知我们设置的Listener,答案就在我们的web.xml中

webx

<listener>
    <listener-class>com.alibaba.citrus.webx.context.WebxContextLoaderListener</listener-class>
</listener>

SpringMVC

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

你去看就会发现都是一样的功能,初始化项目的ApplicationContext。

看一下代码:

public class WebxContextLoaderListener extends ContextLoaderListener {
    @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;
            }
        };
    }

    protected Class<? extends WebxComponentsContext> getDefaultContextClass() {
        return null;
    }
}

复写了父类的createContextLoader方法,使用WebxComponentsLoader去初始化容器。
由于代码量偏大,我直截取部分代码,所以建议你一边开着IDE一边看,虽然我的博客一般没什么点击率[doge]

ContextLoaderListener中的实现了ServletContextListener的方法

public void contextInitialized(ServletContextEvent event) {
    this.contextLoader = createContextLoader();
    if (this.contextLoader == null) {
        this.contextLoader = this;
    }
    this.contextLoader.initWebApplicationContext(event.getServletContext());
}

WebxComponentsLoader通过复写ContextLoader的determineContextClass方法返回了Webx的ApplicationContext–WebxComponentsContext,这个类代码很长,不过首先看到的是它实现了我之前说的两个方法postProcessBeanFactoryfinishRefresh,具体的逻辑都在WebxComponentsLoader中。

postProcessBeanFactory

在WebxComponentsLoader中的实现注册了WebxComponentsCreator这个类,看一下

public static class WebxComponentsCreator implements BeanFactoryPostProcessor, Ordered

在其postProcessBeanFactory方法中执行了createComponents,这个方法首先根据webx里面生面的所有的子模块的名字如app1和app2去找他们对应的webx-app1.xml和webx-app2.xml,然后创建WebxRootController,创建WebxComponentsImpl,这个类包含了WebxRootController,WebApplicationContext等所有重要信息。然后遍历所有的子模块,为其生成独有的WebxController和WebxComponentImpl【跟父类有个s区别】然后注册至WebxComponentsImpl中

finishRefresh

这时真个ApplicationContext都已初始化完毕,从WebxComponentsImpl取出一个个的WebxComponentImpl,并取出器WebxComponentContext,执行其refresh,也就是初始化子模块的ApplicationContext,这时整个Webx已经启动完成。

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

SpringMVC

最近也看了一下SpringMVC的初始化,基本的流程是一样的,也是在ServletContextListener中初始化,不过有很多不一样的,首先就是需要手动的执行ApplicationContext的Location,这时SpringMVC也就初始化完成了基类的ApplicationContext,然后还会在Servlet的init方法中再次根据不同的servlet初始化不同的ApplicationContext。

今天写的有点累,这部分等我总结好再分享吧~~

写在最后

我觉得还是不要因为技术你就死磕吧,如果你的使用时间很短那么不建议你深往下挖,还是需要以你的平时开发为一个基准线,然后遇到问题多去分析一下为什么,慢慢就变的厉害了,切不可一意孤行,走火入魔啊!

猜你喜欢

转载自blog.csdn.net/WSRspirit/article/details/52179416