原文連接:https://blog.csdn.net/qw222pzx/article/details/78191670html
spring的應用初始化流程一直沒有搞明白,剛剛又碰到了相關的問題。決定得好好看看這個流程。咱們在開發spring的項目當中基本上都會在web.xml經過:java
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/conf/application-*.xml </param-value> </context-param>
來初始化各個spring的配置文件,可是咱們只是知道這段代碼的功能, 並非很清楚咱們配置了這段代碼以後爲何就能去初始化配置文件。固然咱們還會加上:web
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
這一個listener,我首先就會想contextConfigLocation這個必定能在ContextLoaderListener這個類當中找到,打開了源碼,這個listener是實現了ServletContextListener這個接口的,這個接口只有兩個方法:spring
public interface ServletContextListener extends EventListener { public abstract void contextInitialized(ServletContextEvent servletcontextevent); public abstract void contextDestroyed(ServletContextEvent servletcontextevent); }
並且它是繼承了EventListener這個接口的,打開這個接口的代碼讓我大吃一驚,裏面沒有方法啥都沒有:api
package java.util; public interface EventListener { }
並且仍是java.util包下的,並非spring之中的東西。app
這樣找了以後沒有找到,往回退到ContextLoaderListener這個類的方法上,contextInitialized方法是用來初始化上下文的:this
public void contextInitialized(ServletContextEvent event) { contextLoader = createContextLoader(); contextLoader.initWebApplicationContext(event.getServletContext()); }
方法中有個createContextLoader方法:spa
protected ContextLoader createContextLoader() { return new ContextLoader(); }
這個方法返回了一個ContextLoader實例,進入到ContextLoader類中,按ctrl+f來尋找contextConfigLocation,這時沒有出現電腦的咚的聲音,找到了它:.net
protected WebApplicationContext createWebApplicationContext(ServletContext servletContext, ApplicationContext parent) throws BeansException { Class contextClass = determineContextClass(servletContext); if(!(org.springframework.web.context.ConfigurableWebApplicationContext.class).isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + (org.springframework.web.context.ConfigurableWebApplicationContext.class).getName() + "]"); } else { ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass); wac.setParent(parent); wac.setServletContext(servletContext); wac.setConfigLocation(servletContext.getInitParameter("<span style="color:#ff0000;">contextConfigLocation</span>")); customizeContext(servletContext, wac); wac.refresh(); return wac; } }
經過代碼,ConfigurableWebApplicationContext設置了從servletContext獲取到的參數的值,再進入ConfigurableWebApplicationContext的代碼中,它只是一個接口,進入StaticWebApplicationContext的setConfigLocation方法:code
public void setConfigLocation(String configLocation) { if(configLocation != null) throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations"); else return; }
這個方法中很奇怪,當參數不爲空就拋出異常,查看spring的文檔:The StaticWebApplicationContext
class does not support this method.說是此類不支持這個方法,這下子又卡住了。又要退回去,看這句:
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
pring使用BeanUtils來初始化contextClass這個類實例,contextClass是經過如下代碼獲得的:
protected Class determineContextClass(ServletContext servletContext) throws ApplicationContextException { String contextClassName = servletContext.getInitParameter("contextClass"); if(contextClassName != null) try { return ClassUtils.forName(contextClassName); } catch(ClassNotFoundException ex) { throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex); } contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName()); try { return ClassUtils.forName(contextClassName, (org.springframework.web.context.ContextLoader.class).getClassLoader()); } catch(ClassNotFoundException ex) { throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex); } }
這裏使用了反射,再來看BeanUtils的instantiateClass方法:
return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null);
經過反射獲得contextClass的構造方法。下面是instantiateClass方法的重載,主要是下面兩句代碼:
ReflectionUtils.makeAccessible(ctor); return ctor.newInstance(args);
ctor是經過反射獲得的contextClass的構造方法,args是構造方法當中的參數。這裏爲null,說明new了contextClass的無參構造方法。
這時又要退回到determineContextClass 這個方法中,咱們主要看:
contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName());
這句代碼,咱們能夠猜它是經過Properties的getProperty方法獲得WebApplicationContext 的實例,這時咱們又到了WebApplicationContext 這個接口當中,這個接口繼承了ApplicationContext這個接口,咱們都知道咱們進行spring開發都會經過Application ctx=new FileSystemXmlApplicationContext("beans.xml");或ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");或ServletContext servletContext = request.getSession().getServletContext();ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext);這三種方法得到一個ApplicationContext,而後就能夠對配置文件當中的bean進行操做了。因此這裏咱們基本上已經搞清楚初始化spring配置文件的流程了。
總結:經過查看這幾個類的源代碼,java的反射使用範圍之廣再次體現出來。如看了以後以爲有錯誤或者不一樣意見,歡迎提出來,我也是第一次才研究這個問題。