SpringMVC源码阅读笔记-ContextLoaderListener

原文链接: http://my.oschina.net/ojeta/blog/716549

基于SpringFramework 4.0.9 版本

配置SpringMVC有2个核心类,一个Listener,一个Servlet。
配置示例如下

<context-param> 
    <param-name>contextConfigLocation</param-name>
    <param-value>
       classpath*:applicationContext.xml
       classpath*:applicationContext-*.xml
    </param-value>
</context-param>

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

<servlet>
    <servlet-name>springServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring-mvc.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

由于Listener总是比Servlet先加载,所以我们先看ContextLoaderListener

ContextLoaderListener

先看ContextLoaderListener的继承结构
image

发现ContextLoaderListener实现了Servlet标准中的ServletContextListener接口,说明它会监听ServletContext对象的创建和销毁。所以ContextLoaderListener一定要实现ServletContextListener中的2个方法contextInitialized(ServletContextEvent event)contextDestroyed(ServletContextEvent event)

contextInitialized(ServletContextEvent event)

在ContextLoaderListener中找到

@Override
public void contextInitialized(ServletContextEvent event) {
    initWebApplicationContext(event.getServletContext());
}

看到,它先从event对象中获取到了ServletContext,然后调用了initWebApplicationContext(ServletContext servletContext)

然后找initWebApplicationContext(ServletContext servletContext)方法,在ContextLoaderListener中没有找到,那只可能在它的父类中。在ContextLoader中找到:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
    if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
        throw new IllegalStateException(
            "Cannot initialize context because there is already a root application context present - " +
            "check whether you have multiple ContextLoader* definitions in your web.xml!");
    }

    Log logger = LogFactory.getLog(ContextLoader.class);
    servletContext.log("Initializing Spring root WebApplicationContext");
    if (logger.isInfoEnabled()) {
        logger.info("Root WebApplicationContext: initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        // Store context in local instance variable, to guarantee that
        // it is available on ServletContext shutdown.
        if (this.context == null) {
            this.context = createWebApplicationContext(servletContext); ---- (1)
        }
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
            if (!cwac.isActive()) {
                // The context has not yet been refreshed -> provide services such as
                // setting the parent context, setting the application context id, etc
                if (cwac.getParent() == null) {
                    // The context instance was injected without an explicit parent ->
                    // determine parent for root web application context, if any.
                    ApplicationContext parent = loadParentContext(servletContext);  ---- (2)
                    cwac.setParent(parent);  ---- (3)
                }
                configureAndRefreshWebApplicationContext(cwac, servletContext);  ---- (4)
            }
        }
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);  ---- (5)

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = this.context;
        }
        else if (ccl != null) {
            currentContextPerThread.put(ccl, this.context);  ---- (6)
        }

        if (logger.isDebugEnabled()) {
            logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" +
                WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
        }
        if (logger.isInfoEnabled()) {
            long elapsedTime = System.currentTimeMillis() - startTime;
            logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
        }

        return this.context;
    }
    catch (RuntimeException ex) {
        logger.error("Context initialization failed", ex);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
        throw ex;
    }
    catch (Error err) {
        logger.error("Context initialization failed", err);
        servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
        throw err;
    }
}

核心代码梳理

  • (1) 创建WebApplicationContext,放到this.context中;
  • (2)、(3) 看是否有父容器,并对父容器设置一个引用,即setParent(ApplicationContext parent)
  • (4) 配置WebApplicationContext
  • (5) 把this.context即创建的WebApplicationContext放到ServletContext中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
  • (6) 把this.context即创建的WebApplicationContext放到currentContextPerThread中,currentContextPerThread的类型为private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1);

小结
从(5)、(6)两行代码的分析可以看出,Spring将初始化好的WebApplicationContext分别放到了ServletContextcurrentContextPerThread中。这样可以便于我们在任意代码处,直接获取到WebApplicationContext

  • ServletContext中获取WebApplicationContext,就是在获取到ServletContext的基础上,根据key(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)获取到;
  • currentContextPerThread获取,Spring为我们提供了一个公有静态方法org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext(),如下:
public static WebApplicationContext getCurrentWebApplicationContext() {
    ClassLoader ccl = Thread.currentThread().getContextClassLoader();
    if (ccl != null) {
        WebApplicationContext ccpt = currentContextPerThread.get(ccl);
        if (ccpt != null) {
            return ccpt;
        }
    }
    return currentContext;
}

createWebApplicationContext(ServletContext sc)

先看源码

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
    Class<?> contextClass = determineContextClass(sc);
    if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
        throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
                "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
    }
    return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

基本逻辑是先决定使用哪个ContextClass,然后再进行实例化。

进入determineContextClass(ServletContext servletContext)方法

protected Class<?> determineContextClass(ServletContext servletContext) {
    String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); ---- (1)
    if (contextClassName != null) {
        try {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); ---- (2)
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load custom context class [" + contextClassName + "]", ex);
        }
    }
    else {
        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); ---- (3)
        try {
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); ---- (4)
        }
        catch (ClassNotFoundException ex) {
            throw new ApplicationContextException(
                    "Failed to load default context class [" + contextClassName + "]", ex);
        }
    }
}

核心代码梳理

  • (1) 先尝试从配置文件的初始化参数CONTEXT_CLASS_PARAM(即"contextClass")中获取contextClassName
  • (2) 如果开发者指定了contextClass,就获取用户指定的这个contextClass的Class<?>
  • (3) 如果开发者没指定,就从默认策略defaultStrategies中获取contextClassName
  • (4) 最后获取这个默认的contextClassNameClass<?>

现在看一下defaultStrategies

private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";

private static final Properties defaultStrategies;

static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
    }
}

代码实现了从classpath下读取ContextLoader.properties,然后放入defaultStrategies。再看一下ContextLoader.properties,它位于spring-web-[version].jar的org.springframework.web.context包下,内容是:

# Default WebApplicationContext implementation class for ContextLoader.
# Used as fallback when no explicit context implementation has been specified as context-param.
# Not meant to be customized by application developers.

org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext

小结
createWebApplicationContext(ServletContext sc)的逻辑是

  1. 先确定contextClassName。如果开发者通过contextClass参数传入了此值就用此值;如果没传入就用默认的写在ContextLoader.properties中org.springframework.web.context.support.XmlWebApplicationContext
  2. 得到contextClassName后,获取对应的Class。
  3. 得到contextClass后,通过反射进行实例化。最终得到了WebApplicationContext对象。

configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc)

先看源码

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
    if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
        // The application context id is still set to its original default value
        // -> assign a more useful id based on available information
        String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
        if (idParam != null) {
            wac.setId(idParam); ---- (1)
        }
        else {
            // Generate default id...
            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                ObjectUtils.getDisplayString(sc.getContextPath())); ---- (1)
        }
    }

    wac.setServletContext(sc); ---- (2)
    String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); ---- (3)
    if (configLocationParam != null) {
        wac.setConfigLocation(configLocationParam); ---- (4)
    }

    // The wac environment's #initPropertySources will be called in any case when the context
    // is refreshed; do it eagerly here to ensure servlet property sources are in place for
    // use in any post-processing or initialization that occurs below prior to #refresh
    ConfigurableEnvironment env = wac.getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); ---- (5)
    }

    customizeContext(sc, wac); ---- (6)
    wac.refresh(); ---- (7)
}

核心代码逻辑梳理

  • (1) 为WebApplicationContext设置了一个id;
  • (2) 为WebApplicationContext建立了一个对ServletContext的引用。回忆之前的逻辑,发现WebApplicationContextServletContext是其实相互引用的;
  • (3) 从初始化参数中获取到配置路径参数,CONFIG_LOCATION_PARAM的值为"contextConfigLocation",恰好对应配置文件中的
<context-param> 
    <param-name>contextConfigLocation</param-name>
    <param-value>
       classpath*:applicationContext.xml
       classpath*:applicationContext-*.xml
    </param-value>
</context-param>

说明Spring后续会加载配置文件。

  • (4) 为WebApplicationContext建立了一个对配置路径参数的引用;
  • (5) 获取当前环境ConfigurableEnvironment,便于后续根据profiles来解析属性;
  • (6) 对WebApplicationContext进行定制,这个方法被定义为protected,便于子类重写;
  • (7) 刷新WebApplicationContext

接下来重点看这两行代码,customizeContext(sc, wac)wac.refresh()

进入customizeContext(sc, wac)

protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
    List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = 
        determineContextInitializerClasses(sc); ---- (1)
    if (initializerClasses.isEmpty()) {
        // no ApplicationContextInitializers have been declared -> nothing to do
        return;
    }

    ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerInstances =
        new ArrayList<ApplicationContextInitializer<ConfigurableApplicationContext>>();

    for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
        Class<?> initializerContextClass =
            GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
        if (initializerContextClass != null) {
            Assert.isAssignable(initializerContextClass, wac.getClass(), String.format(
                "Could not add context initializer [%s] since its generic parameter [%s] " +
                "is not assignable from the type of application context used by this " +
                "context loader [%s]: ", initializerClass.getName(), initializerContextClass.getName(),
                wac.getClass().getName()));
        }
        initializerInstances.add(BeanUtils.instantiateClass(initializerClass)); ---- (2)
    }

    AnnotationAwareOrderComparator.sort(initializerInstances); ---- (3)
    for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
        initializer.initialize(wac); (4)
    }
}

此方法是在WebApplicationContext设置了配置文件路径之后,在容器刷新之前所可能需要做的一些初始化工作。

  • (1) 获取所有上下文初始化的类。determineContextInitializerClasses的源码为
protected List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> 
    determineContextInitializerClasses(ServletContext servletContext) {

    List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> classes =
        new ArrayList<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>();

    String globalClassNames = servletContext.getInitParameter(GLOBAL_INITIALIZER_CLASSES_PARAM);
    if (globalClassNames != null) {
        for (String className : StringUtils.tokenizeToStringArray(globalClassNames, INIT_PARAM_DELIMITERS)) {
            classes.add(loadInitializerClass(className));
        }
    }

    String localClassNames = servletContext.getInitParameter(CONTEXT_INITIALIZER_CLASSES_PARAM);
    if (localClassNames != null) {
        for (String className : StringUtils.tokenizeToStringArray(localClassNames, INIT_PARAM_DELIMITERS)) {
            classes.add(loadInitializerClass(className));
        }
    }

    return classes;
}

简单读下源码,就是从2个初始化参数GLOBAL_INITIALIZER_CLASSES_PARAM(其值为"globalInitializerClasses")和CONTEXT_INITIALIZER_CLASSES_PARAM(其值为"contextInitializerClasses")中分别解析出ApplicationContextInitializer的class,放入类型为List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>>的集合"classes",然后返回;

  • (2) 将所有的ApplicationContextInitializer实例化放入initializerInstances
  • (3) 对所有的initializerInstances进行排序。排序的规则是:根据类的Order来判断,指定Order有两种方式,一种是实现org.springframework.core.Ordered接口,另一种是使用@org.springframework.core.annotation.Order注解指定。具体逻辑参见int org.springframework.core.annotation.AnnotationAwareOrderComparator.getOrder(Object obj)
@Override
protected int getOrder(Object obj) {
    if (obj instanceof Ordered) {
        return ((Ordered) obj).getOrder();
    }
    if (obj != null) {
        Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
        Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
        if (order != null) {
            return order.value();
        }
    }
    return Ordered.LOWEST_PRECEDENCE;
}
  • (4) 按排好序的initializerInstances遍历集合,调用每个ApplicationContextInitializerinitialize(ConfigurableApplicationContext applicationContext)方法;

最后看一下ApplicationContextInitializer接口的定义:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

    void initialize(C applicationContext);

}

目前Spring并未对此接口提供任何实现,由此说明这个接口只是Spring为我们提供的一个扩展点。在容器刷新前,通过实现此接口,可以对容器做一些操作。接口的实现类可以通过在web.xml中指定globalInitializerClasses或contextInitializerClasses参数,告知Spring。

接下来,我们分析wac.refresh()。在进入源码之前,回忆一下,前面我们分析了,我们现在获取到的容器是一个定义在ContextLoader.properties中的org.springframework.web.context.support.XmlWebApplicationContext,先看下XmlWebApplicationContext的继承结构。 image Orz 继承结构超级复杂。

接下来,进入源码

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

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

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

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

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

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

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

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

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

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

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

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

        catch (BeansException ex) {
            logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);

            // Destroy already created singletons to avoid dangling resources.
            destroyBeans();

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

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

核心代码逻辑分析

  • 首先我们发现这个方法是位于org.springframework.context.support.AbstractApplicationContext中,分别又调用了很多方法,我们只挑选一些重要方法详细说明
  • (1) 准备刷新。解析属性和校验一些必填项。
  • (2) 获取BeanFactory。看下源码org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

首先它调用了refreshBeanFactory(),方法位于org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory()

@Override
protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        DefaultListableBeanFactory beanFactory = createBeanFactory(); ---- [1]
        beanFactory.setSerializationId(getId());
        customizeBeanFactory(beanFactory); ---- [2]
        loadBeanDefinitions(beanFactory); ---- [3]
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

先判断是否已经有BeanFactory了,如果有就销毁;
然后[1]处通过createBeanFactory()来创建BeanFactory,返回的是一个DefaultListableBeanFactory

protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

然后给这个DefaultListableBeanFactory设置一个id; 接下来[2]处是一个以"customize"开头的方法,暗示是子类可以做一些定制化的实现的。 最后[3]处真正的去读取配置文件,加载、注册Bean等工作。具体由org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(DefaultListableBeanFactory beanFactory)实现。具体的过程和使用org.springframework.beans.factory.xml.XmlBeanFactory差不多,就不细说了。

然后getBeanFactory()只是做一些对beanFactory是否为空的校验。

  • (3) 对BeanFactory做一些准备。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

    // Configure the bean factory with context callbacks.
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);

    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);

    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }

    // Register default environment beans.
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

说几个重要的方法 addBeanPostProcessor(BeanPostProcessor beanPostProcessor) 为bean添加后处理器,每个bean在初始化完成前都会调用注册的BeanPostProcessor,可以对bean的内容做一些修改; 看下ApplicationContextAwareProcessor的核心源码,它实现了BeanPostProcessor接口。

@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    AccessControlContext acc = null;

    if (System.getSecurityManager() != null &&
            (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                    bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                    bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }

    if (acc != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareInterfaces(bean);
                return null;
            }
        }, acc);
    }
    else {
        invokeAwareInterfaces(bean);
    }

    return bean;
}

private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
                    new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
        }
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

看到每个bean初始化完成后,都会判断它是不是实现了Aware类的接口,然后会调用invokeAwareInterfaces(Object bean)方法,来注入相应的Aware接口对应的对象。
ignoreDependencyInterface(Class<?> ifc) 忽略依赖接口。这里看到忽略的都是一些Aware接口;
registerResolvableDependency(Class<?> dependencyType, Object autowiredValue) 如果代码中需要依赖注入dependencyType类型的变量,就会被自动装配入autowiredValue的值;
registerSingleton(String beanName, Object singletonObject) 向容器中注册单例;

  • (4) 对BeanFactory进行后处理。在AbstractApplicationContext中定义的是一个空方法,交给子类org.springframework.web.context.support.AbstractRefreshableWebApplicationContext.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)处理
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    beanFactory.ignoreDependencyInterface(ServletConfigAware.class);

    WebApplicationContextUtils.registerWebApplicationScopes(beanFactory, this.servletContext);
    WebApplicationContextUtils.registerEnvironmentBeans(beanFactory, this.servletContext, this.servletConfig);
}

又对beanFactory添加了一个后处理器。与前面的ApplicationContextAwareProcessor类似,我们打开ServletContextAwareProcessor的源码,我们看到它实现了BeanPostProcessor接口

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (getServletContext() != null && bean instanceof ServletContextAware) {
        ((ServletContextAware) bean).setServletContext(getServletContext());
    }
    if (getServletConfig() != null && bean instanceof ServletConfigAware) {
        ((ServletConfigAware) bean).setServletConfig(getServletConfig());
    }
    return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    return bean;
}

发现它就是去判断:如果Bean实现了ServletContextAware接口,就设置对ServletContext的引用;如果实现了ServletConfigAware接口,就设置对ServletConfig对象的引用;

然后忽略了这两个Aware接口。

接下来,注册WebAppliction的Scope。看源码

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, ServletContext sc) {
    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope(false));
    beanFactory.registerScope(WebApplicationContext.SCOPE_GLOBAL_SESSION, new SessionScope(true));
    if (sc != null) {
        ServletContextScope appScope = new ServletContextScope(sc);
        beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
        // Register as ServletContext attribute, for ContextCleanupListener to detect it.
        sc.setAttribute(ServletContextScope.class.getName(), appScope);
    }

    beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
    beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
    beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
    if (jsfPresent) {
        FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
    }
}

前3行注册了3个Scope,分别是request、session、global_session。然后通过ServletContextScope包装了一个应用级别的scope,在注册scope后,还放入了ServletContext中。
下面又是之前分析过的方法registerResolvableDependency(Class<?> dependencyType, Object autowiredValue),如果注入dependencyType类型的变量,就会被自动装配入autowiredValue的值。但注意,这里3个都是ObjectFactoryObjectFactory对获取对象的值又做了一个包装,真正注入的值是T ObjectFactory.getObject()。这种方式与FactoryBean<T>的机制有点像。这里只看一下RequestObjectFactory的源码

@SuppressWarnings("serial")
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {

    @Override
    public ServletRequest getObject() {
        return currentRequestAttributes().getRequest();
    }

    @Override
    public String toString() {
        return "Current HttpServletRequest";
    }
}

也就是说,如果我们代码中注入的值是ServletRequest类型的,其值是currentRequestAttributes().getRequest()的返回值。

另外补充一点,上面说会对ObjectFactory类型的自动装配值有特殊处理,这个特殊处理在哪呢?我们打开registerResolvableDependency(Class<?> dependencyType, Object autowiredValue)

this.resolvableDependencies.put(dependencyType, autowiredValue);

看到所有可解析的依赖都放到了变量resolvableDependencies中,它的类型是Map<Class<?>, Object>,那我们看它在什么时候执行了get()呢?
findAutowireCandidates(String beanName, Class<?> requiredType, DependencyDescriptor descriptor)方法中,核心代码是

Object autowiringValue = this.resolvableDependencies.get(autowiringType);
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);

再进入AutowireUtils.resolveAutowiringValue(Object autowiringValue, Class<?> requiredType)

public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
    if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
        ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
        if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
            autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
                    new Class<?>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
        }
        else {
            return factory.getObject();
        }
    }
    return autowiringValue;
}

看到对ObjectFactory的特殊处理了吧?但因为ServletRequest,包括HttpServletRequest都是接口,所以这里是会通过JDK的动态代理生成一个代理类。具体调用时的逻辑,请看ObjectFactoryDelegatingInvocationHandler的逻辑

private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {

    private final ObjectFactory<?> objectFactory;

    public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
        this.objectFactory = objectFactory;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        if (methodName.equals("equals")) {
            // Only consider equal when proxies are identical.
            return (proxy == args[0]);
        }
        else if (methodName.equals("hashCode")) {
            // Use hashCode of proxy.
            return System.identityHashCode(proxy);
        }
        else if (methodName.equals("toString")) {
            return this.objectFactory.toString();
        }
        try {
            return method.invoke(this.objectFactory.getObject(), args);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

不再详述了,看不懂的,去补一下JDK动态代理的知识。

最后,注册环境相关的Bean。这个就不细说了..

  • (5) 调用BeanFactory级的后处理器。
  • (6) 注册所有的BeanPostProcessor。
  • (7) 初始化一些国际化文件。
  • (8) 初始化应用事件组播器。源码
protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                    APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                    "': using default [" + this.applicationEventMulticaster + "]");
        }
    }
}

如果beanFactory中有,会从beanFactory中取;如果没有就用SimpleApplicationEventMulticaster,并将其以单例bean的方式注册在容器中。

  • (9) 刷新容器。只是简单的初始化了一些主题(Theme)。
  • (10) 注册应用监听器。第(8)步已经初始化了事件组播器,这里将监听器都注册好,等待事件分发。
  • (11) 完成beanFactory初始化。对所有非抽象非懒加载的bean,都执行一下getBean()方法,使bean实例缓存。见finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory)的最后一行beanFactory.preInstantiateSingletons();,就不贴源码了。
  • (12) 完成刷新。看源码
protected void finishRefresh() {
    // Initialize lifecycle processor for this context.
    initLifecycleProcessor();

    // Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();

    // Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));

    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}

重点看发布事件这一行,首先构造了一个ContextRefreshedEvent事件,事件源传入的是this,也就是当前的Spring容器。
接下来看publishEvent(ApplicationEvent event)

@Override
public void publishEvent(ApplicationEvent event) {
    Assert.notNull(event, "Event must not be null");
    if (logger.isTraceEnabled()) {
        logger.trace("Publishing event in " + getDisplayName() + ": " + event);
    }
    getApplicationEventMulticaster().multicastEvent(event);
    if (this.parent != null) {
        this.parent.publishEvent(event);
    }
}

代码很简单,利用应用组播器广播事件,如果有父容器,也向父容器中广播。事件对象中还封装了已完全初始化好的Spring容器作为其事件源。此时所有的监听了ContextRefreshedEvent事件的ApplicationListener不仅可以触发事件通知,还能从事件对象中获取到完全初始化好的Spring容器。
这里的事件机制,其实是对jdk中Event相关类的包装。

小结 由此可见,可以通过定义监听ContextRefreshedEvent事件的ApplicationListener,来在已完全初始化好Spring容器后,立刻获取到通知事件,并可以在事件中获取到相应的Spring容器对象。
小例子:

  • 定义一个ApplicationListener
package personal.wanghui.quickstart.event;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger logger = LoggerFactory.getLogger(MyApplicationListener.class);
	
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ApplicationContext applicationContext = event.getApplicationContext();
        logger.info("{}", applicationContext);
    }

}
  • MyApplicationListener配置到Spring的配置文件中
<bean id="myApplicationListener" class="personal.wanghui.quickstart.event.MyApplicationListener" />
  • 启动应用,打印出日志:
    2016-07-06 17:30:09,687 INFO personal.wanghui.quickstart.event.MyApplicationListener:20 - Root WebApplicationContext: startup date [Wed Jul 06 17:29:53 CST 2016]; root of context hierarchy
    2016-07-06 17:30:15,531 INFO personal.wanghui.quickstart.event.MyApplicationListener:20 - WebApplicationContext for namespace 'springServlet-servlet': startup date [Wed Jul 06 17:30:09 CST 2016]; parent: Root WebApplicationContext

如果项目中有SpringMVC会打印出两句;如果没有,则只会打印出第一句;如果将此myApplicationListener配置到SpringMVC的配置文件中,则只会打印出第二句。这是为什么呢?此问题我们在后续分析SpringMVC的容器时做出解答。

现在还有一个问题,为什么只要把myApplicationListener声明为Spring Bean,就可以被注册为监听器呢?Spring是在哪里注册的呢?
答案是在这里org.springframework.context.support.AbstractApplicationContext.registerListeners(),前面讲过这个方法的作用,但没有具体看源码。

protected void registerListeners() {
    // Register statically specified listeners first.
    for (ApplicationListener<?> listener : getApplicationListeners()) { ---- (1)
        getApplicationEventMulticaster().addApplicationListener(listener); ---- (2)
    }
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); ---- (3)
    for (String lisName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(lisName); ---- (4)
    }
}

第(2)和(4)代码分别都是向应用事件组播器添加应用监听器,只不过方式不同。
(1)处是从容器自己添加的应用监听器中获取所有的应用监听器,再添加给事件组播器; (3)处是从容器中根据类型找到所有实现了ApplicationListener接口的bean,再添加给事件组播器;显然我们自己在xml配置文件中配置的myApplicationListener属于此种情况,因此它可以得到正确的事件通知。

至此,Web环境下Spring容器的初始化过程就分析到这里。

contextDestroyed(ServletContextEvent event)

Web应用服务器关闭时,会调用所有ListenercontextDestroyed(ServletContextEvent event)方法,接下来看下ContextLoaderListener中的此方法。

@Override
public void contextDestroyed(ServletContextEvent event) {
    closeWebApplicationContext(event.getServletContext());
    ContextCleanupListener.cleanupAttributes(event.getServletContext());
}

代码很简单,先是关闭容器,然后是清理属性。 进入org.springframework.web.context.ContextLoader.closeWebApplicationContext(ServletContext servletContext)

public void closeWebApplicationContext(ServletContext servletContext) {
    servletContext.log("Closing Spring root WebApplicationContext");
    try {
        if (this.context instanceof ConfigurableWebApplicationContext) {
            ((ConfigurableWebApplicationContext) this.context).close(); ---- (1)
        }
    }
    finally {
        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        if (ccl == ContextLoader.class.getClassLoader()) {
            currentContext = null;
        }
        else if (ccl != null) {
            currentContextPerThread.remove(ccl); ---- (2)
        }
        servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); ---- (3)
        if (this.parentContextRef != null) {
            this.parentContextRef.release();
        }
    }
}

核心代码逻辑梳理

  • (1) 直接调用容器的close()方法
  • 还记得之前分析的吗?实例化完的容器放到了2个地方,一个是ServletContext中,一个是Map<ClassLoader, WebApplicationContext> currentContextPerThread中,(2)和(3)就是分别清理掉这2个地方的容器引用。

然后看一下org.springframework.web.context.ContextCleanupListener.cleanupAttributes(ServletContext sc)

static void cleanupAttributes(ServletContext sc) {
    Enumeration<String> attrNames = sc.getAttributeNames();
    while (attrNames.hasMoreElements()) {
        String attrName = attrNames.nextElement();
        if (attrName.startsWith("org.springframework.")) {
            Object attrValue = sc.getAttribute(attrName);
            if (attrValue instanceof DisposableBean) {
                try {
                    ((DisposableBean) attrValue).destroy();
                }
                catch (Throwable ex) {
                    logger.error("Couldn't invoke destroy method of attribute with name '" + attrName + "'", ex);
                }
            }
        }
    }
}

代码的主要功能是从ServletContext中取出所有key以org.springframework.开头的对象,如果这些对象实现了DisposableBean接口,就调用其destroy()方法。

至此,通过ContextLoaderListener初始化Spring容器的全部过程就梳理完了。

转载于:https://my.oschina.net/ojeta/blog/716549

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值