web.xml 配置 spring 之 contextConfigLocation 详解(转)

spring的应用初始化流程一直没有搞明白,在网上找到下面内容:

我们在开发spring的项目当中基本上都会在web.xml通过:

  1. <context-param>  
  2.         <param-name>contextConfigLocation</param-name>  
  3.         <param-value>  
  4.         /WEB-INF/conf/application-*.xml  
  5.         </param-value>  
  6.     </context-param>  

来初始化各个spring的配置文件,但是我们只是知道这段代码的功能, 并不是很清楚我们配置了这段代码之后为什么就能去初始化配置文件。当然我们还会加上:

  1. <listener>  
  2.         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>  
  3.     </listener>  

这 一个listener,我首先就会想contextConfigLocation这个一定能在ContextLoaderListener这个类当中找 到,打开了源码,这个listener是实现了ServletContextListener这个接口的,这个接口只有两个方法:

  1. public interface ServletContextListener  
  2.     extends EventListener  
  3. {  
  4.   
  5.     public abstract void contextInitialized(ServletContextEvent servletcontextevent);  
  6.   
  7.     public abstract void contextDestroyed(ServletContextEvent servletcontextevent);  
  8. }  

而且它是继承了EventListener这个接口的,打开这个接口的代码让我大吃一惊,里面没有方法啥都没有:

  1. package java.util;  
  2.   
  3.   
  4. public interface EventListener  
  5. {  
  6. }  

而且还是java.util包下的,并不是spring之中的东西。

这样找了之后没有找到,往回退到ContextLoaderListener这个类的方法上,contextInitialized方法是用来初始化上下文的:

  1. public void contextInitialized(ServletContextEvent event)  
  2.     {  
  3.         contextLoader = createContextLoader();  
  4.         contextLoader.initWebApplicationContext(event.getServletContext());  
  5.     }  

方法中有个createContextLoader方法:

  1. protected ContextLoader createContextLoader()  
  2.     {  
  3.         return new ContextLoader();  
  4.     }  

这个方法返回了一个ContextLoader实例,进入到ContextLoader类中,按ctrl+f来寻找contextConfigLocation,这时没有出现电脑的咚的声音,找到了它:

  1. protected WebApplicationContext createWebApplicationContext(ServletContext servletContext, ApplicationContext parent)  
  2.         throws BeansException  
  3.     {  
  4.         Class contextClass = determineContextClass(servletContext);  
  5.         if(!(org.springframework.web.context.ConfigurableWebApplicationContext.class).isAssignableFrom(contextClass))  
  6.         {  
  7.             throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + (org.springframework.web.context.ConfigurableWebApplicationContext.class).getName() + "]");  
  8.         } else  
  9.         {  
  10.             ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);  
  11.             wac.setParent(parent);  
  12.             wac.setServletContext(servletContext);  
  13.             wac.setConfigLocation(servletContext.getInitParameter("<span style="color:#ff0000;">contextConfigLocation</span>"));  
  14.             customizeContext(servletContext, wac);  
  15.             wac.refresh();  
  16.             return wac;  
  17.         }  
  18.     }  

通 过代码,ConfigurableWebApplicationContext设置了从servletContext获取到的参数的值,再进入 ConfigurableWebApplicationContext的代码中,它只是一个接口,进入 StaticWebApplicationContext的setConfigLocation方法:

  1. public void setConfigLocation(String configLocation)  
  2.     {  
  3.         if(configLocation != null)  
  4.             throw new UnsupportedOperationException("StaticWebApplicationContext does not support config locations");  
  5.         else  
  6.             return;  
  7.     }  

这个方法中很奇怪,当参数不为空就抛出异常,查看spring的文档:The StaticWebApplicationContext class does not support this method.说是此类不支持这个方法,这下子又卡住了。又要退回去,看这句:

  1. ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);  

spring使用BeanUtils来初始化contextClass这个类实例,contextClass是通过以下代码得到的:

  1. protected Class determineContextClass(ServletContext servletContext)  
  2.         throws ApplicationContextException  
  3.     {  
  4.         String contextClassName = servletContext.getInitParameter("contextClass");  
  5.         if(contextClassName != null)  
  6.             try  
  7.             {  
  8.                 return ClassUtils.forName(contextClassName);  
  9.             }  
  10.             catch(ClassNotFoundException ex)  
  11.             {  
  12.                 throw new ApplicationContextException("Failed to load custom context class [" + contextClassName + "]", ex);  
  13.             }  
  14.         contextClassName = defaultStrategies.getProperty((org.springframework.web.context.WebApplicationContext.class).getName());  
  15.         try  
  16.         {  
  17.             return ClassUtils.forName(contextClassName, (org.springframework.web.context.ContextLoader.class).getClassLoader());  
  18.         }  
  19.         catch(ClassNotFoundException ex)  
  20.         {  
  21.             throw new ApplicationContextException("Failed to load default context class [" + contextClassName + "]", ex);  
  22.         }  
  23.     }  

这里使用了反射,再来看BeanUtils的instantiateClass方法:

  1. return instantiateClass(clazz.getDeclaredConstructor((Class[])null), null);  

通过反射得到contextClass的构造方法。下面是instantiateClass方法的重载,主要是下面两句代码:

  1. ReflectionUtils.makeAccessible(ctor);  
  2.             return ctor.newInstance(args);  

ctor是通过反射得到的contextClass的构造方法,args是构造方法当中的参数。这里为null,说明new了contextClass的无参构造方法。

这时又要退回到determineContextClass 这个方法中,我们主要看:

  1. 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的反射使用范围之广再次体现出来。

原文地址:https://www.cnblogs.com/sin7/p/2874330.html