org.springframework.web.servlet.DispatcherServlet.render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response){
// 使用语言解析器处理当前请求
Locale locale = (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
// 获取到视图名称,如果不为String,返回空,否则返回viewName
String viewName = mv.getViewName();
// 如果
if (viewName != null) {
// 解析视图
View view = resolveViewName(viewName, mv.getModelInternal(), locale, request);{
// 遍历所有的视图解析器,对视图进行解析
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
// 使用视图解析器解析视图
View view = viewResolver.resolveViewName(viewName, locale);{
// 获取缓存的Key,将视图缓存,因为视图只需要创建一次,不需要每次都创建
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) {
// 创建视图对象
view = createView(viewName, locale);{
// 判断当前视图解析器是否可以解析该视图
if (!canHandle(viewName, locale)) {
return null;
}
// 校验viewName是不是以redirect:为前缀
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
// 截取视图名称
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
// 创建RedirectView
RedirectView view = new RedirectView(redirectUrl,isRedirectContextRelative(), isRedirectHttp10Compatible());
// 获取要重定向的主机
String[] hosts = getRedirectHosts();
if (hosts != null) {
view.setHosts(hosts);
}
// 执行RedirectView的生命周期,成为Bean对象,beanName为返回的视图名,包含前缀
return applyLifecycleMethods(REDIRECT_URL_PREFIX, view);
}
// 校验ViewName是不是forward:为前缀
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
// 截取视图名称
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
// 创建InternalResourceView对象
InternalResourceView view = new InternalResourceView(forwardUrl);
// 执行RedirectView的生命周期,成为Bean对象,beanName为返回的视图名,包含前缀
return applyLifecycleMethods(FORWARD_URL_PREFIX, view);
}
// 如果没有前缀,表示不是重定向,也不是指定转发路径,创建根据视图名创建view对象
return super.createView(viewName, locale);{
return loadView(viewName, locale);{
AbstractUrlBasedView view = buildView(viewName);{
InternalResourceView view = (InternalResourceView) super.buildView(viewName);{
// 初始化view对象
// 因为在创建InternalResourceViewResolver的时候,就会判断是否引入了JSTL的包,如果引入了
// 并且视图对象是InternalResourceView类型,那么创建的View对象就是JstlView,它继承了InternalResourceView
// 用JstlView就能解析一些C:标签
AbstractUrlBasedView view = instantiateView();{
// 默认是InternalResourceView对象,如果为JstlView,JstlView对象,返回直接反射创建view对象
return (getViewClass() == InternalResourceView.class ? new InternalResourceView() :
(getViewClass() == JstlView.class ? new JstlView() : super.instantiateView()));
}
// 设置视图的URL路径,拼接前后缀
view.setUrl(getPrefix() + viewName + getSuffix());
// 下面都是将自己设置的配置设置当View中
// 设置视图自定义的属性
view.setAttributesMap(getAttributesMap());
// 设置内容类型
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
// 获取请求上下文的属性放入view中,这个是手动设置的,如果没有设置就没有
String requestContextAttribute = getRequestContextAttribute();
if (requestContextAttribute != null) {
view.setRequestContextAttribute(requestContextAttribute);
}
return view;
}
return view;
}
// 执行view的生命周期方法,就是Spring的生命周期方法
// 例如invokeAwareMethods,invokeInitMethods...
View result = applyLifecycleMethods(viewName, view);
// 校验当前视图是否存在,并且有效,默认返回true,给子类实现的,根据自身逻辑判断此视图是否有效
return (view.checkResource(locale) ? result : null);
}
}
}
// 如果没有找到或者创建视图,并且当前视图是允许不被解析,cacheUnresolved默认为true
if (view == null && this.cacheUnresolved) {
// 给定一个空view,render方法不处理任何逻辑
view = UNRESOLVED_VIEW;
}
// 找到了或者创建了对应的视图,并且当前视图允许被缓存,默认返回ture,可以根据自身情况设置
if (view != null && this.cacheFilter.filter(view, viewName, locale)) {
// 缓存view对象,防止多次创建
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
// 如果View是UNRESOLVED_VIEW,表示上面获取View失败了,返回null,否则返回符合条件的View对象
return (view != UNRESOLVED_VIEW ? view : null);
}
if (view != null) {
return view;
}
}
}
return null;
}
}
else {
// 当前view不是一个String类型,而是一个View,就不需要解析,直接拿
view = mv.getView();
}
try {
// 如果当前mv设置了状态,将这个状态设置给响应体
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 调用视图的渲染方法
view.render(mv.getModelInternal(), request, response);{
// 将不同的数据源(静态属性、模型数据、路径变量等)合并到一个新的模型对象中,以便在视图渲染过程中使用。
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
// 预处理响应信息
prepareResponse(request, response);{
// 根据不同的View来处理不同的逻辑
if (generatesDownloadContent()) {
response.setHeader("Pragma", "private");
response.setHeader("Cache-Control", "private, must-revalidate");
}
}
// 1. 合并Model数据,转发的逻辑
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);{
// 将模型中的数据放入请求域中
exposeModelAsRequestAttributes(model, request);{
// 将模型中的数据放入请求域中
model.forEach((name, value) -> {
if (value != null) { request.setAttribute(name, value); }
else { request.removeAttribute(name); }
});
}
// 如果有,暴露帮助信息,默认空实现
exposeHelpers(request);
// 渲染之前做的预处理,校验url是不是一个死循环 例如 "/haha" 转发到"/haha"
String dispatcherPath = prepareForRendering(request, response);
// 获取转发器,(通常是JSP)的RequestDispatcher
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
// 用于在一个 JSP 页面中包含另一个资源(可以是另一个 JSP 页面或者控制器返回的视图)的输出内容。
// 这个标签会在当前页面的执行期间,将另一个资源的内容合并到当前页面中,类似于在当前页面中直接插入另一个页面的内容。
// 经常用于在一个页面中包含一些共同的内容,例如页头、页脚、菜单等。
// 包含的资源的输出会被包含在当前页面的输出流中处理。
// if (useInclude(request, response)) {
response.setContentType(getContentType());
rd.include(request, response);
}
else {
// 请求转发
rd.forward(request, response);
}
}
// 2. 合并Model数据,重定向的逻辑
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);{
// 获取重定向URL
String targetUrl = createTargetUrl(model, request);{
// 如果存在路径变量,需要替换路径变量,路径变量在Handler处理的时候就已经保存到请求域中了
if (this.expandUriTemplateVariables && StringUtils.hasText(targetUrl)) {
targetUrl.append(getUrl());
Map<String, String> variables = getCurrentRequestUriVariables(request);
targetUrl = replaceUriTemplateVariables(targetUrl.toString(), model, variables, enc);
}
}
// 根据模型数据和当前的请求信息,动态地更新重定向的目标 URL,确保重定向的目标地址能够包含最新的数据信息
// 因为url可能含有路径,替换路径之后
targetUrl = updateTargetUrl(targetUrl, model, request, response);
// 保存重定向需要用到的属性
RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);
// 重定向
sendRedirect(request, response, targetUrl, this.http10Compatible);{
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));
// 302重定向
response.sendRedirect(encodedURL);
}
}
}
}
// 视图渲染失败,抛出异常
catch (Exception ex) {
throw ex;
}
}
07-03
732
![](https://csdnimg.cn/release/blogv2/dist/pc/img/readCountWhite.png)