(Spring源码解析)一步一步分析,springMVC项目启动过程(一)

 springMVC项目启动过程,分析源码。

1、环境搭建,这步我就省略细节,只把我的大概环境说下:

windows 7 、jdk 8、maven-3.3.9、tomcat 8.5.11、IDEA 2017.1 x64版

 具体环境安装,我就略过,可自行google、baidu安装教程,后续有空我加上一些安装教程链接。

2、首先打开IDEA新建Maven Project,基本项目结构自动生成了,

编写pom文件,以下是我的依赖的jar:



    4.0.0

    com.mvc
    MVCProject
    1.0-SNAPSHOT
    war
    
        UTF-8
        4.3.9.RELEASE
        3.4.4
    

    
        
            org.springframework
            spring-web
            ${spring.version}
        
        
            org.springframework
            spring-webmvc
            ${spring.version}
        
        
            org.springframework
            spring-jdbc
            ${spring.version}
        
        
            org.springframework
            spring-test
            ${spring.version}
            test
        
        
        
            org.mybatis
            mybatis
            ${mybatis.version}
        
        
            org.mybatis
            mybatis-spring
            1.3.1
        
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.6.3
        
        
        
            mysql
            mysql-connector-java
            5.1.35
        
        
        
            org.apache.commons
            commons-lang3
            3.4
        
        
            commons-io
            commons-io
            2.2
        
        
            org.apache.commons
            commons-collections4
            4.1
        
        
            commons-fileupload
            commons-fileupload
            1.3.2
        
        
        
            javax.servlet.jsp
            jsp-api
            2.2
            provided
        
 
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        

        
        
            jstl
            jstl
            1.2
        
        
            taglibs
            standard
            1.1.2
        

        
        
            junit
            junit
            4.10
            test
        
        
        
            aspectj
            aspectjweaver
            1.5.4
        
        
            cglib
            cglib
            2.1_3
        
        
        
            org.apache.commons
            commons-dbcp2
            2.1.1
        
        
        
            log4j
            log4j
            1.2.17
        

        
        
            org.slf4j
            slf4j-api
            1.7.25
        

        
            redis.clients
            jedis
            2.8.1
        
        
        
            com.alibaba
            fastjson
            1.2.6
        

        
            com.google.guava
            guava
            19.0
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.1
                
                    1.8
                    1.8
                    UTF8
                
            
            
                org.apache.maven.plugins
                maven-war-plugin
                3.0.0
                
                    
                        
                            web
                        
                    
                
            
        
    

 WEB-INF目录下的web.xml配置:



    
        index.jsp
    
    
    
        webAppRootKey
        spring.study
    
    
    
        contextConfigLocation
        classpath:config/applicationContext.xml
    
    
    
        org.springframework.web.context.ContextLoaderListener
    

    
        log4jConfigLocation
        classpath*:/config/log4j.xml
    
    
    
        
            org.springframework.web.util.WebAppRootListener
        
    

    
        CharsetFilter
        org.springframework.web.filter.CharacterEncodingFilter
        
            encoding
            UTF-8
        
        
            forceEncoding
            true
        
    
    
        CharsetFilter
        /*
    

    
        springmvc
        org.springframework.web.servlet.DispatcherServlet
        1
    
    
        springmvc
        /
    

3、Spring配置加载方式

 有两种配置,一个是ContextLoaderListener 通过监听器解析配置,一个是
DispatcherServlet ,通过springMVC的servlet

解析。也可以同时配置两种,但是不建议这么做,这篇文章介绍了两种加载配置的关系,以及解释了为什么不建议同时配置。

我只配置了ContextLoaderListener,加载spring配置。以下是启动tomcat,进入ContextLoaderListener源码,这个Listener实现了ServletContextListener,和继承一个ContextLoader,实现方法contextInitialized()是在监听的servlet容器启动时调用的,

里面开始对Spring ApplicationContext容器做初始化处理,通俗点讲开始创建容器类(WebApplicationContext),把spring所有配置加载进去,所依赖的bean创建实例,默认参数等等初始化。可以debug看到event传入的是servletContextEvent对象,这是servlet启动的产生的事件对象,可以看到event获取当前应用的servletContext。


(Spring源码解析)一步一步分析,springMVC项目启动过程(一)_第1张图片

 初始化实现在父类的ContextLoader中,initWebApplicationContext()方法做了哪些事情呢,请看下面:

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
	//这里从ServletContext获取WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,这个key对应着Spring Root WebApplicationContext,
	//这说明springWeb容器依赖于ServletContext存在。这里做了判断是否已经初始化web容器了,防止多个容器初始化冲突
        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");
            }
	    //spring容器初始化开始时间
            long startTime = System.currentTimeMillis();	
            try {
		//创建WebApplicationContext
                if(this.context == null) {
		    //如果当前对象为null,则createWebApplicationContext,创建XmlWebApplicationContext初始化部分字段值
                    this.context = this.createWebApplicationContext(servletContext);
                }
		//判断context类是否实现了ConfigurableWebApplicationContext接口
                if(this.context instanceof ConfigurableWebApplicationContext) {
                    ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)this.context;
		    //判断容器是否没有激活(启动完成),底层通过AtomicBoolean(原子变量对象) 标识active
                    if(!cwac.isActive()) {
			//加载并设置父容器
                        if(cwac.getParent() == null) {
                            ApplicationContext parent = this.loadParentContext(servletContext);
                            cwac.setParent(parent);
                        }
			//整个WebApplicationContext加载配置和创建实例工厂等操作的调用的总方法
                        this.configureAndRefreshWebApplicationContext(cwac, servletContext);
                    }
                }
		//将当前context对象保存到servletContext容器中
                servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
		//获取当前线程的类加载器,绑定并引用对应的currentContext,不是ContextLoader的ClassLoader就保存到对应currentContextPerThread的Map里面
                ClassLoader ccl = Thread.currentThread().getContextClassLoader();
                if(ccl == ContextLoader.class.getClassLoader()) {
                    currentContext = this.context;
                } else if(ccl != null) {
                    currentContextPerThread.put(ccl, 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 elapsedTime = System.currentTimeMillis() - startTime;
                    logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " 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对象
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
	//获取web应用配置的ServletContext类对象
        Class contextClass = this.determineContextClass(sc);
	//这里是判断contextClass类型能否转换为此ConfigurableWebApplicationContext所表示的类型
        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);
        }
    }
	//查找并确定配置的WebApplicationContext实现类
	protected Class determineContextClass(ServletContext servletContext) {
		//获取web.xml是否配置的WebApplicationContext类
		String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
				//如果有则加载对应的实现类
				return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load custom context class [" + contextClassName + "]", ex);
			}
		}
		else {
			//如果没有则加载spring默认配置的类
			contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
			try {
				return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
			}
			catch (ClassNotFoundException ex) {
				throw new ApplicationContextException(
						"Failed to load default context class [" + contextClassName + "]", ex);
			}
		}
	}

接下来看configureAndRefreshWebApplicationContext方法里面的实现:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
	String configLocationParam;
	if(ObjectUtils.identityToString(wac).equals(wac.getId())) {
		//获取是否web.xml配置的contextId
		configLocationParam = sc.getInitParameter("contextId");
		if(configLocationParam != null) {
			wac.setId(configLocationParam);
		} else {
			//设置默认容器id为WebApplicationContext类名+“:”+项目相对路径
			wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath()));
		}
	}
//设置WebApplicationContext引用ServletContext容器
	wac.setServletContext(sc);
	//读取web.xml中contextConfigLocation配置文件路径
	configLocationParam = sc.getInitParameter("contextConfigLocation");
	if(configLocationParam != null) {
		wac.setConfigLocation(configLocationParam);
	}
//获取配置环境对象ConfigurableEnvironment,创建并获取系统相关环境参数,
//主要包含:systemEnvironment、systemProperties、JNDIProperty、servletConfigInitParams、servletContextInitParams的Property
	ConfigurableEnvironment env = wac.getEnvironment();
	if(env instanceof ConfigurableWebEnvironment) {
		//初始化PropertySources,
		((ConfigurableWebEnvironment)env).initPropertySources(sc, (ServletConfig)null);
	}
	//如果有配置自定义的web容器初始化类(需继承实现ApplicationContextInitializer接口),然后在调用这些对象的initialize方法
	//再对ConfigurableWebApplicationContext刷新(refresh)之前进行自定义添加的初始化工作,
	//一般不需要配置(注意这个过程是刷新之前,所有bean对象还没加载)
	this.customizeContext(sc, wac);
	//ConfigurableWebApplicationContext正式开始加载解析配置,实例化beanFactory加载bean等操作,这个方法实现包含容器所有需要加载
	//对象,可以说是spring初始化完成的主要方法。
	wac.refresh();
}

这部分分析了ContextLoader这个类创建容器的过程,主要是为Spring容器确定容器实现类然后创建初始化,和相关对象创建

环境变量上下文获取,ConfigLocation配置获取

下一篇我将分析AbstractApplicationContext的refresh()方法的具体实现,加载配置初始化容器。



你可能感兴趣的:(框架)