转发的页面如果在当前项目下,
相对路径 | 回退两层 return"…/…/hello" |
---|---|
forward: | 转发到一个页面,也可以转发到一个请求,有前缀的转发跟视图解析器无关。 |
redirect: | 重定向到一个页面,也可以重定向到一个请求,有前缀重定向跟视图解析器无关 |
SpringMVC视图解析;
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ExtendedModelMap implicitModel = new BindingAwareModelMap();
这里result=“../../hello”
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
任何方法的返回值都会包装成modelandView对象
ModelAndView mav =
methodInvoker.getModelAndView(
handlerMethod, handler.getClass(),
result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
1.任何方法的返回值都会包装成modelandView对象
- processDispatchResult 来到页面的方法
视图渲染流程:将域中的数据在页面展示;
页面就是用来渲染模型数据的;
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
3.调用render 渲染页面
render(mv, request, response);
mv:要渲染的模型数据/去的页面地址
request,response:用来转发,重定向
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()) {
// We need to resolve the view name.
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;
}
}
View和ViewResolver;
ViewResolver的作用是根据视图名(方法的返回值)得到View对象
public interface ViewResolver {
viewName:视图名(方法的返回值)
得到View对象
View resolveViewName(String viewName, Locale locale) throws Exception;
}
1.视图解析器得到View对象的流程就是,所有配置的视图解析器都来尝试根据视图名(返回值)得到View对象,如果可以得到就返回,得不到就换下一个视图解析器;
2.调用view对象的render方法
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
1.遍历所有的ViewResolvers
现在我们这里只有一个InternalResourceViewResolver
初始化:ioc容器,默认配置
for (ViewResolver viewResolver : this.viewResolvers)
{
2.viewResolver视图解析器根据方法的返回值,得到View对象
resolveViewName
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
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) {
// Ask the subclass to create the View object.
根据方法的返回值创建出视图对象;View对象;
返回一个InternalResourceView对象
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);
}
}
创建View对象
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
如果前缀是redirect,就创建出一个RedirectView对象
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
return applyLifecycleMethods(viewName, view);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
如果前缀是forward,就创建出一个InternalResourceView对象
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
如果没有前缀,就使用父类默认创建一个View对象
return super.createView(viewName, locale);
}
@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
渲染要给页面输出的所有数据
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
InternalResourceView有这个方法renderMergedOutputModel
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
将模型中的数据放在请求域中,
exposeModel AsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
要转发的页面
String dispatcherPath = prepareForRendering(request, response);
转发器
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("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 resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
转发
rd.forward(request, response);
}
}
将模型中的所有数据取出来全放在request域中
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
取出隐含模型的key
String modelName = entry.getKey();
取出隐含模型的value
Object modelValue = entry.getValue();
if (modelValue != null) {
将模型中的所有数据取出来全放在request域中
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
}
}
视图解析器只是为了得到视图对象,
视图对象才能真正的转发(将模型数据全部放在请求域中)或者重定向到页面
视图对象才能真正的渲染视图。
模型数据输出为json格式
谁先解析,谁后解析
jstlView 国际化
SpringMVC管理国际化资源文件;配置一个资源文件管理器
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="il8n" />
</bean>
自定义:
视图解析器根据方法的返回值(viewName)得到视图对象View
多个视图解析器都会尝试能否得到视图对象
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
1.多个视图解析器都会尝试能否得到视图对象
for (ViewResolver viewResolver : this.viewResolvers)
{
2.viewResolver视图解析器根据方法的返回值,得到View对象
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
视图对象不同就可以具有不同渲染功能。