携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
1. DispatcherServlet初始化
我们将断点打在DispatcherServlet中的onRefresh方法上,然后启动项目。
从这个堆栈的图可以看出,最开始是HttpServletBean的init方法执行,然后启动Spring容器,Spring容器初始化的最后一步finishRefresh里面进行了事件通知。
我们先看下HttpServletBean里面的init方法
@Override
public final void init() throws ServletException {
///将web.xml中的<param-value>classpath:spring-mvc.xml</param-value>读取解析,存入到this.requiredProperties中
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
//将需要包装的DispatcherServlet对象包装成一个BeanWrapper
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
//将spring-mvc.xml加载成一个资源装载器
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
}
从web.xml中拿到paramValue属性之后,存放到一个叫requiredProperties的属性中。然后拿到一个包装类,其实bw这个包装类,就是包装了DispatcherServlet。然后将我们的spring-mvc.xml文件加载成一个资源装载器。下面就是给bw这个对象设置一些属性。最后会去调用initServletBean。这个方法在本类中其实也是一个空实现,交给FrameworkServlet这个子类去实现。
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
// 这里初始WebApplicationContext
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}
可以看到这里面没干什么事情。
在进行了一些日志的操作之后,记录一下开始的时间,我们会去进行初始化一个ApplicationContext。
protected WebApplicationContext initWebApplicationContext() {
// 获取web容器
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// 在构造时注入上下文实例 -> 使用它
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// 上下文尚未刷新 -> 提供设置父上下文、设置应用上下文id等服务
if (cwac.getParent() == null) {
// 上下文实例是在没有显式父级的情况下注入的 -> 将根应用程序上下文(如果有的话;可能为空)设置为父级
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 去父容器中找 web 容器
wac = findWebApplicationContext();
}
if (wac == null) {
// 父容器也没有就为此 servlet 定义上下文实例 -> 创建一个本地实例
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
首先去获取web容器,我们在之前是没有设置过的,所以获取到的是null,然后再去父容器中找,找到不到依然为null。那么接下来就要去创建一个WebApplicationContext了。
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 拿到 ApplicationContext 的类型
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
那么创建的这个ApplicationContext的类型到底是什么类型呢?
public Class<?> getContextClass() {
return this.contextClass;
}
->
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
->
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
这里我们就很清楚的可以看到,其实它默认创建的是一个XmlWebApplicationContext。拿到这个类型之后,就会去通过反射去创建这个实例了。创建完成之后,去给他设置一些属性,有父容器设置父容器,再去调用configureAndRefreshWebApplicationContext这个方法。配置和刷新WebApplicationContext。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
前面就没有什么好说的,设置一些参数,但是在最后,看到一个熟悉的方法,refresh。其实初始化DispatcherServlet的时候,是基于事件派发机制实现的。进入到refresh这个方法之后。
没错,这个就是我们学习Spring源码的时候的IOC中的refresh
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
//容器刷新之前初始化一些参数
this.prepareRefresh();
//创建beanFactory
ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
//给beanFactory设置一些参数
this.prepareBeanFactory(beanFactory);
try {
//空方法,交给子类扩展
this.postProcessBeanFactory(beanFactory);
//调用BeanFactoryPostProcessor
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessors
this.registerBeanPostProcessors(beanFactory);
//国际化的一些操作
this.initMessageSource();
//初始化一些时间多播器
this.initApplicationEventMulticaster();
空方法,交给子类扩展
this.onRefresh();
//注册监听器
this.registerListeners();
//初始化一些单实例非懒加载的bean实例
this.finishBeanFactoryInitialization(beanFactory);
// 发布相应的事件。
this.finishRefresh();
} catch (BeansException var9) {
if (this.logger.isWarnEnabled()) {
this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
}
this.destroyBeans();
this.cancelRefresh(var9);
throw var9;
} finally {
this.resetCommonCaches();
}
}
}
由于侧重点不同,我们需要关注的是finishRefresh。
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
}
}
在这个方法中,注册了ContextRefreshedEvent这样一个事件,我们进入到publishEvent这里。
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
在进行了一些赋值的操作之后,判断一下一些早期事件是否为null,因为我们此前并没有对此进行操作,所以肯定是为null。然后拿到事件的多播器之后,就会进行事件的发布了。
在multicastEvent这个方法中,会调用invokeListener来回调监听方法。
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
而doInvokeListener回调onApplicationEvent方法。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event); // <-----这里回调
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
但是,ApplicationListener接口中的方法,别很多子类所重写,那么到底是哪个呢,我们上面提到,我们注册了ContextRefreshedEvent这样一个事件,那自然需要用ContextRefreshListener去监听。
我们DeBug后发现,确实会进入到这样的一个方法。值得注意的是,FrameworkServlet里有个内部类ContextRefreshListener,实现了ApplicationListener接口,接收到了上面的事件通知,执行onApplicationEvent方法。
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
...
}
继续跟踪onApplicationEvent方法
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
发现最终是由onRefresh方法来进行具体处理的,并且这个onRefresh方法在FrameworkServlet里面是个空方法,是由它的子类DispatcherServlet来实现的。
我们来看DispatcherServlet里的onRefresh方法
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
进去
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context); // 初始化文件上传解析器
initLocaleResolver(context); // 本地化解析
initThemeResolver(context); // 主题解析器
initHandlerMappings(context); // 初始化处理器映射器
initHandlerAdapters(context); // 初始化处理器适配器
initHandlerExceptionResolvers(context); // 异常解析器
initRequestToViewNameTranslator(context); // 视图提取器,从request中获取viemName
initViewResolvers(context); // 初始化视图解析器
initFlashMapManager(context); // 参数解析器
}
这样就把SpringMVC的九大组件给初始化了。