在项目中经常需要用到各种url转发,比如一个登录方法:
public String login(HttpServlet request ,HttpServletResponse response){
dosomthing();
return “redirect:/main”;
}
因此非常想知道spring mvc中是如何进行url转发到。红字部分为方法追踪。
一、在DispatcherServlet的doDispatch()中:
// handler处理请求,并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//设置返回的viewName 加上配置的前缀和后缀
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//处理返回结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
DispatcherServlet的processDispatchResult()方法:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//解决view异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
//如果handler返回了ModelAndView
if (mv != null && !mv.wasCleared()) {
//渲染结果
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
//记录信息到日志中:“该servlet路径对应的modelAndView为null”
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
DispatcherServlet的render()方法:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// 根据modelAndView的viewName转换成View类
view = resolveViewName(mv.getViewName(), 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.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
DispatcherServlet的resolveViewName()方法:
protected View resolveViewName(String viewName, Map
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
//根据返回视图名称从视图解析器中匹配,匹配到直接返回。
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
AbstractCachingViewResolver中resolveViewname()
@Override
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) {
// 在子类UrlBasedViewResolver中创建view对象
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
//创建号之后在放入缓存中
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
UrlBasedViewResolver中createView()方法:
@Override
protected View createView(String viewName, Locale locale) throws Exception {
//如果当前解析器不能处理给定的view,则返回null由下一个解析器继续匹配。
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
//redirect前缀跳转
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// forward前缀跳转
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
//这里就是我们在spring-mvc的配置的
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
//常规返回值
return super.createView(viewName, locale);
}