spring启动之xml
本系列源碼都是基于spring-4.3.8版本之上,其他版本略有差異,但總體的核心思想相同。同時(shí),為了使貼出的源代碼盡可能的緊湊,可能會(huì)刪去一些異常捕獲、日志輸出等代碼。若文中存在紕漏錯(cuò)誤,歡迎指正。
?
如上圖ContextLoaderListener繼承自ContextLoader又實(shí)現(xiàn)了ServletContextListener,
前者用于初始化Spring容器,后者用于在web容器初始化時(shí)通知自己。
由于ContextLoaderListener實(shí)現(xiàn)了ServletContextListener,當(dāng)web容器初始化時(shí),會(huì)調(diào)用ServletContextListener#contextInitialized,開(kāi)始初始化servlet上下文監(jiān)聽(tīng)器,ContextLoaderListener就是其中一個(gè)。
public void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());}而ContextLoaderListener通過(guò)調(diào)用父類ContextLoader#initWebApplicationContext方法展開(kāi)了Spring初始化之路。
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {// 當(dāng)前servletContext若已存在上下文,則說(shuō)明已開(kāi)始啟動(dòng),則拋出異常,終止spring容器啟動(dòng)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!");}// 判斷當(dāng)前上下文是否存在,不存在,則進(jìn)行創(chuàng)建if (this.context == null) {this.context = createWebApplicationContext(servletContext);}// 若沒(méi)有進(jìn)行過(guò)特殊配置,那么該context的類型默認(rèn)為XmlWebApplicationContext,具體可見(jiàn)createWebApplicationContext// 且context的類型必然是ConfigurableWebApplicationContext的子類if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {if (cwac.getParent() == null) {ApplicationContext parent = loadParentContext(servletContext);cwac.setParent(parent);}// 該方法為spring容器真正的初始化入口configureAndRefreshWebApplicationContext(cwac, servletContext);}}// 將上下文設(shè)置到servletContext中,用于給其他容器獲取,如SpringMvc的初始化Servlet就是通過(guò)該方法獲取到父容器的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);}return this.context;} protected WebApplicationContext createWebApplicationContext(ServletContext sc) {// 創(chuàng)建context的第一步,即需要確定該context的類型Class<?> contextClass = determineContextClass(sc);// 不論其最終是何類型,其必須實(shí)現(xiàn)ConfigurableWebApplicationContext接口,否則直接拋出異常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);} protected Class<?> determineContextClass(ServletContext servletContext) {// 默認(rèn)從servletContext中獲取初始化參數(shù),即我們web.xml中可配置的contextClass中獲取類的全限定名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 {// 若沒(méi)有獲取到,則會(huì)以默認(rèn)的策略,從本類的同級(jí)目錄下以WebApplicationContext全限定名為key讀取ContextLoader.properties的配置// 最終獲取到的即org.springframework.web.context.support.XmlWebApplicationContextcontextClassName = 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);}}} protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// 從web.xml中獲取初始化參數(shù)contextId,作為當(dāng)前容器的唯一IDString idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}// 將當(dāng)前ServletContext設(shè)置到當(dāng)前容器中wac.setServletContext(sc);// 從web.xml中讀取初始化參數(shù)contextConfigLocation,讀取出的配置文件路徑稍后將逐個(gè)加載String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {// 將配置文件路徑設(shè)置到本容器中wac.setConfigLocation(configLocationParam);}ConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}// 個(gè)性化啟動(dòng)上下文customizeContext(sc, wac);/*** 容器加載的核心方法,* 不論是以注解方式啟動(dòng)的AnnotationConfigWebApplicationContext* 還是spring-boot的啟動(dòng)類SpringApplication* 最終都需要調(diào)用該方法,即AbstractApplicationContext#refresh*/wac.refresh();} protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {/*** 決定上下文需要初始化的類* 從<init-param>中獲取"globalInitializerClasses"、"contextInitializerClasses"*/List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =determineContextInitializerClasses(sc);for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {Class<?> initializerContextClass =GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {throw new ApplicationContextException(String.format("Could not apply 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()));}this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));}// 排序并逐個(gè)初始化ApplicationContextInitializerAnnotationAwareOrderComparator.sort(this.contextInitializers);for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {initializer.initialize(wac);}}由于AbstractApplicationContext#refresh方法太長(zhǎng),限于本篇篇幅,該方法的細(xì)節(jié)將在之后進(jìn)行源碼分析。
總結(jié)
以上是生活随笔為你收集整理的spring启动之xml的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: EssentialPIM Pro 工具使
- 下一篇: 全网最优质的H3C设备命令大全