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

 2019-11-02 21:06  阅读(855)
文章分类:Spring boot

### ### ## 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 warUTF-8 4.3.9.RELEASE 3.4.4org.springframework spring-web ${spring.version}org.springframework spring-webmvc ${spring.version}org.springframework spring-jdbc ${spring.version}org.springframework spring-test ${spring.version} testorg.mybatis mybatis ${mybatis.version}org.mybatis mybatis-spring 1.3.1com.fasterxml.jackson.core jackson-databind 2.6.3mysql mysql-connector-java 5.1.35org.apache.commons commons-lang3 3.4commons-io commons-io 2.2org.apache.commons commons-collections4 4.1commons-fileupload commons-fileupload 1.3.2javax.servlet.jsp jsp-api 2.2 providedjavax.servlet javax.servlet-api 3.1.0 providedjstl jstl 1.2taglibs standard 1.1.2junit junit 4.10 testaspectj aspectjweaver 1.5.4cglib cglib 2.1_3org.apache.commons commons-dbcp2 2.1.1log4j log4j 1.2.17org.slf4j slf4j-api 1.7.25redis.clients jedis 2.8.1com.alibaba fastjson 1.2.6com.google.guava guava 19.0org.apache.maven.plugins maven-compiler-plugin 3.11.8 1.8 UTF8org.apache.maven.plugins maven-war-plugin 3.0.0web``` WEB-INF目录下的web.xml配置: ```index.jspwebAppRootKey spring.studycontextConfigLocation classpath:config/applicationContext.xmlorg.springframework.web.context.ContextLoaderListenerlog4jConfigLocation classpath*:/config/log4j.xmlorg.springframework.web.util.WebAppRootListenerCharsetFilter org.springframework.web.filter.CharacterEncodingFilterencoding UTF-8forceEncoding trueCharsetFilter /*springmvc org.springframework.web.servlet.DispatcherServlet 1springmvc /``` 3、Spring配置加载方式 有两种配置,一个是ContextLoaderListener 通过监听器解析配置,一个是 DispatcherServlet ,通过springMVC的servlet 解析。也可以同时配置两种,但是不建议这么做,这篇文章介绍了[两种加载配置的关系][Link 1],以及解释了为什么不建议同时配置。 我只配置了ContextLoaderListener,加载spring配置。以下是启动tomcat,进入ContextLoaderListener源码,这个Listener实现了ServletContextListener,和继承一个ContextLoader,实现方法contextInitialized()是在监听的servlet容器启动时调用的, 里面开始对Spring ApplicationContext容器做初始化处理,通俗点讲开始创建容器类(WebApplicationContext),把spring所有配置加载进去,所依赖的bean创建实例,默认参数等等初始化。可以debug看到event传入的是servletContextEvent对象,这是servlet启动的产生的事件对象,可以看到event获取当前应用的servletContext。 ![《(Spring源码解析)一步一步分析,springMVC项目启动过程(一)》][Spring_springMVC] ![20191102100569\_1.png][20191102100569_1.png] 初始化实现在父类的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()方法的具体实现,加载配置初始化容器。  [Link 1]: https://www.cnblogs.com/caryfang/p/5675178.html [Spring_springMVC]: [20191102100569_1.png]: https://gitee.com/chenssy/blog-home/raw/master/image/series-images/springboot/20191102100569_1.png ------- 来源:[http://ddrv.cn](http://ddrv.cn)

点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> (Spring源码解析)一步一步分析,springMVC项目启动过程(一)

相关推荐