第六章视图解析
2018-01-21 17:08
120 查看
一、视图解析器的使用
@Controller
public class ViewController {
@RequestMapping("/hello")
public String hello() {
System.out.println("helllo.....");
return "successforward";
}
//forward:代表这是一个转发;虽然是转发但是视图解析器不拼串;
@RequestMapping("/handle01")
public String handle01() {
System.out.println("handle01...");
return "forward:/WEB-INF/pages/successforward.jsp";
}
// redirect:前缀。表示进行一次重定向;视图解析器就不会拼串,我们写的地址就是要去的地址,/:从项目根路径下开始
@RequestMapping("/handle02")
public String handle02() {
System.out.println("handle02...");
return "redirect:/successred.jsp";
}
//forward:代表这是一个转发;表示转发到当前项目下的hello请求;
@RequestMapping("/handle03")
public String handle03() {
System.out.println("handle03....");
return "forward:/hello";
}
}
二、视图解析
视图解析器:作用就是得到View对象;InternalResourceViewResolver;是ViewResolver(SpringMVC九大组件之一 )
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
视图:代表真正的页面。视图渲染(视图页面模板);页面渲染是视图对象View进行的;视图解析器负责得到View对象;
public interface View {
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
视图解析器负责得到一个视图对象,视图对象进行页面渲染(转发,重定向,转发请求);
3、视图渲染流程:(根据方法返回值转发到一个页面而且还能从请求域中取出map、ModelAndView中放的数据);
1)、方法执行完成以后,任何返回值都被包装成ModelAndView对象,而且隐含模型中的所有数据都放在了这个ModelAndView对象中
2)、processDispatchResult()处理页面转发取数据功能;
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception
2.1)、判断ModelAndView是否可用,如果可用执行render(mv, request, response);
2.2)、render()细节
2.2.1)、根据视图名(方法的返回值,ModelAndView里面的viewName)得到视图对象
View view =resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
2.2.2)、所有我们配置的视图解析器轮流尝试根据视图名字符串得到View对象
2.2.3)、视图解析器根据视图名得到视图对象;(createView)InternalResourceViewResolver
1.是否以redirect:前缀开始,如果是 new RedirectView();
2.是否以forward:开始,如果是new InternalResourceView();
3.都不是创建一个InternalResourceView对象
2.2.4)、View对象负责来到页面;
InternalResourceView;
renderMergedOutputModel(mergedModel, request, response);
1)、将隐含模型中的所有数据保存在request域中
2)、拿到转发器调用forward转发到页面
ViewResolver负责得到View对象。
View对象进行渲染(决定是要转发到页面,还是重定向,还是。。。。)
InternalResourceView:作用:转发到页面、将隐含模型中的数据放在请求域中
RedirectView:作用:重定向到页面;
我们想要的效果就叫View;
视图解析器工作流程:
1.目标方法执行返回一个"返回值";
2.任何方法执行后的任何返回值都是会被包装成一个ModelAndView对象;并且把隐含模型中的所有东西都添加到ModelAndView对象中;
3.methodInvoker.updateModelAttributes()把ModelAndView中的所有属性都遍历,看那个是@SessionAttribute的属性,如果是就存在session中;
4.processDispatchResult()转发到页面了,并且页面也有值了;
视图解析器:(每一个视图解析器只是负责得到不同的View对象)
InternalResourceViewResolver是ViewResolver;
InternalResourceView是View;
总结:
1)、所有方法的返回值都包装成ModelAndView(既包含数据又包含页面地址);
2)、视图解析器(InternalResourceViewResolver )根据ModelAndView的信息得到视图(View)对象;
InternalResourceViewResolver--->InternalResourceView;
3)、视图(View)对象调用render()才是来到页面;
InternalResourceView:渲染页面;
1)、把Model数据都放在请求域中
2)、转发到页面;
3)、如果是重定向,会创建一个RedirectView;
4)、视图对象不同效果就不同;
细节流程:
1、根据ModelAndView里面的信息(视图名,隐含模型数据)得到View对象
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//获取ModelAndView 对象
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//来到页面之前进行渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
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();
//方法执行后的任何返回值都是会被包装成一个ModelAndView对象;并且把隐含模型中的所有东西都添加到ModelAndView对象中;
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
if (responseStatusAnn != null) {
HttpStatus responseStatus = responseStatusAnn.value();
String reason = responseStatusAnn.reason();
if (!StringUtils.hasText(reason)) {
webRequest.getResponse().setStatus(responseStatus.value());
}
else {
webRequest.getResponse().sendError(responseStatus.value(), reason);
}
// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
responseArgumentUsed = true;
}
// Invoke custom resolvers if present...
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver.resolveModelAndView(
handlerMethod, handlerType, returnValue, implicitModel, webRequest);
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
}
if (returnValue instanceof HttpEntity) {
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
}
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
else if (returnValue == null) {
// Either returned null or was 'void' return.
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
//将ModelAndVie返回,并把隐含模型中的数据全部添加到ModelAndView中
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
}
}
// Expose model attributes as session attributes, if required.
// Expose BindingResults for all attributes, making custom editors available.
Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
if (model != null) {
try {
//把ModelAndView中的所有属性都遍历,看那个是@SessionAttribute的属性,如果是就存在session中;
String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
for (String attrName : originalAttrNames) {
Object attrValue = model.get(attrName);
boolean isSessionAttr = this.methodResolver.isSessionAttribute(
attrName, (attrValue != null ? attrValue.getClass() : null));
if (isSessionAttr) {
if (this.sessionStatus.isComplete()) {
implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
}
else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
//存在session中;
this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
}
}
if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
(isSessionAttr || isBindingCandidate(attrValue))) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
if (mavModel != null && !model.containsKey(bindingResultKey)) {
WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
initBinder(handler, attrName, binder, webRequest);
mavModel.put(bindingResultKey, binder.getBindingResult());
}
}
}
}
catch (InvocationTargetException ex) {
// User-defined @InitBinder method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
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);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//渲染页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
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);
}
}
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()) {
//得到View对象;
// 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(渲染)方法,实际调用的是InternalResourceView的render方法
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;
}
}
//根据ModelAndView里面的信息(视图名,隐含模型数据)得到View对象;
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
//遍历所有视图解析器,默认就拿到InternalResourceViewResolver;如果成功直接返回View对象,否则下一个视图解析器;
for (ViewResolver viewResolver : this.viewResolvers) {
//第一次访问要创建一个View对象;
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
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.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
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;
}
//判断视图名是否以redirect:开始,如果是创建一个RedirectView
// Check for special "redirect:" prefix.
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);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
//如果不是以这些开头,就创建一个view对象
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//利用反射创建一个InternalResourceView对象;
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
//将返回值和前后缀拼串得到要去的地址;
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
if (this.exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
return view;
}
和render有关的代码:
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, request, response);
}
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
//渲染第一步:暴露模型数据作为request域属性;
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
//拿到转发器
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, 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(requestToExpose, response);
}
}
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
//挨个遍历每一个属性,全部放在请求域中;
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() + "'");
}
}
}
}
自定义视图解析器
package com.nengjie.viewcontroller;
import java.util.Locale;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
/**
* 自定义图片视图解析器:viewResolver 需求:在浏览器上显示图片
*
*/
public class PicViewResolver implements ViewResolver, Ordered {
private int order;
/**
* viewName:方法的返回值 locale:和国际化相关
*/
@Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
if (viewName.startsWith("pic:")) {
// 创建一个自定义视图
PicView picView = new PicView();
String pic = viewName.substring("pic:".length());
picView.setPicName(pic);
return picView;
}
// 如果不能解析,让下一个视图解析器进行解析
return null;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return 0;
}
}
package com.nengjie.viewcontroller;
import java.io.InputStream;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.servlet.View;
/**
* 自定义视图对象:view,自定义如何显示效果
* @author Administrator
*
*/
public class PicView implements View{
private String picName;
public String getPicName() {
return picName;
}
public void setPicName(String picName) {
this.picName = picName;
}
/**
* 返回内容类型
*/
@Override
public String getContentType() {
return "image/png";
}
/**
* 自定义渲染逻辑
*/
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//将图片以流的形式写出
ServletOutputStream outputStream = response.getOutputStream();
//用ServletContext,拿到这个图片的流,ServletContext:一个项目对应一个
ServletContext servletContext = request.getServletContext();
//能将当前项目下的某个资源以流的形式返回;
InputStream inputStream = servletContext.getResourceAsStream("/imgs/"+picName);
//将图片写给浏览器
IOUtils.copy(inputStream, outputStream);
outputStream.close();
inputStream.close();
}
}
springmvc.xml配置文件
<!-- 默认最低优先级 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 视图解析器有顺序;默认按照配置的先后顺序; -->
<bean class="com.nengjie.viewcontroller.PicViewResolver">
<!-- 数字越小优先级越高 -->
<property name="order" value="1"></property>
</bean>
package com.nengjie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 测试自定义视图解析器
* @author Administrator
*
*/
@Controller
public class MyViewController {
@RequestMapping("/showPic")
public String showPic(String name) {
return "pic:"+name;
}
}
JSTLView视图
若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView
若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件
<!-- 使用SpringMVC国际化配置文件 ,必须配置id,并且要求名字必须叫messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 指定国际化配置文件的基础名 -->
<property name="basename" value="i18n"></property>
</bean>
和web的国际化一样,同样需要国际化配置文件
jsp页面上直接使用 <fmt:message>取值
导入这个标签:
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<form action="">
<fmt:message key="username"/>:<input /><br/>
<fmt:message key="password"/>:<input /><br/>
<input type="submit" value="<fmt:message key="loginBtn"/>"/>
</form>
注意:只要导入了jstl jar包,SpringMVC在创建View的时候就会自动的变为JstlView(支持国际化功能的视图);它在来页面之前,帮我们放了国际化相关的内容(暴露区域信息);
@Controller
public class ViewController {
@RequestMapping("/hello")
public String hello() {
System.out.println("helllo.....");
return "successforward";
}
//forward:代表这是一个转发;虽然是转发但是视图解析器不拼串;
@RequestMapping("/handle01")
public String handle01() {
System.out.println("handle01...");
return "forward:/WEB-INF/pages/successforward.jsp";
}
// redirect:前缀。表示进行一次重定向;视图解析器就不会拼串,我们写的地址就是要去的地址,/:从项目根路径下开始
@RequestMapping("/handle02")
public String handle02() {
System.out.println("handle02...");
return "redirect:/successred.jsp";
}
//forward:代表这是一个转发;表示转发到当前项目下的hello请求;
@RequestMapping("/handle03")
public String handle03() {
System.out.println("handle03....");
return "forward:/hello";
}
}
二、视图解析
视图解析器:作用就是得到View对象;InternalResourceViewResolver;是ViewResolver(SpringMVC九大组件之一 )
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
视图:代表真正的页面。视图渲染(视图页面模板);页面渲染是视图对象View进行的;视图解析器负责得到View对象;
public interface View {
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
视图解析器负责得到一个视图对象,视图对象进行页面渲染(转发,重定向,转发请求);
3、视图渲染流程:(根据方法返回值转发到一个页面而且还能从请求域中取出map、ModelAndView中放的数据);
1)、方法执行完成以后,任何返回值都被包装成ModelAndView对象,而且隐含模型中的所有数据都放在了这个ModelAndView对象中
2)、processDispatchResult()处理页面转发取数据功能;
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception
2.1)、判断ModelAndView是否可用,如果可用执行render(mv, request, response);
2.2)、render()细节
2.2.1)、根据视图名(方法的返回值,ModelAndView里面的viewName)得到视图对象
View view =resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
2.2.2)、所有我们配置的视图解析器轮流尝试根据视图名字符串得到View对象
2.2.3)、视图解析器根据视图名得到视图对象;(createView)InternalResourceViewResolver
1.是否以redirect:前缀开始,如果是 new RedirectView();
2.是否以forward:开始,如果是new InternalResourceView();
3.都不是创建一个InternalResourceView对象
2.2.4)、View对象负责来到页面;
InternalResourceView;
renderMergedOutputModel(mergedModel, request, response);
1)、将隐含模型中的所有数据保存在request域中
2)、拿到转发器调用forward转发到页面
ViewResolver负责得到View对象。
View对象进行渲染(决定是要转发到页面,还是重定向,还是。。。。)
InternalResourceView:作用:转发到页面、将隐含模型中的数据放在请求域中
RedirectView:作用:重定向到页面;
我们想要的效果就叫View;
视图解析器工作流程:
1.目标方法执行返回一个"返回值";
2.任何方法执行后的任何返回值都是会被包装成一个ModelAndView对象;并且把隐含模型中的所有东西都添加到ModelAndView对象中;
3.methodInvoker.updateModelAttributes()把ModelAndView中的所有属性都遍历,看那个是@SessionAttribute的属性,如果是就存在session中;
4.processDispatchResult()转发到页面了,并且页面也有值了;
视图解析器:(每一个视图解析器只是负责得到不同的View对象)
InternalResourceViewResolver是ViewResolver;
InternalResourceView是View;
总结:
1)、所有方法的返回值都包装成ModelAndView(既包含数据又包含页面地址);
2)、视图解析器(InternalResourceViewResolver )根据ModelAndView的信息得到视图(View)对象;
InternalResourceViewResolver--->InternalResourceView;
3)、视图(View)对象调用render()才是来到页面;
InternalResourceView:渲染页面;
1)、把Model数据都放在请求域中
2)、转发到页面;
3)、如果是重定向,会创建一个RedirectView;
4)、视图对象不同效果就不同;
细节流程:
1、根据ModelAndView里面的信息(视图名,隐含模型数据)得到View对象
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
//获取ModelAndView 对象
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//来到页面之前进行渲染
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
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();
//方法执行后的任何返回值都是会被包装成一个ModelAndView对象;并且把隐含模型中的所有东西都添加到ModelAndView对象中;
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
public ModelAndView getModelAndView(Method handlerMethod, Class<?> handlerType, Object returnValue,
ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
if (responseStatusAnn != null) {
HttpStatus responseStatus = responseStatusAnn.value();
String reason = responseStatusAnn.reason();
if (!StringUtils.hasText(reason)) {
webRequest.getResponse().setStatus(responseStatus.value());
}
else {
webRequest.getResponse().sendError(responseStatus.value(), reason);
}
// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
responseArgumentUsed = true;
}
// Invoke custom resolvers if present...
if (customModelAndViewResolvers != null) {
for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
ModelAndView mav = mavResolver.resolveModelAndView(
handlerMethod, handlerType, returnValue, implicitModel, webRequest);
if (mav != ModelAndViewResolver.UNRESOLVED) {
return mav;
}
}
}
if (returnValue instanceof HttpEntity) {
handleHttpEntityResponse((HttpEntity<?>) returnValue, webRequest);
return null;
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
else if (returnValue instanceof ModelAndView) {
ModelAndView mav = (ModelAndView) returnValue;
mav.getModelMap().mergeAttributes(implicitModel);
return mav;
}
else if (returnValue instanceof Model) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
}
else if (returnValue instanceof View) {
return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
}
else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else if (returnValue instanceof Map) {
return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map<String, ?>) returnValue);
}
else if (returnValue instanceof String) {
return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
}
else if (returnValue == null) {
// Either returned null or was 'void' return.
if (this.responseArgumentUsed || webRequest.isNotModified()) {
return null;
}
else {
//将ModelAndVie返回,并把隐含模型中的数据全部添加到ModelAndView中
// Assuming view name translation...
return new ModelAndView().addAllObjects(implicitModel);
}
}
else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
// Assume a single model attribute...
addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
return new ModelAndView().addAllObjects(implicitModel);
}
else {
throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
}
}
public final void updateModelAttributes(Object handler, Map<String, Object> mavModel,
ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
}
}
// Expose model attributes as session attributes, if required.
// Expose BindingResults for all attributes, making custom editors available.
Map<String, Object> model = (mavModel != null ? mavModel : implicitModel);
if (model != null) {
try {
//把ModelAndView中的所有属性都遍历,看那个是@SessionAttribute的属性,如果是就存在session中;
String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
for (String attrName : originalAttrNames) {
Object attrValue = model.get(attrName);
boolean isSessionAttr = this.methodResolver.isSessionAttribute(
attrName, (attrValue != null ? attrValue.getClass() : null));
if (isSessionAttr) {
if (this.sessionStatus.isComplete()) {
implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
}
else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
//存在session中;
this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
}
}
if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
(isSessionAttr || isBindingCandidate(attrValue))) {
String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
if (mavModel != null && !model.containsKey(bindingResultKey)) {
WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
initBinder(handler, attrName, binder, webRequest);
mavModel.put(bindingResultKey, binder.getBindingResult());
}
}
}
}
catch (InvocationTargetException ex) {
// User-defined @InitBinder method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
}
}
}
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
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);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//渲染页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
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);
}
}
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()) {
//得到View对象;
// 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(渲染)方法,实际调用的是InternalResourceView的render方法
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;
}
}
//根据ModelAndView里面的信息(视图名,隐含模型数据)得到View对象;
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
//遍历所有视图解析器,默认就拿到InternalResourceViewResolver;如果成功直接返回View对象,否则下一个视图解析器;
for (ViewResolver viewResolver : this.viewResolvers) {
//第一次访问要创建一个View对象;
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
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.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
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;
}
//判断视图名是否以redirect:开始,如果是创建一个RedirectView
// Check for special "redirect:" prefix.
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);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
//如果不是以这些开头,就创建一个view对象
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
//利用反射创建一个InternalResourceView对象;
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
//将返回值和前后缀拼串得到要去的地址;
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
if (this.exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
return view;
}
和render有关的代码:
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, request, response);
}
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine which request handle to expose to the RequestDispatcher.
HttpServletRequest requestToExpose = getRequestToExpose(request);
//渲染第一步:暴露模型数据作为request域属性;
// Expose the model object as request attributes.
exposeModelAsRequestAttributes(model, requestToExpose);
// Expose helpers as request attributes, if any.
exposeHelpers(requestToExpose);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(requestToExpose, response);
//拿到转发器
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(requestToExpose, 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(requestToExpose, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(requestToExpose, 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(requestToExpose, response);
}
}
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
//挨个遍历每一个属性,全部放在请求域中;
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() + "'");
}
}
}
}
自定义视图解析器
package com.nengjie.viewcontroller;
import java.util.Locale;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
/**
* 自定义图片视图解析器:viewResolver 需求:在浏览器上显示图片
*
*/
public class PicViewResolver implements ViewResolver, Ordered {
private int order;
/**
* viewName:方法的返回值 locale:和国际化相关
*/
@Override
public View resolveViewName(String viewName, Locale locale)
throws Exception {
if (viewName.startsWith("pic:")) {
// 创建一个自定义视图
PicView picView = new PicView();
String pic = viewName.substring("pic:".length());
picView.setPicName(pic);
return picView;
}
// 如果不能解析,让下一个视图解析器进行解析
return null;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return 0;
}
}
package com.nengjie.viewcontroller;
import java.io.InputStream;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.web.servlet.View;
/**
* 自定义视图对象:view,自定义如何显示效果
* @author Administrator
*
*/
public class PicView implements View{
private String picName;
public String getPicName() {
return picName;
}
public void setPicName(String picName) {
this.picName = picName;
}
/**
* 返回内容类型
*/
@Override
public String getContentType() {
return "image/png";
}
/**
* 自定义渲染逻辑
*/
@Override
public void render(Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//将图片以流的形式写出
ServletOutputStream outputStream = response.getOutputStream();
//用ServletContext,拿到这个图片的流,ServletContext:一个项目对应一个
ServletContext servletContext = request.getServletContext();
//能将当前项目下的某个资源以流的形式返回;
InputStream inputStream = servletContext.getResourceAsStream("/imgs/"+picName);
//将图片写给浏览器
IOUtils.copy(inputStream, outputStream);
outputStream.close();
inputStream.close();
}
}
springmvc.xml配置文件
<!-- 默认最低优先级 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 视图解析器有顺序;默认按照配置的先后顺序; -->
<bean class="com.nengjie.viewcontroller.PicViewResolver">
<!-- 数字越小优先级越高 -->
<property name="order" value="1"></property>
</bean>
package com.nengjie.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 测试自定义视图解析器
* @author Administrator
*
*/
@Controller
public class MyViewController {
@RequestMapping("/showPic")
public String showPic(String name) {
return "pic:"+name;
}
}
JSTLView视图
若项目中使用了JSTL,则SpringMVC 会自动把视图由InternalResourceView转为 JstlView
若使用 JSTL 的 fmt 标签则需要在 SpringMVC 的配置文件中配置国际化资源文件
<!-- 使用SpringMVC国际化配置文件 ,必须配置id,并且要求名字必须叫messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<!-- 指定国际化配置文件的基础名 -->
<property name="basename" value="i18n"></property>
</bean>
和web的国际化一样,同样需要国际化配置文件
jsp页面上直接使用 <fmt:message>取值
导入这个标签:
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<form action="">
<fmt:message key="username"/>:<input /><br/>
<fmt:message key="password"/>:<input /><br/>
<input type="submit" value="<fmt:message key="loginBtn"/>"/>
</form>
注意:只要导入了jstl jar包,SpringMVC在创建View的时候就会自动的变为JstlView(支持国际化功能的视图);它在来页面之前,帮我们放了国际化相关的内容(暴露区域信息);
相关文章推荐
- 《Spring实战》学习笔记-第六章:web视图解析
- 《Spring实战》学习笔记-第六章:web视图解析
- 《Spring实战》学习笔记-第六章:web视图解析
- ASP.NET视图的保存与加载解析(一)——视图的保存
- 文档视图结构解析
- 第六章 用堆栈视图(Stack Views)来设计UI
- CakePHP 2.x CookBook 中文版 第六章 视图 之二
- iOS视图生命周期解析
- SpringMVC-视图解析
- discuz代码解析(二、控制器与视图解析)
- iOS 第六章之视图控制器
- Android视图绘制流程完全解析,带你一步步深入了解View(二)
- Spring MVC Rest Json,XML多视图解析 配置
- 优化myshool第六章 事务 视图 索引 上机 简答
- Android视图绘制流程完全解析(二)
- 【Java.Spring.MVC】解析视图
- iOS开发- 自定义遮罩视图(引导, 功能说明)源码+解析
- 解析几何:第六章 二次曲面(1)球面 椭球面 双曲面
- MFC复习和学习 第六章 MFC视图文档 MFC视图文档各部分关系 MFC绘图 MFC绘图对象
- 第六章:【UCHome二次开发】缓存机制解析