场景
前段时间工作上的任务比较闲,就拐回去看了看spring的工作机制相关的东西,进而想到了要看看springMVC框架的实现思路,用了有半年了还不知道它内部的实现原理,所以这次想一探究竟。
servlet机制
上下文环境
ServletContext接口定义了运行Servlet的应用程序环境的一些行为和观点,可以使用ServletContext实现对象来取得锁清秋资源的URL、设置与储存 属性、应用程序初始参数,设置动态设置Servlet实例。当整个Web应用程序加载Web容器之后,容器会生成一个ServletContext对象作为整个应用程序的代表,并设置给ServletConfig,只要通过Servlet的getServletContext()方法就可以取得ServletContext对象。
tomcat服务器就是根据servlet定义加载web.xml生成servletContext对象,这个时候我的理解就是web框架需要做的就是实现一个初始化监听器,并且接收servletContext作用初始化参数:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
contextLoaderListener实现servletContextListener接口,使得tomcat在初始化加载完web.xml后调用servletContextListener的contextInitialized(ServletContextEvent)方法,spring实现contextLoaderListener在实现的contextInitialized方法中初始化spring的上下文对象用于对spring上下文的管理。
/**
* Initialize Spring's web application context for the given servlet context,
* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and
* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.
* @param servletContext current servlet context
* @return the new WebApplicationContext
* @see #CONTEXT_CLASS_PARAM
* @see #CONFIG_LOCATION_PARAM
*/
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
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 {
// Determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
this.context = createWebApplicationContext(servletContext, parent);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
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");
}
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;
}
}
这个时候我在想,spring的配置文件在哪里读取的?明明在web.xml文件中已经配置了。然后又去ContextLoader中找contextConfigLocation,结果有这个属性:
/**
* Name of servlet context parameter (i.e., "<code>contextConfigLocation</code>")
* that can specify the config location for the root context, falling back
* to the implementation's default otherwise.
* @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION
*/
public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";
并且在创建spring上下文对象ApplicationContext的时候付给该对象:
这里wac为ConfigurableWebApplicationContext的实例,该类是spring上下文对象的接口类。
这里的refresh方法是对spring管理的bean工厂、事件广播器、消息源、事件监听器等的初始化及bean定义的加载。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
到这里springmvc的初始化工作基本完成了,第一次看框架源代码,可能有些地方理解有偏差,不对的地方,希望猿们帮忙指正,共同进步~~~