九大组件
【1. HandlerMapping
】
是用来查找Handler的。在SpringMVC
中会有很多请求,每个请求都需要一个Handler处理,具体接收到一个请求之后使用哪个Handler进行处理呢?这就是HandlerMapping
需要做的事。
【2. HandlerAdapter
】
从名字上看,它就是一个适配器。因为SpringMVC
中的Handler可以是任意的形式,只要能处理请求就ok,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter
要做的事情。
小结:Handler是用来干活的工具;HandlerMapping
用于根据需要干的活找到相应的工具;HandlerAdapter
是使用工具干活的人。
【3. HandlerExceptionResolver
】
其它组件都是用来干活的。在干活的过程中难免会出现问题,出问题后怎么办呢?这就需要有一个专门的角色对异常情况进行处理,在SpringMVC
中就是HandlerExceptionResolver
。具体来说,此组件的作用是根据异常设置ModelAndView
,之后再交给render方法进行渲染。
【4. ViewResolver
】
ViewResolver用来将String类型的视图名和Locale解析为View类型的视图。View是用来渲染页面的,也就是将程序返回的参数填入模板里,生成html(也可能是其它类型)文件。这里就有两个关键问题:使用哪个模板?用什么技术(规则)填入参数?这其实是ViewResolver
主要要做的工作,ViewResolver
需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染,具体的渲染过程则交由不同的视图自己完成。
【5. RequestToViewNameTranslator
】
ViewName
是根据ViewName
查找View,但有的Handler处理完后并没有设置View也没有设置ViewName
,这时就需要从request获取ViewName
了,如何从request中获取ViewName
就是RequestToViewNameTranslator
要做的事情了。RequestToViewNameTranslator
在Spring MVC
容器里只可以配置一个,所以所有request到ViewName
的转换规则都要在一个Translator里面全部实现。
【6. LocaleResolver】
解析视图需要两个参数:一是视图名,另一个是Locale。视图名是处理器返回的,Locale是从哪里来的?这就是LocaleResolver
要做的事情。LocaleResolver
用于从request解析出Locale,Locale就是zh-cn之类,表示一个区域,有了这个就可以对不同区域的用户显示不同的结果。SpringMVC
主要有两个地方用到了Locale:一是ViewResolver
视图解析的时候;二是用到国际化资源或者主题的时候。
【7. ThemeResolver】
用于解析主题。SpringMVC
中一个主题对应一个properties文件,里面存放着跟当前主题相关的所有资源、如图片、css样式等。SpringMVC
的主题也支持国际化,同一个主题不同区域也可以显示不同的风格。SpringMVC
中跟主题相关的类有 ThemeResolver
、ThemeSource
和Theme。主题是通过一系列资源来具体体现的,要得到一个主题的资源,首先要得到资源的名称,这是ThemeResolver
的工作。然后通过主题名称找到对应的主题(可以理解为一个配置)文件,这是ThemeSource
的工作。最后从主题中获取资源就可以了。
【8. MultipartResolver】
用于处理上传请求。处理方法是将普通的request包装成MultipartHttpServletRequest
,后者可以直接调用getFile
方法获取File,如果上传多个文件,还可以调用getFileMap
得到FileName->File结构的Map。此组件中一共有三个方法,作用分别是判断是不是上传请求,将request包装成MultipartHttpServletRequest
、处理完后清理上传过程中产生的临时资源。
【9. FlashMapManager】
用来管理FlashMap
的,FlashMap
主要用在redirect中传递参数。
原文关于九大组件的介绍:https://zhuanlan.zhihu.com/p/73312778
九大组件的初始化
发布ContextRefreshedEvent事件
在spring的refresh最后一步,调用了finishRefresh
方法:发布了一个ContextRefreshedEvent
事件。
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);
}
}
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);
}
}
}
multicastEvent源码如下:这里会在ioc容器中找到所有监听ContextRefreshedEvent
事件的监听器,并执行invokeListener
最后调用监听器的onApplicationEvent
方法:
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);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//执行监听器的onApplicationEvent方法
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;
}
}
}
这里有一个监听器,叫做SourceFilteringListener
是在容器配置并刷新的时候就添加到容器中的:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//设置id。。。
//设置一些属性
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
//添加SourceFilteringListener
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
//web环境准备
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//在刷新容器前的后置处理,默认为空方法
postProcessWebApplicationContext(wac);
//执行所有的ApplicationContextInitializer<ConfigurableApplicationContext>
applyInitializers(wac);
//调用容器刷新方法
wac.refresh();
}
注意SourceFilteringListener
的构造方法和onApplicationEvent
方法:构造方法传入了一个ContextRefreshListener
作为委托类,而SourceFilteringListener
内部调用onApplicationEvent
方法实际上是调用ContextRefreshListener
委托类的方法onApplicationEvent
。
public SourceFilteringListener(Object source, ApplicationListener<?> delegate) {
this.source = source;
this.delegate = (delegate instanceof GenericApplicationListener ?
(GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate));
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event.getSource() == this.source) {
onApplicationEventInternal(event);
}
}
protected void onApplicationEventInternal(ApplicationEvent event) {
if (this.delegate == null) {
throw new IllegalStateException(
"Must specify a delegate object or override the onApplicationEventInternal method");
}
this.delegate.onApplicationEvent(event);
}
再看ContextRefreshListener
类,是FrameworkServlet
的内部类,内部调用的是FrameworkServlet
的onApplicationEvent
方法,内部又调用自己的onRefresh
方法,FrameworkServlet
中的onRefresh
方法是空实现,由其子类DispatcherServlet
实现。
/**
* ApplicationListener endpoint that receives events from this servlet's WebApplicationContext
* only, delegating to {@code onApplicationEvent} on the FrameworkServlet instance.
*/
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
FrameworkServlet.this.onApplicationEvent(event);
}
}
public void onApplicationEvent(ContextRefreshedEvent event) {
this.refreshEventReceived = true;
synchronized (this.onRefreshMonitor) {
onRefresh(event.getApplicationContext());
}
}
DispatcherServlet
实现如下:
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* 初始化所有策略
* 九大组件的初始化在这里
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
到这里,终于开始九大组件的初始化。
初始化MultipartResolver
代码很简单,方法如下:
private void initMultipartResolver(ApplicationContext context) {
try {
//从容器中获取名字叫做multipartResolver的 class类型为MultipartResolver的bean
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
//容器中有,就用,没有默认为空
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
初始化组件的套路
第一个初始化MultipartResolver
后,后面还有八个init方法的调用,但是点进去,发现都是一个套路:
例如:initLocaleResolver
方法和initThemeResolver
,都是先从容器中查找对应的组件,如果找不到,就从默认的策略中加载组件
private void initLocaleResolver(ApplicationContext context) {
try {
//从容器中获取LocaleResolver
this.localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.localeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.localeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// We need to use the default.
//从默认策略中加载LocaleResolver
this.localeResolver = getDefaultStrategy(context, LocaleResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No LocaleResolver '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this.localeResolver.getClass().getSimpleName() + "]");
}
}
}
private void initThemeResolver(ApplicationContext context) {
try {
//从容器中获取ThemeResolver
this.themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.themeResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.themeResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
//从默认策略中加载ThemeResolver
this.themeResolver = getDefaultStrategy(context, ThemeResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("No ThemeResolver '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this.themeResolver.getClass().getSimpleName() + "]");
}
}
}
默认的策略方法getDefaultStrategies
如下:
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
//去DispatcherServlet所在的类路径下找DispatcherServlet.properties资源
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
//从DispatcherServlet.properties文件中加载默认策略
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
//按照逗号分隔出全类名
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
//加载Class
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
//调用createDefaultStrategy方法,创建出Class对应的组件实例
Object strategy = createDefaultStrategy(context, clazz);
//添加到strategies集合中
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
//返回strategies集合
return strategies;
}
else {
return Collections.emptyList();
}
}
DispatcherServlet.properties资源在spring-webmvc模块中:在这个文件中配置了一些默认的组件。所以DispatcherServlet中默认的组件都是在该文件中配置的。
下面看createDefaultStrategy
方法,因为getDefaultStrategies
方法通过它来创建组件:获取ioc容器并调用它的createBean方法,所以组件的创建、生命周期也会被IOC容器管理。
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) {
return context.getAutowireCapableBeanFactory().createBean(clazz);//启动ioc容器的创建bean的流程
}
HandlerMapping的创建
我们知道HandlerMapping中保存着请求url与对应handler的映射关系,那么该映射关系是如何加载的,这就需要研究对应组件的源码了。
HandlerMapping是一个接口,DispatcherServlet默认创建其中的三个实现:
- org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
- org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
- org.springframework.web.servlet.function.support.RouterFunctionMapping
RouterFunctionMapping与webflux有关系,不再本文讨论之内。
所以本文详细介绍BeanNameUrlHandlerMapping
和RequestMappingHandlerMapping
的加载。
BeanNameUrlHandlerMapping加载
先看BeanNameUrlHandlerMapping类的继承关系:
熟悉ioc容器流程的都知道,当组件实现了ApplicationContextAware
接口的bean会被ApplicationContextAwareProcessor
干预bean的创建,调用它的setApplicationContext
方法,
而BeanNameUrlHandlerMapping
的setApplicationContext
在它父类ApplicationObjectSupport
中实现:
@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
// Reset internal context state.
this.applicationContext = null;
this.messageSourceAccessor = null;
}
else if (this.applicationContext == null) {
// Initialize with passed-in context.
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
}
//设置applicationContext和MessageSourceAccessor
this.applicationContext = context;
this.messageSourceAccessor = new MessageSourceAccessor(context);
//执行initApplicationContext方法
initApplicationContext(context);
}
else {
// Ignore reinitialization if same context passed in.
if (this.applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this.applicationContext + "], passed-in one is [" + context + "]");
}
}
}
这里比较复杂,用代码描述比较乱,我这里画了一个流程图,还是花了不少时间的。
流程图如下:
最后所有beanName的url和handler的映射信息保存到AbstractUrlHandlerMapping
类的handlerMap
中。
RequestMappingHandlerMapping加载
还是先看该类的继承关系:
该类实现了一系列的Aware接口,上面我们看BeanNameUrlHandlerMapping
时候,它也实现了ApplicationContextAware
,同时它们两有一个公共父类AbstractHandlerMapping
,所以RequestMappingHandlerMapping
类的初始化和BeanNameUrlHandlerMapping
有类似。
同时,还注意到RequestMappingHandlerMapping
类实现了InitializingBean
接口,该类会在bean初始化的时候被回调,所以这也是重要的一点。
流程图如下:
跟踪源码后发现,最后handlerMethod、url、bean等信息都保存到AbstractHandlerMethodMapping
类的内部类MappingRegistry
中。