您的位置:首页 > 其它

第六章视图解析

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(支持国际化功能的视图);它在来页面之前,帮我们放了国际化相关的内容(暴露区域信息);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  视图解析