由源码理解spring初始化过程

来源:互联网 发布:淘宝回收充值卡 编辑:程序博客网 时间:2024/06/13 20:20
0.WebApplicationContext必须有web容器才能启动,常见的web容器是ServletContext实例,我们可以在web.xml配置自启动的Servlet或者定义ServletContextListener(web容器的监听器),只需要定义其中一个即可完成启动Spring的WebApplicationContext的相应工作。

1. org.springframework.web.context.ContextLoaderListener类进行容器的初始化时,该类会被Web容器(如Tomcat)自动实例化,并调用contextInitialized方法。
public void contextInitialized(ServletContextEvent event) {    this.contextLoader = this.createContextLoader();    if(this.contextLoader == null) {        this.contextLoader = this;    }    this.contextLoader.initWebApplicationContext(event.getServletContext());}
2. initWebApplicationContext继承自父类ContextLoader,进入该方法后首先判断servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) 是否为空,查看WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE对应的源码可知其表示Spring的根容器,该句判断主要是确保web容器中只有一个Spring根容器。
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!");    } else {        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 {            if(this.context == null) {                this.context = this.createWebApplicationContext(servletContext);            }            if(this.context instanceof ConfigurableWebApplicationContext) {                ConfigurableWebApplicationContext err = (ConfigurableWebApplicationContext)this.context;                if(!err.isActive()) {                    if(err.getParent() == null) {                        ApplicationContext elapsedTime = this.loadParentContext(servletContext);                        err.setParent(elapsedTime);                    }                    this.configureAndRefreshWebApplicationContext(err, servletContext);                }            }            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);            ClassLoader err1 = Thread.currentThread().getContextClassLoader();            if(err1 == ContextLoader.class.getClassLoader()) {                currentContext = this.context;            } else if(err1 != null) {                currentContextPerThread.put(err1, this.context);            }            if(logger.isDebugEnabled()) {                logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");            }            if(logger.isInfoEnabled()) {                long elapsedTime1 = System.currentTimeMillis() - startTime;                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime1 + " ms");            }            return this.context;        } catch (RuntimeException var8) {            logger.error("Context initialization failed", var8);            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var8);            throw var8;        } catch (Error var9) {            logger.error("Context initialization failed", var9);            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, var9);            throw var9;        }    }}

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE对应的源码,如下:
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
紧接着调用createWebApplicationContext(servletContext)方法,很显然这个方法是用来创建web容器的;
3.createWebApplicationContext首先调用determineContextClass方法,用来确定Context的具体实现类;
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {    Class contextClass = this.determineContextClass(sc);    if(!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {        throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");    } else {        return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);    }}
4.determineContextClass方法根据web.xml初始化参数来确定Context的具体实现类
protected Class<?> determineContextClass(ServletContext servletContext) {    String contextClassName = servletContext.getInitParameter("contextClass");    if(contextClassName != null) {        try {            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());        } catch (ClassNotFoundException var4) {            throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", var4);        }    } else {        contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());        try {            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());        } catch (ClassNotFoundException var5) {            throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", var5);        }    }}

5.当前类(ContextLoader)中,查看defaultStrategies.getProperty(WebApplicationContext.class.getName())获取的默认Context实现类信息,如下
static {    try {        ClassPathResource ex = new ClassPathResource("ContextLoader.properties", ContextLoader.class);        defaultStrategies = PropertiesLoaderUtils.loadProperties(ex);    } catch (IOException var1) {        throw new IllegalStateException("Could not load \'ContextLoader.properties\': " + var1.getMessage());    }    currentContextPerThread = new ConcurrentHashMap(1);}

可知spring是通过ContextLoader.properties文件来找到相应的实现类信息,打开ContextLoader.properties文件,发现spring默认的Context实现类是org.springframework.web.context.support.XmlWebApplicationContext
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
6.回到initWebApplicationContext方法,在createWebApplicationContext方法后,会执行if(this.context instanceof ConfigurableWebApplicationContext)判断,由于Context默认返回类型为ConfigurableWebApplicationContext,由于此时尚未初始化,程序会进入loadParentContext这个方法,名字也很清楚明白,这个方法是用来加载父类发上下文对象。然后,执行configureAndRefreshWebApplicationContext方法,主要用于刷新上下文。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {    String initParameter;    if(ObjectUtils.identityToString(wac).equals(wac.getId())) {        initParameter = sc.getInitParameter("contextId");        if(initParameter != null) {            wac.setId(initParameter);        } else if(sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) {            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getServletContextName()));        } else {            wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));        }    }    wac.setServletContext(sc);    initParameter = sc.getInitParameter("contextConfigLocation");    if(initParameter != null) {        wac.setConfigLocation(initParameter);    }    this.customizeContext(sc, wac);    wac.refresh();}
在该方法中:
①根据Context实现类的名称设置上下文的名称;
public static String identityToString(Object obj) {    return obj == null?"":obj.getClass().getName() + "@" + getIdentityHexString(obj);}

this.customizeContext(sc,wac)主要用于自定义初始化
wac.refresh()加载xml配置,该方法用于设置代理工厂类、生成beanFactory、实例化类、向web容器注册beanFactory等等操作,其中根据web.xml文件中contextConfigLocation配置信息实例化类是重中之重。
public void refresh() throws BeansException, IllegalStateException {    Object var1 = this.startupShutdownMonitor;    synchronized(this.startupShutdownMonitor) {        this.prepareRefresh();        ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();        this.prepareBeanFactory(beanFactory);        try {            this.postProcessBeanFactory(beanFactory);            this.invokeBeanFactoryPostProcessors(beanFactory);            this.registerBeanPostProcessors(beanFactory);            this.initMessageSource();            this.initApplicationEventMulticaster();            this.onRefresh();            this.registerListeners();            this.finishBeanFactoryInitialization(beanFactory);            this.finishRefresh();        } catch (BeansException var5) {            this.destroyBeans();            this.cancelRefresh(var5);            throw var5;        }    }}
web.xml文件中contextConfigLocation的配置信息
<context-param>    <param-name>contextConfigLocation</param-name>    <param-value>        classpath*:applicationContext.xml    </param-value></context-param>

1 0
原创粉丝点击