官网介绍
Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就已包含在Spring框架中。正式名称“ Spring Web MVC”来自其源模块的名称(spring-webmvc),但它通常被称为“ Spring MVC”。
最典型的MVC就是JSP + servlet + javabean的模式。
所以SpringMVC 就是在Servlet的基础上进行了封装,帮我把这些麻烦事都给我们做了
首先我们需要配置web.xml文件
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-core.xmlparam-value>
context-param>
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring-mvc.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
<async-supported>trueasync-supported>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
web-app>
DispatcherServlet 是 Spring MVC 框架中的核心组件,用于处理 HTTP 请求并将其分派到合适的处理器(Controller)进行处理。
- Servlet容器初始化: 当应用启动时,Servlet 容器(如Tomcat)会加载和初始化 DispatcherServlet。在初始化过程中,DispatcherServlet 会创建并初始化自己的 ApplicationContext,该上下文包含了整个 Spring 应用的配置信息。
- WebApplicationContext初始化: DispatcherServlet 内部使用了一个 WebApplicationContext,该上下文是 ApplicationContext 的子接口,专门用于 Web 应用。WebApplicationContext 的初始化会根据配置文件(如 web.xml 或者注解配置)加载相应的 Bean 定义。
- HandlerMapping的注册: 在 DispatcherServlet 初始化时,会注册一个或多个 HandlerMapping。HandlerMapping 的作用是根据请求的 URL 映射到具体的处理器(Controller)。
- HandlerAdapter的注册: HandlerAdapter 负责执行具体的处理器方法,根据请求的参数和返回值进行适当的处理。在 DispatcherServlet 初始化时,会注册多个 HandlerAdapter,每个适配器负责处理不同类型的处理器。
- ViewResolver的注册: ViewResolver 负责将处理器方法的逻辑视图名解析为实际的视图对象。DispatcherServlet 初始化时,会注册一个或多个 ViewResolver。
- 请求分发过程: 当有 HTTP 请求到达时,DispatcherServlet 会根据请求的 URL 通过注册的 HandlerMapping 找到相应的处理器。然后,使用合适的 HandlerAdapter 执行处理器方法,并得到处理结果。
- 视图解析和渲染: 处理器方法执行完毕后,DispatcherServlet 会将处理结果交给注册的 ViewResolver 进行解析,得到实际的视图对象。最后,使用该视图对象进行渲染,并将渲染结果返回给客户端。
- 拦截器的应用: DispatcherServlet 还支持拦截器的应用,拦截器可以在请求处理前、处理后或渲染视图前进行一些额外的逻辑处理。
然后创建控制器
@RestController
public class TulingController {
@Autowired
private TulingServiceImpl tulingServiceImpl;
@RequestMapping(value = {"/angle"})
public String testTuling(HttpServletRequest httpServletRequest) {
System.out.println("URL:"+httpServletRequest.getRequestURL());
System.out.println("URI:"+httpServletRequest.getRequestURI());
System.out.println("contextPath:"+httpServletRequest.getContextPath());
System.out.println("serlvetPath:"+httpServletRequest.getServletPath());
ServletContext servletContext = httpServletRequest.getServletContext();
tulingServiceImpl.sayHello();
return "smlz";
}
@RequestMapping(value = {"/tuling"})
public String testAngle(HttpServletRequest httpServletRequest, HttpServletResponse response) {
ServletContext servletContext = httpServletRequest.getServletContext();
return "smlz";
}
@RequestMapping("/returnJson")
public Object returnJson() {
Map<String,String> retMap = new HashMap<>();
retMap.put("name","张三");
return retMap;
}
@RequestMapping("/testQuestPram")
public String testRequestParam(@RequestParam("${name}") String name) {
System.out.println("name="+name);
return name;
}
public TulingController() {
System.out.println("TulingController 执行构造方法");
}
@RequestMapping("/initbinder/user")
public User getFormatData(User user) {
System.out.println("user:"+user.toString());
return user;
}
/**
* 作用于单个controller
* WebDataBinder 的作用
* @param webDataBinder
*/
@InitBinder
public void initWebBinderDataFormatter(WebDataBinder webDataBinder) {
//作用一:加入类型转化器
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
CustomDateEditor dateEditor = new CustomDateEditor(df, true);
webDataBinder.registerCustomEditor(Date.class,dateEditor);
}
}
创建xml配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">
<context:component-scan base-package="com.tuling.xml">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
context:component-scan>
beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.tuling.xml">context:component-scan>
<mvc:annotation-driven>mvc:annotation-driven>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" name="viewResolver">
<property name="prefix" value="/WEB-INF/jsp/">property>
<property name="suffix" value=".jsp">property>
bean>
beans>
Spring MVC 是围绕前端控制器模式设计的,其中:中央 Servlet DispatcherServlet 为请求处理流程提供统一调度,实际工作则交给可配置组件执行。这个模型是灵活的且开放的,我们可以通过自己去定制这些组件从而进行定制自己的工作流。
结合上面给的案例,我们进行调试,首先浏览器输入请求地址,此时方法就会进入DispatcherServlet
类的doDispatch
方法。这个方法的重要性和Spring中的Spring框架中的refresh
方法是一样重要的,几乎所有的工作都是在这个方法中完成的。
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
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler. HTTP缓存相关
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果mv有 视图没有,给你设置默认视图
applyDefaultViewName(processedRequest, mv);
//后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
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()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
我们先看主流程,其它细节后面再讲。它执行到源码中的mappedHandler = getHandler(processedRequest);
这句代码,我们进入getHandler
方法。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/** 拿到所有handlerMappings (容器启动阶段初始化:拿到所有实现了HandlerMapping的Bean)
* @see DispatcherServlet#initHandlerMappings
* 测试发现: 不同的HandlerMapping可以有相同path, 谁先解析到就用哪个
* */
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
这个方法首先会使用一个for循环,遍历所有的handlerMapping
。他会调用每个HandlerMapping
的getHandler
方法进行映射,如果某个HandlerMapping
映射成功了就会直接返回(可以使用@Order注解配置优先级),不会再接着映射了,返回一个处理器映射器链(为什么会是链,因为可能会有拦截器需要执行),如果没有配置HandlerMapping
,就会直接返回null。
在Spring MVC中,HandlerMapping的作用是将HTTP请求的URL映射到相应的处理器(Controller)。它负责根据请求的信息,如URL和请求方法,找到合适的处理器,以便进行进一步的请求处理。不同的HandlerMapping实现支持不同的映射策略,其中RequestMappingHandlerMapping通过注解配置实现了常用的URL映射。这一过程使得开发者能够通过定义Controller类和相应的映射关系,实现对不同URL请求的处理逻辑。
继续回到doDispatch
方法,下面就会执行HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
去找到合适的HandlerAdapter
。我们进入该方法。
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
首先它会遍历所有的HandlerAdapter
,然后调用supports
方法判断当前的HandlerAdapter
是否支持。它会根据前面handlerMapping
返回的Handler
的具体类去调用不同的supports
的实现方法,来判断。具体的作用就是根据不同的Handler
去选择最合适的HandlerAdapter
。继续回到doDispatch
方法。然后调用下面代码。
HandlerAdapter 是 Spring MVC 中的一个关键组件,它的主要作用是负责执行处理器(Controller)方法,并处理方法的参数、返回值等。通过适配不同类型的处理器方法,HandlerAdapter 实现了框架与各种处理器之间的解耦,使得不同类型的处理器方法能够被统一地执行和处理,从而实现了灵活的方法调用和适配。
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
上面代码就是处理前置拦截器的,首先我们先回顾一下SpringMVC的拦截器的使用方法。
Spring MVC 拦截器是一种强大的机制,允许在请求处理的不同阶段执行额外的逻辑。拦截器通常用于日志记录、权限验证、国际化等需求。下面是一个详细的案例,演示如何在 Spring MVC 中使用拦截器。
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("Interceptor: preHandle method is called");
// 返回true表示继续执行后续拦截器和处理器,返回false则中断执行
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor: postHandle method is called");
// 在处理器方法执行完毕后,在渲染视图前执行
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("Interceptor: afterCompletion method is called");
// 在渲染视图后执行,可以用于资源清理等操作
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="com.example.interceptor.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping("/hello")
@ResponseBody
public String helloWorld() {
return "Hello, Spring MVC!";
}
}
当访问 /hello 路径时,拦截器的 preHandle 方法会被调用。如果 preHandle 返回 true,则执行相应的处理器方法(Controller中的 helloWorld 方法)。执行完处理器方法后,拦截器的 postHandle 方法被调用。最后,在渲染视图后,拦截器的 afterCompletion 方法被调用。
继续回到源码:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
这个就是调用了applyPreHandle
方法,我们进入该方法。
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
上面代码就是调用了前置拦截器preHandler
方法。继续doDispatch
方法。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
上面代码就执行了最合适的HandlerAdapter
的handle方法。我们进入该方法:
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查当前请求的method是否为支持的method(默认Null,可通过继承AbstractController设置supportedMethods)
// 检查当前请求是否必须session (默认false,可通过继承AbstractController设置requireSession)
checkRequest(request);
/**
* 判断当前是否需要支持在同一个session中只能线性地处理请求
* 因为锁是通过 synchronized 是 JVM 进程级,所以在分布式环境下,
* 无法达到同步相同 Session 的功能。默认情况下,synchronizeOnSession 为 false
*/
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// *如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
//判断当前请求头中是否包含Cache-Control请求头,如果不包含,则对当前response进行处理
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
// 如果当前SessionAttribute中存在配置的attributes,则为其设置过期时间。
// 这里SessionAttribute主要是通过@SessionAttribute注解生成的
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
// 如果当前不存在SessionAttributes,则判断当前是否存在Cache-Control设置,
// 如果存在,则按照该设置进行response处理,如果不存在,则设置response中的
// Cache的过期时间为-1,即立即失效
prepareResponse(response);
}
}
return mav;
}
然后执行invokeHandlerMethod
方法。
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 把我们的请求req resp包装成 ServletWebRequest
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
// 配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
// 这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 让invocableMethod拥有参数解析能力
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 让invocableMethod拥有返回值处理能力
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 让invocableMethod拥有InitBinder解析能力
invocableMethod.setDataBinderFactory(binderFactory);
// 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndView处理容器
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
// 将request的Attribute复制一份到ModelMap
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
// 重定向的时候,忽略model中的数据 默认false
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
// 获取当前的AsyncWebRequest,这里AsyncWebRequest的主要作用是用于判断目标
// handler的返回值是否为WebAsyncTask或DeferredResult,如果是这两种中的一种,
// 则说明当前请求的处理应该是异步的。所谓的异步,指的是当前请求会将Controller中
// 封装的业务逻辑放到一个线程池中进行调用,待该调用有返回结果之后再返回到response中。
// 这种处理的优点在于用于请求分发的线程能够解放出来,从而处理更多的请求,提高吞吐。
// 只有待目标任务完成之后才会回来将该异步任务的结果返回。
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
// 封装异步任务的线程池、request、interceptors到WebAsyncManager中
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);
}
// *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
上面代码主要就是执行了很多的初始化的操作,然后执行invocableMethod.invokeAndHandle(webRequest, mavContainer);
方法。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
/*真正的调用我们的目标对象 很重要 很重要*/
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置相关的返回状态
setResponseStatus(webRequest);
// 如果请求处理完成,则设置requestHandled属性
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
// 如果请求失败,但是有错误原因,那么也会设置requestHandled属性
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
// 如果支持,则使用该handler处理该返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
然后执行invokeForRequest
执行我们真正的Controller中方法的逻辑。
@Nullable
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);
}
这里同样用的是反射,这里就是解析我们的参数,然后调用目标方法。继续回到invokeAndHandle
方法,拿到了执行的返回值后,我们会对返回值结果进行解析。
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
调用了handleReturnValue
进行返回值解析
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
HandlerMethodReturnValueHandler
方法会根据你的返回值类型是String
或者ModelAndView
来选择合适的返回值解析器,然后在handleReturnValue
方法中调用返回值解析器的handleReturnValue
方法对返回值进行解析。回到handleInternal
返回上面代码的处理结果最后会被封装为一个ModelAndView
对象,返回到handleInternal
方法中(如果返回值是Json这个mva对象就是null,就没有后续处理了)。继续回到doDispatch
方法。继续执行下面代码:
applyDefaultViewName(processedRequest, mv);
这个就是给你的ModelAndView对象设置一个默认视图,如果我们没有手动在Controller方法中设置视图的话。
ModelAndView 是 Spring MVC 中的一个对象,用于封装控制器方法的处理结果和模型数据。它包含了视图名(View Name)和一个模型对象(Model),其中模型对象是一个 Map 类型,存储了控制器方法产生的数据。通过 ModelAndView,控制器方法可以同时指定要展示的视图和传递给视图的模型数据,实现了控制器与视图之间的松耦合,并提供了一种方便的方式来组织和传递数据以渲染视图。
继续执行下面代码:
mappedHandler.applyPostHandle(processedRequest, response, mv);
这句代码就是执行后置拦截器了。实际就是执行postHandler方法。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
继续执行下面代码:
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
上面代码就是真正的开始渲染视图了,我们看看它底层是怎么做的。
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);
}
}
// Did the handler return a view to render?
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()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
// Exception (if any) is already handled.. 拦截器:AfterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
当没有出现异常的时候会调用,就调用render
方法去解析、渲染视图。
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 调用resolveViewName方法去解析视图名称
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
上面代码首先会解析视图的名称(我们知道ModelAndView中的视图名称可能只是一个具体的jsp的文件名,但实际要解析成有前缀和后缀的详细文件名称),调用的代码是view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
上面代码同样是拿到一个视图解析器就直接开始解析文件名,然后返回解析结果。继续回到render
方法,以切准备工作做完之后就开始真正解析和渲染视图了。
view.render(mv.getModelInternal(), request, response);
进入该方法:
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
// 这里
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
然后执行renderMergedOutputModel(mergedModel, getRequestToExpose(request), response)
这句代码:
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes. 将model设置到request的attribute中.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any. 设置国际化资源
exposeHelpers(request);
// Determine the path for the request dispatcher. 防止死循环请求
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
// 通过request拿到RequestDispatcher request.getRequestDispacther("/test.jsp")
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
} // RequestDispatcher.forward直接转发,就这么简单粗暴
rd.forward(request, response);
}
}
上面代码就讲model中所有的值都设置到了request的Attribute属性当中,实际执行的是这句代码exposeModelAsRequestAttributes(model, request);
。
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
// 将model解析到request的attribute中
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
}
else {
request.removeAttribute(name);
}
});
}
上面代码就是讲model解析到request的attribute属性中。回到renderMergedOutputModel
方法。然后就是设置一些国际化资源,然后执行RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
拿到RequestDispatcher
对象。最后调用rd.forward(request, response);
进行请求转发。回到processDispatchResult
方法,最后执行最后的拦截器afterCompletion
方法。
if (mappedHandler != null) {
// Exception (if any) is already handled.. 拦截器:AfterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
}
以上SpringMVC的大致流程就讲解完了。
SpringMVC启动时会将所有的@RequestMapping
解析出来,解析流程图。HandlerMapping
有很多种,这里我们就看一下用的最多的RequestMappingHandlerMapping
。
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
private boolean useSuffixPatternMatch = false;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private Map<String, Predicate<Class<?>>> pathPrefixes = Collections.emptyMap();
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
@Nullable
private StringValueResolver embeddedValueResolver;
private RequestMappingInfo.BuilderConfiguration config = new RequestMappingInfo.BuilderConfiguration();
@Deprecated
public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
}
@Deprecated
public void setUseRegisteredSuffixPatternMatch(boolean useRegisteredSuffixPatternMatch) {
this.useRegisteredSuffixPatternMatch = useRegisteredSuffixPatternMatch;
this.useSuffixPatternMatch = (useRegisteredSuffixPatternMatch || this.useSuffixPatternMatch);
}
public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) {
this.useTrailingSlashMatch = useTrailingSlashMatch;
if (getPatternParser() != null) {
getPatternParser().setMatchOptionalTrailingSeparator(useTrailingSlashMatch);
}
}
public void setPathPrefixes(Map<String, Predicate<Class<?>>> prefixes) {
this.pathPrefixes = (!prefixes.isEmpty() ?
Collections.unmodifiableMap(new LinkedHashMap<>(prefixes)) :
Collections.emptyMap());
}
public Map<String, Predicate<Class<?>>> getPathPrefixes() {
return this.pathPrefixes;
}
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
Assert.notNull(contentNegotiationManager, "ContentNegotiationManager must not be null");
this.contentNegotiationManager = contentNegotiationManager;
}
public ContentNegotiationManager getContentNegotiationManager() {
return this.contentNegotiationManager;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch()); // 尾部斜杠
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
super.afterPropertiesSet();
}
@Deprecated
public boolean useSuffixPatternMatch() {
return this.useSuffixPatternMatch;
}
@Deprecated
public boolean useRegisteredSuffixPatternMatch() {
return this.useRegisteredSuffixPatternMatch;
}
public boolean useTrailingSlashMatch() {
return this.useTrailingSlashMatch;
}
@Nullable
@Deprecated
@SuppressWarnings("deprecation")
public List<String> getFileExtensions() {
return this.config.getFileExtensions();
}
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 如果方法上面有@RequestMapping:解析出RequestMappingInfo
// RequestMappingInfo 是用来在请求的时候做匹对的
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 如果方法上面有@RequestMapping,看看类上面是不是有@RequestMapping
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
// 类上面也有@RequestMapping 那就合并
// 比如 类:/user 方法:/info 合并为 /user/info
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// 合并前缀 5.1新增 默认null
// 可通过 WebMvcConfigurer#configurePathMatch 进行定制
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
@Nullable
String getPathPrefix(Class<?> handlerType) {
for (Map.Entry<String, Predicate<Class<?>>> entry : this.pathPrefixes.entrySet()) {
if (entry.getValue().test(handlerType)) {
String prefix = entry.getKey();
if (this.embeddedValueResolver != null) {
prefix = this.embeddedValueResolver.resolveStringValue(prefix);
}
return prefix;
}
}
return null;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取RequestMapping注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 获取请求调解:[可扩展], 如果有:该条件会在请求时匹对
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 如果有RequestMapping注解,封装成RequestMappingInfo
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
@Nullable
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
@Nullable
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 将@RequestMapping注解属性的值构建成一个 RequestMappingInfo
RequestMappingInfo.Builder builder = RequestMappingInfo
//构建路径
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
//构建方法(get还是post等)
.methods(requestMapping.method())
//参数 对应http request parameter
.params(requestMapping.params())
//头部
.headers(requestMapping.headers())
//request的提交内容类型content type,如application/json, text/html
.consumes(requestMapping.consumes())
//指定返回的内容类型的content type,仅当request请求头中的(Accept)类型中包含该指定类型才返回
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
// 构造RequestMappingInfo:将上面的属性构建成一个个的RequestCondition对象方便在请求的时候组合匹对
return builder.options(this.config).build();
}
protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
if (this.embeddedValueResolver == null) {
return patterns;
}
else {
String[] resolvedPatterns = new String[patterns.length];
for (int i = 0; i < patterns.length; i++) {
resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
}
return resolvedPatterns;
}
}
@Override
public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
super.registerMapping(mapping, handler, method);
updateConsumesCondition(mapping, method);
}
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
updateConsumesCondition(mapping, method);
}
private void updateConsumesCondition(RequestMappingInfo info, Method method) {
ConsumesRequestCondition condition = info.getConsumesCondition();
if (!condition.isEmpty()) {
for (Parameter parameter : method.getParameters()) {
MergedAnnotation<RequestBody> annot = MergedAnnotations.from(parameter).get(RequestBody.class);
if (annot.isPresent()) {
condition.setBodyRequired(annot.getBoolean("required"));
break;
}
}
}
}
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
Assert.isNull(getPatternParser(), "This HandlerMapping requires a PathPattern");
RequestMappingInfo info = RequestMappingInfo.paths(pattern).options(this.config).build();
RequestMappingInfo match = info.getMatchingCondition(request);
return (match != null && match.getPatternsCondition() != null ?
new RequestMatchResult(
match.getPatternsCondition().getPatterns().iterator().next(),
UrlPathHelper.getResolvedLookupPath(request),
getPathMatcher()) : null);
}
@Override
protected CorsConfiguration initCorsConfiguration(Object handler, Method method, RequestMappingInfo mappingInfo) {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
Class<?> beanType = handlerMethod.getBeanType();
CrossOrigin typeAnnotation = AnnotatedElementUtils.findMergedAnnotation(beanType, CrossOrigin.class);
CrossOrigin methodAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, CrossOrigin.class);
if (typeAnnotation == null && methodAnnotation == null) {
return null;
}
CorsConfiguration config = new CorsConfiguration();
updateCorsConfig(config, typeAnnotation);
updateCorsConfig(config, methodAnnotation);
if (CollectionUtils.isEmpty(config.getAllowedMethods())) {
for (RequestMethod allowedMethod : mappingInfo.getMethodsCondition().getMethods()) {
config.addAllowedMethod(allowedMethod.name());
}
}
return config.applyPermitDefaultValues();
}
private void updateCorsConfig(CorsConfiguration config, @Nullable CrossOrigin annotation) {
if (annotation == null) {
return;
}
for (String origin : annotation.origins()) {
config.addAllowedOrigin(resolveCorsAnnotationValue(origin));
}
for (String patterns : annotation.originPatterns()) {
config.addAllowedOriginPattern(resolveCorsAnnotationValue(patterns));
}
for (RequestMethod method : annotation.methods()) {
config.addAllowedMethod(method.name());
}
for (String header : annotation.allowedHeaders()) {
config.addAllowedHeader(resolveCorsAnnotationValue(header));
}
for (String header : annotation.exposedHeaders()) {
config.addExposedHeader(resolveCorsAnnotationValue(header));
}
String allowCredentials = resolveCorsAnnotationValue(annotation.allowCredentials());
if ("true".equalsIgnoreCase(allowCredentials)) {
config.setAllowCredentials(true);
}
else if ("false".equalsIgnoreCase(allowCredentials)) {
config.setAllowCredentials(false);
}
else if (!allowCredentials.isEmpty()) {
throw new IllegalStateException("@CrossOrigin's allowCredentials value must be \"true\", \"false\", " +
"or an empty string (\"\"): current value is [" + allowCredentials + "]");
}
if (annotation.maxAge() >= 0 ) {
config.setMaxAge(annotation.maxAge());
}
}
private String resolveCorsAnnotationValue(String value) {
if (this.embeddedValueResolver != null) {
String resolved = this.embeddedValueResolver.resolveStringValue(value);
return (resolved != null ? resolved : "");
}
else {
return value;
}
}
}
这个类在容器启动的时候会被初始化为一个bean,然后在该bean的生命周期中会调用它的回调方法afterPropertiesSet
方法。首先我们回顾一下Spring的回调方法的使用:
Spring 框架中的回调方法通常是通过接口或注解实现的,用于在特定的生命周期事件发生时执行相应的逻辑。下面详细介绍一些常见的 Spring 回调方法,并结合案例说明它们的用法。
- InitializingBean 和 DisposableBean 接口:
InitializingBean: 实现该接口的类在初始化时会执行 afterPropertiesSet() 方法。DisposableBean: 实现该接口的类在销毁时会执行 destroy() 方法。
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
public class MyBean implements InitializingBean, DisposableBean {
// 初始化逻辑
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean is being initialized...");
}
// 销毁逻辑
@Override
public void destroy() throws Exception {
System.out.println("Bean is being destroyed...");
}
}
- @PostConstruct 和 @PreDestroy 注解:
@PostConstruct: 用于在构造函数执行之后、依赖注入完成之后执行初始化逻辑。@PreDestroy: 用于在 Bean 销毁之前执行清理逻辑。
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBean {
// 初始化逻辑
@PostConstruct
public void init() {
System.out.println("Bean is being initialized...");
}
// 销毁逻辑
@PreDestroy
public void cleanUp() {
System.out.println("Bean is being destroyed...");
}
}
- ApplicationListener 接口:
实现该接口可以监听 Spring 应用中的事件,执行相应的逻辑。
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
public class MyListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
// Spring 上下文初始化完成时的逻辑
System.out.println("Context is refreshed...");
}
}
}
这些回调方法可以用于执行一些特定于应用程序生命周期的逻辑,确保在初始化和销毁阶段执行必要的操作。通过实现相应的接口或使用注解,开发者可以方便地与 Spring 框架的生命周期集成。
@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setTrailingSlashMatch(useTrailingSlashMatch()); // 尾部斜杠
this.config.setContentNegotiationManager(getContentNegotiationManager());
if (getPatternParser() != null) {
this.config.setPatternParser(getPatternParser());
Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
"Suffix pattern matching not supported with PathPatternParser.");
}
else {
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setPathMatcher(getPathMatcher());
}
super.afterPropertiesSet();
}
然后调用super.afterPropertiesSet();
方法,我们进入该方法。
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
该方法中就调用了一个initHandlerMethods
方法,我们进入该方法
protected void initHandlerMethods() {
// 获得所有候选beanName—— 当前容器所有的beanName
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// *处理候选bean——即解析@RequestMapping和映射路径
processCandidateBean(beanName);
}
}
// 解析完所有@RequestMapping的时候调用
handlerMethodsInitialized(getHandlerMethods());
}
上面方法首先获得了Spring容器中所有bean得名称,然后调用processCandidateBean
方法处理所有以scopedTarget.
开头的bean,即解析@RequestMapping
和映射路径。
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
//获得当前bean得类型
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// 这一步判断是关键 是否有Controller 或 RequestMapping注解
if (beanType != null && isHandler(beanType)) {
// 解析HandlerMethods
detectHandlerMethods(beanName);
}
}
首先根据bean的名称获取bean得类型,然后执行if(beanType != null && isHandler(beanType))
,首先判断beanType != null
不为空,然后调用isHandler(beanType)
判断是否有Controller 或 RequestMapping注解 。我们进入该方法:
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
回到processCandidateBean
方法,如果有Controller.class
和RequestMapping.class
注解,然后就是执行detectHandlerMethods
方法,解析HandlerMethods
。我们进入该方法:
protected void detectHandlerMethods(Object handler) {
//判断传入进来的handler的类型是否为String,如果为String就获取对应的bean的名称,如果本来就是bean,就直接获取bean得类型。
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
//如果bean的类型不为空
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(formatMappings(userType, methods));
}
else if (mappingsLogger.isDebugEnabled()) {
mappingsLogger.debug(formatMappings(userType, methods));
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
上面代码首先执行下面代码:
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);
}
});
这里就是调用了一个selectMethods
方法,然后参数一个是bean的类型以及一个函数式接口,我们先看一下这个函数式接口。其实就是调用了一个getMappingForMethod
方法。
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 如果方法上面有@RequestMapping:解析出RequestMappingInfo
// RequestMappingInfo 是用来在请求的时候做匹对的
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 如果方法上面有@RequestMapping,看看类上面是不是有@RequestMapping
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
// 类上面也有@RequestMapping 那就合并
// 比如 类:/user 方法:/info 合并为 /user/info
if (typeInfo != null) {
info = typeInfo.combine(info);
}
// 合并前缀 5.1新增 默认null
// 可通过 WebMvcConfigurer#configurePathMatch 进行定制
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
}
}
return info;
}
上面方法首先调用createRequestMappingInfo
方法来解析@RequestMapping
注解。进入该方法如下:
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取RequestMapping注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 获取请求调解:[可扩展], 如果有:该条件会在请求时匹对
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 如果有RequestMapping注解,封装成RequestMappingInfo
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
上面代码就是解析了@RequestMapping
注解,然后调用 createRequestMappingInfo(requestMapping, condition)
封装为一个 RequestMappingInfo
对象返回,这个对象就封装了注解的所有信息。
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
// 将@RequestMapping注解属性的值构建成一个 RequestMappingInfo
RequestMappingInfo.Builder builder = RequestMappingInfo
//构建路径
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
//构建方法(get还是post等)
.methods(requestMapping.method())
//参数 对应http request parameter
.params(requestMapping.params())
//头部
.headers(requestMapping.headers())
//request的提交内容类型content type,如application/json, text/html
.consumes(requestMapping.consumes())
//指定返回的内容类型的content type,仅当request请求头中的(Accept)类型中包含该指定类型才返回
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
// 构造RequestMappingInfo:将上面的属性构建成一个个的RequestCondition对象方便在请求的时候组合匹对
return builder.options(this.config).build();
}
然后回到detectHandlerMethods
方法,调用selectMethods
方法。
public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
final Map<Method, T> methodMap = new LinkedHashMap<>();
Set<Class<?>> handlerTypes = new LinkedHashSet<>();
Class<?> specificHandlerType = null;
//获取原始的class对象
if (!Proxy.isProxyClass(targetType)) {
specificHandlerType = ClassUtils.getUserClass(targetType);
handlerTypes.add(specificHandlerType);
}
//获取class的接口
handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));
//循环我们的class集合
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, method -> {
//获取具体的方法对象
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
//这就是调用了前面的lamda表达时
T result = metadataLookup.inspect(specificMethod);
if (result != null) {
// 看看有没有桥接方法,泛型实现类jvm会自动生成桥接类,不知道有啥意义
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
//把方法对象作为key,RequestMappingInfo对象作为value保存到map中
methodMap.put(specificMethod, result);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return methodMap;
}
上面我们就获得了所有加@RequestMapping
注解的方法。回到detectHandlerMethods
,接下来执行下面代码:
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
上面的forEach就遍历了获得的所有方法,然后调用registerHandlerMethod(handler, invocableMethod, mapping);
这句代码。
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
this.readWriteLock.writeLock().lock();
try {
//根据
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);
Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
}
String name = null;
if (getNamingStrategy() != null) {
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
corsConfig.validateAllowCredentials();
this.corsLookup.put(handlerMethod, corsConfig);
}
this.registry.put(mapping,
new MappingRegistration<>(mapping, handlerMethod, directPaths, name, corsConfig != null));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
上面代码的核心就是下面这两句代码,将信息封装到了下面两个重要集合中,第一个集合pathLookup
就存储了path,也就是我们在@RequestMapping
中路径信息,T就是前面的RquestMappingInfo
。到此RequestMapping
注解就解析完毕了。
this.pathLookup.add(path, mapping);
this.corsLookup.put(handlerMethod, corsConfig);
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
上面就已经成功解析了所有的RequestMapping
,然后如果我们通过浏览器访问的时候,来到doDispatch
方法。在执行该方法时,前面受过它会执行mappedHandler = getHandler(processedRequest);
进行映射。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
/** 拿到所有handlerMappings (容器启动阶段初始化:拿到所有实现了HandlerMapping的Bean)
* @see DispatcherServlet#initHandlerMappings
* 测试发现: 不同的HandlerMapping可以有相同path, 谁先解析到就用哪个
* */
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
继续进入mapping.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;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// Ensure presence of cached lookupPath for interceptors and others
if (!ServletRequestPathUtils.hasCachedPath(request)) {
initLookupPath(request);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !DispatcherType.ASYNC.equals(request.getDispatcherType())) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
上面就有一句代码getHandlerInternal
,进入该方法
@Override
@Nullable
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
// 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径
String lookupPath = initLookupPath(request);
this.mappingRegistry.acquireReadLock();
try {
// 通过lookupPath解析最终的handler——HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
this.mappingRegistry.releaseReadLock();
}
}
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
// 根据uri从mappingRegistry.pathLookup获取 RequestMappingInfo
// pathLookup会在初始化阶段解析好
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果无path匹配,用所有的RequestMappingInfo 通过AntPathMatcher匹配
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 选择第一个为最匹配的
Match bestMatch = matches.get(0);
/**
* 如果匹配到多个
@RequestMapping(value="/mappin?")
@RequestMapping(value="/mappin*")
@RequestMapping(value="/{xxxx}")
@RequestMapping(value="/**")
*/
if (matches.size() > 1) {
//创建MatchComparator的匹配器对象
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
/** 根据精准度排序 大概是这样的: ? > * > {} >** 具体可以去看:
* @see org.springframework.util.AntPathMatcher.AntPatternComparator#compare(java.lang.String, java.lang.String)*/
matches.sort(comparator);
// 排完序后拿到优先级最高的
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
// 是否配置CORS并且匹配
if (CorsUtils.isPreFlightRequest(request)) {
for (Match match : matches) {
if (match.hasCorsConfig()) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
}
}
else {
//获取第二最匹配的
Match secondBestMatch = matches.get(1);
//若第一个和第二个是一样的 抛出异常
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.getHandlerMethod().getMethod();
Method m2 = secondBestMatch.getHandlerMethod().getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
}
//把最匹配的设置到request中
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
//返回最匹配的
return bestMatch.getHandlerMethod();
}
else { // return null
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
可以看见上面代码就是请求过程中如何获得HandlerMethod
对象的流程。