- 简介
这两个星期都在看spring mvc源码,看来看去还是还是很多细节没了解清楚,在这里把看明白的记录下,欢迎在评论中一起讨论。
一、铺垫
spring mvc是基于servlet的,在正式分析之前,我们来看一下servlet的知识。servlet的生命周期通过三个方法init、service、destory来构建的。
- init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。
- service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
- destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
二、spring mvc启动过程
spring mcv的入口是DispatcherServlet,顾名思义就是调度servlet是服务请求调度,它的继承结构如下:
在整合spring mvc时,web.xml有这样配置,相信用过的都知道
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-dispatcher.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
那根据servlet的知识,在服务器启动,创建dispatcherServlet对象,会执行init方法,根据DispaterServlet的继承关系,找到init方法在HttpServletBean中,下面我们来看一下这个
方法
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
/*PropertyValuesz是封装在web.xml配置servlet参数信息
* <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:mvc-dispatcher.xml</param-value>
</init-param>
*/
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
//将配置的初始化值设置到DispatcherServlet
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
//初始化spring mvc容器方法webApplicationContext,由子类的FrameworkServlet来实现
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
2.1、接下来看FrameworkServlet的initServletBean(),主要调了initWebApplicationContext()方法
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始化spring mcv容器
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
2.2、initWebApplicationContext方法
protected WebApplicationContext initWebApplicationContext() {
/*
在spring启动过程中,ContextLoaderListener回监听到,实例化IoC容器,并将此容器实例注册到ServletContext中,现在把IOC容器取出来
*/
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (wac == null) {
//把IOC容器传进去,创建spring mvc自己的容器
wac = createWebApplicationContext(rootContext);
}
return wac;
if (this.publishContext) {
String attrName = getServletContextAttributeName();
//把创建好的spring mcv自己的容器设置到ServletContext容器中
getServletContext().setAttribute(attrName, wac);
}
}
2.3、createWebApplicationContext方法
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
/*根据web.xml中的配置决定使用何种WebApplicationContext。默认情况下使用XmlWebApplicationContext
web.xml中相关的配置context-param的名称“contextClass” */
Class<?> contextClass = getContextClass();
//获得spring mvc自己容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
//指定父容器为IOC容器
wac.setParent(parent);
//指定spring mcv 核心配置文件的位置classpath:mvc-dispatcher.xml
wac.setConfigLocation(getContextConfigLocation());
//spring mcv 容器继续初始化
configureAndRefreshWebApplicationContext(wac);
return wac;
}
2.4、配置和刷新spring mvc自己容器方法configureAndRefreshWebApplicationContext(wac),就是针对spring mcv容器对象一些属性,然后初始化
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//设置ServletContext
wac.setServletContext(getServletContext());
//设置ServletConfig
wac.setServletConfig(getServletConfig());
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh(); }
2.5、接下来,我们看看刷新方法,做了什么事
public void refresh() throws BeansException, IllegalStateException {
//完成Bean工厂初始化
finishBeanFactoryInitialization(beanFactory);
//完成刷新spring mvc自己的容器功能
finishRefresh();
}
在这里主要看完成BeanFactory初始化工作
2.6、接下来我们看看初始化BeanFactory工作finishBeanFactoryInitialization,主要看到实例化单例对象
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//实例化单例对象
beanFactory.preInstantiateSingletons();
}
2.7、接下来实例化单例对象方法preInstantiateSingletons()
public void preInstantiateSingletons() throws BeansException {
//获得所有定义好beanNames名称
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
smartSingleton.afterSingletonsInstantiated();
return null;
}
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
这个方法beanNames有以下
是通过getBean(beanName)来实例化对象的,在这里我主要讲spring mvc实例化RequestMappingHandlerMapping做了什么事,不要急,我们先来看一下,RequestMappingHandlerMapping的继承结构
可以看到RequestMappingHandleMapping继承了AbstractHandlerMethodMapping,在初始化的时候,会执行到AbstractHandlerMethodMapping的构造方法,看看AbstractHandlerMethodMapping这个
实现了InitializingBean,所以这个类在初始化完成时会执行到afterPropertiesSet()方法
2.8、接下来我们看看initHandlerMethods方法到底做了什么事
protected void initHandlerMethods() {
//获得所有的beanNames
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//遍历取出每个beanName
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
//获得BeanName对应的Class对象
beanType = getApplicationContext().getType(beanName);
}
//判断这个Class时候否有@Controller和@RequestMapping()
if (beanType != null && isHandler(beanType)) {
//如果类上含有@Controller和@RequestMapping()注解,执行探测处理器方法
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
2.9、接下来我们看看detectHandlerMethods(beanName)这个方法到底干了什么事
protected void detectHandlerMethods(final Object handler) {
//传进来的handle是个字符串类型的BeanName,首先根据Bean名称创建Class对象
Class<?> handlerType = (handler instanceof String ?
getApplicationContext().getType((String) handler) : handler.getClass());
final Class<?> userType = ClassUtils.getUserClass(handlerType);
//获得Controller类对应methods对应的Map
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
new MethodIntrospector.MetadataLookup<T>() {
@Override
public T inspect(Method method) {
return getMappingForMethod(method, userType);
}
});
//遍历Map,取出Controller类的每一个方法和对应的Mapping
for (Map.Entry<Method, T> entry : methods.entrySet()) {
Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
T mapping = entry.getValue();
registerHandlerMethod(handler, invocableMethod, mapping);
}
}
下图是Map<Method, T> methods的值
可以看到T封装了Controller类的请求方式,和匹配条件等等数据
2.10、接下来我们来看看spring mvc是怎么注册Controller方法
registerHandlerMethod(handler, invocableMethod, mapping)
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
2.11、接下来我们看看mappingRegistry的register方法
public void register(T mapping, Object handler, Method method) {
//拿到写锁
this.readWriteLock.writeLock().lock();
try {
//根据handler(是你的controller类的类名)和controller的method方法创建handlerMethod对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//存储到名称为mappingLookup的Map中
this.mappingLookup.put(mapping, handlerMethod);
//然后从mapping中取出请求url
List<String> directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//然后我们再把url和mapping存储到MultiValueMap类型的urlLookup中,所以我们可以通过url找到mapping,再通过mapping找到handleMethod
this.urlLookup.add(url, mapping);
}
//把mapping和appingRegistration存储到HashMap类型的registry中
this.registry.put(mapping, new AbstractHandlerMethodMapping.MappingRegistration<T>(mapping, handlerMethod, directUrls, name));
}
finally {
//释放写锁
this.readWriteLock.writeLock().unlock();
}
}
这里解释下传进来3个参数 mapping封装了匹配条件,请求方式等数据,handler是我们的controller类名(字符串),method(controller类的方法对象)
2.12、接下来我们回到的2.8的initHandlerMethods方法
protected void initHandlerMethods() {
//获得所有的beanNames
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
//遍历取出每个beanName
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
//获得BeanName对应的Class对象
beanType = getApplicationContext().getType(beanName);
}
//判断这个Class时候否有@Controller和@RequestMapping()
if (beanType != null && isHandler(beanType)) {
//如果类上含有@Controller和@RequestMapping()注解,执行探测处理器方法
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
执行完了detectHandlerMethods(beanName);方法,继续往下执行handlerMethodsInitialized(getHandlerMethods())方法,这个方法是个 是个模板 方法,什么都没执行,到这里
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
}
afterPropertiesSet方法就算执行完了。
2.13、接下来执行继续回到2.7的实例化单例对象方法preInstantiateSingletons();
接下没什么什么好说,preInstantiateSingletons()结束后,finishBeanFactoryInitialization()方法也就结束了,回到了refresh(),继续完成刷新spring mvc自己的容器功能
public void refresh() throws BeansException, IllegalStateException {
//完成Bean工厂初始化
finishBeanFactoryInitialization(beanFactory);
//完成刷新spring mvc自己的容器功能
finishRefresh();
}
2.14、finishRefresh();这一步会触发一个ApplicationEvent:
protected void finishRefresh() {
//其中this是指XmlWebApplicationContext对象
publishEvent(new ContextRefreshedEvent(this));
2.15、接下来解析初始化DispatcherServlet类的各种成员变量
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
//初始化handlerMappings
initHandlerMappings(context);
//handlerAdapters
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}
2.16、接下来回到2.1的FrameworkServlet的initServletBean()方法中,然后initServletBean()执行完之后,HttpServletBean中的init初始化方法也就执行完了,到这里spring mvc启动的源码已经分析完成,
希望你看会有收获。
三、总结
其实分析完spring mvc自己启动的源码会发现,大部分内容都在构建spring mcv自己的容器,构建完成之后将容器放到SevletContext中