# 概述

Spring框架提供的构建WEB应用程序的全功能MVC模块.

# MVC框架

  • 一种软件编程模式,M代表业务模型,V指用户界面,C则是控制器。
  • MVC框架的目的是将M和V的实现代码分离,控制器则负责确保M和V的同步,一旦M改变,V同步更新;

# 源码

# 架构设计

  • HandlerMapping
/*xxx: springMvc的九大组件之一*/
/*xxx: 用于查找 Handler的,springMVC会处理很多请求,具体的某一个请求才以哦那个哪个 Handler处理,需要使用该类来处理*/
public interface HandlerMapping {
    @Nullable
        /*xxx: 只有提供了一个抽象方法*/
        /*xxx: 获取处理器,但是实际返回的是 处理器执行链*/
        /*xxx: 能否返回处理器执行链,取决于当前所采用的策略*/
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
  • Handler(逻辑存在)
/*xxx: 处理器过滤链*/
public class HandlerExecutionChain {
    /*xxx: handler是一个对象,只要能够实际处理请求的 ,就是handler,可以是类,也可以是方法,也可以是其它*/
    private final Object handler;

    /*xxx: 处理器拦截器*/
    private final List<HandlerInterceptor> interceptorList = new ArrayList<>();
    
}
  • HandlerAdapter
/*xxx: springMVC的 九大组件之一*/
/*xxx: 用它的原因是因为: springMVC中,并没有对Handler做任何限制*/
public interface HandlerAdapter {
    /*xxx: 执行具体的Handler方法*/
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
  • ModelAndView
/*xxx: 业务逻辑与视图映射关系*/
public class ModelAndView {
    /*xxx: 与处理器类似,没有对 view 做限制 */
    private Object view;

    private ModelMap model;
}
  • ViewResolver
/*xxx: springMVC九大组件之一*/
	/*xxx: 用来将 String类型的视图名 和 Locale 解析为 View类型的视图*/
public interface ViewResolver {
    /*xxx: 将String类型的视图名,解析为实际的视图*/
    /*xxx: 它主要处理两个问题: 1.使用什么模板  2.采用什么技术填入参数*/
    View resolveViewName(String viewName, Locale locale) throws Exception;
}
  • View
/*xxx: 抽象视图*/
public interface View {
    /*xxx: 渲染,实际上是将相应的解析结果,输出到response中*/
    void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response)
            throws Exception;
}

# 流程设计

# 装载基础设施

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
    /*xxx: 在服务器容器启动后,将会自动初始化 (init-param 设置为非负数,或者没设 在context完成后,会自动执行初始化)*/
    /*xxx: 装载servlet时,会初始化上下文*/
    public final void init() throws ServletException {
        /*xxx: 模板方法,子类初始化的入口方法*/
        initServletBean();
    }

    protected void initServletBean() throws ServletException {
    }
}
/*xxx: 该方法重写了 除了 doHead外的所有方法,所以 本质上 是侵入了Servlet规范的*/
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    protected final void initServletBean() throws ServletException {
        /*xxx: 初始化 WebApplicationContext*/
        /*xxx: 初始化上下文时,分为两类,一类是springboot,通过外部设置进来,一类是spring,通过实例化上下文进行初始化*/
        this.webApplicationContext = initWebApplicationContext();
    }

    protected WebApplicationContext initWebApplicationContext() {
        WebApplicationContext wac = null;
        /*xxx: 如果已经通过构造方法设置了  webApplicationContext*/
        if (this.webApplicationContext != null) {
            wac = this.webApplicationContext;
        }
        if (wac == null) {
            /*xxx: 当 webAppicationContext 已经存在 ServletContext中时,通过配置在 Servlet中的 contextAttribute参数获取*/
            wac = findWebApplicationContext();
        }
        if (wac == null) {
            /*xxx: 如果 webApplicationContext还没有创建,则创建一个*/
            /*xxx: 正常情况下,就是走这个分支*/
            wac = createWebApplicationContext(rootContext);
        }
        if (!this.refreshEventReceived) {
            synchronized (this.onRefreshMonitor) {
                /*xxx: 当ContextRefreshedEvent事件没有触发时,调用此方法,为模板方法*/
                onRefresh(wac);
            }
        }
    }

    /*xxx: 表示已经调用过 refresh方法*/
    public void onApplicationEvent(ContextRefreshedEvent event) {
        this.refreshEventReceived = true;
        synchronized (this.onRefreshMonitor) {
            onRefresh(event.getApplicationContext());
        }
    }

    protected void onRefresh(ApplicationContext context) {
    }
}
public class DispatcherServlet extends FrameworkServlet {
    @Override
    /*xxx: 该方法 是  DispatcherServlet 的入口方法*/
    protected void onRefresh(ApplicationContext context) {
        /*xxx: 初始化9个组件*/
        initStrategies(context);
    }

    protected void initStrategies(ApplicationContext context) {
        /*xxx: 4.handlerMapping组件*/
        initHandlerMappings(context);

        /*xxx: 5.handlerAdapter组件*/
        initHandlerAdapters(context);

        /*xxx: 8.viewResolver组件*/
        initViewResolvers(context);
    }

    /*xxx: 初始化组件策略*/
    private void initHandlerMappings(ApplicationContext context) {
        this.handlerMappings = null;

        /*xxx: 从上下文中获取 handlerMapping,分为按类型获取多个,以及按名称获取单个*/
        /*xxx: 代码,略*/

        /*xxx: 上下文中,没有相应实现,则采用默认策略*/
        if (this.handlerMappings == null) {
            this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
        }
    }
}

# 正常运行逻辑

public class DispatcherServlet extends FrameworkServlet {
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        /*xxx: 处理请求的处理器链(包含处理器 和对应的 Interceptor)*/
        HandlerExecutionChain mappedHandler = null;

        try {
            /*xxx: 封装 Model 和 View 的容器*/
            ModelAndView mv = null;

            /*xxx: 根据 request 找到 handler*/
            mappedHandler = getHandler(processedRequest);

            /*xxx: 根据handler找到 handlerAdapter*/
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            /*xxx: 执行 相应 Interceptor 的 preHandle*/
            /*xxx: 前置拦截 */
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            /*xxx: 用 handlerAdapter 处理 handler*/
            /*xxx: controller 便是在此处执行的*/
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            /*xxx: 执行 相应 Interceptor 的 postHandle*/
            /*xxx: 后置拦截*/
            mappedHandler.applyPostHandle(processedRequest, response, mv);

            /*xxx: 处理上面处理之后的结果 (包括 处理异常,渲染页面,发出完成通知触发  Interceptor 的 afterCompletion)*/
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
            
        }catch (Exception e){
            
        }
    }

    /*xxx: 需要注意,HandlerMapping 具有优先级关系*/
    /*xxx: 在前的优先*/
    @Nullable
    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;
    }
    
    /*xxx: handlerAdapter也具有优先级关系*/
    /*xxx: 在前的优先 */
    protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
        if (this.handlerAdapters != null) {
            for (HandlerAdapter adapter : this.handlerAdapters) {
                if (adapter.supports(handler)) {
                    return adapter;
                }
            }
        }
    }

    /*xxx: 处理分发结果 */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        /*xxx: 渲染页面,具体在 render方法中执行*/
        render(mv, request, response);

        /*xxx: 发出请求处理完成的通知,触发 Interceptor 的 afterCompletion*/
        mappedHandler.triggerAfterCompletion(request, response, null);
    }

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        View view;

        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);

        view.render(mv.getModelInternal(), request, response);
        
    }

    /*xxx: viewResolvers 也具有优先级关系*/
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
                                   Locale locale, HttpServletRequest request) throws Exception {

        if (this.viewResolvers != null) {
            /*xxx: 使用 viewResolver进行渲染*/
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }
    
}

# 无视图返回逻辑

public class DispatcherServlet extends FrameworkServlet {
    /*xxx: 处理分发结果 */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        /*xxx: 渲染页面*/
        /*xxx: 当采用 @RestController,或者 @ResponseBody时,无视图返回,不进行渲染逻辑*/
        if (mv != null && !mv.wasCleared()) {
            /*xxx: 渲染页面,具体在 render方法中执行*/
            render(mv, request, response);
        }
        

        /*xxx: 发出请求处理完成的通知,触发 Interceptor 的 afterCompletion*/
        mappedHandler.triggerAfterCompletion(request, response, null);
    }
}

# 视图返回逻辑

public class DispatcherServlet extends FrameworkServlet {
    /*xxx: 处理分发结果 */
    private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                       @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                       @Nullable Exception exception) throws Exception {
        /*xxx: 渲染页面*/
        if (mv != null && !mv.wasCleared()) {
            /*xxx: 渲染页面,具体在 render方法中执行*/
            render(mv, request, response);
        }
        

        /*xxx: 发出请求处理完成的通知,触发 Interceptor 的 afterCompletion*/
        mappedHandler.triggerAfterCompletion(request, response, null);
    }

    protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
        View view;
        String viewName = mv.getViewName();
        if (viewName != null) {
            view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
        }
    }

    /*xxx: viewResolvers 也具有优先级关系*/
    protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
                                   Locale locale, HttpServletRequest request) throws Exception {
        if (this.viewResolvers != null) {
            /*xxx: 使用 viewResolver进行渲染*/
            for (ViewResolver viewResolver : this.viewResolvers) {
                View view = viewResolver.resolveViewName(viewName, locale);
                if (view != null) {
                    return view;
                }
            }
        }
        return null;
    }
    
}

# 视图重定向逻辑

/*xxx: 可用于解析 jsp视图*/
public class InternalResourceViewResolver extends UrlBasedViewResolver {
    @Override
    protected AbstractUrlBasedView buildView(String viewName) throws Exception {
        InternalResourceView view = (InternalResourceView) super.buildView(viewName);
        return view;
    }
}
public class InternalResourceView extends AbstractUrlBasedView {
    @Override
    /*xxx: 将逻辑视图的数据进行融合为实际的视图*/
    protected void renderMergedOutputModel(
            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        /*xxx: 获取分发视图的路径*/
        String dispatcherPath = prepareForRendering(request, response);
        /*xxx: 构造视图请求分发器*/
        RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
        rd.forward(request, response);
    }
}
final class ApplicationDispatcher implements AsyncDispatcher, RequestDispatcher {
    public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        this.doForward(request, response);
    }

    private void doForward(ServletRequest request, ServletResponse response) throws ServletException, IOException {
        this.processRequest(request, response, state);
    }

    private void processRequest(ServletRequest request, ServletResponse response, ApplicationDispatcher.State state) throws IOException, ServletException {
        this.invoke(state.outerRequest, response, state);
    }

    private void invoke(ServletRequest request, ServletResponse response, ApplicationDispatcher.State state) throws IOException, ServletException {
        Servlet servlet = null;
        servlet = this.wrapper.allocate();
        ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, this.wrapper, servlet);
        filterChain.doFilter(request, response);
    }
}

# 实践

# 在spring环境中的应用

  • 通过xml方式加载Servlet组件
<xml>
  <servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:META-INF/spring/springmvc-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</xml>
    

# 在springBoot环境中的应用

public class DispatcherServletAutoConfiguration {

    protected static class DispatcherServletRegistrationConfiguration {
        @Bean(
                name = {"dispatcherServletRegistration"}
        )
        @ConditionalOnBean(
                value = {DispatcherServlet.class},
                name = {"dispatcherServlet"}
        )
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }
    }
    
    protected static class DispatcherServletConfiguration {
        @Bean(
                name = {"dispatcherServlet"}
        )
        public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
            DispatcherServlet dispatcherServlet = new DispatcherServlet();
            return dispatcherServlet;
        }
    }
}

# 三个初始化器

/*xxx: 由spring-web提供,与 ServletContainerInitializer 搭配使用*/
public interface WebApplicationInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

/*xxx: 由springWeb提供的容器初始化器*/
/*xxx: 在springWebmvc模块的 services,将该类进行了注册,由tomcat容器的 SPI机制进行生效 */
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
            throws ServletException {
        List<WebApplicationInitializer> initializers = new ArrayList<>(webAppInitializerClasses.size());
        for (Class<?> waiClass : webAppInitializerClasses) {
            initializers.add((WebApplicationInitializer)
                    ReflectionUtils.accessibleConstructor(waiClass).newInstance());
        }
        
        List<WebApplicationInitializer> initializers = Collections.emptyList();
        AnnotationAwareOrderComparator.sort(initializers);
        for (WebApplicationInitializer initializer : initializers) {
            initializer.onStartup(servletContext);
        }
    }
}
/*xxx: spi机制生效节选*/
public class ContextConfig implements LifecycleListener {
    
    protected void processServletContainerInitializers() {
        List<ServletContainerInitializer> detectedScis;
        WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
        detectedScis = loader.load(ServletContainerInitializer.class);
        /*xxx: 省略其它抽象...*/
    }
}

public class WebappServiceLoader<T> {
    private static final String CLASSES = "/WEB-INF/classes/";
    private static final String LIB = "/WEB-INF/lib/";
    private static final String SERVICES = "META-INF/services/";

    public List<T> load(Class<T> serviceType) throws IOException {
        String configFile = SERVICES + serviceType.getName();
        ClassLoader loader = context.getParentClassLoader();
        Enumeration<URL> containerResources;
        if (loader == null) {
            containerResources = ClassLoader.getSystemResources(configFile);
        } else {
            containerResources = loader.getResources(configFile);
        }
        /*xxx: 省略其它抽象...*/
    }
}
/*xxx: 由springBoot提供,与 ServletContainerInitializer 搭配使用*/
public interface ServletContextInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

/*xxx: 由springBoot提供的,内置tomcat的唯一容器类型初始化器*/
final class TomcatStarter implements ServletContainerInitializer {
    private final ServletContextInitializer[] initializers;

    @Override
    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
        for (ServletContextInitializer initializer : this.initializers) {
            initializer.onStartup(servletContext);
        }
    }
}
/*xxx: servlet容器初始化器,由servlet规范提供*/
public interface ServletContainerInitializer {
    /*xxx: @HandlesTypes,由servelt规范提供*/
    void onStartup(Set<Class<?>> annotatedByHandlesTypes, ServletContext servletContext) throws ServletException;
}

需要注册原生servlet上下文原生组件时,需要采用此方法,通过ServletWebServerApplication完成装载
更多内容详见: tomcat学习笔记-内置tomcat如何装载Servlet,Filter和Listener

# springMvc的详细流程

# HandlerMapping结构

/*xxx: springMvc的九大组件之一*/
/*xxx: 用于查找 Handler的,springMVC会处理很多请求,具体的某一个请求才以哦那个哪个 Handler处理,需要使用该类来处理*/
public interface HandlerMapping {

	/*xxx: 获取处理器,但是实际返回的是 处理器执行链*/
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;   
}

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
 	/*xxx: 用于配置 springMVC的拦截器*/
	private final List<Object> interceptors = new ArrayList<>();
    
    @Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
     	/*xxx: 模板方法,留给子类实现,也是子类主要做的事*/
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			/*xxx: 没有获取到,则使用默认的 Handler*/
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		if (handler instanceof String) {
			String handlerName = (String) handler;
			/*xxx: 如果找到的是 String 类型,则去 MVC的容器里面查找对应的Bean */
			handler = obtainApplicationContext().getBean(handlerName);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);   
        
        if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
            if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
				//xxx: 如果是cors的检查请求,则会进行处理
			CorsConfiguration config = getCorsConfiguration(handler, request);
                //xxx: 省略其它抽象...
                //xxx: 跨域检查请求与常规的请求,是相互独立的控制器
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
            }
            return executionChain;
        }
    }
    
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    	//xxx: 将匹配的拦截器与控制器进行组装,略...
    }
    
    protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
			HandlerExecutionChain chain, @Nullable CorsConfiguration config) {
     	//xxx: 将cors的拦截器 CorsInterceptor加入列表,略...   
    }
    
    /*xxx: 获取处理器*/
	protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
}
/*xxx: 该 系列,将Method 作为Handler来使用*/
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
 	   //xxx: 映射存储器
	private final MappingRegistry mappingRegistry = new MappingRegistry();
    
    @Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		/*xxx: 根据获取路径*/
		String lookupPath = initLookupPath(request);
		this.mappingRegistry.acquireReadLock();
		try {
			/*xxx: 通过路径和请求 找到HandlerMethod*/
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			/*xxx: 如果找到了Handler,则使用  createWithResolvedBean 创建新的 HandlerMethod并返回*/
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
    
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
     	//xxx:根据匹配规则,获取到控制器,略...
        //xxx: 否则返空
    }
}

/*xxx: 基于控制器的具体实现*/
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
		implements MatchableHandlerMapping, EmbeddedValueResolverAware {
 	   
}

# 映射器的存储

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
 	@Override
	public void afterPropertiesSet() {
		/*xxx: 调用 initHandlerMethods,从spring容器中获取符合条件的bean,进行装配*/
		initHandlerMethods();
	}
    
    protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				/*xxx: 处理 controller类*/
				processCandidateBean(beanName);
			}
		}
		/*xxx: 将@Controller 或 @RequestMapping的注解给提出来进行注册*/
		handlerMethodsInitialized(getHandlerMethods());
	}
    
    protected void processCandidateBean(String beanName) {
     	Class<?> beanType = obtainApplicationContext().getType(beanName);   
        if (beanType != null && isHandler(beanType)) {
			/*xxx: 从controller中,将方法提出来*/
			detectHandlerMethods(beanName);
		}
    }
    
    /*xxx: 判断某个类是否是控制器*/
	protected abstract boolean isHandler(Class<?> beanType);
    
    /*xxx: 根据方法的映射,返回泛型控制器*/
	protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
    
    protected void detectHandlerMethods(Object handler) {
     	   /*xxx: 获取Handler的类型*/
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			/*xxx: 获取当前bean里面,所有符合Handler要求的Method*/
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
							return getMappingForMethod(method, userType);
					});
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				/*xxx: 符合要求的method注册起来*/
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
    }
    
    /*xxx: 将映射路径存储到注册表中*/
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}
}

# 映射器注册表

class MappingRegistry {
    
    //xxx: 注册表
    private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
	//xxx: 路径查找表
	private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
	//xxx: 名称查找表
	private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
	//xxx: cors查找表
	private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
    
    
 	//xxx: 注册控制器信息
    public void register(T mapping, Object handler, Method method) {
     	/*xxx:根据处理器, 创建  HandlerMethod*/
		HandlerMethod handlerMethod = createHandlerMethod(handler, method);
		/*xxx: 同时验证 HandlerMethod的合法性,如果重复,则会抛出错误*/
		validateMethodMapping(handlerMethod, mapping);   
        
        
       Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
		//xxx: 更新查找表
		for (String path : directPaths) {
			this.pathLookup.add(path, mapping);
		}

		//xxx: 更新名称表
		String name = null;
		if (getNamingStrategy() != null) {
			name = getNamingStrategy().getName(handlerMethod, mapping);
			/*xxx: 注册新的 HandlerMethod*/
			addMappingName(name, handlerMethod);
		}
        
        //xxx: 更新映射信息
		this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
    }
}

# HandlerMethod的结构

/*xxx: 用于封装Handler 和其中 具体处理请求的 Method,分别对应其中的bean 和 method属性*/
public class HandlerMethod {
 	/*xxx: 实际的handler,为一个bean*/
	private final Object bean;
    
    /*xxx: 需要执行的方法*/
	private final Method method;
    
    //xxx: 省略其它抽象...
}
public class InvocableHandlerMethod extends HandlerMethod {
 	@Nullable
	/*xxx: 可以创建WebDataBinder,用于参数解析器 ArgumentResolver中*/
	private WebDataBinderFactory dataBinderFactory;

	/*xxx: 用于解析参数*/
	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();

	/*xxx: 用来获取参数名, 用于 MethodParameter中*/
	private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    
    public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

		/*xxx: 准备方法所需要的参数*/
		Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
     	/*xxx: 具体调用Method,我们写的方法都是通过该方法来执行的*/
		return doInvoke(args);   
    }
    
    @Nullable
	protected Object doInvoke(Object... args) throws Exception {
     	/*xxx: 通过反射,调用bean的对应方法*/
		return getBridgedMethod().invoke(getBean(), args);   
    }
    
    /*xxx: 参数绑定的方法*/
	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {

        /*xxx: 获取方法的参数,在HandlerMethod中*/
		MethodParameter[] parameters = getMethodParameters();
		if (ObjectUtils.isEmpty(parameters)) {
			return EMPTY_ARGS;
		}

		/*xxx: 用于保存解析出参数的值*/
		Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			/*xxx: 给parameter设置参数名解析器*/
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			/*xxx: 如果相应类型的参数 已经在proivdedArgs中提供了,则直接设置到 parameter*/
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			/*xxx: 使用 argumentResolvers解析参数*/
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				//xxx: 抛错,略...
			}
		}
		return args;
    }
}
/*xxx: 一种Handler,增加了 方法执行,参数解析,返回值处理等功能*/
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
   public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
       	/*xxx: 首先调用父类,执行请求*/
		Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        /*xxx: 接着 处理 @ResponseStatus 注释,设置http状态码*/
		setResponseStatus(webRequest);
        /*xxx: 最后处理返回值*/
		if (returnValue == null) {
			
			if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                /*xxx: 如果 request的notModified为真, @ResponseStatus存在,或者 mavContainer的requestandled为true,
			   	则设置为请求已经处理,并返回*/
                //xxx: 将不再进行视图的渲染等操作
				mavContainer.setRequestHandled(true);
				return;
			}
		}
       
       /*xxx: 使用returnValueHandlers 处理返回值*/
	   this.returnValueHandlers.handleReturnValue(
					returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
       
    }
}

# HandlerAdapter的结构

/*xxx: springMVC的 九大组件之一*/
/*xxx: 用它的原因是因为: springMVC中,并没有对Handler做任何限制*/
public interface HandlerAdapter {
 	 /*xxx: 判断是否支持处理某个 Handler*/
	boolean supports(Object handler);  

	/*xxx: 执行具体的Handler方法*/
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
 	public final boolean supports(Object handler) {
		/*xxx: 在handler必须是 HandlerMethod的子类 */
		return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
	}
    
    public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//xxx: 子类处理
		return handleInternal(request, response, (HandlerMethod) handler);
	}
    
    protected abstract ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
}

/*xxx: springMVC中,最复杂的组件*/
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
 	@Override
	protected ModelAndView handleInternal(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        //xxx: 基于控制器的方法执行时,不论返回值是多少,都会被处理成 ModelAndView
        //xxx: springMvc的返回值,与方法的返回值,不是同一个概念
		ModelAndView mav;
        /*xxx: 如果当前的session需要同步,则加锁*/
		if (this.synchronizeOnSession) {
			HttpSession session = request.getSession(false);
			if (session != null) {
				Object mutex = WebUtils.getSessionMutex(session);
				synchronized (mutex) {
					/*xxx: 加锁同步执行*/
					mav = invokeHandlerMethod(request, response, handlerMethod);
				}
			}
			else {
				/*xxx: 没有session,则直接运行*/
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {
			/*xxx: 没有同步要求,也是直接运行*/
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
    }

    /*xxx: 具体执行请求的处理*/
    protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                               HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
        //xxx: 实际的处理方法,springMvc控制器执行的核心,见下文   
    }
}

# 适配器的初始化

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
 	@Override
	public void afterPropertiesSet() {
		/*xxx: 初始化 注释了 @ControllerAdvice的类的相关属性*/
		initControllerAdviceCache();

		/*xxx: 用于给处理器方法  和 注释了 @ModelAttribute的方法设置参数*/
		if (this.argumentResolvers == null) {
			/*xxx: 获取默认的 参数解析器*/
			List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
			this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		/*xxx: 用于 给 注释了 @initBinder的方法设置参数*/
		if (this.initBinderArgumentResolvers == null) {
			List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
			this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
		}
		/*xxx: 用于 将 处理器的返回值 处理成 ModelAndView的类型*/
		if (this.returnValueHandlers == null) {
			List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
			this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
		}
	}
    
    private void initControllerAdviceCache() {
        //xxx: 省略其它抽象...
        
     	/*xxx: 缓存 @ControllerAdvice注释的类里面 注释了  @ModelAttribute 的方法*/
		this.modelAttributeAdviceCache.put(adviceBean, attrMethods);   
        
        /*xxx: 缓存 @ControllerAdvice注释的类里面 注释了 @InitBinder 的方法*/
		this.initBinderAdviceCache.put(adviceBean, binderMethods);
        
        /*xxx: 表明@RequestAdvice注解的类,具有最高优先级*/
		this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
    
    private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
        
		/*xxx:添加基于注解解析参数的 解析器*/
		/*xxx: @RequestParam 注解*/
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		/*xxx: 多个@RequestParam 注解*/
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		/*xxx: @PathVariable 注解*/
		resolvers.add(new PathVariableMethodArgumentResolver());
		/*xxx: 多个 @PathVariable 注解*/
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		/*xxx: @MatrixVariable 注解*/
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		/*xxx: 多个 @MatrixVariable 注解*/
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		/*xxx: @ModelAttribute注解*/
		resolvers.add(new ServletModelAttributeMethodProcessor(false));
		/*xxx: @RequestBody @ResponseBody 注解处理*/
		resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		/*xxx: @RequestPart 注解*/
		resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
		/*xxx: @RequestHeader注解*/
		resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
		/*xxx: 多个 @RequestHeader 注解*/
		resolvers.add(new RequestHeaderMapMethodArgumentResolver());
		/*xxx: @CookieValue 注解*/
		resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
		/*xxx: @Value注解,它是spring工厂的注解*/
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		/*xxx: @SessionAttribute注解处理*/
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		/*xxx: @RequestAttribute注解*/
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		/*xxx: 基于 类型解析参数的解析器*/
		/*xxx: 请求时 处理这些原生类型:
		   WebRequest,
		   ServletRequest,
		   MultipartRequest,
		   HttpSession,
		   Principle,
		   InputStream,
		   Reader,
		   HttpMethod.
		   Locale,
		   TimeZone,
		   ZoneId
		   */
		resolvers.add(new ServletRequestMethodArgumentResolver());
		/*xxx: 响应时,处理这些原生类型:
		*  	ServletResponse,
		* 	OutputStream,
		* 	Writer
		* 	*/
		resolvers.add(new ServletResponseMethodArgumentResolver());
		/*xxx: 处理  HttpEntity,RequestEntity*/
		resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
		/*xxx: 处理 RedirectAttributes 类型*/
		resolvers.add(new RedirectAttributesMethodArgumentResolver());
		/*xxx: 处理 Model类型*/
		resolvers.add(new ModelMethodProcessor());
		/*xxx: 处理 Map类型,并且没有注解*/
		resolvers.add(new MapMethodProcessor());
		/*xxx: 处理 Errors类型(spring-context 的 validation包下的)*/
		resolvers.add(new ErrorsMethodArgumentResolver());
		/*xxx: 处理 SessionStatus类型*/
		resolvers.add(new SessionStatusMethodArgumentResolver());
		/*xxx: 处理 UriComponentsBuilder 和 ServletUriComponentBuilder类型*/
		resolvers.add(new UriComponentsBuilderMethodArgumentResolver());

		/*xxx: 添加自定义参数解析器,用于解析自定义类型*/
		/*xxx: 自定义解析器,是在 基于注解 和类型 参数解析器都无法解析的时候,才会用到*/
		/*xxx: 该顺序是固定的,无法改变*/
		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}

		/*xxx: 这两个解析器,可以解析所有类型的参数*/
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
		resolvers.add(new ServletModelAttributeMethodProcessor(true));

		return resolvers;
	}
    
    /*xxx: @InitBinder使用的参数解析器*/
	private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
		List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20);

		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
		resolvers.add(new RequestParamMapMethodArgumentResolver());
		resolvers.add(new PathVariableMethodArgumentResolver());
		resolvers.add(new PathVariableMapMethodArgumentResolver());
		resolvers.add(new MatrixVariableMethodArgumentResolver());
		resolvers.add(new MatrixVariableMapMethodArgumentResolver());
		resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
		resolvers.add(new SessionAttributeMethodArgumentResolver());
		resolvers.add(new RequestAttributeMethodArgumentResolver());

		resolvers.add(new ServletRequestMethodArgumentResolver());
		resolvers.add(new ServletResponseMethodArgumentResolver());

		if (getCustomArgumentResolvers() != null) {
			resolvers.addAll(getCustomArgumentResolvers());
		}
        
		resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));

		return resolvers;
	}
        
    /*xxx: 返回值参数处理器*/
	private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
		List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);

		/*xxx: 方法返回值为ModelAndView的处理 */
		handlers.add(new ModelAndViewMethodReturnValueHandler());
		/*xxx: 方法返回值为 Model 处理 */
		handlers.add(new ModelMethodProcessor());
		/*xxx: 方法返回值为 View 的处理 */
		handlers.add(new ViewMethodReturnValueHandler());
		handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
				this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
		/*xxx: 方法返回值为 StreamingResponseBody 的处理 */
		handlers.add(new StreamingResponseBodyReturnValueHandler());
		/*xxx: 方法返回值为 httpEntity的处理*/
		handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));
		/*xxx: 方法返回值 为 HttpHeader 的处理*/
		handlers.add(new HttpHeadersReturnValueHandler());
		/*xxx: 方法返回值 为 Callable 的处理 */
		handlers.add(new CallableMethodReturnValueHandler());
		/*xxx: 方法返回值为 DeferredResult 的处理 */
		handlers.add(new DeferredResultMethodReturnValueHandler());
		/*xxx: 方法返回值为 WebAsyncTask 的处理*/
		handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

		handlers.add(new ServletModelAttributeMethodProcessor(false));
		/*xxx: 注释了@Response返回值处理*/
		handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
				this.contentNegotiationManager, this.requestResponseBodyAdvice));

		/*xxx: 返回值为 void 或者 String时的处理*/
		handlers.add(new ViewNameMethodReturnValueHandler());
		/*xxx: 返回值为  Map的处理, 同时也可处理 Map类型 的参数*/

		if (getCustomReturnValueHandlers() != null) {
			handlers.addAll(getCustomReturnValueHandlers());
		}

		if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
			/*xxx: 全局返回值处理,其它处理器都没处理时,进行处理*/
			handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
		}
		else {
			handlers.add(new ServletModelAttributeMethodProcessor(true));
		}

		return handlers;
	}

}

# 适配器的执行流程

/*xxx: springMVC中,最复杂的组件*/
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
		implements BeanFactoryAware, InitializingBean {
    
 	/*xxx: 具体执行请求的处理*/
	protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
			HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
      	/*xxx: 使用  request 和 response 创建了 ServletWebRRequest类型的 webRequest*/
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
        
        /*xxx: 用于参数绑定,主要功能就是 实现参数 跟 String 之间的类型转换*/
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		/*xxx: 用来处理Model, 1.用于在处理器具体处理之前,对Model进行初始化 2.处理完请求后,对Model参数进行更新*/
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
        
        /*xxx: 该类型非常重要, 继承自 HandlerMethod,并且可以直接执行*/
		/*xxx: 实际请求的处理 就是通过它来执行的*/
		/*xxx: 参数绑定,处理请求  以及 返回值处理,都在这里完成*/
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
        
        if (this.argumentResolvers != null) {
				/*xxx: 设置参数解析器*/
				invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
				/*xxx: 设置返回值处理器*/
				invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
        
        /*xxx: 设置参数绑定工厂*/
		invocableMethod.setDataBinderFactory(binderFactory);
		/*xxx: 设置parameterNameDiscoverer*/
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        
        /*xxx: 新建ModelAndViewContainer,用于保存 Model 和 View,它贯穿整个处理过程*/
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		/*xxx: 将FlashMap中的数据,设置到Model*/
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		/*xxx: 使用modelFactory 将 sessionAttributes 和 注释了 @ModelAttribute的方法的参数设置到Model*/
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		/*xxx: 根据配置,对 ignoreDefaultModelOnRedirect进行设置*/
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
        
        /*xxx: 绑定参数的数据来源:
		* 	request中参数(url,post请求体,请求头), cookie,session  -> 通过request管理
		* 	FlashMap中的参数(主要用于redirect的参数传递),sessionAttributes传递的参数,@ModelAttribute设置的参数 -> Model管理
		* 	其中,前三类 直接在request获取,不用过多准备工作
		* 	第四类: FlashMap 直接在 RequestMappingHandlerAdapter处理,请求前 设置到Model中,请求后则视情况将值设置到 FlashMap
		* 	sessionAttributes,ModelAttribute 使用ModelFactory管理
		* */
        
        //xxx: 省略其它抽象...
        
        /*xxx: 执行请求*/
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
        
        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    
    @Nullable
	private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
			ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
     	/*xxx: 更新Model,包括设置 SessionAttributes 和 给Model 设置 BindingResult*/
		modelFactory.updateModel(webRequest, mavContainer);
        
        /*xxx: 根据mavContainer创建 ModelAndView */
		ModelMap model = mavContainer.getModel();
		ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
		if (!mavContainer.isViewReference()) {
			mav.setView((View) mavContainer.getView());
		}
		/*xxx: 如果 mavContainer里的Model是 RedirectAttributes类型,则将其设置到 FlashMap*/
		if (model instanceof RedirectAttributes) {
			Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
			HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
			if (request != null) {
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
		}
		return mav;
    }
}

# 控制器关键流程

# 参数解析

public class InvocableHandlerMethod extends HandlerMethod {
    /*xxx: 用于解析参数*/
	private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
    
 	/*xxx: 参数绑定的方法*/
	protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
			Object... providedArgs) throws Exception {
     	/*xxx: 获取方法的参数信息,在HandlerMethod中*/
		MethodParameter[] parameters = getMethodParameters();   
        /*xxx: 用于保存解析出参数的值*/
		Object[] args = new Object[parameters.length];
		for (int i = 0; i < parameters.length; i++) {
			MethodParameter parameter = parameters[i];
			/*xxx: 给parameter设置参数名解析器*/
			parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
			/*xxx: 如果相应类型的参数 已经在proivdedArgs中提供了,则直接设置到 parameter*/
			args[i] = findProvidedArgument(parameter, providedArgs);
			if (args[i] != null) {
				continue;
			}
			/*xxx: 使用 argumentResolvers解析参数*/
			if (!this.resolvers.supportsParameter(parameter)) {
				throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
			}
			try {
				args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
			}
			catch (Exception ex) {
				throw ex;
			}
		}
		return args;
    }
}
public interface HandlerMethodArgumentResolver {
 	/*xxx: 用于判断是否支持传入参数的解析*/
	boolean supportsParameter(MethodParameter parameter);
    
    /*xxx: 用于实际解析参数*/
	Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
                           NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}

public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
 	/*xxx: 参数解析器*/
	private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
    
    /*xxx:解析参数 */
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {

		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
	}
    
    private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
		HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
		if (result == null) {
			for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
				if (resolver.supportsParameter(parameter)) {
					result = resolver;
					this.argumentResolverCache.put(parameter, result);
					break;
				}
			}
		}
		return result;
	}
}
/*xxx: 使用 HttpMessageConverter解析 request body 类型参数的基类*/
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
 	//xxx: 消息转换器 
	protected final List<HttpMessageConverter<?>> messageConverters;
    
    /*xxx: 读取消息*/
	protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
			Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
     	/*xxx: 消息类型*/
		MediaType contentType;

        /*xxx: 如果没有获取到消息类型,则默认使用 octet_stream作为消息类型*/
		contentType = MediaType.APPLICATION_OCTET_STREAM;
        
        /*xxx: 遍历消息转换器进行处理*/
		for (HttpMessageConverter<?> converter : this.messageConverters) {
            
            /*xxx: 通过指定类型,确定消息转换器是否能读取*/
            if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
						(targetClass != null && converter.canRead(targetClass, contentType))) {
                /*xxx: 消息体读取前置操作*/
                HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
                body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
                                    ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
                /*xxx: 消息体读取后置操作*/
                body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType); 
            }
         }
        return body;
    }
}

/*xxx: 解析注释了 @RequestBody 和 @ResponseBody的参数*/
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
 	@Override
	/*xxx: 通过消息转换器读取参数 */
	protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
			Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
        
		/*xxx: 从request中读取参数 */
		Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
		if (arg == null && checkRequired(parameter)) {
			/*xxx: 如果参数是必填项,则抛错*/
			throw new HttpMessageNotReadableException("Required request body is missing: " +
					parameter.getExecutable().toGenericString(), inputMessage);
		}
		return arg;
	}
    
    /*xxx: 解析参数*/
	public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
			NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
     	/*xxx: 读取到参数的结果*/
		Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
        return arg;
    }
}
/*xxx: http消息转换器*/
public interface HttpMessageConverter<T> {
 	/*xxx: 是否可读取*/
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
    
    //xxx: 是否可写入
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
    
    /*xxx: 读取*/
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
    
    /*xxx: 写入*/
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}

public interface GenericHttpMessageConverter<T> extends HttpMessageConverter<T> {
 	boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType);
    
    T read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
    
    boolean canWrite(@Nullable Type type, Class<?> clazz, @Nullable MediaType mediaType);
    
    void write(T t, @Nullable Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}
/*xxx: 消息转换器抽象类 */
public abstract class AbstractGenericHttpMessageConverter<T> extends AbstractHttpMessageConverter<T>
		implements GenericHttpMessageConverter<T> {
 	/*xxx: 写入消息*/
	public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType,
			HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
     	/*xxx: 设置响应头信息*/
		final HttpHeaders headers = outputMessage.getHeaders();
		addDefaultHeaders(headers, t, contentType);
        
        //xxx: 写入消息,并刷新缓存
        writeInternal(t, type, outputMessage);
		outputMessage.getBody().flush();
    }
    
    /*xxx: 写入*/
	protected abstract void writeInternal(T t, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}
/*xxx: 基于jackson的实现*/
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
 	/*xxx: jackson的转换器*/
	protected ObjectMapper objectMapper;
    
    @Override
	/*xxx: 读取参数 */
	public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		JavaType javaType = getJavaType(type, contextClass);
		return readJavaType(javaType, inputMessage);
	}
    
    /*xxx: 读取为java类*/
	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {
     	return this.objectMapper.readValue(inputMessage.getBody(), javaType);   
    }
    
    /*xxx: 写入json信息*/
	protected void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
        //xxx: 省略其它抽象...
        
     	JsonGenerator generator = this.objectMapper.getFactory().createGenerator(outputMessage.getBody(), encoding);
        
        ObjectWriter objectWriter = (serializationView != null ?
					this.objectMapper.writerWithView(serializationView) : this.objectMapper.writer());
        //xxx: 写入json信息
        objectWriter.writeValue(generator, value);
    }
}

public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
 	private String jsonPrefix;
    
    protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
		if (this.jsonPrefix != null) {
			generator.writeRaw(this.jsonPrefix);
		}
	}
}

# @RequestBody参数切面

/*xxx: 参数切面*/
/*xxx: 作用域 使用了 @ControllerAdvice 并且 实现了 @RequestBodyAdvice 的类*/
public interface RequestBodyAdvice {
 	/*xxx: 是否支持当前参数*/
	boolean supports(MethodParameter methodParameter, Type targetType,
                     Class<? extends HttpMessageConverter<?>> converterType);
    
    /*xxx: 读取参数前处理*/
	HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
                                    Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
    
    /*xxx: 读取参数后处理*/
	Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                         Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
    
    /*xxx: 空实体处理*/
	Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                           Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
}

# 返回值处理

public interface HandlerMethodReturnValueHandler {
 	/*xxx: 是否支持返回值解析*/
	boolean supportsReturnType(MethodParameter returnType);
    
    /*xxx: 处理返回值*/
	void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                           ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
 	/*xxx: 处理返回值为void ,或者为 String的参数*/
	public boolean supportsReturnType(MethodParameter returnType) {
		Class<?> paramType = returnType.getParameterType();
		return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
	}
    
    public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

		if (returnValue instanceof CharSequence) {
			String viewName = returnValue.toString();
			/*xxx: 如果返回值为String,则将其设置到 mavContainer的view中*/
			mavContainer.setViewName(viewName);
			if (isRedirectViewName(viewName)) {
				mavContainer.setRedirectModelScenario(true);
			}
		}
		else if (returnValue != null) {
			throw new UnsupportedOperationException("Unexpected return type: " +
					returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
		}
        //xxx: void返回值,实际上什么也没处理
	}
    
    /*xxx: 是否是重定向视图*/
	protected boolean isRedirectViewName(String viewName) {
		return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
	}
    
    
}
  • 写出消息的两类实现
/*xxx: 返回资源信息*/
public class ResourceHttpRequestHandler extends WebContentGenerator
		implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource {
 	public void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
     	   //xxx: 省略抽象...
        this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
    }
}
/*xxx: 解析注释了 @RequestBody 和 @ResponseBody的参数*/
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
    @Override
	public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        /*xxx: 设置该参数后,将不再进行 viewResolver的步骤*/
        mavContainer.setRequestHandled(true);

        /*xxx: responseBodyAdvice 可以介入处理*/
        writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
    }
}
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
		implements HandlerMethodReturnValueHandler {
    
    
 	protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
     	//xxx: 省略其它抽象...
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            //xxx: 类型可写入时,进行处理 
            if (genericConverter != null ?
						((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
						converter.canWrite(valueType, selectedMediaType)) {
            		/*xxx: 写入前处理, responseBodyAdvice*/
					body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
							(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
							inputMessage, outputMessage);
                	//xxx: 写入消息
                	genericConverter.write(body, targetType, selectedMediaType, outputMessage);
            }
        }
    }
}

# 返回值写出操作委派

/*xxx: 解析注释了 @RequestBody 和 @ResponseBody的参数*/
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
  @Override
  public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                                ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
          throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    /*xxx: responseBodyAdvice 可以介入处理*/
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
  }
}
/*xxx: 定义相关工具,不直接解析参数*/
public abstract class AbstractMessageConverterMethodProcessor extends AbstractMessageConverterMethodArgumentResolver
        implements HandlerMethodReturnValueHandler {

  //xxx: 消息转换器
  protected final List<HttpMessageConverter<?>> messageConverters;

  public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
                                                        @Nullable List<Object> requestResponseBodyAdvice) {
    this.messageConverters = converters;
    this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
    this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
  }
    
  protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
                                                ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
          throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
      //xxx: 根据解析到的返回值类型 进行处理
      if (selectedMediaType != null) {
      selectedMediaType = selectedMediaType.removeQualityValue();
      //xxx: 遍历消息转换器进行处理
      for (HttpMessageConverter<?> converter : this.messageConverters) {
        GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                (GenericHttpMessageConverter<?>) converter : null);
        //xxx: 类型可写入时,进行处理
        if (genericConverter != null ?
                ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                converter.canWrite(valueType, selectedMediaType)) {
          /*xxx: 写入前处理, responseBodyAdvice*/
          body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                  (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                  inputMessage, outputMessage);
          if (body != null) {
            Object theBody = body;
            addContentDispositionHeader(inputMessage, outputMessage);
            if (genericConverter != null) {
              //xxx: 写入消息
              genericConverter.write(body, targetType, selectedMediaType, outputMessage);
            }
            else {
              ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
            }
          }
          return;
        }
      }
    }
  }
}

/*xxx: springMVC中,最复杂的组件*/
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
  private List<HttpMessageConverter<?>> messageConverters;
}
/*xxx: http消息转换器*/
public interface HttpMessageConverter<T> {
  /*xxx: 是否可读取*/
  boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

  //xxx: 是否可写入
  boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

  List<MediaType> getSupportedMediaTypes();

  /*xxx: 读取*/
  T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
          throws IOException, HttpMessageNotReadableException;

  /*xxx: 写入*/
  void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
          throws IOException, HttpMessageNotWritableException;
}

# @ResponseBody返回值切面

/*xxx: 返回值切面*/
/*xxx: 作用于  注解了 @ControllerAdvice 且 实现了 ResponseBodyAdvice接口的类*/
public interface ResponseBodyAdvice<T> {
 	/*xxx: 是否支持该参数处理*/
	boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);   
    
    /*xxx: 参数写入前的处理*/
	T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
                      Class<? extends HttpMessageConverter<?>> selectedConverterType,
                      ServerHttpRequest request, ServerHttpResponse response);
}

# 控制器增强器

@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
}
@Component
public @interface ControllerAdvice {
}
  • 作用于所有注解了**@RequestMapping**的方法
  • 该类的方法可以使用**@ExceptionHandler**,@InitBinder,@ModelAttribute实现全局功能
@ControllerAdvice  
public class GlobalController{  
     
    //(1)全局数据绑定
    //应用到所有@RequestMapping注解方法  
    //此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对  
    @ModelAttribute 
    public void addUser(Model model) {   
        model.addAttribute("msg", "此处将键值对添加到全局,注解了@RequestMapping的方法都可以获得此键值对");  
    }    
    //(2)全局数据预处理
    //应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器  
    //用来设置WebDataBinder  
    @InitBinder("user")
    public void initBinder(WebDataBinder binder) {
    }    
    
    // (3)全局异常处理
    //应用到所有@RequestMapping注解的方法,在其抛出Exception异常时执行  
    //定义全局异常处理,value属性可以过滤拦截指定异常,此处拦截所有的Exception  
    @ExceptionHandler(Exception.class)    
    public String handleException(Exception e) {    
        return "error";
    }    
}  

# ModelAndView构建

  • 根据返回值,将view的名称设置到ModelAndViewContainer
  • 某些返回值处理器,会设置mavContainer.setRequestHandled(true),将阻断后续的流程

# FlushMap参数传递

  • RedirectAttributes可传递redirect参数

# Model参数传递

  • 使用**@ModelAttribute**
    • 作用于方法上,该方法将会在controller每个方法执行前执行;
    • 作用于参数定义上,则作为参数的默认初始值,参考 (opens new window)
  • 使用**@SessionAttribute**,会将参数暂存在Session中,下次请其时恢复到model

# 异常处理

# 结构设计

/*xxx: 异常处理器*/
public interface HandlerExceptionResolver {
 	/*xxx: 从异常中,解析出 ModelAndView*/
	ModelAndView resolveException(
            HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);   
}
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {
    /*xxx: 允许异常处理的handler*/
	private Set<?> mappedHandlers;
    
 	public ModelAndView resolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
     	/*xxx: 判断短期的ExceptionResolver 是否可以解析所传入的处理器抛出的异常,不可以则返回nul,交给其它ExceptionResolver解析*/
		if (shouldApplyTo(request, handler)) {
			/*xxx: 根据 preventResponseCaching 标识判断 是否给response 设置禁用缓存的属性*/
			prepareResponse(ex, response);
			/*xxx: 模板方法,留给子类实现*/
			ModelAndView result = doResolveException(request, response, handler, ex);
			return result;
		}
		else {
			return null;
		}   
    }
    
    /*xxx: 解析异常*/
	protected abstract ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

public abstract class AbstractHandlerMethodExceptionResolver extends AbstractHandlerExceptionResolver {
 	protected final ModelAndView doResolveException(
			HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {

        //xxx: 处理 HandlerMethod的异常
		HandlerMethod handlerMethod = (handler instanceof HandlerMethod ? (HandlerMethod) handler : null);
		return doResolveHandlerMethodException(request, response, handlerMethod, ex);
	}
    
    protected abstract ModelAndView doResolveHandlerMethodException(
			HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception ex);

}

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
		implements ApplicationContextAware, InitializingBean {
 	protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
			HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
     	/*xxx: 找到处理异常的方法*/
		ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
		if (exceptionHandlerMethod == null) {
			return null;
		}

		/*xxx: 设置 argumentResolvers 和returnValueHandlers*/
		if (this.argumentResolvers != null) {
			exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		if (this.returnValueHandlers != null) {
			exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
        
        /*xxx: 执行exceptionHandler方法解析异常*/
		exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
        
        /*xxx: 异常处理,可以看成执行了另外一个 handlerMethod,也会通过返回值处理器继续处理*/
		if (mavContainer.isRequestHandled()) {
			return new ModelAndView();
		}
		else {
			/*xxx: 保存异常处理的参数信息*/
			ModelMap model = mavContainer.getModel();
			HttpStatus status = mavContainer.getStatus();
			ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
			mav.setViewName(mavContainer.getViewName());
			if (!mavContainer.isViewReference()) {
				mav.setView((View) mavContainer.getView());
			}
			if (model instanceof RedirectAttributes) {
				Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
				RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
			}
			return mav;
		}
    }
}

# 流程设计

public class DispatcherServlet extends FrameworkServlet {
    
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //xxx: 省略其它抽象...  为该方法的最后一个步骤
        
     	 /*xxx: 处理上面处理之后的结果 (包括 处理异常,渲染页面,发出完成通知触发  Interceptor 的 afterCompletion)*/
		processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    
    /*xxx: 处理分发结果 */
	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {
     	/*xxx: 如果请求处理的过程中,有异常抛出,则处理异常*/
        //xxx: 为该方法的第一个步骤
		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
                 //xxx: 统一异常处理,并未阻断流程 
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}
        //xxx: 省略其它抽象...
    }
    
    /*xxx: 处理异常 */
	protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
			@Nullable Object handler, Exception ex) throws Exception {
     	/*xxx: 异常处理器进行异常处理 */
		if (this.handlerExceptionResolvers != null) {
			for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
				exMv = resolver.resolveException(request, response, handler, ex);
				/*xxx: 链上处理之后,将不再处理*/
				if (exMv != null) {
					break;
				}
			}
        }
        //xxx: 省略其它抽象...
    }
    
    
}

# 资源处理

# SimpleHandlerMapping结构

/*xxx:采用模板模式,设计了HandlerMapping 实现的整体结构*/
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
		implements HandlerMapping, Ordered, BeanNameAware {
 	@Override
	protected void initApplicationContext() throws BeansException {
		/*xxx: 模板方法,用于给子类提供一个 添加(或修改) Interceptors的入口*/
		extendInterceptors(this.interceptors);
		/*xxx: 用于将 springMVC容器 及 父容器中的所有 MappedInterceptor类型的Bean 添加到 mappedInterceptors属性*/
		detectMappedInterceptors(this.adaptedInterceptors);
		/*xxx: 初始化 Interceptor */
		initInterceptors();
	}
    
    @Override
	@Nullable
	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		/*xxx: 模板方法,留给子类实现,也是子类主要做的事*/
		Object handler = getHandlerInternal(request);
		if (handler == null) {
			/*xxx: 没有获取到,则使用默认的 Handler*/
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}
		if (handler instanceof String) {
			String handlerName = (String) handler;
			/*xxx: 如果找到的是 String 类型,则去 MVC的容器里面查找对应的Bean */
			handler = obtainApplicationContext().getBean(handlerName);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
			//xxx: 如果是cors的检查请求,则会进行处理
			CorsConfiguration config = getCorsConfiguration(handler, request);
			//xxx: 省略其它抽象...
			//xxx: 跨域检查请求与常规的请求,是相互独立的控制器
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}
    
    /*xxx: 获取处理器*/
	protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
}
/*xxx: 通过url进行匹配*/
	/*xxx: 此系列的原理是 将 url 与对应的 Handler保存在 一个Map中,使用时,则根据url来取Handler*/
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping implements MatchableHandlerMapping {
 	/*XXX: 交给了具体的子孙类 去完成*/
	private final Map<String, Object> handlerMap = new LinkedHashMap<>();
    
    //xxx: 省略其它抽象...
}

/*xxx: 根据url进行映射 */
public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

	/*xxx: 定义了一个内部的map*/
	private final Map<String, Object> urlMap = new LinkedHashMap<>();
 	
    @Override
	public void initApplicationContext() throws BeansException {
		super.initApplicationContext();
		/*xxx: 初始化时,将子类的map注册到父类当中*/
		registerHandlers(this.urlMap);
	}
    
    /*xxx: 初始化时,需要显示指定 url->handler的映射关系*/
    public SimpleUrlHandlerMapping(Map<String, ?> urlMap) {
		setUrlMap(urlMap);
	}
}

# HttpRequestHandlerAdapter结构

/*xxx: 根据 RequestHandler 进行处理的 适配器*/
public class HttpRequestHandlerAdapter implements HandlerAdapter {
 	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		/*xxx: 根据请求直接执行*/
		((HttpRequestHandler) handler).handleRequest(request, response);
		/*xxx: 返回空*/
		return null;
	}   
}

# ResourceHttpRequestHandler结构

/*xxx: 请求直接处理*/
public interface HttpRequestHandler {
 	/*xxx: 根据请求、响应直接处理*/
	void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException;   
}
/*xxx: web内容生成器*/
public abstract class WebContentGenerator extends WebApplicationObjectSupport {
 	/*xxx: 检测请求合法性*/
	protected final void checkRequest(HttpServletRequest request) throws ServletException {
		String method = request.getMethod();
		/*xxx: 探测是否支持当前的method类型(get,post,put,delete)*/
		/*xxx: supportedMethods 默认为空*/
		if (this.supportedMethods != null && !this.supportedMethods.contains(method)) {
			throw new HttpRequestMethodNotSupportedException(method, this.supportedMethods);
		}
		/*xxx: 如果需要session,则先检测session是否合法(默认不检测)*/
		if (this.requireSession && request.getSession(false) == null) {
			throw new HttpSessionRequiredException("Pre-existing session required but none found");
		}
	} 
    
    /*xxx: 设置资源缓存特性*/
	protected final void prepareResponse(HttpServletResponse response) {
		if (this.cacheControl != null) {
			/*xxx: 给 response设置缓存过期时间*/
			applyCacheControl(response, this.cacheControl);
		}
		else {
			applyCacheSeconds(response, this.cacheSeconds);
		}
		/*xxx: 将请求的vary请求头,原样返回*/
		if (this.varyByRequestHeaders != null) {
			for (String value : getVaryRequestHeadersToAdd(response, this.varyByRequestHeaders)) {
				response.addHeader("Vary", value);
			}
		}
	}
}
/*xxx: 处理资源请求的处理器  */
public class ResourceHttpRequestHandler extends WebContentGenerator
		implements HttpRequestHandler, EmbeddedValueResolverAware, InitializingBean, CorsConfigurationSource {
 	/*xxx: 抽象资源定位符*/
	private final List<Resource> locations = new ArrayList<>(4);
    
    /*xxx: 实际处理请求的方法*/
	public void handleRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
        /*xxx: 获取资源定位符,如果获取失败,则报404*/
		Resource resource = getResource(request);
		if (resource == null) {
			logger.debug("Resource not found");
			/*xxx: 获取不到,则抛错*/
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
			return;
		}
        
        //xxx: 省略其它抽象...
        /*xxx: 检测方法与session是否满足条件*/
		checkRequest(request);
        
        /*xxx: 检测资源是否更改过,没更改过则不处理*/
		if (isUseLastModified() && new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
			logger.trace("Resource not modified");
			return;
		}

        /*xxx: 设置缓存*/
		prepareResponse(response);

		/*xxx: 设置资源类型*/
		MediaType mediaType = getMediaType(request, resource);
        
        //xxx: 省略其它抽象...
        ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
        /*xxx: 通过消息转换器写入资源内容 */
		this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
     	 //xxx: 写资源的时候,分为分段写入,以及全量写入,状态码分别为: 206 以及 
    }
    
    @Nullable
	protected Resource getResource(HttpServletRequest request) throws IOException {
		/*xxx: 该值在父流程中,已经设置好*/
		String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
     	//xxx: 省略其它抽象...
        /*xxx: 通过资源解析器链解析资源*/
		Resource resource = this.resolverChain.resolveResource(request, path, getLocations());
		if (resource != null) {
			resource = this.transformerChain.transform(request, resource);
		}
		return resource;
    }
    
}

# ResourceHttpMessageConverter资源转换器结构

/*xxx: http消息转换器*/
public interface HttpMessageConverter<T> {
 	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
    
    boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
    
    T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
    
    void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}
public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConverter<T> {
 	/*xxx: 支持的媒体类型列表*/
	private List<MediaType> supportedMediaTypes = Collections.emptyList();
    
    @Override
	public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		return readInternal(clazz, inputMessage);
	}
    
    protected abstract T readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
    
    public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {

		final HttpHeaders headers = outputMessage.getHeaders();
		//xxx: 添加默认头部信息
		addDefaultHeaders(headers, t, contentType);
        
        if (outputMessage instanceof StreamingHttpOutputMessage) {
			StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;
			//xxx: 流消息
			streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {
				@Override
				public OutputStream getBody() {
					return outputStream;
				}
				@Override
				public HttpHeaders getHeaders() {
					return headers;
				}
			}));
		}
		else {
			writeInternal(t, outputMessage);
			outputMessage.getBody().flush();
		}
    }
    
    protected void addDefaultHeaders(HttpHeaders headers, T t, @Nullable MediaType contentType) throws IOException {
        //xxx: 设置响应类型,略
        
     	//xxx: 设置内容长度
		if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
			Long contentLength = getContentLength(t, headers.getContentType());
			if (contentLength != null) {
				headers.setContentLength(contentLength);
			}
		}   
    }
    
    protected abstract void writeInternal(T t, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}
/*xxx: 资源消息转换器*/
public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<Resource> {
 	@Override
	protected Resource readInternal(Class<? extends Resource> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		/*xxx: 流工具操作,流没法直接操作,理论上,它由底层硬件控制*/
		if (this.supportsReadStreaming && InputStreamResource.class == clazz) {
			return new InputStreamResource(inputMessage.getBody()) {
				@Override
				public String getFilename() {
					return inputMessage.getHeaders().getContentDisposition().getFilename();
				}
				@Override
				public long contentLength() throws IOException {
					long length = inputMessage.getHeaders().getContentLength();
					return (length != -1 ? length : super.contentLength());
				}
			};
		}
		else if (Resource.class == clazz || ByteArrayResource.class.isAssignableFrom(clazz)) {
			byte[] body = StreamUtils.copyToByteArray(inputMessage.getBody());
			return new ByteArrayResource(body) {
				@Override
				@Nullable
				public String getFilename() {
					return inputMessage.getHeaders().getContentDisposition().getFilename();
				}
			};
		}
		else {
			throw new HttpMessageNotReadableException("Unsupported resource class: " + clazz, inputMessage);
		}
	}   
    
    @Override
	/*xxx: 写入资源*/
	protected void writeInternal(Resource resource, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {

		writeContent(resource, outputMessage);
	}
    
    protected void writeContent(Resource resource, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
		try {
			InputStream in = resource.getInputStream();
			try {
				/*xxx: 流拷贝*/
				StreamUtils.copy(in, outputMessage.getBody());
			}
			catch (NullPointerException ex) {
				// ignore, see SPR-13620
			}
			finally {
				try {
					in.close();
				}
				catch (Throwable ex) {
					// ignore, see SPR-12999
				}
			}
		}
		catch (FileNotFoundException ex) {
			// ignore, see SPR-12999
		}
	}
}

# springMvc前端静态资源原理

# springBoot-Mvc自动装配

  • 官方的优先级低于用户优先级
  • 官方判断条件为@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)

# 自动配置的细节

@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
public class WebMvcAutoConfiguration {
 	   
    @Import(EnableWebMvcConfiguration.class)
    /*xxx: webMvc的自动配置, configurer配置器,相当于插件化配置 */
    //xxx: 主要用于装配相应的属性
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
    	//略
    }
    
    //xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件: HandlerMapping,HandlerAdapter
	//xxx: 其父类会配置 springMvc的其它机制的套件
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
     	//xxx: 略   
    }
}
/*xxx: 为springMvc提供插件化配置的功能*/
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
 	//略   
}

/*xxx: springMvc官方提供的标准配置项*/
public class WebMvcConfigurationSupport implements ApplicationContextAware, ServletContextAware {
 	
    
    @Bean
	@SuppressWarnings("deprecation")
	/*xxx: 标准配置里面提供该配置,但在springBoot的环境下,会被替换*/
	public RequestMappingHandlerMapping requestMappingHandlerMapping(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
        //略...
        return null;
    }
    
    @Bean
    /*xxx: 标准配置里面提供该配置,但在springBoot的环境下,会被替换*/
	public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcValidator") Validator validator) {
     	//略...
        return null;
    }
    
    @Bean
	@Nullable
	/*xxx: 处理资源的 HandlerMapping,本质上为 SimpleUrlHandlerMapping */
	public HandlerMapping resourceHandlerMapping(
			@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
			@Qualifier("mvcConversionService") FormattingConversionService conversionService,
			@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
     	ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
				this.servletContext, contentNegotiationManager, pathConfig.getUrlPathHelper());
		addResourceHandlers(registry);

		AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
        
        return handlerMapping;
    }
    
    @Bean
	/*xxx: 处理资源的 适配器套件 */
	public HttpRequestHandlerAdapter httpRequestHandlerAdapter() {
		return new HttpRequestHandlerAdapter();
	}
}

# 生效的HandlerMapping

  • SimpleUrlHandlerMapping实现细节,见上文资源处理

# HandlerMapping的默认位置

  • 更多内容详见tomcat部分-最佳实践springMVC的结合章节
public class DispatcherServlet extends FrameworkServlet {
     	
        /*xxx: 初始化组件策略*/
	private void initHandlerMappings(ApplicationContext context) {
     	  /*xxx: 从上下文中获取 handlerMapping,分为按类型获取多个,以及按名称获取单个*/
		/*xxx: 该属性默认为开启状态, 即默认情况下,通过类型的方式从容器中获取 handlerMapping*/
		if (this.detectAllHandlerMappings) {
         	Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				//xxx: 根据order配置对handlerMapping进行排序, 所有的HandlerMapping都实现了Ordered接口
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}   
        }
        
        /*xxx: 上下文中,没有相应实现,则采用默认策略*/
		//xxx: 默认策略不改动的情况下为: BeanNameUrl,RequestMapping,RouterFunction
		if (this.handlerMappings == null) {
			this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
            //xxx: 略...
		}
    }
 }
public class WebMvcConfigurationSupport{
            @Bean
        public RequestMappingHandlerMapping requestMappingHandlerMapping(){
                RequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();
            mapping.setOrder(0);
            //xxx: 略...
            return null;
            }

            @Bean
        public BeanNameUrlHandlerMapping beanNameHandlerMapping(){
                BeanNameUrlHandlerMapping mapping = new BeanNameUrlHandlerMapping();
            mapping.setOrder(2);
            //略...
            return null;
            }

            @Bean
        public RouterFunctionMapping routerFunctionMapping(){
            RouterFunctionMapping mapping = new RouterFunctionMapping();
            mapping.setOrder(3);
            //略
            return null;
        }
    
    @Bean
		public WelcomePageHandlerMapping welcomePageHandlerMapping(){
            WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping();
            
            setOrder(2);
            //xxx: 略
            return null;
        }
}

public class WebMvcEndpointManagementContextConfiguration {
 	void ControllerEndpointHandlerMapping(){
        setOrder(-100);
        //略
    }   
}
public class ResourceHandlerRegistry {
 	
    //xxx: 该值作为资源处理 处理器 的默认顺序
	private int order = Ordered.LOWEST_PRECEDENCE - 1;
}
  • RequestMapping的默认位置为0,Resource的默认位置为Ordered.LOWEST_PRECEDENCE-1这意味着资源请求的处理总是在requestMapping之后完成
  • 常见的HandlerMapping都有固定的顺序。RequestMapping顺序默认为0

# springBoot-Mvc的自动配置参数

public class WebMvcAutoConfiguration {
    //xxx: webMvc的实际配置项 该类主要配置了 基于 RequestMapping的mvc套件
	//xxx: 其父类会配置 springMvc的其它机制的套件
	public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {
        @Override
		protected void addResourceHandlers(ResourceHandlerRegistry registry) {
         	//xxx: 默认情况下, 识别为资源的路径有:
			//xxx: /webjars/**、/** (不配置的情况下)
			
			//xxx: 默认寻找的路径再不配置的情况下有:
			//xxx:   classpath:/META-INF/resources/webjars/,
			// 		"classpath:/META-INF/resources/",
			//		"classpath:/resources/", 
			//		"classpath:/static/", 
			//		"classpath:/public/"
			ServletContext servletContext = getServletContext();
			addResourceHandler(registry, "/webjars/**", "classpath:/META-INF/resources/webjars/");
			addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
				registration.addResourceLocations(this.resourceProperties.getStaticLocations());
				if (servletContext != null) {
					registration.addResourceLocations(new ServletContextResource(servletContext, SERVLET_LOCATION));
				}
			});   
        }
    }
}

# 配置资源形式

spring.mvc.static-path-pattern=/** 
  • 默认为/**
  • 配置项为单个参数,即只能满足指定一个统配的要求,如指定/static/video/**为视频请求,/template/myword/**为自定义资源请求,无法做到精细化配置;
  • 当前默认情况下/**可以处理所有的静态资源(在前面没有RequestMapping以及自定义的HandlerMapping的情况下)

# 配置资源地址

spring.resources.staticLocations=aaa,bbb,ccc
  • 默认识别地址包括:classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public

# 非SpringBoot环境注意事项

# MVC基础设置装配

  • 装配方式,可以粗糙的理解为两种:

    • 使用了标准springMVC配置的方式:WebMvcConfigurationSupport
    • 没有使用标准springMVC配置的方式
  • 二者的springMVC装配行为存在着较大差距

# 静态资源处理

  • springMvc在没有使用标准springMVC配置的情况下,不存在静态资源处理功能

  • 使用了标准配置,仍然需要进行配置才可使用静态资源功能

# springMvc静态资源与动态网页

  • 动态网页与静态资源不是一个概念,但静态资源可以充当网页,代码层面上来说,它们也不再一个层次,静态资源在动态网页的更上层;
  • 动态网页基于RequestMapping,静态资源基于SimpleUrl
  • 动态网页的核心在于视图解析器,静态资源的核心在于静态资源地址

# springboot项目放置静态资源

# 方式一

  • 1.配置 spring.mvc.static-path-pattern: /template/*
  • 2.将html文档放在 templates放在默认的资源目录下,通过template访问(所有的静态资源,均需要template前缀才能够访问),不配置的情况下,所有路径都能够访问静态资源
  • 默认资源目录:classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/,classpath:/public/,不用考虑是 springboot项目,还是tomcat项目,直接把html文档也当成静态资源;

# 方式二

spring:
  web:
    resources:
      static-locations: classpath:/META-INF/resources/, classpath:/resources/, classpath:/static/,classpath:/public/,classpath:/templates/
  • classpath:/templates/就是需要新增的资源路径

  • 默认情况下,templates用于存放Thymeleaf、FreeMarker等模板引擎的模板文件

  • 将templates目录设置为静态资源可能会导致安全风险,因为这会使原本仅限于服务器端渲染的模板文件暴露给客户端。请确保这样做不会泄露敏感信息。

  • 如果你的项目同时使用了模板引擎(如Thymeleaf),将templates目录设为静态资源可能会影响模板的正常工作,因为这些文件不再经过模板引擎的解析。

  • 注意事项:

spring:
  mvc:
    view:
      prefix: /template/
      suffix: .html
  • 该配置是为了用于寻找模板引擎的模板文件,必须结合模板引擎的视图解析器使用;它与静态资源有所区别(服务端的角度而言);

# 方式三

  • 通过自定义静态资源处理器,实现自定义静态资源路径
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class StaticResourceConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/template/**")
                .addResourceLocations("classpath:/templates/");
    }
}
  • 这个配置告诉Spring Boot当请求以/template/开头时,从类路径的/templates/目录中查找资源。