携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第16天,点击查看活动详情
由于我这里Controller是这么写的
@RequestMapping("/j1") public String j1(@RequestParam String name)是有参数的,如果没有参数在
getMethodArgumentValues方法下面的if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; }这个判断直接return,没有后面的事情了
resolver.resolveArgument->
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 = resolveEmbeddedValuesAndExpressions(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 = resolveEmbeddedValuesAndExpressions(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 = resolveEmbeddedValuesAndExpressions(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());
}
// Check for null value after conversion of incoming argument value
if (arg == null && namedValueInfo.defaultValue == null &&
namedValueInfo.required && !nestedParameter.isOptional()) {
handleMissingValueAfterConversion(namedValueInfo.name, nestedParameter, webRequest);
}
}
handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
return arg;
}
这是AbstractNamedValueMethodArgumentResolver类实现的
首先就是去拿到参数名字,拿到名字之后,resolveName去解析。
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;
}
我们这里不是上传请求,所以直接到getParameterValues方法。
public String[] getParameterValues(String paramName) {
return getRequest().getParameterValues(paramName);
}
这里比较熟悉了,request.getParameterValues()方法获取参数值。我们拿到了参数名字,自然能获取到参数值。获取到之后,判断是不是只有一个值,如果是返回数组第一个位置,否则直接返回数组。返回到resolveArgument当中。经过这样的处理,我们就匹配到了参数,得到了参数数组,返回到invokeForRequest方法中,最后doInvoke反射执行处理方法。
这里又有点不一样,如果我是用Model的,如:
@RequestMapping("/hello") public String sayHello(Model model){ //向模型中添加属性msg与值,可以在JSP页面中取出并渲染 model.addAttribute("msg","hello,SpringMVC"); //web-inf/jsp/hello.jsp return "hello"; }则,
resolveArgument是这样的ModelMethodProcessor类实现的
来看到return后面的方法
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
Assert.state(mavContainer != null, "ModelAndViewContainer is required for model exposure");
return mavContainer.getModel();
}
->
public ModelMap getModel() {
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
这里要返回我们的视图,也就是model.addAttribute("msg","hello,SpringMVC");中的键值对,然后返回给args[i],拿到注解里面的参数后,回到invokeForRequest方法,最后doInvoke反射执行处理方法。到这其实大体流程就结束了。
但还有个视图解析并渲染到页面的方法要执行
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
如果是传递的json,mv就是null,基本里面的方法都不执行。如果不是,这里面既可以处理exception,也可以处理modelandview。
如果处理视图
// 处理程序是否返回要渲染的视图?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
进入render渲染
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// 配置中文语言环境
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 我们需要解析视图名称
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;
}
}
在对我们视图的语言环境进行配置过后,需要解析视图名称,进入resolveViewName方法
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;
}
this.viewResolvers就是我们自己配置的视图解析器,再在for循环中遍历所有视图解析器,一般就我们配置的一个,然后再单独处理视图进入resolveViewName获取视图名称
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache");
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
先判断是否开启缓存,不开启直接创建你view,开启的话优先从缓存中查找,然后可以看到我们先从map的缓存中取,没有就向下执行取到我们的视图后就返回,然后缓存里面就有了,下次来就可以直接拿了。
得到了视图,我们回到render方法里面,调用 View 的 render 方法渲染视图
view.render(mv.getModelInternal(), request, response);
进入里面
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);
}
里面进行的视图拼接的操作,不怎么重要,就不解释了,基本大致重要的流程就在这了。
总结:
-
tomcat的Servlet调起Spring容器启动,Spring容器启动完,事件通知到SpringMVC的DispatcherServlet。
-
这时会扫描所有的bean,将注解了@Controller和@RequestMapping的解析出来。
-
前端请求发过来,DispatcherServlet接收到(因为它是个servlet,配置在web.xml的),根据上一步处理好的映射关系,找到对应的方法来处理。
如:通过/test能找到test方法
@RequestMapping("/test") public String test(String name, HttpServletRequest request,Model model){ System.out.println("name"); model.addAttribute("date",new Date()); return "success"; } -
找到对应的方法后,反射调用
method.invoke(Object obj,Object... args) -
组装modelAndView渲染视图到前端