原文链接:https://blog.csdn.net/qw222pzx/article/details/78191670

spring的应用初始化流程一直没有搞明白,刚刚又碰到了相关的问题。决定得好好看看这个流程。我们在开发spring的项目当中基本上都会在web.xml通过:

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/conf/application-*.xml
</param-value>
</context-param>

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

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

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

public interface ServletContextListener
extends EventListener
{ public abstract void contextInitialized(ServletContextEvent servletcontextevent); public abstract void contextDestroyed(ServletContextEvent servletcontextevent);
}

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

package java.util;  

public interface EventListener
{
}

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

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

public void contextInitialized(ServletContextEvent event)
{
contextLoader = createContextLoader();
contextLoader.initWebApplicationContext(event.getServletContext());
}

方法中有个createContextLoader方法:

protected ContextLoader createContextLoader()
{
return new ContextLoader();
}

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

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方法:

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的反射使用范围之广再次体现出来。如看了之后觉得有错误或者不同意见,欢迎提出来,我也是第一次才研究这个问题。

最新文章

  1. 【BZOJ1503】 [NOI2004]郁闷的出纳员 splay
  2. 使用 AFNetworking 进行 XML 和 JSON 数据请求
  3. 二十七、EFW框架BS系统开发中的MVC模式探讨
  4. Week8 软件规格说明书
  5. Intellij IDEA 新建一个EJB工程(三)
  6. MyBatis学习 之 四、MyBatis配置文件
  7. web服务器分析与设计(一)
  8. UML-用例
  9. AndroidStudio中安装可自动生成json实体类的jar包
  10. Android Service的生命周期
  11. 调试exynos4412—ARM嵌入式Linux—LEDS/GPIO驱动之一
  12. 创建兼容的XHR对象
  13. 使用Python实现子区域数据分类统计
  14. json对象和json字符串之间的转化
  15. MySQL基本操作练习
  16. ubuntu下安装和配置pycharm和pyqt5
  17. C博客作业02--循环结构
  18. 学习笔记之Python爬虫
  19. webAPI文件上传时文件过大404错误的问题
  20. 自学Python1.8-python input/print用法 格式化输出

热门文章

  1. 5.2 Nginx Http 反向代理
  2. Java中很少用的CopyOnWriteArrayList
  3. navcat工具常用快捷键
  4. 吴裕雄--天生自然JAVA数据库编程:JDBC操作步骤及数据库连接操作
  5. python进阶强化学习
  6. swoole之任务和定时器
  7. arm linux 移植 PHP
  8. 5G时代能携号转网,你会提前换新手机吗?
  9. 【WPF学习】第二十二章 文本控件
  10. Python面试常问的10个问题