做了这么多年的web开发,对于常用的@RequestMapping,@RequestParam等;我们是否知道它们实现原理呢?本文就来做个揭秘。(这篇文章的阅读建议先了解Spring-MVC请求的基本流程)
以前开发中一直有一个疑问:我们的请求的参数@RequestParam和不使用这个注解匹配的参数值有什么不同?性能有什么不同?持着一份探究源码的专注分析了源码,不仅解决了这个疑问,还顺带把相关参数的解析都了解了,这里也给大家分享下自己的收获。
相关学习和知识可以参考:spring-mvc官网
版本:springboot-2.2.6
版本:spring-webmvc-5.2.5
我创建了一个springboot的demo,便于分析
@RestController
@SpringBootApplication
public class RequestparamDemoApplication {
public static void main(String[] args) {
SpringApplication.run(RequestparamDemoApplication.class, args);
}
@RequestMapping("/check")
public String check(HttpServletRequest httpServletRequest, HttpServletResponse response,
@RequestParam(value = "key") String key){
return "ok:"+key;
}
@RequestMapping("/checkNo")
public String checkNo(HttpServletRequest httpServletRequest, HttpServletResponse response,
String keyNo){
return "okNo:"+keyNo;
}
1.知识背景
在分析参数前,先了解这两个概念。
1.1 回忆下spring-mvc的流程图
来自于网络,先回忆回忆spring-mvc请求流程,不做过多解释。
1.2 我们请求的流程url资源的存储
在具体分析前,还需要知道我们在每次通过一个url请求服务器后,服务器都能找到这个接口进行执行,那它的实现是什么呢?不禁我给出一个猜测:在容器初始化的时候会把所有url和所在的控制器关联起来了,那具体是不是呢?
这里我们基于spring-boot项目来分析,我们在初始化的项目的中会通过WebMvcAutoConfiguration加载RequestMappingHandlerMapping
,其他几大DispatcherServlet组件就不一一解析了,这里和原来xml启动有区别
@Bean
@Primary
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
// Must be @Primary for MvcUriComponentsBuilder to work
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
resourceUrlProvider);
}
这个加载完会进行bean的初始化,RequestMappingHandlerMapping
继承RequestMappingInfoHandlerMapping
继承AbstractHandlerMethodMapping
,所以最终通过AfterPropertiesSet()来初始化,这里也就是我们url的注册位置
public abstact class AbstractHandlerMethodMapping{
public void afterPropertiesSet() {
initHandlerMethods();
}
/**
* 通过检测和注册handler method,官网也给了很清晰的注释
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #getCandidateBeanNames()
* @see #processCandidateBean
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
//这里就是核心流程
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
//接着往下看
protected void processCandidateBean(String beanName) {
Class> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName); //检测handlerMethod
}
}
//这里判定是否是handler,就直接通过是否有Controller或者RequestMapping注解来判定
@Override
protected boolean isHandler(Class> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
}
往下看就是具体的注册流程逻辑了
/**
* Look for handler methods in the specified handler bean.
* @param handler either a bean name or an actual handler instance
* @see #getMappingForMethod
*/
protected void detectHandlerMethods(Object handler) {
Class> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class> userType = ClassUtils.getUserClass(handlerType);
//这里遍历控制器的方法,将含有RequestMapping注解的方法给提取出来
Map methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup) 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));
}
//这里就是具体注册逻辑了
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
我这里debug把demo中的方法截了个图:
我们继续看registerHandlerMethod方法做了些什么:
//RequestMappingHandlerMapping做了方法重写,不顾还是调用了父类的注册
@Override
protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
super.registerHandlerMethod(handler, method, mapping);
//这里会增强处理RequestBody注解
updateConsumesCondition(mapping, method);
}
//发现这里直接调用mappingRegistry直接注册了,
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
//继续瞧
// MappingRegistry是AbstractHandlerMethodMapping内部类,从名字看也是作为mapping注册的
下面就是我们平时请求路径的核心存储了
class MappingRegistry{
private final Map> registry = new HashMap<>();
private final Map mappingLookup = new LinkedHashMap<>();
private final MultiValueMap urlLookup = new LinkedMultiValueMap<>();
private final Map> nameLookup = new ConcurrentHashMap<>();
private final Map corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//针对我们当前的demo情况先把入参说明下:
//@param mapping代表我们之前的RequestMappingInfo,封装有url信息,比如“/check”,当然还有其他对于请求和返回的封装
//@param handler 只beanName requestParamDemoApplicaiton
//@param method 只我们bean中的一个方法,比如我们demo中"check"
public void register(T mapping, Object handler, Method method) {
// Assert that the handler method is not a suspending one.
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
}
//读写锁,利用的写锁
this.readWriteLock.writeLock().lock();
try {
//handler就是我们的控制器名称beanName,
//method包含当前的所有信息了
//HandlerMethod会封装好方法名,参数,bean等信息
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
//这里验证对于mapping不会存在多个handlerMethod,否则抛异常
validateMethodMapping(handlerMethod, mapping);
//通过map存储 url->method的映射
this.mappingLookup.put(mapping, handlerMethod);
//这里直接获取我们客户端要请求的url
List directUrls = getDirectUrls(mapping);
for (String url : directUrls) {
//这里存储 url和mapping的映射管理
this.urlLookup.add(url, mapping);
}
String name = null;
//存储在nameLookUp的map中
if (getNamingStrategy() != null) {
//name 默认是bean的大写提取然后通过#连接方法名
//RDA#check (RDA=RequestDemoApplication)
name = getNamingStrategy().getName(handlerMethod, mapping);
addMappingName(name, handlerMethod);
}
//这里就是跨域配置@CrossOrigin的封装了,存储起来
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
//registry存储mapping和后面的再次封装的映射
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}
finally {
this.readWriteLock.writeLock().unlock();
}
}
}
总结url的存储:
1.在创建RequestMappingHandlerMapping通过实现的
InitializingBean
的接口初始化所有控制器的url的解析及存储
- 最终通过MappingRegistry把所有url,mapping,调用方法信息用map一一映射起来了;
这里我们基本就大概有一个概念了,我们这里有请求url和实际调用方法映,每次请求直接就可以通过url获取方法,然后通过反射直接调用就是,接下来理解就更容易了。
2.请求参数适配
先给出请求的处理中心流程
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);
// 根据当前的请求获取mappedHandlerChain(包含当前的MappingHandler)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
//根据handler获得对应的适配器
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//前置调用,方法是否拦截(拦截器遍历执行)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//
applyDefaultViewName(processedRequest, mv);
//拦截器后置调用
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// 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);
}
}
}
}
2.1 Handler的获取
mappedHandler = getHandler(processedRequest);
下面的handlerMapping我们在初始化的时候会创建五个,按照下面顺序
RequestMappingHandlerMapping 我
BeanNameUrlHandlerMapping
RouterFunctionMapping
SimpleUrlHandlerMapping
WelcomePageHandlerMapping
//
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//上面我们也分析了我们RequestMappingHandlerMapping封装了url和handlerMethod的映射,往下接着看
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//这里获取handler
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);
}
//
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
...
//cors跨域拦截器
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
获取handler最后会调用到AbstractHandlerMethodMapping的getHandlerInternal
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
this.mappingRegistry.acquireReadLock();
try {
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 matches = new ArrayList<>();
//获取适配的url
List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
//这里会获取对应的handlerMethod并封装进Match中,
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
//如果当前match有多个满足,会做一个排序并选出一个最优的
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
总结下handler这里返回的是HanderExecutionChain
,包含有我们请求的url最优的HanderMethod以及一系列对应的Interceptors
2.2 HandlerAdapter的生成
这里还是看看HandlerAdapter,也许曾经的我们有一个疑问:为什么这里会需要用一个HandlerAdapter,后面还要用handlerAdapter来执行Method?我们上面已经拿到method直接执行不就好了吗?
来自于原作者注释:
HanderAdapter:MVC framework SPI, allowing parameterization of the core MVC workflow.
This interface is not intended for application developers. It is available
to handlers who want to develop their own web workflow.
看看HanderAdapter接口信息:
public interface HandlerAdapter {
//是否支持当前Adapter
boolean supports(Object handler);
//处理请求
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
//获取上一次修改时间
long getLastModified(HttpServletRequest request, Object handler);
}
这里从官方注释中也发现了这就是MVC框架提供的一个SPI,我们可以对不同的Handler自己做定制化的Adapter来请求;只要我们实现这个HandlerAdapter注入容器,然后support方法 return (handler instanceof MyHandler)
;
这里默认有四个Adapter,按照如下顺序排列
RequestMappingHandlerAdapter
HandlerFunctionAdapter
HttpRequestHandlerAdapter
SimpleControllerHandlerAdatper
稍微总结下:HandlerAdapter是和Handler对应的,我们对于不同的Handler适配不同的适配器来处理这个handler。MVC框架提供这个SPI可以让我们自己构建不同的Handler,然后自己来构造自定义的适配器处理自己的请求。
当然RequestMappingHandlerAdapter
就直接适配到了我们的HandlerMethod
//模板方法
public abstract class AbstractHandlerMethodAdapter{
public final boolean supports(Object handler) {
//这里需要支持数据handlerMethod
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
//子类RequestMappingHandlerAdapter直接返回true
protected abstract boolean supportsInternal(HandlerMethod handlerMethod);
}
protected boolean supportsInternal(HandlerMethod handlerMethod) {
return true;
}
到这里HanderAdapter就简单分析到这里
2.3 Handler的调用执行
// Actually invoke the handler. 这里RequestMappingHandlerAdatper执行
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
上面的handler会调用父类的方法,然后通过模板方法handlerInternel调用实际执行方法
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// 执行方法调用
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
//执行
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//再次包装
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//重点是这里实心方法的调用,上面都是为当前的method做保证,封装移一些属性:包含参数解析器,返回值解析器,binderFactory(开发员可以对于参数做一定的操作,自动转换数据)等
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
ServletInvocableHandlerMethod
这个类被委托做方法内部的调用和返回数据的封装。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//方法调用
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//后面的代码是对于返回的数据的封装处理
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
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;
}
}
//InvocableHandlerMethod的执行请求处理
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);
}
做了太多的铺垫了,终于找到这篇文章要做的中心了,参数的解析
getMethodArgumentValules
了
//这里就是需要我们把方法参数的值返回;
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
//获取我们方法的参数对象数据
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS; //没有参数,返回空数组
}
//创建同样大小的arg数组用来装我们相对应的value
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
//我们传入的providedArgs为null,所以这里可以忽略
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
//这里通过resolver来判定当前的parameter是否支持当前的parameter的解析,如果支持就用当前resolver解析这个参数,并返回value值
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) {
// 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;
}
我们发现这里的resolver和上面的Adapter是不是很相似,如果support给定的某一个handler 那么就handle it;所以这里也会用大量的resolver来处理不同的参数了,也便于我们自己后期设计框架扩展了。
比如我们在参数中用@RequestParam,@PathVariable等参数了,猜测肯定会有不同的解析器resolver了。
//这是一个对于方法参数解析器的组合了。
HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
public class HandlerMethodArgumentResolverComposite {
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}
@Nullable
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
//先从缓存中获取,如果有的话就直接返回了。
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
//遍历resolver,然后返回支持这个参数的resovler
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}
}
框架默认的解析器,按照先后有排名,如下图:
框架提供了26个解析器,发现有些解析器来自相同的类,但是却有两个,比如我们下面要解析的RequestParammethodArgumentResolver;还要其他。那么直接给答案了,这里相同类的不同得对象,他们的对象不同就用来处理不同的参数了。
分析RequestParamMethodArgumentResolver
,用来处理带有@RequestParam注解的参数和没有注解的参数,没有注解解析器order要放在比较靠后的。
public boolean supportsParameter(MethodParameter parameter) {
//1.判定是否含有@RequestParam注解
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
return (requestParam != null && StringUtils.hasText(requestParam.name()));
}
else {
return true;
}
}
else {
//含有 @RequestPart注解的不处理
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
//可以处理文件上传的
if (MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}
//useDefaultRelustion默认解析器,这个就是解释了上面我们为什么创建了两个对象;false表示只处理@RequestParam,另外一个就是默认解析器了,用来解析就没有特殊类型的参数了
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}
else {
return false;
}
}
}
上面的parameter.getNestedParameterType表示获取参数的类型,如果我们定义的参数是(String key)那么就会是java.lang.String
Beanutils.isSimpleProperty
public static boolean isSimpleProperty(Class> type) {
Assert.notNull(type, "'type' must not be null");
//这里要么是基本类型,或者基本类型的数组
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
public static boolean isSimpleValueType(Class> type) {
return (Void.class != type && void.class != type &&
(ClassUtils.isPrimitiveOrWrapper(type) ||
Enum.class.isAssignableFrom(type) ||
CharSequence.class.isAssignableFrom(type) ||
Number.class.isAssignableFrom(type) ||
Date.class.isAssignableFrom(type) ||
Temporal.class.isAssignableFrom(type) ||
URI.class == type ||
URL.class == type ||
Locale.class == type ||
Class.class == type));
}
找到了参数解析的resolver了,接着我们看在执行解析的过程
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
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);
}
这里会调用AbstractNamedValueMethodArgumentResolver的参数解析方法
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
//通过参数名构建nameValue对象,这里也是我们分别出唯一@RequestParam和不含注解的区别了。
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
//如果有嵌套的参数需要解析出出来
MethodParameter nestedParameter = parameter.nestedIfOptional();
//解析占位参数 placeholders名
Object resolvedName = resolveStringValue(namedValueInfo.name);
if (resolvedName == null) {
throw new IllegalArgumentException(
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
}
Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
if (arg == null) {
if (namedValueInfo.defaultValue != null) {
//如果我们有默认值,这里按照默认值处理
arg = resolveStringValue(namedValueInfo.defaultValue);
}
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
//如果我们的required是必须的,当没有值时处理会抛异常
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
//处理null值,如果是boolean值,转为false返回否则抛出异常
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;
}
private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
//会从缓存中获取
NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
if (namedValueInfo == null) {
namedValueInfo = createNamedValueInfo(parameter);
namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
this.namedValueInfoCache.put(parameter, namedValueInfo);
}
return namedValueInfo;
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
//这里会把我们设置的RequestParam包装起来,而普通的就简单的new一个对象
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
交给子类的模板方法,RequestParamMethodArgumentResolver实现
@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 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;
}
总结:
1.项目初始化将所有的url和HandlerMethod都通过容器存储起来了,访问的时候直接通过映射查找
2.获取后的handlerMethod会封装为HandlerExecutionChain
3 查找不同的Adapter做操作执行
4 先遍历方法的参数找到需要的Resolver,然后解析对应的值,而且找到每一个方法参数的resolver也会存储一个缓存,便于下一次查找。
5 通过找到的resovler解析参数的值
....
我的疑问也给解决了:@RequestParam和不使用注解是一样的效果,性能上也不会差多少,只是我们通过@RequestParam可以使代码更加简洁,可以给出默认值,或者强制必传而不用在业务代码中再次验证。