SpringMVC源码解析 xml配置文件版 0. 准备阶段 maven springMVC工程搭建
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns ="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation ="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id ="WebApp_ID" version ="3.0" >
<context-param >
<param-name > contextConfigLocation</param-name >
<param-value > classpath:spring-config/spring-*.xml</param-value >
</context-param >
<listener >
<listener-class > org.springframework.web.context.ContextLoaderListener</listener-class >
</listener >
<servlet >
<servlet-name > springMVC</servlet-name >
<servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class >
<init-param >
<param-name > contextConfigLocation</param-name >
<param-value > classpath:spring-config/springmvc-config.xml</param-value >
</init-param >
<load-on-startup > 1</load-on-startup >
</servlet >
<servlet-mapping >
<servlet-name > springMVC</servlet-name >
<url-pattern > /</url-pattern >
</servlet-mapping >
<filter >
<filter-name > characterEncodingFilter</filter-name >
<filter-class > org.springframework.web.filter.CharacterEncodingFilter</filter-class >
<init-param >
<param-name > encoding</param-name >
<param-value > utf-8</param-value >
</init-param >
<init-param >
<param-name > forceEncoding</param-name >
<param-value > true</param-value >
</init-param >
</filter >
<filter-mapping >
<filter-name > characterEncodingFilter</filter-name >
<url-pattern > /*</url-pattern >
</filter-mapping >
</web-app >
Copy springmvc-config.xml
<beans xmlns ="http://www.springframework.org/schema/beans"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop ="http://www.springframework.org/schema/aop"
xmlns:context ="http://www.springframework.org/schema/context"
xmlns:mvc ="http://www.springframework.org/schema/mvc"
xmlns:tx ="http://www.springframework.org/schema/tx"
xsi:schemaLocation ="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd" >
<mvc:default-servlet-handler />
<mvc:annotation-driven />
<context:component-scan base-package ="com.tca.springmvc.learning.controller" />
</beans >
Copy controller
@Controller
@RequestMapping ("/hello" )
public class HelloController {
@GetMapping ("/test" )
@ResponseBody
public String test ( ) {
return String .valueOf (System .currentTimeMillis ());
}
@RequestMapping ("/message" )
@ResponseBody
public String message (@RequestBody JSONObject request ) {
System .out .println ("收到消息: " + request.toJSONString ());
return "ok" ;
}
}
Copy 1.1 启动原理
1.web容器(如tomcat)启动时, 会根据tomcat的配置文件读取项目的web.xml配置文件, web.xml配置文件中主要涉及的标签有:
<context-param > 、<listener > 、<fileter > 、<servlet >
加载的顺序是: <context-param > 、<listener > 、<fileter > 、<servlet >
2.web容器启动时(如tomcat), 首先加载依赖的jar, 然后读取web.xml文件, 创建ServletContext, 容器会将context-param的配置以key-value键值对形式传给servletContext, 并且实例化<listener > 标签中配置的类, 然后初始化Filter和Servlet
3.我们在springmvc工程中配置的<listener > 为org.springframework.web.context.ContextLoaderListener, 它实现了ServletContextListener接口, 这个接口用于监听web容器的开启和关闭, 并有对应方法进行回调
Copy 1.2 ServletContextListener
public interface ServletContextListener extends EventListener {
public void contextInitialized ( ServletContextEvent sce ) ;
public void contextDestroyed ( ServletContextEvent sce ) ;
}
Copy 这个接口提供了两个方法,分别用来监听web容器启动和关闭进行回调,可以看到:contextInitialized的执行顺序优先于filter 和servlet的初始化!
Copy 2.ContextLoaderListener
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
public ContextLoaderListener () {
}
public ContextLoaderListener (WebApplicationContext context ) {
super(context);
}
@Override
public void contextInitialized (ServletContextEvent event ) {
initWebApplicationContext(event .getServletContext());
}
@Override
public void contextDestroyed (ServletContextEvent event ) {
closeWebApplicationContext(event .getServletContext());
ContextCleanupListener.cleanupAttributes(event .getServletContext());
}
}
Copy 2.1ContextLoaderListener#contextInitialized(ServletContextEvent event)
@Override
public void contextInitialized (ServletContextEvent event ) {
initWebApplicationContext (event.getServletContext ());
}
Copy 2.2 ContextLoader#initWebApplicationContext(ServletContext servletContext)
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 {
if (this .context == null ) {
this .context = createWebApplicationContext(servletContext);
}
if (this .context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this .context;
if (!cwac.isActive()) {
if (cwac.getParent() == null ) {
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
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;
}
}
Copy 总结
1 .web 容器(如tomcat)启动时, 创建ServletContext对象, 作为web容器Servlet上下文, 同时去加载web.xml 文件
2 .web .xml 文件中配置了<listener>监听器, 其实现类为ContextLoaderListener, 用于监听web容器的开启和关闭, 因此web容器启动时会回调ContextLoaderListener#initWebApplicationContext (ServletContext servletContext)方法, 该方法用于创建一个XmlWebApplicationContext的spring-web容器, 并将其作为ServletContext的一个属性, 属性为ROOT
Copy 问题 1.父容器的作用是什么? 为什么需要设置父子容器?
Copy 3.FrameworkServlet 想要了解另一个核心类DispatcherServlet,需要先熟悉其父类FrameworkServlet
3.1 结构图
可以看到,其继承了HttpServletBean,同时实现了ApplicationContextAware接口
3.2 初始化 3.2.1 流程 step1:web容器在实例化servlet时会调用Servlet的init(ServletConfig)方法,由Servlet子类GenericServlet实现了该方法,因而最终会调用GenericServlet.init(ServletConfig)方法。
GenericServlet#init(ServletConfig)
public void init (ServletConfig config) throws ServletException {
this .config = config;
this .init ();
}
Copy step2:调用init()方法,init()方法由GenericServlet的子类HttpServletBean实现,实际上是执行了HttpServletBean.init()方法
HttpServletBean#init()
@Override
public final void init () throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'" );
}
try {
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);
bw.setPropertyValues(pvs, true );
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'" , ex);
}
throw ex;
}
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully" );
}
}
Copy step3 上述中调用了HttpServletBean.init()方法,这个方法中调用了initServletBean()方法,这个方法由子类FrameworkServlet实现
FrameworkServlet#initServletBean()
@Override
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 {
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" );
}
}
Copy FrameworkServlet#initWebApplicationContext()
protected WebApplicationContext initWebApplicationContext() {
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()) {
if (cwac.getParent() == null ) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null ) {
wac = findWebApplicationContext();
}
if (wac == null ) {
wac = createWebApplicationContext(rootContext);
}
if (!this .refreshEventReceived) {
onRefresh(wac);
}
if (this .publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this .logger.isDebugEnabled()) {
this .logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]" );
}
}
return wac;
}
Copy FrameworkServlet#createWebApplicationContext(ApplicationContext parent)
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this .logger.isDebugEnabled()) {
this .logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]" );
}
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);
wac.setConfigLocation(getContextConfigLocation());
configureAndRefreshWebApplicationContext(wac);
return wac;
}
Copy FrameworkServlet#configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac)
protected void configureAndRefreshWebApplicationContext (ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
if (this .contextId != null ) {
wac.setId(this .contextId);
}
else {
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 ()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
Copy 3.2.2 回调 上述,创建完子容器之后,添加了监听器new SourceFilteringListener(wac, new ContextRefreshListener())监听容器refresh,容器启动时会回调ContextRefreshListener的onApplicationEvent(ContextRefreshedEvent event)方法
private class ContextRefreshListener implements ApplicationListener <ContextRefreshedEvent > {
@Override
public void onApplicationEvent (ContextRefreshedEvent event ) {
FrameworkServlet.this .onApplicationEvent(event );
}
}
Copy FrameworkServlet#onApplicationEvent(ContextRefreshedEvent event)
public void onApplicationEvent (ContextRefreshedEvent event ) {
this .refreshEventReceived = true ;
onRefresh(event .getApplicationContext());
}
Copy 总结 web容器启动时, 会加载并初始化web.xml 文件中配置的<servlet>, 这里只有核心类DispatcherServlet, 继承了FrameworkServlet, 初始化FrameworkServlet时会创建子spring web容器XmlWebApplicationContext, 创建时会添加监听器ContextRefreshListener, 所以创建完成时回调其onApplicationEvent (ContextRefreshedEvent event)方法, 该方法用于初始化DispatcherServlet相关组件!
Copy 4.DispatcherServlet 4.1 初始化 综上,子容器XmlWebApplicationContext启动时,发布ContextRefreshedEvent事件,被已经添加的ContextRefreshListener监听,回调其onApplicationEvent(ContextRefreshedEvent event)方法,这个方法中调用了DispatcherServlet的onRefresh(ApplicationContext context)方法
DispatcherServlet#onRefresh(ApplicationContext context)
@Override
protected void onRefresh (ApplicationContext context ) {
initStrategies (context);
}
Copy DispatcherServlet#initStrategies(ApplicationContext context)
protected void initStrategies (ApplicationContext context) {
initMultipartResolver (context);
initLocaleResolver (context);
initThemeResolver (context);
initHandlerMappings (context);
initHandlerAdapters (context);
initHandlerExceptionResolvers (context);
initRequestToViewNameTranslator (context);
initViewResolvers (context);
initFlashMapManager (context);
}
Copy initMultipartResolver(ApplicationContext context)
private void initMultipartResolver (ApplicationContext context ) {
try {
this .multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("Using MultipartResolver [" + this .multipartResolver + "]" );
}
}
catch (NoSuchBeanDefinitionException ex) {
this .multipartResolver = null ;
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
"': no multipart request handling provided" );
}
}
}
Copy 初始化MultipartResolver, 用于处理文件上传, 这个是可配置的, 如果未配置, 为空
initLocaleResolver(ApplicationContext context)
private void initLocaleResolver(ApplicationContext context) {
try {
this .localeResolver = context.getBean(LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("Using LocaleResolver [" + this .localeResolver + "]" );
}
}
catch (NoSuchBeanDefinitionException ex) {
this .localeResolver = getDefaultStrategy(context, LocaleResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate LocaleResolver with name '" + LOCALE_RESOLVER_BEAN_NAME +
"': using default [" + this .localeResolver + "]" );
}
}
}
Copy 初始化LocaleResolver,如果没有配置,使用默认的
initThemeResolver(ApplicationContext context)
private void initThemeResolver(ApplicationContext context) {
try {
this .themeResolver = context.getBean(THEME_RESOLVER_BEAN_NAME, ThemeResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("Using ThemeResolver [" + this .themeResolver + "]" );
}
}
catch (NoSuchBeanDefinitionException ex) {
this .themeResolver = getDefaultStrategy(context, ThemeResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ThemeResolver with name '" + THEME_RESOLVER_BEAN_NAME +
"': using default [" + this .themeResolver + "]" );
}
}
}
Copy 初始化ThemeResolver,如果没有配置,使用默认的
initHandlerMappings(ApplicationContext context)
private void initHandlerMappings(ApplicationContext context) {
this .handlerMappings = null ;
if (this .detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class );
this .handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerMappings == null ) {
this .handlerMappings = getDefaultStrategies(context, HandlerMapping.class );
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default" );
}
}
}
Copy 从容器中获取所有HandlerMapping处理器,并排序,目前有三个,顺序为:RequestMappingHandlerMapping、BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping
initHandlerAdapters(ApplicationContext context)
private void initHandlerAdapters(ApplicationContext context) {
this .handlerAdapters = null ;
if (this .detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerAdapters = new ArrayList<HandlerAdapter>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class );
this .handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerAdapters == null ) {
this .handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class );
if (logger.isDebugEnabled()) {
logger.debug("No HandlerAdapters found in servlet '" + getServletName() + "': using default" );
}
}
}
Copy 从容器中获取所有HandlerAdapter处理器,并排序,目前有三个,顺序为:HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter
initHandlerExceptionResolvers(ApplicationContext context)
private void initHandlerExceptionResolvers(ApplicationContext context) {
this .handlerExceptionResolvers = null ;
if (this .detectAllHandlerExceptionResolvers) {
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class );
this .handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerExceptionResolvers == null ) {
this .handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default" );
}
}
}
Copy 从容器中获取所有HandlerExceptionResolver处理器,并排序,目前有三个,顺序为:ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
initRequestToViewNameTranslator(ApplicationContext context)
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
this .viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class );
if (logger.isDebugEnabled()) {
logger.debug("Using RequestToViewNameTranslator [" + this .viewNameTranslator + "]" );
}
}
catch (NoSuchBeanDefinitionException ex) {
this .viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class );
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate RequestToViewNameTranslator with name '" +
REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME + "': using default [" + this .viewNameTranslator +
"]" );
}
}
}
Copy 初始化RequestToViewNameTranslator,如果没有配置,使用默认的
initViewResolvers(ApplicationContext context)
private void initViewResolvers(ApplicationContext context) {
this .viewResolvers = null ;
if (this .detectAllViewResolvers) {
Map<String, ViewResolver> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class , true , false );
if (!matchingBeans.isEmpty()) {
this .viewResolvers = new ArrayList<ViewResolver>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .viewResolvers);
}
}
else {
try {
ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class );
this .viewResolvers = Collections.singletonList(vr);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .viewResolvers == null ) {
this .viewResolvers = getDefaultStrategies(context, ViewResolver.class );
if (logger.isDebugEnabled()) {
logger.debug("No ViewResolvers found in servlet '" + getServletName() + "': using default" );
}
}
}
Copy 初始化ViewResolver,如果没有配置,使用默认的
initFlashMapManager(ApplicationContext context)
private void initFlashMapManager(ApplicationContext context) {
try {
this .flashMapManager = context.getBean(FLASH_MAP_MANAGER_BEAN_NAME, FlashMapManager.class );
if (logger.isDebugEnabled()) {
logger.debug("Using FlashMapManager [" + this .flashMapManager + "]" );
}
}
catch (NoSuchBeanDefinitionException ex) {
this .flashMapManager = getDefaultStrategy(context, FlashMapManager.class );
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate FlashMapManager with name '" +
FLASH_MAP_MANAGER_BEAN_NAME + "': using default [" + this .flashMapManager + "]" );
}
}
}
Copy 初始化FlashMapManager,如果没有配置,使用默认的
总结 这里主要初始化DispatcherServlet的相关组件, 其中最重要的组件有: HandlerMapping、 HandlerAdapter
Copy 4.2 建立Map<url, controller>关系 综上,我们从容器中获取所有的HandlerMapping对象时,会获取到其中的BeanNameUrlHandlerMapping、SimpleUrlHandlerMapping和RequestMappingHandlerMapping,这个类继承了AbstractDetectingUrlHandlerMapping,AbstractDetectingUrlHandlerMapping继承了AbstractUrlHandlerMapping,AbstractUrlHandlerMapping继承了WebApplicationObjectSupport,WebApplicationObjectSupport继承了ApplicationObjectSupport,ApplicationObjectSupport实现了ApplicationContextAware接口
即:HandlerMapping实例间接实现了ApplicationContextAware接口
4.2.1 SimpleUrlHandlerMapping 4.2.1.1 结构图
根据[@Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext](/Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext) context)方法
4.3.1.2 ApplicationObjectSupport#setApplicationContext(ApplicationContext context)
@Override
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
this .applicationContext = null ;
this .messageSourceAccessor = null ;
}
else if (this .applicationContext == null ) {
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]" );
}
this .applicationContext = context;
this .messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
if (this .applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this .applicationContext + "], passed-in one is [" + context + "]" );
}
}
}
Copy 4.2.1.3 WebApplicationObjectSupport#initApplicationContext(ApplicationContext context)
@Override
protected void initApplicationContext (ApplicationContext context ) {
super .initApplicationContext (context);
if (this .servletContext == null && context instanceof WebApplicationContext ) {
this .servletContext = ((WebApplicationContext ) context).getServletContext ();
if (this .servletContext != null ) {
initServletContext (this .servletContext );
}
}
}
Copy 4.2.1.4 SimpleUrlHandlerMapping#initApplicationContext()
@Override
public void initApplicationContext () throws BeansException {
super .initApplicationContext();
registerHandlers(this .urlMap);
}
Copy 4.2.1.5 SimpleUrlHandlerMapping#registerHandlers(Map<String, Object> urlMap)
protected void registerHandlers (Map <String , Object > urlMap) throws BeansException {
if (urlMap.isEmpty ()) {
logger.warn ("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping" );
}
else {
for (Map .Entry <String , Object > entry : urlMap.entrySet ()) {
String url = entry.getKey ();
Object handler = entry.getValue ();
if (!url.startsWith ("/" )) {
url = "/" + url;
}
if (handler instanceof String ) {
handler = ((String ) handler).trim ();
}
registerHandler (url, handler);
}
}
}
Copy 4.2.2 BeanNameUrlHandlerMapping 4.2.2.1 结构图
根据[@Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext](/Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext) context)方法
4.3.2.2 ApplicationObjectSupport#setApplicationContext(ApplicationContext context)
@Override
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
this .applicationContext = null ;
this .messageSourceAccessor = null ;
}
else if (this .applicationContext == null ) {
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]" );
}
this .applicationContext = context;
this .messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
if (this .applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this .applicationContext + "], passed-in one is [" + context + "]" );
}
}
}
Copy 4.2.2.3 WebApplicationObjectSupport#initApplicationContext(ApplicationContext context)
@Override
protected void initApplicationContext (ApplicationContext context ) {
super .initApplicationContext (context);
if (this .servletContext == null && context instanceof WebApplicationContext ) {
this .servletContext = ((WebApplicationContext ) context).getServletContext ();
if (this .servletContext != null ) {
initServletContext (this .servletContext );
}
}
}
Copy 4.2.2.4 AbstractDelectingUrlHandlerMapping#initApplicationContext()
@Override
public void initApplicationContext () throws ApplicationContextException {
super .initApplicationContext();
detectHandlers();
}
Copy 4.2.2.5 AbstractDelectingUrlHandlerMapping#detectHandlers()
protected void detectHandlers () throws BeansException {
if (logger.isDebugEnabled ()) {
logger.debug ("Looking for URL mappings in application context: " + getApplicationContext ());
}
String [] beanNames = (this .detectHandlersInAncestorContexts ?
BeanFactoryUtils .beanNamesForTypeIncludingAncestors (getApplicationContext (), Object .class ) :
getApplicationContext ().getBeanNamesForType (Object .class ));
for (String beanName : beanNames) {
String [] urls = determineUrlsForHandler (beanName);
if (!ObjectUtils .isEmpty (urls)) {
registerHandler (urls, beanName);
}
else {
if (logger.isDebugEnabled ()) {
logger.debug ("Rejected bean name '" + beanName + "': no URL paths identified" );
}
}
}
}
Copy 4.2.3 RequestMappingHandlerMapping(重点) 4.2.3.1 结构图
根据[@Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext](/Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext) context)方法
4.3 处理请求 4.3.1 预备
1 .当一个请求过来时,优先使用Filter 处理,处理完之后根据配置找到对应的Servlet,我们这里只配置了一个DispatcherServlet用于处理所有请求
2 .Dispatcher 处理请求时,优先调用service (req, resp)方法,该方法由其父类FrameworkServlet实现
Copy 4.3.2 FrameworkServlet#service(HttpServletRequest request, HttpServletResponse response)
@Override
protected void service (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null ) {
processRequest(request, response);
}
else {
super .service(request, response);
}
}
Copy 4.3.3 HttpServlet#service(HttpServletRequest req, HttpServletResponse resp) 这个方法根据不同的请求方法调用不同的处理方法,如doGet(),doPost()等,具体的方法有子类FrameworkServlet实现
protected void service (HttpServletRequest req, HttpServletResponse resp ) throws ServletException, IOException {
String method = req.getMethod();
if (method.equals (METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1 ) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000 )) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals (METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals (METHOD_POST)) {
doPost(req, resp);
} else if (method.equals (METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals (METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals (METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals (METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented" );
Object[] errArgs = new Object[1 ];
errArgs[0 ] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
Copy 4.3.4 Framework#doGet(HttpServletRequest request, HttpServletResponse response)
@Override
protected final void doGet (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
Copy 4.3.5 Framework#processRequest(HttpServletRequest request, HttpServletResponse response)
protected final void processRequest (HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null ;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor ());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException ("Request processing failed" , ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null ) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null ) {
this .logger.debug("Could not complete request" , failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing" );
}
else {
this .logger.debug("Successfully completed request" );
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
Copy 4.3.6 DispatcherServlet#doService(HttpServletRequest request, HttpServletResponse response)
@Override
protected void doService (HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled ()) {
String resumed = WebAsyncUtils .getAsyncManager (request).hasConcurrentResult () ? " resumed" : "" ;
logger.debug ("DispatcherServlet with name '" + getServletName () + "'" + resumed +
" processing " + request.getMethod () + " request for [" + getRequestUri (request) + "]" );
}
Map <String , Object > attributesSnapshot = null ;
if (WebUtils .isIncludeRequest (request)) {
attributesSnapshot = new HashMap <String , Object >();
Enumeration <?> attrNames = request.getAttributeNames ();
while (attrNames.hasMoreElements ()) {
String attrName = (String ) attrNames.nextElement ();
if (this .cleanupAfterInclude || attrName.startsWith ("org.springframework.web.servlet" )) {
attributesSnapshot.put (attrName, request.getAttribute (attrName));
}
}
}
request.setAttribute (WEB_APPLICATION_CONTEXT_ATTRIBUTE , getWebApplicationContext ());
request.setAttribute (LOCALE_RESOLVER_ATTRIBUTE , this .localeResolver );
request.setAttribute (THEME_RESOLVER_ATTRIBUTE , this .themeResolver );
request.setAttribute (THEME_SOURCE_ATTRIBUTE , getThemeSource ());
FlashMap inputFlashMap = this .flashMapManager .retrieveAndUpdate (request, response);
if (inputFlashMap != null ) {
request.setAttribute (INPUT_FLASH_MAP_ATTRIBUTE , Collections .unmodifiableMap (inputFlashMap));
}
request.setAttribute (OUTPUT_FLASH_MAP_ATTRIBUTE , new FlashMap ());
request.setAttribute (FLASH_MAP_MANAGER_ATTRIBUTE , this .flashMapManager );
try {
doDispatch (request, response);
}
finally {
if (!WebAsyncUtils .getAsyncManager (request).isConcurrentHandlingStarted ()) {
if (attributesSnapshot != null ) {
restoreAttributesAfterInclude (request, attributesSnapshot);
}
}
}
}
Copy 4.3.7 DispatcherServlet#doDispatch(HttpServletRequest request, HttpServletResponse response)
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null ;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null ;
Exception dispatchException = null ;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null ) {
noHandlerFound(processedRequest, response);
return ;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET" .equals(method);
if (isGet || "HEAD" .equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest (request, response).checkNotModified(lastModified) && isGet) {
return ;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return ;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return ;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException ("Handler dispatch failed" , err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException ("Handler processing failed" , err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null ) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
Copy 4.3.8 DispatcherServlet#getHandler(HttpServletRequest request)
protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this .handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'" );
}
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null ) {
return handler;
}
}
return null ;
}
Copy 4.3.9 DispatcherServlet#getHandlerAdapter(Object handler)
protected HandlerAdapter getHandlerAdapter (Object handler) throws ServletException {
for (HandlerAdapter ha : this .handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]" );
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException ("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler" );
}
Copy 4.3.10 AbstractHandlerMethodAdapter#handle(HttpServletRequest request, HttpServletResponse response, Object handler)
public final ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
Copy 4.3.11 RequstMappingHandlerAdapter#handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
protected ModelAndView handleInternal (HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
if (this .synchronizeOnSession) {
HttpSession session = request.getSession(false );
if (session != null ) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this .cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
Copy 5.springmvc组件解析 5.1 入口 1.由前面分析, servlet容器在加载web.xml时, 会初始化DispatcherServlet, 此时会执行DispatcherServlet的静态代码块
static {
try {
ClassPathResource resource = new ClassPathResource (DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException ("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
Copy DispatcherServlet.properties
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation .RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation .RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation .ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation .ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
Copy 2.子容器XmlWebApplicationContext启动时,发布ContextRefreshedEvent事件,被已经添加的ContextRefreshListener监听,回调其onApplicationEvent(ContextRefreshedEvent event)方法,这个方法中调用了DispatcherServlet的onRefresh(ApplicationContext context)方法,在onRefresh方法中会调用核心方法 initStrategies,在 initStrategies 方法中完成九大组件的初始化
3.注意:以上配置文件中的组件不会一开始就加载到子容器XmlWebApplicationContext中,子容器一开始只会加载其他核心bean以及配置文件springmvc中bean,所以如果我们在springmvc.xml文件中配置了相关组件,优先会使用我们配置的,如果没有,才会使用 DispatcherServlet.properties 配置的相关组件
DispatcherServlet#onRefresh(ApplicationContext context)
@Override
protected void onRefresh (ApplicationContext context ) {
initStrategies (context);
}
Copy DispatcherServlet#initStrategies(ApplicationContext context)
protected void initStrategies (ApplicationContext context) {
initMultipartResolver (context);
initLocaleResolver (context);
initThemeResolver (context);
initHandlerMappings (context);
initHandlerAdapters (context);
initHandlerExceptionResolvers (context);
initRequestToViewNameTranslator (context);
initViewResolvers (context);
initFlashMapManager (context);
}
Copy 5.2 MultipartResolver MultipartResolver是九大组件之一,用于处理文件上传
Copy 5.2.1 初始化
private void initMultipartResolver(ApplicationContext context) {
try {
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) {
this .multipartResolver = null ;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared" );
}
}
}
Copy 5.2.2 实现 MultipartResolver有两个实现:CommonsMultipartResolver 和 StandardServletMultipartResolver,如果在springmvc中需要使用,则需要在springmvc-config.xml文件中进行注册,否则默认无法解析文件上传
5.2.2.1 CommonsMultipartResolver 1.在springmvc.config文件中配置CommonsMultipartResolver
<bean class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name ="maxUploadSize" value ="104857600" />
<property name ="maxInMemorySize" value ="104857600" />
</bean >
Copy 2.pom.xml文件中需要引入commons-file,因为CommonsMultipartResolver借助了commons-file
<dependency >
<groupId > commons-fileupload</groupId >
<artifactId > commons-fileupload</artifactId >
<version > 1.3.1</version >
</dependency >
Copy 5.2.2.2 StandardServletMultipartResolver 这个是springboot中默认引入的bean,springboot采用spi机制,在spring-boot-autoconfigure包的resources/META-INF/spring.factories文件中,引入了MultipartAutoConfiguration,在MultipartAutoConfiguration中根据条件,向spring容器中注册了 StandardServletMultipartResolver
5.2.3 应用 在核心处理流程中,DispatcherServlet#doDispatch方法中
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null ;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null ;
Exception dispatchException = null ;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
......
}
Copy 5.3 HandlerMapping HandlerMapping是九大组件之一,负责为请求找到合适的 HandlerExecutionChain 处理器执行链,包含处理器(handler)和拦截器(interceptors),处理器通常就是我们在@Controller 下@RequestMapping 注解对应的方法的封装,拦截器是对处理请求进行增强处理,用于对处理器的增强
Copy 5.3.1 初始化
private void initHandlerMappings(ApplicationContext context) {
this .handlerMappings = null ;
if (this .detectAllHandlerMappings) {
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerMappings = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class );
this .handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerMappings == null ) {
this .handlerMappings = getDefaultStrategies(context, HandlerMapping.class );
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties" );
}
}
}
Copy 5.3.2 实现 5.3.2.1 结构图
1. 蓝色框 AbstractHandlerMapping 抽象类, 实现了getHandler方法, 用于处理器映射器找到对应的 HandlerExecutionChain, getHandler中采用模板方法模式, 子类需要实现 getHandlerInternal(HttpServletRequest request) 抽象方法
2. AbstractHandlerMapping的子类主要分为两类:
黄色框 AbstractUrlHandlerMapping, 是基于url进行匹配的, 目前这种方式已经基本不用了, 我们初始化获取的 SimpleUrlHandlerMapping 和 BeanNameUrlHandlerMapping 就是属于此类, 所以接下来也不会分析了
红色框 AbstractHandlerMethodMapping系, 基于Method 匹配, 我们现在使用的@RequestMapping 注解就是基于这种方式, 这个是我们接下来会具体分析的
Copy 5.3.2.2 AbstractHandlerMapping
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping , Ordered , BeanNameAware {
@Nullable
private Object defaultHandler;
private UrlPathHelper urlPathHelper = new UrlPathHelper ();
private PathMatcher pathMatcher = new AntPathMatcher ();
private final List<Object> interceptors = new ArrayList <>();
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList <>();
......
}
Copy initApplicationContext
@Override
protected void initApplicationContext() throws BeansException {
extendInterceptors (this.interceptors);
detectMappedInterceptors (this.adaptedInterceptors);
initInterceptors ();
}
Copy getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null ) {
handler = getDefaultHandler();
}
if (handler == null ) {
return null ;
}
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this .corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
Copy getHandlerExecutionChain
protected HandlerExecutionChain getHandlerExecutionChain (Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler
: new HandlerExecutionChain (handler));
String lookupPath = this .urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this .adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this .pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain
}
Copy 5.3.2.3 RequestMappingHandlerMapping 结构图
由结构图可以看出,RequestMappingHandlerMapping间接继承了父类ApplicationObjectSupport,实现了Aware接口,根据[@Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext](/Spring源码,在bean的创建和初始化过程中,会先后八次调用BeanPostProcessor,在属性注入之后,会第七次调用BeanPostProcessor,执行部分Aware回调接口,这里会回调其父类ApplicationObjectSupport的setApplicationContext(ApplicationContext) context)方法
ApplicationObjectSupport#setApplicationContext(ApplicationContext context)
@Override
public final void setApplicationContext(ApplicationContext context) throws BeansException {
if (context == null && !isContextRequired()) {
this .applicationContext = null ;
this .messageSourceAccessor = null ;
}
else if (this .applicationContext == null ) {
if (!requiredContextClass().isInstance(context)) {
throw new ApplicationContextException(
"Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]" );
}
this .applicationContext = context;
this .messageSourceAccessor = new MessageSourceAccessor(context);
initApplicationContext(context);
}
else {
if (this .applicationContext != context) {
throw new ApplicationContextException(
"Cannot reinitialize with different application context: current one is [" +
this .applicationContext + "], passed-in one is [" + context + "]" );
}
}
}
Copy WebApplicationObjectSupport#initApplicationContext(ApplicationContext context)
@Override
protected void initApplicationContext (ApplicationContext context ) {
super .initApplicationContext (context);
if (this .servletContext == null && context instanceof WebApplicationContext ) {
this .servletContext = ((WebApplicationContext ) context).getServletContext ();
if (this .servletContext != null ) {
initServletContext (this .servletContext );
}
}
}
Copy AbstractHandlerMapping#initApplicationContext()
@Override
protected void initApplicationContext () throws BeansException {
extendInterceptors(this .interceptors);
detectMappedInterceptors(this .adaptedInterceptors);
initInterceptors();
}
Copy 在第七次BeanPostProcessor调用后,会调用bean的初始化方法,因为RequestMappingHandlerMapping实现了InitializingBean接口,所以会回调其afterPropertiesSet()方法,在这里真正完成url和HandlerMethod的绑定
RequestMappingHandlerMapping#afterPropertiesSet()
@Override
public void afterPropertiesSet() {
this .config = new RequestMappingInfo.BuilderConfiguration();
this .config.setUrlPathHelper(getUrlPathHelper());
this .config.setPathMatcher(getPathMatcher());
this .config.setSuffixPatternMatch(this .useSuffixPatternMatch);
this .config.setTrailingSlashMatch(this .useTrailingSlashMatch);
this .config.setRegisteredSuffixPatternMatch(this .useRegisteredSuffixPatternMatch);
this .config.setContentNegotiationManager(getContentNegotiationManager());
super .afterPropertiesSet();
}
Copy AbstractHandlerMethodMapping#afterPropertiesSet()
@Override
public void afterPropertiesSet ( ) {
initHandlerMethods ();
}
Copy AbstractHandlerMethodMapping#initHandlerMethods()
protected void initHandlerMethods () {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean (beanName);
}
}
handlerMethodsInitialized (getHandlerMethods());
}
Copy AbstractHandlerMethodMapping#processCandidateBean(String beanName)
protected void processCandidateBean (String beanName ) {
Class <?> beanType = null ;
try {
beanType = obtainApplicationContext ().getType (beanName);
}
catch (Throwable ex) {
if (logger.isTraceEnabled ()) {
logger.trace ("Could not resolve type for bean '" + beanName + "'" , ex);
}
}
if (beanType != null && isHandler (beanType)) {
detectHandlerMethods (beanName);
}
}
Copy AbstractHandlerMethodMapping#detectHandlerMethods
protected void detectHandlerMethods (Object handler ) {
Class <?> handlerType = (handler instanceof String ?
obtainApplicationContext ().getType ((String ) handler) : handler.getClass ());
if (handlerType != null ) {
Class <?> userType = ClassUtils .getUserClass (handlerType);
Map <Method , T> methods = MethodIntrospector .selectMethods (userType,
(MethodIntrospector .MetadataLookup <T>) method -> {
try {
return getMappingForMethod (method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException ("Invalid mapping on handler class [" + userType.getName () + "]: " + method, ex);
}
});
if (logger.isTraceEnabled ()) {
logger.trace ("Mapped " + methods.size () + " handler method(s) for " + userType + ": " + methods);
}
methods.forEach ((method, mapping) -> {
Method invocableMethod = AopUtils .selectInvocableMethod (method, userType);
registerHandlerMethod (handler, invocableMethod, mapping);
});
}
}
Copy AbstractHandlerMethodMapping#registerHandlerMethod
protected void registerHandlerMethod (Object handler, Method method, T mapping ) {
this .mappingRegistry .register (mapping, handler, method);
}
Copy MappingRegistry#register
public void register (T mapping, Object handler, Method method ) {
this .readWriteLock .writeLock ().lock ();
try {
HandlerMethod handlerMethod = createHandlerMethod (handler, method);
assertUniqueMethodMapping (handlerMethod, mapping);
this .mappingLookup .put (mapping, handlerMethod);
List <String > directUrls = getDirectUrls (mapping);
for (String url : directUrls) {
this .urlLookup .add (url, mapping);
}
String name = null ;
if (getNamingStrategy () != null ) {
name = getNamingStrategy ().getName (handlerMethod, mapping);
addMappingName (name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration (handler, method, mapping);
if (corsConfig != null ) {
this .corsLookup .put (handlerMethod, corsConfig);
}
this .registry .put (mapping, new MappingRegistration <>(mapping, handlerMethod, directUrls, name));
}
finally {
this .readWriteLock .writeLock ().unlock ();
}
}
Copy 5.3.2.4 总结 初始化HandlerMapping时, 扫描有 @Controller 或者 @RequestMapping 注解的类下面的方法, 如果方法上面有 @RequestMapping 注解(包括GetMapping, PostMapping等), 则会为该方法创建对应的 RequestMappingInfo 对象, 将所有的 RequestMappingInfo 对象和 Method 以及方法所在类, 往 MappingRegistry 进行注册, 会生成 HandlerMethod 处理器(Method 所有信息)对象。这样一来, 当 Spring MVC 的 DispatcherServlet 处理请求的时候, 获取到对应的 HandlerMethod 处理器, 就可以通过反射执行对应的方法了
Copy 5.3.3 应用 在 DispatcherServlet 的doDispatch的方法中
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null ;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null ;
Exception dispatchException = null ;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null ) {
noHandlerFound(processedRequest, response);
return ;
}
......
}
Copy DispatcherServlet#getHandler
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this .handlerMappings != null ) {
for (HandlerMapping mapping : this .handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null ) {
return handler;
}
}
}
return null ;
}
Copy 在AbstractHandlerMapping中的getHandler方法,采用模板方法设计模式,回调子类的getHandlerInternal方法,这里我们看 RequestMappingHandlerMapping 的getHandlerInternal方法
RequestMappingHandlerMapping#getHandlerInternal
protected HandlerMethod getHandlerInternal (HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this .mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null );
}
finally {
this .mappingRegistry.releaseReadLock();
}
}
Copy RequestMappingHandlerMapping#lookupHandlerMethod
protected HandlerMethod lookupHandlerMethod (String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList <>();
List<T> directPathMatches = this .mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null ) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this .mappingRegistry.getMappings().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Comparator<Match> comparator = new MatchComparator (getMappingComparator(request));
matches.sort(comparator);
Match bestMatch = matches.get(0 );
if (matches.size() > 1 ) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1 );
if (comparator.compare(bestMatch, secondBestMatch) == 0 ) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException (
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}" );
}
}
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this .mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
Copy 5.4 HandlerAdapter HandlerAdapter 组件, 是DispatcherServlet 的九大组件之一。在上一步中, 我们通过HandlerMapping 组件获取了 HandlerExecutionChain , HandlerExecutionChain 是对对应handler和interceptor的封装, 这里的 handler 是object 类型, 需要有一个调用者来实现 handler 是怎么被执行。Spring 中的处理器的有多种实现, 比如用户的处理器可以实现 Controller 接口或者 HttpRequestHandler 接口(这两种方式基本不用了), 也可以用 @RequestMapping 注解将方法作为一个处理器等(这是目前主流的方式), 这就导致 SpringMVC 无法直接执行这个处理器。所以这里需要一个处理器适配器,由它去执行处理器。
Copy 5.4.1 初始化
private void initHandlerAdapters(ApplicationContext context) {
this .handlerAdapters = null ;
if (this .detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class );
this .handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerAdapters == null ) {
this .handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class );
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties" );
}
}
}
Copy 这里的初始化和 HandlerMapping 的初始化类似,首先从子springmvc容器中获取,即先获取我们在springmvc-config.xml配置文件中配置的 HandlerAdapter以及springmvc容器中自带的(子springmvc容器在初始化时会向容器中注册一些默认的bean, 其中包括一些组件),如果未配置,则获得默认配置 DispatcherServlet.properties 中的 HandlerAdapter 类
5.4.2 实现 5.4.2.1 结构图
5.4.2.2 HandlerAdapter
public interface HandlerAdapter {
boolean supports (Object handler) ;
@Nullable
ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
long getLastModified (HttpServletRequest request, Object handler) ;
}
Copy 5.4.2.3 AbstractHandlerMethodAdapter
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter , Ordered {
private int order = Ordered.LOWEST_PRECEDENCE;
public AbstractHandlerMethodAdapter () {
super (false );
}
@Override
public final boolean supports (Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
protected abstract boolean supportsInternal (HandlerMethod handlerMethod) ;
@Override
@Nullable
public final ModelAndView handle (HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Nullable
protected abstract ModelAndView handleInternal (HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
@Override
public final long getLastModified (HttpServletRequest request, Object handler) {
return getLastModifiedInternal(request, (HandlerMethod) handler);
}
protected abstract long getLastModifiedInternal (HttpServletRequest request, HandlerMethod handlerMethod) ;
}
Copy 5.4.2.4 RequestMappingHandlerAdapter
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware , InitializingBean {
...
}
Copy 由上可以看到,其实现了InitializingBean接口,由@spring源码可知,实例化时会回调其afterPropertiesSet方法
RequestMappingHandlerAdapter#afterPropertiesSet
@Override
public void afterPropertiesSet ( ) {
initControllerAdviceCache ();
if (this .argumentResolvers == null ) {
List <HandlerMethodArgumentResolver > resolvers = getDefaultArgumentResolvers ();
this .argumentResolvers = new HandlerMethodArgumentResolverComposite ().addResolvers (resolvers);
}
if (this .initBinderArgumentResolvers == null ) {
List <HandlerMethodArgumentResolver > resolvers = getDefaultInitBinderArgumentResolvers ();
this .initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite ().addResolvers (resolvers);
}
if (this .returnValueHandlers == null ) {
List <HandlerMethodReturnValueHandler > handlers = getDefaultReturnValueHandlers ();
this .returnValueHandlers = new HandlerMethodReturnValueHandlerComposite ().addHandlers (handlers);
}
}
Copy RequestMappingHandlerAdapter#initControllerAdviceCache
private void initControllerAdviceCache () {
if (getApplicationContext() == null ) {
return ;
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null ) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this .modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this .initBinderAdviceCache.put(adviceBean, binderMethods);
}
if (RequestBodyAdvice.class .isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add (adviceBean);
}
if (ResponseBodyAdvice.class .isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add (adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this .requestResponseBodyAdvice.addAll(0 , requestResponseBodyAdviceBeans);
}
if (logger.isDebugEnabled()) {
int modelSize = this .modelAttributeAdviceCache.size();
int binderSize = this .initBinderAdviceCache.size();
int reqCount = getBodyAdviceCount(RequestBodyAdvice.class );
int resCount = getBodyAdviceCount(ResponseBodyAdvice.class );
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0 ) {
logger.debug("ControllerAdvice beans: none" );
}
else {
logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + ", ResponseBodyAdvice" );
}
}
}
Copy 总结
@ControllerAdvice 注解:用于 Controller 类的增强类, 其中可定义多种增强的方法, 例如 @ExceptionHandler 注解的方法用于处理器 Controller 抛出的异常等
Copy RequestMappingHandlerAdapter#handleInternal
protected ModelAndView handleInternal (HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
if (this .synchronizeOnSession) {
HttpSession session = request.getSession(false );
if (session != null ) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this .cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
Copy RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this .argumentResolvers != null ) {
invocableMethod.setHandlerMethodArgumentResolvers(this .argumentResolvers);
}
if (this .returnValueHandlers != null ) {
invocableMethod.setHandlerMethodReturnValueHandlers(this .returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this .parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this .ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this .asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this .taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this .callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this .deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0 ];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]" ;
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null ;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
Copy 5.4.2.5 ServletInvocableHandlerMethod 结构图
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle (ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null ) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true );
return ;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true );
return ;
}
mavContainer.setRequestHandled(false );
Assert.state(this .returnValueHandlers != null , "No return value handlers" );
try {
this .returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
Copy InvocableHandlerMethod#invokeForRequest
public Object invokeForRequest (NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object ... providedArgs) throws Exception {
Object [] args = getMethodArgumentValues (request, mavContainer, providedArgs);
if (logger.isTraceEnabled ()) {
logger.trace ("Arguments: " + Arrays .toString (args));
}
return doInvoke (args);
}
Copy InvocableHandlerMethod#getMethodArgumentValues
protected Object [] getMethodArgumentValues (NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object ... providedArgs) throws Exception {
MethodParameter [] parameters = getMethodParameters ();
Object [] args = new Object [parameters.length ];
for (int i = 0 ; i < parameters.length ; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery (this .parameterNameDiscoverer );
args[i] = resolveProvidedArgument (parameter, providedArgs);
if (args[i] != null ) {
continue ;
}
if (this .argumentResolvers .supportsParameter (parameter)) {
try {
args[i] = this .argumentResolvers .resolveArgument (
parameter, mavContainer, request, this .dataBinderFactory );
continue ;
}
catch (Exception ex) {
if (logger.isDebugEnabled ()) {
String message = ex.getMessage ();
if (message != null && !message.contains (parameter.getExecutable ().toGenericString ())) {
logger.debug (formatArgumentError (parameter, message));
}
}
throw ex;
}
}
if (args[i] == null ) {
throw new IllegalStateException (formatArgumentError (parameter, "No suitable resolver" ));
}
}
return args;
}
Copy 5.4.3 应用
综上, 在我们最常见的 @RequestMapping 注解类型, 首先会由对应的 HandlerMapping --> RequestMappingHandlerMapping, 获取到对应的HandlerExecutionChain, HandlerExecutionChain封装了的 HandlerMethod 和 HandlerInterceptor, 再由适配器模式根据Handler(这里是HandlerMethod)获取到对应的HandlerAdapter --> RequestMappingHandlerAdapter, 由 RequestMappingHandlerAdapter 执行对应的 HandlerMethod 和 HandlerInterceptor。
具体执行过程:
RequestMappingHandlerAdapter 会先将 HandlerMethod 封装成 ServletInvocableHandlerMethod, 通过 HandlerMethodArgumentResolver 组件解析请求参数,如:通过 RequestResponseBodyMethodProcessor 解析 @RequestBody 参数等; 然后再通过反射机制调用对应的方法(controller中对应的方法), 获取到执行结果后需要通过 HandlerMethodReturnValueHandler 结果处理器来进行处理。
在 HandlerMethodArgumentResolver 解析参数和 HandlerMethodReturnValueHandler 解析返回值的过程中, 会借助 HttpMessageConverter 进行参数解析
Copy 5.5 HandlerExceptionResolver HandlerExceptionResolver是处理器异常解析器,将处理器执行时发生的异常,解析转换成对应的ModelAndView
Copy 5.5.1 初始化
private void initHandlerExceptionResolvers(ApplicationContext context) {
this .handlerExceptionResolvers = null ;
if (this .detectAllHandlerExceptionResolvers) {
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class , true , false );
if (!matchingBeans.isEmpty()) {
this .handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this .handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class );
this .handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this .handlerExceptionResolvers == null ) {
this .handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class );
if (logger.isTraceEnabled()) {
logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties" );
}
}
}
Copy 初始化的过程和上述组件初始化过程类似,先从springmvc容器中获取相关组件并排序,如果获取失败,则获取 DispatcherServlet.properties 配置文件中的默认配置,这里默认获取3个:ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
5.5.2 应用 DispatcherServlet#doDispatch
protected void doDispatch (HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null ;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null ;
Exception dispatchException = null ;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null ) {
noHandlerFound(processedRequest, response);
return ;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET" .equals(method);
if (isGet || "HEAD" .equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest (request, response).checkNotModified(lastModified) && isGet) {
return ;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return ;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return ;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException ("Handler dispatch failed" , err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException ("Handler processing failed" , err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null ) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
Copy DispatcherServlet#processDispatchResult
private void processDispatchResult (HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false ;
if (exception != null ) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered" , exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null );
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null );
}
}
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned." );
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return ;
}
if (mappedHandler != null ) {
mappedHandler.triggerAfterCompletion(request, response, null );
}
}
Copy DispatcherServlet#processHandlerException
@Nullable
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
ModelAndView exMv = null ;
if (this .handlerExceptionResolvers != null ) {
for (HandlerExceptionResolver resolver : this .handlerExceptionResolvers) {
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null ) {
break ;
}
}
}
if (exMv != null ) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null ;
}
if (!exMv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null ) {
exMv.setViewName(defaultViewName);
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using resolved error view: " + exMv, ex);
}
if (logger.isDebugEnabled()) {
logger.debug("Using resolved error view: " + exMv);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
Copy 5.6 RequestToViewNameTranslator 5.6.1 初始化
private void initRequestToViewNameTranslator(ApplicationContext context) {
try {
this .viewNameTranslator =
context.getBean(REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME, RequestToViewNameTranslator.class );
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this .viewNameTranslator.getClass().getSimpleName());
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this .viewNameTranslator);
}
}
catch (NoSuchBeanDefinitionException ex) {
this .viewNameTranslator = getDefaultStrategy(context, RequestToViewNameTranslator.class );
if (logger.isTraceEnabled()) {
logger.trace("No RequestToViewNameTranslator '" + REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME +
"': using default [" + this .viewNameTranslator.getClass().getSimpleName() + "]" );
}
}
}
Copy 初始化的过程和上述组件初始化过程类似,先从springmvc容器中获取相关组件并排序,如果获取失败,则获取 DispatcherServlet.properties 配置文件中的默认配置,这里默认获取1个:DefaultRequestToViewNameTranslator
5.6.2 应用 在 DispatcherServlet #doDispatch方法中,调用了applyDefaultViewName
DispatcherServlet#applyDefaultViewName
private void applyDefaultViewName (HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
if (mv != null && !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if (defaultViewName != null ) {
mv.setViewName(defaultViewName);
}
}
}
Copy DefaultRequestToViewNameTranslator#getViewName
public String getViewName (HttpServletRequest request ) {
String lookupPath = this .urlPathHelper .getLookupPathForRequest (request);
return (this .prefix + transformPath (lookupPath) + this .suffix );
}
Copy 5.7 LocaleResolver LocaleResolver 组件, 本地化(国际化)解析器, 提供国际化支持, 这里暂略
Copy 5.8 ViewResolver ViewResolver 组件, 是试图解析器, 这里暂略
Copy 注解版 0.准备阶段 WebAppInitializer
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class <?>[] getRootConfigClasses ( ) {
return new Class []{RootConfig .class };
}
@Override
protected Class <?>[] getServletConfigClasses ( ) {
return new Class []{WebConfig .class };
}
@Override
protected String [] getServletMappings ( ) {
return new String []{"/" };
}
}
Copy config
@ComponentScan (value ={"com.tca" },
excludeFilters={@ComponentScan .Filter (type = FilterType.ANNOTATION,value = {Controller.class})})
public class RootConfig {
}
Copy
@EnableWebMvc
@Configuration
@ComponentScan (value = {"com.tca" }, includeFilters={@ComponentScan .Filter (
type= FilterType.ANNOTATION, value={Controller.class})}, useDefaultFilters = false)
public class WebConfig {
}
Copy controller
@RequestMapping ("/hello" )
@Controller
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping ("/test" )
@ResponseBody
public String test () {
return helloService .test ();
}
}
Copy service
@Service
public class HelloService {
public String test ( ) {
return "test" ;
}
}
Copy 1.Servlet3.0 1.1 servlet3.0规范 目的 servlet3.0规范主要是用于去web.xml化, 使用纯注解的方式
Copy 实现 实现servlet3.0规范的servlet容器(如tomcat),必须实现以下规范
1. servlet容器(如tomcat)启动时会扫描当前应用里所有的jar中ServletContainerInitializer的实现类
2. 当前jar中ServletContainerInitializer的实现类, 需要绑定在当前jar下resource目录下的META_INF/services/javax.servlet.ServletContainerInitializer文件, 文件里内容是ServletContainerInitializer实现类的全类名
3. 自定义ServletContainerInitializer的实现上可以添加@HandlesTypes 注解, 注解中可以传入一些class /interface , 这些class /interface 的所有子类实现都会作为onStartup方法的参数
4. 在onstart方法中, 我们可以向servlet容器中注册web三大组件: Listener(监听器), Filter(过滤器), Servlet, 完美替代了web.xml的作用
Copy 总结 容器启动时会扫描所有jar中的META_INF/services/javax.servlet.ServletContainerInitializer文件, 实例化文件中的类, 并调用onStartup方法(SPI机制)
Copy 2.springmvc注解版对servlet3.0规范的适配 2.1 实现
1. spring-web的jar中, 在 resource/META_INF/services/javax.servlet.ServletContainerInitializer 目录下, 有一个ServletContainerInitializer的实现类:
org.springframework.web.SpringServletContainerInitializer
2. SpringServletContainerInitializer配置了@HandlesTypes 注解, 指定了WebApplicationInitializer接口
3. 容器启动时, 会调用SpringServletContainerInitializer的onStartup方法, 方法的参数有两个: 一是WebApplicationInitializer的所有实现类对应的Class对象, 另一个是ServletContext
Copy 2.2 SpringServletContainerInitializer SpringServletContainerInitializer上注解:@HandlesTypes(WebApplicationInitializer.class)
SpringServletContainerInitializer#onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) 根据上述,servlet容器(tomcat)启动时调用ServletContainerInitializer实现类的onStartup方法
@Override
public void onStartup (@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
List<WebApplicationInitializer> initializers = new LinkedList <>();
if (webAppInitializerClasses != null ) {
for (Class<?> waiClass : webAppInitializerClasses) {
if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
try {
initializers.add((WebApplicationInitializer)
ReflectionUtils.accessibleConstructor(waiClass).newInstance());
}
catch (Throwable ex) {
throw new ServletException ("Failed to instantiate WebApplicationInitializer class" , ex);
}
}
}
}
if (initializers.isEmpty()) {
servletContext.log("No Spring WebApplicationInitializer types detected on classpath" );
return ;
}
servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath" );
AnnotationAwareOrderComparator.sort(initializers);
for (WebApplicationInitializer initializer : initializers) {
initializer.onStartup(servletContext);
}
}
Copy 2.3 WebAppInitializer 2.3.1 结构图 2.3.2 启动流程 1.调用WebAppInitializer的onStartup方法,该方法其父类AbstractDispatcherServletInitializer、AbstractContextLoaderInitializer都有实现,首先调用AbstractDispatcherServletInitializer#onStartup方法
AbstractDispatcherServletInitializer#onStartup(ServletContext servletContext)
@Override
public void onStartup (ServletContext servletContext) throws ServletException {
super .onStartup(servletContext);
registerDispatcherServlet(servletContext);
}
Copy AbstractDispatcherServletInitializer#registerDispatcherServlet(ServletContext servletContext)
protected void registerDispatcherServlet (ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty" );
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null" );
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null" );
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null ) {
throw new IllegalStateException ("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name." );
}
registration.setLoadOnStartup(1 );
registration.addMapping(getServletMappings());
registration.setAsyncSupported(isAsyncSupported());
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
customizeRegistration(registration);
}
Copy AbstractContextLoaderInitializer#onStartup(ServletContext servletContext)
@Override
public void onStartup (ServletContext servletContext) throws ServletException {
registerContextLoaderListener(servletContext);
}
Copy AbstractContextLoaderInitializer#registerContextLoaderListener(ServletContext servletContext)
protected void registerContextLoaderListener (ServletContext servletContext) {
WebApplicationContext rootAppContext = createRootApplicationContext();
if (rootAppContext != null ) {
ContextLoaderListener listener = new ContextLoaderListener (rootAppContext);
listener.setContextInitializers(getRootApplicationContextInitializers());
servletContext.addListener(listener);
}
else {
logger.debug("No ContextLoaderListener registered, as " +
"createRootApplicationContext() did not return an application context" );
}
}
Copy 2.4 总结
1 .spring-web 的jar中, 在 resource/META_INF/services/javax.servlet .ServletContainerInitializer 目录下, 有一个ServletContainerInitializer的实现类:
org.springframework.web.SpringServletContainerInitializer
2 .SpringServletContainerInitializer配置了@HandlesTypes注解, 指定了WebApplicationInitializer接口
3 .容器启动时, 会调用SpringServletContainerInitializer的onStartup方法, 方法的参数有两个: 一是WebApplicationInitializer的所有实现类对应的Class对象, 另一个是ServletContext; 这里 WebApplicationInitializer的实现类只有一个: WebAppInitializer (自定义的)
4 .调用其onStartup方法时会完成父spring容器的创建, 子spring容器的创建, 以及完成DispatcherServlet的创建并注册到spring容器中, 添加监听器, 过滤器等
Copy 原文来自:https://gitee.com/tca/springmvc-configfile/blob/master/SpringMVC%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90.md
评论