spring mvc - 源码解析(一) 初始化

本文介绍了Spring MVC的初始化过程,从web.xml配置入手,详细解析了ContextLoaderListener监听器和DispatcherServlet控制器的初始化流程,以及XML配置文件的加载过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     了解spring 的原理,一般从 官方提供的运行机制的图,然后根据代码 进行源码分析,就可以了,这里先从spring mvc 启动开始分析。先来看看web.xml 的配置吧。这里基于spring 3.2

    

<!-- 启动监听 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>/WEB-INF/classes/META-INF/spring/applicationContext*.xml</param-value>
</context-param>

<!-- app 请求 -->
<servlet>
	<servlet-name>SpringMVCServlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
			<init-param>
				<param-name>contextConfigLocation</param-name>
				<param-value>
				/WEB-INF/config/app-config.xml
			</param-value>
			</init-param>
			<load-on-startup>1</load-on-startup>
		</servlet>
		<servlet-mapping>
			<servlet-name>SpringMVCServlet</servlet-name>
			<url-pattern>/c/*</url-pattern>
		</servlet-mapping>

 

上面主要有两个东西:

1.ContextLoaderListener 监听器:

2.DispatcherServlet  控制器:

 

在启动的时候,如果观察启动日志 就能发现,1.首先启动,2.然后是DispatcherServlet  ,我们来看看这两个东西 在初始化的时候干了什么?

 

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
        private ContextLoader contextLoader;
        // 这是实现了ServletContextListener 监听,一个初始化方法  和一个销毁的方法
 	public void contextInitialized(ServletContextEvent event) {
                // 这里已经废弃,不用了,一直未null
		this.contextLoader = createContextLoader();
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
                // 主要看看初始化,他到底做了什么,进入源码分析
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}
	public void contextDestroyed(ServletContextEvent event) {
	// ....略
	}

}

 

  进入ContextLoader,找到initWebApplicationContext 方法:

  

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
// 这里只贴方法

// 先判断 是否已经加载过了
if (servletContext.getAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
// ...
}

// 创建 WebApplicationContext 对象,详细的看 create 方法
if (this.context == null) {
		this.context = createWebApplicationContext(servletContext);
  }
// 这一步就是加载你xml 配置<context-param> 里面的内容,默认是application...
// 看后面他是如何实现的。
if (this.context instanceof ConfigurableWebApplicationContext) {
   
 configureAndRefreshWebApplicationContext((ConfigurableWebApplicationContext)this.context, servletContext);

  }
 }
}

 

 

createWebApplicationContext 方法,负责创建 WebApplicationContext  上下文对象

 

protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 这里拿到XmlWebApplicationContext 的对象
Class<?> contextClass = determineContextClass(sc);
// 这里转成他的扩展接口对象ConfigurableWebApplicationContext ,多加了一些获得配置信息的方法
// 至于这里一步的原因我不是很明白,大概是为了一个兼容的的问题,以前的设计这里会出错。
ConfigurableWebApplicationContext wac =
   
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
  
return wac;
}

// 这个类默认创建指定处理xml/bean 的类对象
protected Class<?> determineContextClass(ServletContext servletContext) {
        // 这里表示你在web.xml 可以手动自己写一个需要处理的加载类对象,
	// 默认是XmlWebApplicationContext
        // 如何你需要额外的加载,一般是继承XmlWebApplicationContext 行了
	String contextClassName = servletContext.getInitParameter(
	CONTEXT_CLASS_PARAM);
		if (contextClassName != null) {
			try {
                        // 这里是默认是获得当前线程的加载器
			return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
			}
		}
		else {  
                        // 这里是通过properties 进行加载,defaultStrategies 是一个静态类
			// 默认获得当前目录下ContextLoader.properties里面的XmlWebApplicationContext 类路径
			contextClassName = defaultStrategies.getProperty(
				WebApplicationContext.class.getName());
			try {
                        // 这里同样通过类路径装载类,加载器是ContextLoader
			return ClassUtils.forName(contextClassName,
			ContextLoader.class.getClassLoader());
			}
		}
}

 

  

    这里主要负责加载xml 文件

   

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {

// 这里官方解释 和EJB 有关系,具体请看API。类似我们这种纯web 中,这里一般都是空的
ApplicationContext parent = loadParentContext(sc);
// 这里就是加载我们配置的contextConfigLocation 的参数,这也是为什么需要这个名字的原因
// 同时里面所有对应的xml文件都会找到,对应的参数值是:
// classpath*:com/home/config/spring/spring-*.xml
String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (initParameter != null) {
			wac.setConfigLocation(initParameter);
		}
// 这里是定制自己的context,也就是你可以扩展,这里我是没扩展,所以没用,不解释
customizeContext(sc, wac);
// 这里方法,在子类 AbstractApplicationContext 里面
// 它负责加载xml properties 以及database schema ,看看他是如何操作的!
wac.refresh();

}
 

 

   refresh()方法

   

public void refresh() throws BeansException, IllegalStateException {
// 这里会更新当前操作的时间,以及操作状态,表示现在可以操作了。
prepareRefresh();
// 这里是实例化bean工厂,准备开始了,详细看下面
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
}
      
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
                // 初始化工厂,并获得配置的xml 指向的地址
		refreshBeanFactory();
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		return beanFactory;
	}
  

 

  

protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {  
                        // 创建新工厂
			DefaultListableBeanFactory beanFactory = createBeanFactory();
			beanFactory.setSerializationId(getId());
			customizeBeanFactory(beanFactory);
                        // 工厂加载对应的xml bean,接着看下面
			loadBeanDefinitions(beanFactory);
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		}
	}

 

 这是XmlWebApplicationContext 里面的

// 加载初始化 设置的bean 信息
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
		// Create a new XmlBeanDefinitionReader for the given BeanFactory.
		XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

       		// 设置一些资源信息,为加载做准备
                beanDefinitionReader.setEnvironment(this.getEnvironment());
		beanDefinitionReader.setResourceLoader(this);
		beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

		// 执行加载
		initBeanDefinitionReader(beanDefinitionReader);
		loadBeanDefinitions(beanDefinitionReader);
	}

  

   // 具体的加载步骤,通过XmlBeanDefinitionReader 进行操作

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
// 这里会获得classpath*:....*.xml 等你配置的地址 		
String[] configLocations = getConfigLocations();
		if (configLocations != null) {
			for (String configLocation : configLocations) {
				// 这里根据地址得到相对文件并进行加载
				// 主要通过PathMatchingResourcePatternResolver  解析获
				// 相对路径
				// 然后通过 XmlBeanDefinitionReader里面各种方法,
				// 最后 doLoadBeanDefinitions 进行处理xml 文件属性
				reader.loadBeanDefinitions(configLocation);
			}
		}
	}

 

     到现在为止,我们配置的xml bean文件已经加载完了,下面看看 bean 里面相关联的properties 文件如何加载的。继续回到AbstractApplicationContext   refresh()方法:

    

public void refresh() {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 这是准备bean 工厂,初始化一些工具类
prepareBeanFactory(beanFactory);

postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 加载的bean,
registerBeanPostProcessors(beanFactory);
// 初始化消息
initMessageSource();
// 初始化事件列表,或者监听,对加载的bean 的一些添加移除等操作进行管理
initApplicationEventMulticaster();

// 扩展的时候需要
onRefresh();
// 注册监听,并通过name 把所有的bean 放入进行管理
registerListeners();
// 把context 剩下的延迟加载的bean 加载完
finishBeanFactoryInitialization(beanFactory);
// 发布时间,对一些生命周期进行监控。 这个里面的方法 是主体方法,后面再分开研究吧。
finishRefresh();
}

 

  小结: 整个初始化步骤

          1.ContextLoaderListener  启动,开始调用初始化方法

          2.ContextLoader 具体执行初始化任务,主要创建上下文对象 WebApplicationContext

     2.1 读取一些配置配置文件路径,设置一些初始化参数

     3 XmlWebApplicationContext  xml 文件进行解析,封装成集合信息

          4.AbstractApplicationContext  把刚那些集合对象信息,进行基本的初始化 监听 发布等等操作,最后

我们的就能从上下文信息,里面取得我们想要的的信息了。

 

整个过程,只写了个大概,辅助了解,深入需要自己去看官方文档和源码,建议设计图结合看。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值