一、dispatcherServlet的核心方法分析
当请求过来时,首先会调用到dispatcherServlet的doService方法,最终会调用到dispatcherServlet中的doDispatch方法。
该方法核心功能包含以下几点:
- WebAsyncManager异步管理
- processedRequest文件上传
- HandlerMapping根据request对象获取获取HandlerMethod和过滤器链的包装类HandlerExecutionChain
- HandlerAdapter映射关系处理,获取跟HandlerMethod匹配的HandlerAdapter对象
- applyPreHandle前置过滤器,如果为false则直接返回
- ha.handle调用到Controller具体方法,处理返回结果对象ModelAndView,核心方法调用
- applyPostHandle中置过滤器,可以对ModelAndView对象进行修改
- Render视图渲染
- afterCompletion后置过滤器,资源释放
- ExceptionHandler异常处理
1、HandlerMapping处理分析
1.1、根据请求url获取HandlerExecutionChain对象
org.springframework.web.servlet.DispatcherServlet#doDispatch
//这个方法很重要,重点看
// Determine handler for the current request.
mappedHandler =getHandler(processedRequest);
if(mappedHandler ==null) {
noHandlerFound(processedRequest, response);
return;
}
寻找HandlerMethod的过程,感觉没什么好说的,前面映射关系已经建立好了,现在就是只需要从request对象中获取请求url,然后从映射关系中获取HandlerMethod对象就可以了,代码如下:
@Override
protectedHandlerMethodgetHandlerInternal(HttpServletRequest request)throwsException {
//从request对象中获取uri,/common/query2
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try{
//根据uri从映射关系中找到对应的HandlerMethod对象
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
//把Controller类实例化
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}finally{
this.mappingRegistry.releaseReadLock();
}
}
获取过程是,先从urlLookup中
List<String> directUrls = getDirectUrls(mapping);
for(String url : directUrls) {
//建立url和RequestMappingInfo映射关系
this.urlLookup.add(url, mapping);
}
获取RequestMappingInfo对象,然后再跟进RequestMappingInfo对象从mappingLookup中
//建立uri对象和handlerMethod的映射关系
this.mappingLookup.put(mapping, handlerMethod);
获取HandlerMethod对象。
1.2、把HandlerMethod对象封装到HandlerExecutionChain对象
获取到HandlerMethod对象后,把HandlerMethod对象封装到HandlerExecutionChain对象中了。
org.springframework.web.servlet.DispatcherServlet#getHandler
@Nullable
protectedHandlerExecutionChain getHandler(HttpServletRequest request)throwsException {
//handlerMappering实例
if(this.handlerMappings!=null) {
for(HandlerMapping mapping :this.handlerMappings) {
//获取HandlerMethod和过滤器链的包装类
HandlerExecutionChain handler = mapping.getHandler(request);
if(handler !=null) {
returnhandler;
}
}
}
return null;
}
1.3、获取HandlerMethod和过滤器链的包装类HandlerExecutionChain
org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandler
@Override
@Nullable
public finalHandlerExecutionChain getHandler(HttpServletRequest request)throwsException {
//根据请求的uri拿到对应的HandlerMethod对象
Object handler = getHandlerInternal(request);
if(handler ==null) {
handler = getDefaultHandler();
}
if(handler ==null) {
return null;
}
// Bean name or resolved handler?
if(handlerinstanceofString) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
//获取HandlerMethod和过滤器链的包装类
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if(logger.isTraceEnabled()) {
logger.trace("Mapped to "+ handler);
}
else if(logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to "+ executionChain.getHandler());
}
//是否是跨域请求,就是查看request请求头中是否有Origin属性
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig =this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig !=null? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
returnexecutionChain;
}
这个对象,启动就是封装了HandlerMethod和一个拦截器数组而已。
2、前置过滤器applyPreHandle
2.1、前置过滤器入口,如果为false则直接返回
org.springframework.web.servlet.DispatcherServlet#doDispatch
//前置过滤器,如果为false则直接返回
if(!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
拿到HandlerExecutionChain对象进行过滤器的调用,调用了preHandle方法,只要这个方法返回为false,则后续请求就不会继续。
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
booleanapplyPreHandle(HttpServletRequest request, HttpServletResponse response)throwsException {
HandlerInterceptor[] interceptors = getInterceptors();
if(!ObjectUtils.isEmpty(interceptors)) {
for(inti =0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response,null);
return false;
}
this.interceptorIndex= i;
}
}
return true;
}
2.2、interceptor.preHandle前置方法分析
com.chj.interceptor.UserInterceptor#preHandle
@Component
public classUserInterceptorimplementsHandlerInterceptor {
@Override
public booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException {
System.out.println("======UserInterceptor用户权限校验=========");
return true;
}
2.3、如果前置返回false调用后置处理
org.springframework.web.servlet.HandlerExecutionChain#triggerAfterCompletion
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
voidtriggerAfterCompletion(HttpServletRequest request, HttpServletResponse response,@NullableException ex)
throwsException {
HandlerInterceptor[] interceptors = getInterceptors();
if(!ObjectUtils.isEmpty(interceptors)) {
for(inti =this.interceptorIndex; i >=0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try{
interceptor.afterCompletion(request, response, this.handler, ex);
}catch(Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
3、核心方法ha.handle调用
3.1、HandlerAdapter调用ha.handle核心方法
进行具体Controller中方法的调用这个调用过程,返回ModelAndView对象,关键点就在于参数的解析。
org.springframework.web.servlet.DispatcherServlet#doDispatch
//调用到Controller具体方法,核心方法调用,重点看看 Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
@Override
@Nullable
public finalModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throwsException {
returnhandleInternal(request, response, (HandlerMethod) handler);
}
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
@Override
protectedModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)throwsException {
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{
//Controller里面具体方法调用,重点看
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if(!response.containsHeader(HEADER_CACHE_CONTROL)) {
if(getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response,this.cacheSecondsForSessionAttributeHandlers);
}else{
prepareResponse(response);
}
}
returnmav;
}
3.2、Controller里面具体方法调用
该方法里面包括参数处理,返回对象分装处理等等。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
protectedModelAndViewinvokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)throwsException {
ServletWebRequest webRequest =newServletWebRequest(request, response);
try{
//获取数据绑定工厂
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//Model工厂
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 =newModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
//调用有@ModelAttribute注解的方法
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);
}
//Controller方法调用,重点看看
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if(asyncManager.isConcurrentHandlingStarted()) {
return null;
}
returngetModelAndView(mavContainer, modelFactory, webRequest);
}finally{
webRequest.requestCompleted();
}
}
3.3、Controller方法具体调用逻辑invokeAndHandle
public voidinvokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
//具体调用逻辑,重点看
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if(returnValue ==null) {
if(isRequestNotModified(webRequest) || getResponseStatus() !=null|| mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}else if(StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers!=null,"No return value handlers");
try{
// 返回结果处理
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}catch(Exception ex) {
if(logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throwex;
}
}
3.4、invokeForRequest具体调用逻辑分析
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
@Nullable
publicObjectinvokeForRequest(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
//获取参数数组,重点看
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if(logger.isTraceEnabled()) {
logger.trace("Arguments: "+ Arrays.toString(args));
}
return doInvoke(args);
}
org.springframework.web.method.support.InvocableHandlerMethod#doInvoke
@Nullable
protectedObjectdoInvoke(Object... args) throwsException {
ReflectionUtils.makeAccessible(getBridgedMethod());
try{
returngetBridgedMethod().invoke(getBean(), args);
}
3.5、通过java的反射方法得到需要返回的mv对应:
java.lang.reflect.Method#invoke
@CallerSensitive
publicObjectinvoke(Object obj, Object... args)
throwsIllegalAccessException, IllegalArgumentException, InvocationTargetException{
if(!override) {
if(!Reflection.quickCheckMemberAccess(clazz,modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller,clazz, obj,modifiers);
}
}
MethodAccessor ma =methodAccessor; // read volatile
if(ma ==null) {
ma = acquireMethodAccessor();
}
returnma.invoke(obj, args);
}
publicObject invoke(Object var1, Object[] var2)throwsIllegalArgumentException, InvocationTargetException {
return this.delegate.invoke(var1, var2);
}
首先获取方法的参数列表,并且把参数封装成MethodParameter对象,这个对象记录了参数在参数列表中的索引,参数类型,参数上面的注解数组等等信息。然后循环参数列表,一个个参数来处理,这里是一个典型的策略模式的运用(this.resolvers.supportsParameter(parameter)),根据参数获取一个处理该参数的类。
3.6、策略模式根据参数获取一个处理该参数的类
入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
protectedObject[]getMethodArgumentValues(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
if(ObjectUtils.isEmpty(getMethodParameters())) {
returnEMPTY_ARGS;
}
//入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
MethodParameter[] parameters = getMethodParameters();
Object[] args =newObject[parameters.length];
for(inti =0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//设置参数名称解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] =findProvidedArgument(parameter, providedArgs);
if(args[i] !=null) {
continue;
}
//典型的策略模式,根据parameter能否找到对应参数的处理类,能找到就返回true
if (!this.resolvers.supportsParameter(parameter)) {
throw newIllegalStateException(formatArgumentError(parameter,"No suitable resolver"));
}
try{
//具体参数值解析过程,重点看看
args[i] =this.resolvers.resolveArgument(parameter, mavContainer, request,this.dataBinderFactory);
}catch(Exception ex) {
if(logger.isDebugEnabled()) {
String error = ex.getMessage();
if(error !=null&& !error.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, error));
}
}
throwex;
}
}
returnargs;
}
二、参数解析、返回值解析与视图渲染
上面的章节分析了参数的查找方法getMethodArgumentValues,其中入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息。下面我们将详细分析请求的参数类型处理逻辑与返回参数的类型处理逻辑。
Controller类实例代码如下:
@Controller
@RequestMapping("/common")
public classCommonController {
@Autowired
ApplicationContextapplicationContext;
@Autowired
privateAreaServiceareaService;
// @Autowired
// RequestSessionBean requestSessionBean;
@RequestMapping("/index")
public voidindex() {
System.out.println(applicationContext.getBean("requestSessionBean"));
}
@RequestMapping("/query1")
public@ResponseBody
String query1() {
returnareaService.queryAreaFromRedisOne(null);
}
/*
* consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
media type
* */
@RequestMapping(value = "/getUser",
method = RequestMethod.GET,
params = "username=jack",
consumes = "application/json",
produces = "application/json",
headers = "Referer=http://www.xx.com/")
public@ResponseBody
String getUser(HttpSession session, OutputStream outputStream) {
return"xx";
}
/*
Host localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
* */
@RequestMapping("/annotationParamTest")
publicString annotationParamTest(@CookieValue Cookie cookie, @RequestHeader("Host") String host,
HttpServletRequest request,HttpServletResponse response,
HttpSession session) {
return"OK";
}
/*
* 动态url, url中带参数,
* 形如:http://localhost:9090/common/pathVariableTest/jack/123
* */
@RequestMapping("/pathVariableTest/{id}/{password}")
public@ResponseBody
String pathVariableTest(@PathVariable("id") String username, @PathVariable String password) {
System.out.println("=======pathVariableTest:username-->"+ username +"-->password:"+ password);
returnusername +"->"+ password;
}
/*
* 形如:http://localhost:9090/common/requestParamTest?id=jack&password=123
* */
@RequestMapping(value ="/requestParamTest",method = RequestMethod.GET)
public@ResponseBodyString requestParamTest(@RequestParam("id") String username,@RequestParamString password) {
System.out.println("=======requestParamTest:username-->"+ username +"-->password:"+ password);
returnusername +"->"+ password;
}
/*
* 前端 ajax, 消费端 请求接口
* */
@RequestMapping(value ="/noRequestParamTest",method = RequestMethod.GET)
public@ResponseBodyString noRequestParamTest(String username, String password) {
System.out.println("=======requestParamTest:username-->"+ username +"-->password:"+ password);
returnusername +"->"+ password;
}
/*
* 这个方法,所有的请求都会先调用这个方法
* */
@ModelAttribute("areaBean")
publicList<ConsultConfigArea> queryArea(@RequestParamString areaCode) {
Map map =newHashMap<>();
map.put("areaCode",areaCode);
returnareaService.queryAreaFromDB(map);
}
@RequestMapping(value ="/checkArea",method = RequestMethod.GET)
public@ResponseBodyString checkArea(@ModelAttribute("areaBean") ConsultConfigArea area) {
if(area !=null&& area.getAreaCode().equalsIgnoreCase("HN1")) {
System.out.println("=======业务处理1=======");
}else{
System.out.println("=======业务处理2=======");
}
return"OK";
}
}
1、具体参数值解析过程
1.1、根据参数获取对应参数的解析类
请求参数类型包装对象:HandlerMethodArgumentResolverComposite
public classHandlerMethodArgumentResolverCompositeimplementsHandlerMethodArgumentResolver {
protected finalLoglogger= LogFactory.getLog(getClass());
private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<>(256);
org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
@Nullable
publicObjectinvokeForRequest(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
//获取参数数组,重点看
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if(logger.isTraceEnabled()) {
logger.trace("Arguments: "+ Arrays.toString(args));
}
returndoInvoke(args);
}
protectedObject[]getMethodArgumentValues(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
if(ObjectUtils.isEmpty(getMethodParameters())) {
returnEMPTY_ARGS;
}
//入参的包装类,里面包装了参数类型,参数名称,参数注解等等信息
MethodParameter[] parameters = getMethodParameters();
Object[] args =newObject[parameters.length];
for(inti =0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
//设置参数名称解析器
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] =findProvidedArgument(parameter, providedArgs);
if(args[i] !=null) {
continue;
}
//典型的策略模式,根据parameter能否找到对应参数的处理类,能找到就返回true
if(!this.resolvers.supportsParameter(parameter)) {
throw newIllegalStateException(formatArgumentError(parameter,"No suitable resolver"));
}
try{
//具体参数值解析过程,重点看看
args[i] =this.resolvers.resolveArgument(parameter, mavContainer, request,this.dataBinderFactory);
}
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#resolveArgument
@Override
@Nullable
publicObjectresolveArgument(MethodParameter parameter,@NullableModelAndViewContainer mavContainer,
NativeWebRequest webRequest,@NullableWebDataBinderFactory binderFactory)throwsException {
//根据参数获取对应参数的解析类
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
if(resolver ==null) {
throw newIllegalArgumentException("Unsupported parameter type ["
+ parameter.getParameterType().getName() +"]."+" supportsParameter should be called first.");
}
//策略模式去调用具体参数解析类
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}
1.2、根据参数获取对应参数的解析类
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver
@Nullable
privateHandlerMethodArgumentResolvergetArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result =this.argumentResolverCache.get(parameter);
if(result ==null) {
for(HandlerMethodArgumentResolver methodArgumentResolver :this.argumentResolvers) {
if(methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
returnresult;
}
支持的对应参数注解如下:
1.3、路劲参数PathVariable类型:
PathVariable参数表示路劲的一部分可以作为参数传递,对应的解析类PathVariable.class
/*
* 动态url, url中带参数,
* 形如:http://localhost:9090/common/pathVariableTest/jack/123
* */
@RequestMapping("/pathVariableTest/{id}/{password}")
public@ResponseBody
String pathVariableTest(@PathVariable("id") String username, @PathVariable String password) {
System.out.println("=======pathVariableTest:username-->"+ username +"-->password:"+ password);
returnusername +"->"+ password;
}
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#supportsParameter
@Override
public booleansupportsParameter(MethodParameter parameter) {
if(!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if(Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return(pathVariable !=null&& StringUtils.hasText(pathVariable.value()));
}
return true;
}
org.springframework.web.bind.annotation.PathVariable
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfacePathVariable{
@AliasFor("name")
String value()default"";
@AliasFor("value")
String name()default"";
booleanrequired()default true;
}
1.4、RequestParam参数类型解析
对应形如:http://localhost:9090/common/requestParamTest?id=jack&password=123类型的请求参数解析会用到@RequestParam注解,对应的参数解析类就是RequestParam.class。
@RequestMapping(value ="/requestParamTest",method = RequestMethod.GET)
public@ResponseBodyString requestParamTest(
@RequestParam("id") String username, @RequestParam String password){
System.out.println("=======requestParamTest:username-->"+ username +"-->password:"+ password);
returnusername +"->"+ password;
}
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#supportsParameter
@Override
public booleansupportsParameter(MethodParameter parameter) {
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if(Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
RequestParamrequestParam = parameter.getParameterAnnotation(RequestParam.class);
return(requestParam !=null&& StringUtils.hasText(requestParam.name()));
}else{
// 注意:代码意思是参数里面如果注解里面不自定义参数名字,默认是读取参数名字一样
return true;
}
}else{//参数里面如果不加RequestParam注解处理逻辑
if(parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
parameter = parameter.nestedIfOptional();
if(MultipartResolutionDelegate.isMultipartArgument(parameter)) {
return true;
}else if(this.useDefaultResolution) {
returnBeanUtils.isSimpleProperty(parameter.getNestedParameterType());
}else{
return false;
}
}
}
注意:
关于RequestParam代码判断逻辑意思,代码意思是参数里面如果注解里面不自定义参数名字,默认是读取参数名字一样。
@RequestMapping(value ="/requestParamTest",method = RequestMethod.GET)
public@ResponseBodyString requestParamTest(@RequestParam String username, @RequestParam String password)
另外参数里面如果不加注解默认也是可以的,及我们可以直接写参数而不用加注解:
@RequestMapping(value ="/noRequestParamTest",method = RequestMethod.GET)
public@ResponseBodyString noRequestParamTest(String username, String password) {
org.springframework.web.bind.annotation.RequestParam
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceRequestParam{
@AliasFor("name")
String value()default"";
@AliasFor("value")
String name()default"";
booleanrequired()default true;
String defaultValue()defaultValueConstants.DEFAULT_NONE;
}
1.5、例如RequestBody参数类型:
入参或者返回值的类型有很多种,例如string、对象、list、map等,如果加了@RequestBody或者@ResponseBody,则spring会将结果进行包装,转化为json格式的数据类型,供给前后端之间进行交互。
@Override
public booleansupportsParameter(MethodParameter parameter) {
returnparameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public booleansupportsReturnType(MethodParameter returnType) {
return(AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(),ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
org.springframework.web.bind.annotation.RequestBody
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceRequestBody{
booleanrequired()default true;
}
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interfaceResponseBody{
}
通过上面代码不难发现每个入参参数类型都会对应故一个参数类型的处理对象。
1.6、策略模式去调用具体参数解析类
//获取参数数组,重点看
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
通过debug调式可以看到,处理参数的解析类有26个,如下所示:
org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver#resolveArgument
@Override
@Nullable
public finalObjectresolveArgument(MethodParameter parameter,@NullableModelAndViewContainer mavContainer,
NativeWebRequest webRequest,@NullableWebDataBinderFactory binderFactory)throwsException {
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
MethodParameter nestedParameter = parameter.nestedIfOptional();
Object resolvedName = resolveStringValue(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()) {
handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
}
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
}else if("".equals(arg) && namedValueInfo.defaultValue!=null) {
arg = resolveStringValue(namedValueInfo.defaultValue);
}
......
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
returnarg;
}
处理参数值,最后将解析后的参数放到Map<String, Object>中。
org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver#handleResolvedValue
protected voidhandleResolvedValue(@NullableObject arg, String name, MethodParameter parameter,
@NullableModelAndViewContainer mavContainer, NativeWebRequest request) {
String key = View.PATH_VARIABLES;
intscope = RequestAttributes.SCOPE_REQUEST;
Map<String, Object> pathVars = (Map<String, Object>) request.getAttribute(key, scope);
if(pathVars ==null) {
pathVars =newHashMap<>();
request.setAttribute(key, pathVars, scope);
}
pathVars.put(name, arg);
}
把参数一个个处理完成后,放到一个参数数组中了:Object[] args,接下来就是反射调用了,有方法method对象,有类对象,有参数数组就可以进行反射调用了。
@Nullable
publicObject invokeForRequest(NativeWebRequest request,@NullableModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
//获取参数数组,重点看
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if(logger.isTraceEnabled()) {
logger.trace("Arguments: "+ Arrays.toString(args));
}
returndoInvoke(args);
}
2、返回值处理invokeAndHandle
当反射调用成功后,有可能方法会有返回值,而返回值处理也是一个比较重要的事情,根据什么样的方式把返回值响应回去,返回值响应时有可能是数据有可能是界面,而如果返回数据的话,要把返回值解析成对应的格式,例如如果返回值是一个list对象,就需要解析这个list对象把list对象解析成json格式。返回值解析讨论跟入参解析基本上类似。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
protectedModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod)throwsException {
ServletWebRequest webRequest =newServletWebRequest(request, response);
try{
......
//Controller方法调用,重点看看
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if(asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
} finally{
webRequest.requestCompleted();
}
}
2.1、返回值出路逻辑梳理如下:
1)把返回值封装成对象,对象跟MethodParameter对象差不多,里面包括参数名称、类型、参数注解等等信息;
2)根据返回值类型用策略模式找到一个解析类;
3)用这个解析类解析;
4)这块跟两个比较典型的就差不多了,一个是带@ResponseBody注解的,一个是直接返回字符串响应一个界面的,里面涉及到一个ModelAndViewContainer容器,这个容器会把视图名称设置到里面,已经Model数据,就是响应到界面的数据也会放到这个容器中。
2.2、返回值处理逻辑入口invokeForRequest
前面的3.3章节关于Controller方法具体调用逻辑方法invokeAndHandle代码中,关于返回值的处理部分代码逻辑具体进行具体分析。
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public voidinvokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs)throwsException {
//具体调用逻辑,重点看
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if(returnValue ==null) {
if(isRequestNotModified(webRequest) || getResponseStatus() !=null|| mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}else if(StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers!=null,"No return value handlers");
try{
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}catch(Exception ex) {
if(logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throwex;
}
}
2.3、返回值处理结果对应的返回值类型对象
org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite#handleReturnValue
@Override
public voidhandleReturnValue(@NullableObject returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throwsException {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if(handler ==null) {
throw newIllegalArgumentException("Unknown return value type: "+ returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
通过代码调试可以看到返回值处理类如下,有15中处理类型;
2.4、入参与返回参数进行网络传输时调用的处理方法:
请求与相应最后都会通过下面的代码逻辑处理,转化为输入输出流包装成json格式的方式进行消息的传递,具体用到了消息转化器(writeWithMessageConverters方法这里不去详细分析,里面用到了很多类型转化器)。
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue
@Override
public voidhandleReturnValue(@NullableObject returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throwsIOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
注意:
mavContainer.setRequestHandled(true);表示返回的结果是以流的形式返回处理结果,可以在方法:RequestMappingHandlerAdapter.invokeHandlerMethod里面的返回结果方法getModelAndView(mavContainer, modelFactory, webRequest);方法中去判断mavContainer.isRequestHandled()是否为true,如果为true则返回null。反之返回ModelAndView对象。
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
@Nullable
privateModelAndViewgetModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest)throwsException {
modelFactory.updateModel(webRequest, mavContainer);
if (mavContainer.isRequestHandled()) {
return null;
}
2.5、ModelAndView返回对象类型处理示例:
org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler#handleReturnValue
@Override
public voidhandleReturnValue(@NullableObject returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throwsException {
if(returnValue ==null) {
mavContainer.setRequestHandled(true);
return;
}
ModelAndView mav = (ModelAndView) returnValue;
if(mav.isReference()) {
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
if(viewName !=null&& isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}else{
View view = mav.getView();
mavContainer.setView(view);
if(viewinstanceofSmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
mavContainer.setStatus(mav.getStatus());
mavContainer.addAllAttributes(mav.getModel());
}
2.6、没有@ResponseBody返回页面
如果没有请求接口的返回值类型前面没有加注解@ResponseBody,则spring会将return返回的字符串当做返回页面,代码如下:
@RequestMapping(value ="/index",method = RequestMethod.GET)
public Stringindex() {
System.out.println("=======业务处理=======");
return "OK";
}
org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler#handleReturnValue
@Override
public voidhandleReturnValue(@NullableObject returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throwsException {
if(returnValueinstanceofCharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if(isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}else if(returnValue !=null){ // should not happen
throw newUnsupportedOperationException("Unexpected return type: "+
returnType.getParameterType().getName() +" in method: "+ returnType.getMethod());
}
}
3、中置过滤器applyPostHandle
3.1、中置过滤器调用入口
中置过滤器的调用时序,是当ha.handle掉完以后,也就是Controller里面具体方法调用完以后才轮到中置过滤器调用。
org.springframework.web.servlet.DispatcherServlet#doDispatch
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
org.springframework.web.servlet.HandlerExecutionChain#applyPostHandle
/**
* Apply postHandle methods of registered interceptors.
*/
voidapplyPostHandle(HttpServletRequest request, HttpServletResponse response,@NullableModelAndView mv)
throwsException {
HandlerInterceptor[] interceptors =getInterceptors();
if(!ObjectUtils.isEmpty(interceptors)) {
for(inti = interceptors.length-1; i >=0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
org.springframework.web.servlet.handler.MappedInterceptor#postHandle
@Override
public voidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@NullableModelAndView modelAndView)throwsException {
this.interceptor.postHandle(request, response, handler, modelAndView);
}
3.2、中置过滤器的应用:
@Component
public classUserInterceptorimplementsHandlerInterceptor {
@Override
public booleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throwsException {
System.out.println("======UserInterceptor用户权限校验=========");
return true;
}
@Override
public voidpostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throwsException {
System.out.println("========UserInterceptor修改modelAndView======");
HttpSession session = request.getSession();
if(modelAndView !=null) {
String modifyViewName = modelAndView.getViewName() +"_"+ session.getAttribute("language");
modelAndView.setViewName(modifyViewName);
}
}
@Override
public voidafterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throwsException {
System.out.println("========UserInterceptor资源释放======");
}
}
可以用来修改视图。
4、视图渲染
其实就是响应界面,如果返回值没有加@ResponseBody注解时,这时候是需要响应一个界面给前端的,视图渲染借助了servlet中的api,示例代码如下:
com.chj.servlet.InitServlet#doGet
public classInitServletextendsHttpServlet {
@Override
protected voiddoGet(HttpServletRequest req, HttpServletResponse resp)throwsException {
System.out.println("=====doget===");
PrintWriter writer = resp.getWriter();
writer.print("<h1>Jack</h1>");
RequestDispatcher requestDispatcher = req.getRequestDispatcher("/jsp/ok_en.jsp");
requestDispatcher.forward(req,resp);
}
这样servlet就可以响应一个界面给前端。而我们spring也是差不多的处理方式
4.1、视图渲染入口:
org.springframework.web.servlet.DispatcherServlet#doDispatch
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
org.springframework.web.servlet.DispatcherServlet#processDispatchResult
// Did the handler return a view to render?
if(mv !=null&& !mv.wasCleared()) {
render(mv, request, response);
if(errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
org.springframework.web.servlet.DispatcherServlet#render
try{
if(mv.getStatus() !=null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
@Override
public voidrender(@NullableMap<String, ?> model, HttpServletRequest request,
HttpServletResponse response)throwsException {
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);
}
4.2、视图响应输出返回对象
org.springframework.web.servlet.view.InternalResourceView#renderMergedOutputModel
@Override
protected voidrenderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)throwsException {
// 把响应数据设置到request中 Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
//获取到跳转地址path Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if(rd ==null) {
throw newServletException("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() +"]");
}
// 视图响应jsp方式
rd.forward(request, response);
}
}
org.springframework.web.servlet.DispatcherServlet#applyDefaultViewName
/**
* Do we need view name translation?
*/
private voidapplyDefaultViewName(HttpServletRequest request,@NullableModelAndView mv)throwsException {
if(mv !=null&& !mv.hasView()) {
String defaultViewName = getDefaultViewName(request);
if(defaultViewName !=null) {
mv.setViewName(defaultViewName);
}
}
}
afterCompletion后置过滤器,资源释放