博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringMVC源码阅读笔记-ContextLoaderListener
阅读量:5817 次
发布时间:2019-06-18

本文共 37380 字,大约阅读时间需要 124 分钟。

  hot3.png

原文链接:

基于

配置SpringMVC有2个核心类,一个Listener,一个Servlet。

配置示例如下

contextConfigLocation
classpath*:applicationContext.xml classpath*:applicationContext-*.xml
org.springframework.web.context.ContextLoaderListener
springServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
/WEB-INF/spring-mvc.xml
1
springServlet
/

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

ContextLoaderListener

先看ContextLoaderListener的继承结构

image

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

contextInitialized(ServletContextEvent event)

在ContextLoaderListener中找到

@Overridepublic 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",恰好对应配置文件中的
contextConfigLocation
classpath*:applicationContext.xml classpath*:applicationContext-*.xml

说明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
>> initializerClasses = determineContextInitializerClasses(sc); ---- (1) if (initializerClasses.isEmpty()) { // no ApplicationContextInitializers have been declared -> nothing to do return; } ArrayList
> initializerInstances = new ArrayList
>(); for (Class
> 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
initializer : initializerInstances) { initializer.initialize(wac); (4) }}

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

  • (1) 获取所有上下文初始化的类。determineContextInitializerClasses的源码为
protected List
>> determineContextInitializerClasses(ServletContext servletContext) { List
>> classes = new ArrayList
>>(); 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)
@Overrideprotected 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
{ void initialize(C applicationContext);}

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

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

接下来,进入源码

@Overridepublic 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()

@Overrideprotected 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接口。

@Overridepublic 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() {            @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)处理
@Overrideprotected 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接口

@Overridepublic 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;}@Overridepublic 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
, 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)

@Overridepublic 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
{ private static final Logger logger = LoggerFactory.getLogger(MyApplicationListener.class); @Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext applicationContext = event.getApplicationContext(); logger.info("{}", applicationContext); }}
  • MyApplicationListener配置到Spring的配置文件中
  • 启动应用,打印出日志:
    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中的此方法。

@Overridepublic 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
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

你可能感兴趣的文章
数据库基础
查看>>
表格排序
查看>>
关于Android四大组件的学习总结
查看>>
java只能的round,ceil,floor方法的使用
查看>>
由于无法创建应用程序域,因此未能执行请求。错误: 0x80070002 系统找不到指定的文件...
查看>>
新开的博客,为自己祝贺一下
查看>>
puppet任务计划
查看>>
【CQOI2011】放棋子
查看>>
采用JXL包进行EXCEL数据写入操作
查看>>
一周总结
查看>>
将txt文件转化为json进行操作
查看>>
线性表4 - 数据结构和算法09
查看>>
C语言数据类型char
查看>>
Online Patching--EBS R12.2最大的改进
查看>>
Binary Search Tree Iterator leetcode
查看>>
Oracle性能优化--DBMS_PROFILER
查看>>
uva-317-找规律
查看>>
Event事件的兼容性(转)
查看>>
我的2014-相对奢侈的生活
查看>>
zoj 2412 dfs 求连通分量的个数
查看>>