zoukankan      html  css  js  c++  java
  • Spring启动流程

    spring启动是建立在servlet容器上的,web工程的初始都是在web.xml

    <!-- Spring资源上下文定义,在指定地址找到Spring的xml配置文件 -->
    <context-param>
    	<param-name>contextConfigLocation</param-name>
    	<param-value>classpath*:/spring-context*.xml</param-value>
    </context-param>
    <!-- 1.Spring的上下文监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
    </listener>
    
    <!-- 2.SpringMVC的前端控制器 -->
    <servlet>
    	<servlet-name>springServlet</servlet-name>
    	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    	<init-param>
    		<param-name>contextConfigLocation</param-name>
    		<param-value>classpath*:/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>
    

    1.Spring的上下文监听器

    进入 ContextLoaderListener 可以看到他会去调用 initWebApplicationContext

    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 {
            // 1.创建WebApplicationContext
    		if (this.context == null) {
    			this.context = createWebApplicationContext(servletContext);
    		}
    		if (this.context instanceof ConfigurableWebApplicationContext) {
    			ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
    			if (!cwac.isActive()) {
    				if (cwac.getParent() == null) {
    					ApplicationContext parent = loadParentContext(servletContext);
    					cwac.setParent(parent);
    				}
                    // 2.加载spring配置文件中的Bean,这里会去扫描配置中contextConfigLocation的路径文件加载
    				configureAndRefreshWebApplicationContext(cwac, servletContext);
    			}
    		}
            // 3.把WebApplicationContext放入ServletContext(Java Web的全局变量)中
    		ServletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
    		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 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;
    	}
    }
    

    2.SpringMVC的前端控制器

    加载 DispatcherServlet 时,根据父类 FrameworkServlet->HttpServletBean->HttpServlet->GenericServlet 的关系,web容器启动会去调用 HttpServletBean 的 init 方法,这个方法覆盖了 GenericServlet 中的 init 方法。

    @Override
    public final void init() throws ServletException {
    	if (logger.isDebugEnabled()) {
    		logger.debug("Initializing servlet '" + getServletName() + "'");
    	}
    	// 1.如下代码的作用是将Servlet初始化参数设置到该组件上
    	PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    	if (!pvs.isEmpty()) {
    		try {
    			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
    			ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
    			bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
    			initBeanWrapper(bw);
    			bw.setPropertyValues(pvs, true);
    		}
    		catch (BeansException ex) {
    			if (logger.isErrorEnabled()) {
    				logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
    			}
    			throw ex;
    		}
    	}
    	// 2.提供给子类初始化的扩展点,该方法由FrameworkServlet覆盖
    	initServletBean();
    	if (logger.isDebugEnabled()) {
    		logger.debug("Servlet '" + getServletName() + "' configured successfully");
    	}
    }
    

    FrameworkServletinitServletBean内调用 initWebApplicationContext 初始化web上下文,其中核心方法是 onRefresh()

    protected WebApplicationContext initWebApplicationContext() {
    	//ROOT上下文(ContextLoaderListener加载的)
       WebApplicationContext rootContext =
    		  WebApplicationContextUtils.getWebApplicationContext(getServletContext());
       WebApplicationContext wac = null;
       if (this.webApplicationContext != null) {
    	   // 1、在创建该Servlet注入的上下文
    	   wac = this.webApplicationContext;
    	   if (wac instanceof ConfigurableWebApplicationContext) {
    		  ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
    		  if (!cwac.isActive()) {
    			  if (cwac.getParent() == null) {
    				  cwac.setParent(rootContext);
    			  }
    			  configureAndRefreshWebApplicationContext(cwac);
    		  }
    	   }
       }
       if (wac == null) {
    		 //2、查找已经绑定的上下文
    	   wac = findWebApplicationContext();
       }
       if (wac == null) {
    		//3、如果没有找到相应的上下文,并指定父亲为ContextLoaderListener
    	   wac = createWebApplicationContext(rootContext);
       }
       if (!this.refreshEventReceived) {
    		 //4、刷新上下文(执行一些初始化)
    	   onRefresh(wac);
       }
       if (this.publishContext) {
    	   // Publish the context as a servlet context attribute.
    	   String attrName = getServletContextAttributeName();
    	   getServletContext().setAttribute(attrName, wac);
    	   //省略部分代码
       }
       return wac;
    }
    

    onRefresh() 提供一些前端控制器相关的配置

    public class DispatcherServlet extends FrameworkServlet {
         //实现子类的onRefresh()方法,该方法委托为initStrategies()方法。
        @Override
        protected void onRefresh(ApplicationContext context) {
           initStrategies(context);
        }
    
        //初始化默认的Spring Web MVC框架使用的策略(如HandlerMapping)
        protected viod initStrategies(ApplicationContext context){
    		initMultipartResolver(context);//初始化上传文件解析器
    		initLocaleResolver(context);//初始化本地解析器
    		initThemeResolver(context);//初始化主题解析器
    		initHandlerMapping(context);
    		//初始化处理器映射器,将请求映射到处理器
    		initHandlerAdapters(context);//初始化处理器适配器
    		initHandlerExceptionResolver(context);
    		//初始化处理器异常解析器,如果执行过程中遇到异常将交给HandlerExceptionResolver来解析
    		initRequestToViewNameTranslator(context);
    		//初始化请求到具体视图名称解析器
    		initViewResolvers(context);
    		//初始化视图解析器,通过ViewResolver解析逻辑视图名到具体视图实现
    		initFlshMapManager(context);//初始化flash映射管理
    	}
    }
    

    参考文章:https://www.jianshu.com/p/280c7e720d0c

  • 相关阅读:
    Luckysheet如何初始化含合并单元格的数据
    Luckysheet如何一键导入本地Excel
    又发现一款纯js开源电子表格Luckysheet
    Sublime Text3 注册码(Windows/Build 3176版本)| 开发工具
    Python初学者笔记(4)-简单的通讯录
    Python初学者笔记(3):输出列表中的奇数/奇数项,字符串中的偶数项,字符串大小写转换
    安装wampserver出现“The Program can't start because MSVCR110.dll is missing from your computer. Try reinstalling the program to fix this problem”
    python初学者笔记(2):阿拉伯数字转换成中文大写
    Python初学者笔记:打印出斐波那契数列的前10项
    JavaScript弹出对话框的三种方式
  • 原文地址:https://www.cnblogs.com/nicori/p/14340014.html
Copyright © 2011-2022 走看看