javascript
spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程...
它既是 DispatcherServlet 的 (WebApplicationContext)默認(rèn)策略,又是 ContextLoaderListener 創(chuàng)建 root WebApplicationContext(根容器,同時(shí)也是 DispatcherServlet 的 WebApplicationContext 的父容器)的默認(rèn)策略。
繼承體系
一、XmlWebApplicationContext實(shí)例化過(guò)程
spring的配置文件加載是以監(jiān)聽的方式加載的xml配置文件
spring-web-4.3.14.RELEASE.jar中的org.springframework.web.context.ContextLoader.java類,通過(guò)ContextLoader初始化和銷毀Spring Web上下文的過(guò)程。
1、ContextLoader類中有一個(gè)靜態(tài)代碼塊,這個(gè)靜態(tài)代碼塊就是從配置中讀取到“XmlWebApplicationContext”類
"contextLoader.properties"文件就在ContextLoader.class的相同目錄中,
contextLoader.properties:(配置中配置的就是XmlWebApplicationContext)
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext2、上面只是將配置讀取到ContextLoader中,下面看看XmlWebApplicationContext怎么初始化的,ContextLoader.initWebApplicationContext方法:
org.springframework.web.context.ContextLoader.java
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {//檢查是否已經(jīng)創(chuàng)建了Application context,如果已經(jī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!"); } 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 { // Store context in local instance variable, to guarantee that // it is available on ServletContext shutdown. if (this.context == null) { //調(diào)用createWebApplicationContext,創(chuàng)建XmlWebApplicationContext, this.context = createWebApplicationContext(servletContext); } // 如果當(dāng)前的應(yīng)用上下文對(duì)象是 ConfigurableWebApplicationContext if (this.context instanceof ConfigurableWebApplicationContext) { //強(qiáng)制類型轉(zhuǎn)換 ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // 如果應(yīng)用上下文沒有生效 if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc 如果該上下文對(duì)象為nul if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> // determine parent for root web application context, if any. //加載父上下文 ApplicationContext parent = loadParentContext(servletContext); // 設(shè)置父上下文 cwac.setParent(parent); } configureAndRefreshWebApplicationContext(cwac, servletContext); } } //將該上下文對(duì)象放入servlet上下文參數(shù)中servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); //獲取當(dāng)前線程的類加載器 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); // 如果ContextLoader的類加載器和當(dāng)前線程的類加載器一樣,則應(yīng)用上下文對(duì)象賦值給currentContext if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } //否則,就將ContextLoader的類加載器放入到Map中,Map的value是應(yīng)用上下文對(duì)象 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"); } //最后返回應(yīng)用上下文對(duì)象 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; } }
在ContextLoader.createWebApplicationContext方法中
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {//獲取上下文類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() + "]");}// 返回該上下文類的實(shí)例,調(diào)用BeanUtils.instantiateClass(contextClass),通過(guò)反射,調(diào)用XmlWebApplicationContext的無(wú)參構(gòu)造函數(shù)實(shí)例化XmlWebApplicationContext對(duì)象return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);}----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------
這里插入BeanUtils.instantiateClass(),BeanUtils使用instantiateClass初始化對(duì)象注意:必須保證初始化類必須有public默認(rèn)無(wú)參數(shù)構(gòu)造器,注意初始化內(nèi)部類時(shí),內(nèi)部類必須是靜態(tài)的,否則報(bào)錯(cuò)!
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {Assert.notNull(clazz, "Class must not be null");if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {return instantiateClass(clazz.getDeclaredConstructor());}catch (NoSuchMethodException ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {Assert.notNull(ctor, "Constructor must not be null");try {ReflectionUtils.makeAccessible(ctor);return ctor.newInstance(args);}//... }@CallerSensitivepublic T newInstance(Object ... initargs)throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException{if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Reflection.getCallerClass();checkAccess(caller, clazz, null, modifiers);}}if ((clazz.getModifiers() & Modifier.ENUM) != 0)throw new IllegalArgumentException("Cannot reflectively create enum objects");ConstructorAccessor ca = constructorAccessor; // read volatileif (ca == null) {ca = acquireConstructorAccessor();}@SuppressWarnings("unchecked")T inst = (T) ca.newInstance(initargs);return inst;}----------------------------BeanUtils.instantiateClass()-----------------------------------------------------------------------------------------------------------------------
ContextLoader.java中的determineContextClass()方法:
/*** 返回上下文類型*/protected Class<?> determineContextClass(ServletContext servletContext) {//從servlet上下文中獲取初始化配置參數(shù)contextClass的值String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);// 如果contextClassName不為null則放回配置的Class對(duì)象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 {// 如果沒有配置則使用XmlWebApplicationContext,這個(gè)代碼就是從contextLoad.properties配置中加載進(jìn)來(lái)的,配置中的就是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);}}}在Spring web項(xiàng)目中XmlWebApplicationContext是如何創(chuàng)建的?
首先在web.xml中我們可以看到如下配置:
<context-param><param-name>contextConfigLocation</param-name><param-value>classpath*:META-INF/spring/*.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>ContextLoaderListener繼承Spring的ContextLoader上下文加載器類,同時(shí)實(shí)現(xiàn)ServletContextListener接口(Servlet上下文監(jiān)聽器),監(jiān)聽Web服務(wù)器上下文的啟動(dòng)和停止事件,管理Web環(huán)境中Spring的啟動(dòng)和銷毀過(guò)程,
首先我們看看這個(gè)監(jiān)聽器的源碼。初始化的入口是contextInitialized方法,它只是簡(jiǎn)單地將初始化功能委托為了ContextLoader進(jìn)行處理。
org.springframework.web.context.ContextLoaderListener.java
/*** Initialize the root web application context.初始化根WEB應(yīng)用上下文*/@Overridepublic void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext()); //調(diào)用ContextLoader的initWebApplicationContext()}通過(guò)對(duì)ContextLoaderListener的源碼分析,我們看到ContextLoaderListener繼承ContextLoader,所以ContextLoaderListener本身也是Spring的上下文加載器。
ContextLoaderListener實(shí)現(xiàn)了ServletContextListener接口,當(dāng)Web應(yīng)用在Web服務(wù)器中被被啟動(dòng)和停止時(shí),Web服務(wù)器啟動(dòng)和停止事件會(huì)分別觸發(fā)ContextLoaderListener的contextInitialized和contextDestroyed方法來(lái)初始化和銷毀Spring上下文。我們通過(guò)上述對(duì)ContextLoaderListener的源碼分析看到真正實(shí)現(xiàn)Spring上下文的初始化和銷毀功能的是ContextLoader類,分析ContextLoader初始化和銷毀Spring Web上下文的過(guò)程見上面。
?ContextLoader的initWebApplicationContext()的源碼見上面的分析。
在springmvc中,如何實(shí)例化XmlWebApplicationContext的?
1、springmvc加載配置文件
org.springframework.web.servlet.DispatcherServlet是通過(guò)這個(gè)servlet,加載配置文件
FrameworkServlet中有一個(gè)屬性
接著看FrameworkServlet的initWebApplicationContext()方法:
protected WebApplicationContext initWebApplicationContext() { .....if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = createWebApplicationContext(rootContext);} }?方法中有一個(gè)FrameworkServlet.createWebApplicationContext(rootContext)方法
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {//這個(gè)方法就是創(chuàng)建XmlWebApplicationContext實(shí)例的ClassClass<?> contextClass = getContextClass();if (this.logger.isDebugEnabled()) {this.logger.debug("Servlet with name '" + getServletName() +"' will try to create custom WebApplicationContext context of class '" +contextClass.getName() + "'" + ", using parent context [" + parent + "]");}//如果該上下文類沒有實(shí)現(xiàn)ConfigurableWebApplicationContext接口則拋出異常 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Fatal initialization error in servlet with name '" + getServletName() +"': custom WebApplicationContext class [" + contextClass.getName() +"] is not of type ConfigurableWebApplicationContext");}
//調(diào)用BeanUtils.instantiateClass(contextClass),通過(guò)反射,調(diào)用XmlWebApplicationContext的無(wú)參構(gòu)造函數(shù)實(shí)例化XmlWebApplicationContext對(duì)象ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);wac.setConfigLocation(getContextConfigLocation());configureAndRefreshWebApplicationContext(wac);return wac; }
接口看getContextClass()方法:
Class<?> contextClass = getContextClass();這個(gè)方法就是創(chuàng)建XmlWebApplicationContext實(shí)例的Class
public Class<?> getContextClass() {return this.contextClass;}?this.contextClass就是前面FrameworkServlet定義的全局變量。
至此,實(shí)例化XmlWebApplicationContext的步驟基本相同:
1、通過(guò)讀取配置文件方式,讀取到org.springframework.web.context.WebApplicationContext的類型為“org.springframework.web.context.support.XmlWebApplicationContext”;
2、檢查上下文類沒有實(shí)現(xiàn)ConfigurableWebApplicationContext接口則拋出異常;
3、調(diào)用BeanUtils.instantiateClass(contextClass),通過(guò)反射,調(diào)用XmlWebApplicationContext的無(wú)參構(gòu)造函數(shù)實(shí)例化XmlWebApplicationContext對(duì)象;
二、XmlWebApplicationContext源碼
ContextLoader初始化Spring Web上下文的determineContextClass方法中,我們知道Spring首先通過(guò)Servlet上下文從web.xml文件中獲取用戶自定義配置的contextClass參數(shù)值,如果沒有獲取到,則默認(rèn)使用Spring的XmlWebApplicationContext作為Spring Web應(yīng)用的IoC容器,XmlWebApplicationContext是WebApplicationContext的實(shí)現(xiàn)類ConfigurableWebApplicationContext的子類
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {//Web應(yīng)用中Spring配置文件的默認(rèn)位置和名稱,如果沒有特別指定,則Spring會(huì)根據(jù)//此位置定義Spring Bean定義資源public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";//Spring Bean定義資源默認(rèn)前綴public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";//Spring Bean定義資源默認(rèn)后置public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";//在分析Spring IoC初始化過(guò)程中我們已經(jīng)分析過(guò),加載Spring Bean定義資源的方法,//通過(guò)Spring容器刷新的refresh()方法觸發(fā)protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {//為Spring容器創(chuàng)建XML Bean定義讀取器,加載Spring Bean定義資源XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// resource loading environment. beanDefinitionReader.setEnvironment(getEnvironment());//設(shè)置Bean定義讀取器,因?yàn)閄mlWebApplicationContext是DefaultResourceLoader的子類,所以使用默認(rèn)資源加載器來(lái)定義Bean定義資源beanDefinitionReader.setResourceLoader(this);//為Bean定義讀取器設(shè)置SAX實(shí)體解析器beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));//在加載Bean定義之前,調(diào)用子類提供的一些用戶自定義初始化Bean定義讀取器的方法 initBeanDefinitionReader(beanDefinitionReader);//使用Bean定義讀取器加載Bean定義資源 loadBeanDefinitions(beanDefinitionReader);}//用戶自定義初始化Bean定義讀取器的方法protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {}//加載Bean定義資源protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {//獲取定位的Bean定義資源路徑String[] configLocations = getConfigLocations();if (configLocations != null) {//遍歷加載所有定義的Bean定義資源for (String configLocation : configLocations) {reader.loadBeanDefinitions(configLocation);}}}//獲取默認(rèn)Bean定義資源protected String[] getDefaultConfigLocations() {//獲取web.xml中的命名空間,如命名空間不為null,則返回 “/WEB-INF/命名空間.xml”if (getNamespace() != null) {return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};}//如果命名空間為null,則返回"/WEB-INF/applicationContext.xml"else {return new String[] {DEFAULT_CONFIG_LOCATION};}} }XmlWebApplicationContext將Web應(yīng)用中配置的Spring Bean定義資源文件載入到Spring IoC容器中后,接下來(lái)的Spring IoC容器初始化和依賴注入的過(guò)程后面再分析。
?
轉(zhuǎn)載于:https://www.cnblogs.com/duanxz/p/3507449.html
總結(jié)
以上是生活随笔為你收集整理的spring之:XmlWebApplicationContext作为Spring Web应用的IoC容器,实例化和加载Bean的过程...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 密码破解—Hashcat
- 下一篇: 判断字符串中有多少汉字