SpringMVC框架学习—SpringMVC之源码讲解
1.前端控制器DispatcherServlet的继承结构
HttpServlet
中含有doGet/doPost
方法但是并没有做什么处理操作HttpServletBean
中没有doGet/doPost
方法- 真正执行
doGet/doPost
方法的是在FrameworkServlet
中执行的,他重写了HttpServlet
中的doGet/doPost
方法 - 在
FrameworkServlet
中的doPost
方法中包含方法processRequest(request, response)
,这里面包含了抽象方法doService(request, response)
,这也是抽象方法 - 点击抽象方法
doService(request, response)
会找到他的实现类方法,在DispatcherServlet
中重写doService
方法,查看方法会发现处理请求的核心方法doDispatch
,这个方法类似spring
框架中的refresh
方法
2.SpringMVC处理请求的大致流程
- 调⽤
getHandler()
获取到能够处理当前请求的执⾏链HandlerExecutionChain(Handler+拦截器)
- 调⽤
getHandlerAdapter()
;获取能够执⾏流程1
中Handler的适配器,但是如何去getHandlerAdapter的? - 适配器调⽤
Handler
执⾏ha.handle(总会返回⼀个ModelAndView对象)
- 调⽤
processDispatchResult()
⽅法完成视图渲染跳转
3.核⼼步骤getHandler⽅法剖析
-
取得处理当前请求的
Controller
,这里也称为Handler
,即处理器
。这里并不是直接返回Controller
,而是返回HandlerExecutionChain
请求处理链对象该对象封装了Handler
和Inteceptor
-
进入getHandler方法中发现,方法中在遍历
handlerMapping
(处理器映射器
:负责解析请求路径找到对应的Handler
,所以里面存储的)@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; }
-
handlerMapping
有两个,SpringMVC
在容器启动,初始化容器的时候将0和1都初始化了- 0是
Spring
在早起版本定义很多配置信息的时候使用的 - 1是使用注解开发的时候使用的
- 0是
-
遍历到第二个
handlerMapping
时,会发现有东西出现了,出现的是HandlerExecutionChain
(处理器执行链),里面指向那个Controller
的哪个方法和intercepter
(拦截器)
-
最后将
HandlerExecutionChain
对象返回,之后再看方法的请求参数HttpServletRequest request
,里面有个属性requestDispatcherPath
存储的内容是/demo/handle01
这个是请求的url路径,通过这个路径进行筛选找到方法 -
handlerMapping
里面的映射关系是什么时候创建的? -
Spring
容器启动的时候,ioc
容器在扫描controller
对象的时候会扫描RequestMapping
注解,扫描RequestMapping
注解的时候就会建立url
和handler
方法之间的映射
4.getHandlerAdapter适配器获取分析
- 获取处理请求的处理器适配器
HandlerAdapter
- 进入方法发现在遍历
handlerAdapters
,里面包含很多HandlerAdapter
类对象,其中handlerAdapters有三个,分别是下图中的0,1,2
adapter.supports(handler)
通过这行代码判断HandlerAdapter类对象(包含三种)
是否支持当前的handler对象
5. SpringMVC九⼤组件初始化
- 九⼤组件都是定义了接⼝,接⼝其实就是定义了该组件的规范,⽐如
ViewResolver
、HandlerAdapter
等都是接⼝/** MultipartResolver used by this servlet. */ // 多部件解析器 @Nullable private MultipartResolver multipartResolver; /** LocaleResolver used by this servlet. */ // 区域化 国际化解析器 @Nullable private LocaleResolver localeResolver; /** ThemeResolver used by this servlet. */ // 主题解析器 @Nullable private ThemeResolver themeResolver; /** List of HandlerMappings used by this servlet. */ // 处理器映射器组件 @Nullable private List<HandlerMapping> handlerMappings; /** List of HandlerAdapters used by this servlet. */ // 处理器适配器组件 @Nullable private List<HandlerAdapter> handlerAdapters; /** List of HandlerExceptionResolvers used by this servlet. */ // 异常解析器组件 @Nullable private List<HandlerExceptionResolver> handlerExceptionResolvers; /** RequestToViewNameTranslator used by this servlet. */ // 默认视图名转换器组件 @Nullable private RequestToViewNameTranslator viewNameTranslator; /** FlashMapManager used by this servlet. */ // flash属性管理组件 @Nullable private FlashMapManager flashMapManager; /** List of ViewResolvers used by this servlet. */ // 视图解析器 @Nullable private List<ViewResolver> viewResolvers;
6. SpringMVC九⼤组件初始化细节
-
在
DispatcherServlet
当中有个方法onRefresh
(主要是完成组件初始化的),它调用了initStrategies
方法,是初始化策略方法protected void initStrategies(ApplicationContext context) { // 多文件上传的组件 initMultipartResolver(context); // 初始化本地语言环境 initLocaleResolver(context); // 初始化模板处理器 initThemeResolver(context); // 初始化HandlerMapping initHandlerMappings(context); // 初始化参数适配器 initHandlerAdapters(context); // 初始化异常拦截器 initHandlerExceptionResolvers(context); // 初始化视图预处理器 initRequestToViewNameTranslator(context); // 初始化视图转换器 initViewResolvers(context); // 初始化 FlashMap 管理器 initFlashMapManager(context); }
-
onRefresh
方法是怎样被调用的,调用链是怎样的- 在
DispatcherServlet
的onRefresh
方法中打上断点
onRefresh
方法触发调用机制
- 这个
事件
什么时候发布的
- 在
7. SpringMVC九⼤组件初始化细节—initHandlerMappings
- 查看
initHandlerMappings
方法初始化过程
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
// detectAllHandlerMappings是DispatcherServlet的默认值=true
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 找到所有的HandlerMapping
// 整个这部操作是到当前springmvc的容器以及springmvc的父容器中去查找所有实现HandlerMapping接口的类对应的对象,把他们加载出来
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
// 否则在ioc中按照固定名称去找
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 最后还为空则按照默认策略生成
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
- 如果前两种内容都不匹配,最后
handlerMappings
还为空则按照默认策略生成,进入getDefaultStrategies
方法 - 以第二个参数作为策略,按照默认策略生成
handlerMappings
(处理器映射器)
@SuppressWarnings("unchecked")
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
// 第二个参数的名字作为key
String key = strategyInterface.getName();
// 通过defaultStrategies对象的getProperty方法获取value,defaultStrategies是通过静态代码块获取的值
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
- defaultStrategies执行过程
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
// DEFAULT_STRATEGIES_PATH这个值是 DispatcherServlet.properties 这个文件
// DispatcherServlet.properties 这个文件记录了 springmvc默认加载的组件实现是哪个
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
8. SpringMVC核心步骤Handler方法执行细节剖析
doDispatch()
方法执行到如下步骤
- 打断点进入
handle
方法
- 进入
handleInternal
方法
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 判断当前是否需要支持在同一个session中只能线性地处理请求
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 {
// No HttpSession available -> no mutex necessary
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配,进入步骤4
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
- 进入
invokeHandlerMethod
方法
@Nullable
// handlerMethod:一个handler对象,一个方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
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对象(可调用的方法)
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置当前容器中配置的所有ArgumentResolver,设置参数
// argumentResolvers:参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置当前容器中配置的所有ReturnValueHandler,设置返回值
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
// 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
// webRequest:请求参数
// 说白了就是反射调用handle方法,反射调用的时候要准备参数,进入步骤5
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
- 进入
invokeAndHandle
方法
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 对目标handler的参数进行处理,并且调用目标handler,进入步骤6
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置相关的返回状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
- 进入
invokeForRequest
方法
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 将request中的参数转换为当前handler的参数形式
// 获取方法参数传入的值,过程见步骤7,8,9
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用,过程见步骤10
return doInvoke(args);
}
- 进入
getMethodArgumentValues
方法
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
// 获取当前handler所声明的所有参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
// 最终参数值存放的数组
Object[] args = new Object[parameters.length];
// 遍历所有的参数类型
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// providedArgs是调用方提供的参数,这里主要是判断这些参数中是否有当前类型,如果有,则直接使用调用方提供的参数,对于请求处理而言,默认情况下,
// 调用方提供的参数都是长度为0的数组
// 貌似有一个赋值的动作,通过方法传入的Object... providedArgs(参数)和遍历出来的parameter参数类型进行匹配,拿到参数,获取参数的值
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 如果在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中所有的
// ArgumentResolver,判断哪种类型的Resolver支持对当前参数的解析,这里的判断
// 方式比较简单,比如RequestParamMethodArgumentResolver就是判断当前参数
// 是否使用@RequestParam注解进行了标注
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 如果能够找到对当前参数进行处理的ArgumentResolver,则调用其
// resolveArgument()方法从request中获取对应的参数值,并且进行转换
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
- 进入
resolveArgument
方法
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 寿险拿到了一个 HandlerMethodArgumentResolver 的解析器
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if (resolver == null) {
throw new IllegalArgumentException("Unsupported parameter type [" +
parameter.getParameterType().getName() + "]. supportsParameter should be called first.");
}
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
@Override
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 对我们传入的参数进行封装
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
// 对传入的字符串参数进行处理,可能有占位,所以要处理一下
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
// 找一找传入的参数值是什么了,见第9步
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
if (binderFactory != null) {
WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
try {
arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
namedValueInfo.name, parameter, ex.getCause());
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
- 进入
resolveName
方法
@Override
@Nullable
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
// 从请求中拿到参数的值
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
- 进入
doInvoke
方法
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通过反射调用完成方法的执行,通过getBean()拿到对应的controller
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}