当前位置: 首页 > news >正文

北京网站开发怎么做/百度商城app

北京网站开发怎么做,百度商城app,网站建设参考网站的说明书,株洲做网站哪家好每篇一句 学好模式比具体学一门技术来得更重要。因为技术是战术,设计模式(策略)才是战略。战术是经常变的,但战略是基本不变的。 所以建议学会站在一个更好的角度去看待问题 相关阅读 【小家Spring】Spring MVC容器的web九大组件之—ViewResolver源码详…

每篇一句

学好模式比具体学一门技术来得更重要。因为技术是战术,设计模式(策略)才是战略。战术是经常变的,但战略是基本不变的。 所以建议学会站在一个更好的角度去看待问题

相关阅读

【小家Spring】Spring MVC容器的web九大组件之—ViewResolver源码详解—视图解析器ViewResolver详解
【小家Spring】Spring MVC容器的web九大组件之—HandlerAdapter源码详解—HttpMessageConverter 消息转换器详解
【小家Spring】Spring MVC容器的web九大组件之—HandlerAdapter源码详解—HttpMessageConverter的匹配规则(选择原理)

前言

上篇文章已经重点讲解过了:ViewResolver视图解析器
【小家Spring】Spring MVC容器的web九大组件之—ViewResolver源码详解—视图解析器ViewResolver详解

SpringMVC用于处理视图最重要的两个接口是ViewResolverViewViewResolver的主要作用 是把一个逻辑上的视图名称解析为一个真正的视图,SpringMVC中用于把View对象呈现给客户端的 是View对象本身,ViewResolver只是把逻辑视图名称解析为对象的View对象View接口的主要 作用是用于处理视图,然后返回给客户端。

View

View是用于MVC交互的Web视图。实现负责呈现内容,并公开模型。单个视图可显示多个模型属性

视图实现可能差异很大,比如我们最基础的实现:JSP就是一种视图展示方式。当然还有后面的Jstl以及FreeMarker等。此接口旨在避免限制可能的实现范围

视图应该是bean(但不一定需要放进容器)。它们很可能被viewresolver实例化为bean。由于这个接口是无状态的,视图实现应该是线程安全的。

public interface View {// @since 3.0// HttpStatus的key,可议根据此key去获取。备注:并不是每个视图都需要实现的。目前只有`RedirectView`有处理String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";// @since 3.1  也会这样去拿:request.getAttribute(View.PATH_VARIABLES)String PATH_VARIABLES = View.class.getName() + ".pathVariables";// The {@link org.springframework.http.MediaType} selected during content negotiation// @since 3.2// MediaType mediaType = (MediaType) request.getAttribute(View.SELECTED_CONTENT_TYPE)String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";// Return the content type of the view, if predetermined(预定的)@Nullabledefault String getContentType() {return null;}// 这是最重要的 根据model里面的数据,request等  把渲染好的数据写进response里~void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

看看它的继承树:
在这里插入图片描述
可以看出来它只有两个分支:AbstractViewSmartView,而SmartView的唯一实现为:RedirectView并且它也继承自AbstractView

我们可以粗略的认为:Spring MVC内置的所有的View都是AbstractView的子类

AbstractView

AbstractView实现了render方法,主要做的操作是将model中的参数和request中的参数全部都放到Request中,然后就转发Request就可以了

public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {/** Default content type. Overridable as bean property. */public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=ISO-8859-1";/** Initial size for the temporary output byte array (if any). */private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 4096;// 这几个属性值,没有陌生的。在视图解析器章节里面都有解释过~~~@Nullableprivate String contentType = DEFAULT_CONTENT_TYPE;@Nullableprivate String requestContextAttribute;// "Static" attributes are fixed attributes that are specified in the View instance configuration// "Dynamic" attributes, on the other hand,are values passed in as part of the model.private final Map<String, Object> staticAttributes = new LinkedHashMap<>();private boolean exposePathVariables = true;private boolean exposeContextBeansAsAttributes = false;@Nullableprivate Set<String> exposedContextBeanNames;@Nullableprivate String beanName;// 把你传进俩的Properties 都合并进来~~~public void setAttributes(Properties attributes) {CollectionUtils.mergePropertiesIntoMap(attributes, this.staticAttributes);}...@Overridepublic void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// 合并staticAttributes、pathVars、model数据到一个Map里来// 其中:后者覆盖前者的值(若有相同key的话~~)也就是所谓的model的值优先级最高~~~~// 最终还会暴露RequestContext对象到Model里,因此model里可以直接访问RequestContext对象哦~~~~Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);// 默认实现为设置几个响应头~~~// 备注:默认情况下pdf的view、xstl的view会触发下载~~~prepareResponse(request, response);// getRequestToExpose表示吧request暴露成:ContextExposingHttpServletRequest(和容器相关,以及容器内的BeanNames)// renderMergedOutputModel是个抽象方法 由子类去实现~~~~renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);}//================下面是一些方法,父类提供  子类可以直接使用的方法==============// 一个temp输出流,缓冲区大小为4096  字节流protected ByteArrayOutputStream createTemporaryOutputStream() {return new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);}// 把字节流写进response里面~~~protected void writeToResponse(HttpServletResponse response, ByteArrayOutputStream baos) throws IOException {// Write content type and also length (determined via byte array).response.setContentType(getContentType());response.setContentLength(baos.size());// Flush byte array to servlet output stream.ServletOutputStream out = response.getOutputStream();baos.writeTo(out);out.flush();}// 相当于如果request.getAttribute(View.SELECTED_CONTENT_TYPE) 指定了就以它为准~protected void setResponseContentType(HttpServletRequest request, HttpServletResponse response) {MediaType mediaType = (MediaType) request.getAttribute(View.SELECTED_CONTENT_TYPE);if (mediaType != null && mediaType.isConcrete()) {response.setContentType(mediaType.toString());}else {response.setContentType(getContentType());}}...
}

该抽象类主要是提供了对render方法的模版实现,以及提供一些基础方法供给子类来使用,比如createTemporaryOutputStream()等等

AbstractJackson2View

这个是一个比较新的Viw(@since 4.1),它是基于Jackson渲染的视图。

//@since 4.1 
// Compatible with Jackson 2.6 and higher, as of Spring 4.3.
public abstract class AbstractJackson2View extends AbstractView {private ObjectMapper objectMapper;private JsonEncoding encoding = JsonEncoding.UTF8;@Nullableprivate Boolean prettyPrint;private boolean disableCaching = true;protected boolean updateContentLength = false;// 唯一构造函数,并且还是protected的~~protected AbstractJackson2View(ObjectMapper objectMapper, String contentType) {this.objectMapper = objectMapper;configurePrettyPrint();setContentType(contentType);setExposePathVariables(false);}... // get/set方法// 复写了父类的此方法~~~   setResponseContentType是父类的哟~~~~@Overrideprotected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {setResponseContentType(request, response);// 设置编码格式,默认是UTF-8response.setCharacterEncoding(this.encoding.getJavaName());if (this.disableCaching) {response.addHeader("Cache-Control", "no-store");}}// 实现了父类的渲染方法~~~~@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request,HttpServletResponse response) throws Exception {ByteArrayOutputStream temporaryStream = null;OutputStream stream;// 注意此处:updateContentLength默认值是false   所以会直接从response里面吧输出流拿出来   而不用temp流if (this.updateContentLength) {temporaryStream = createTemporaryOutputStream();stream = temporaryStream;}else {stream = response.getOutputStream();}Object value = filterAndWrapModel(model, request);// value是最终的从model中出来的~~~~这里就是把value值写进去~~~~// 先通过stream得到一个JsonGenerator,然后先writePrefix(generator, object)// 然后objectMapper.writerWithView// 最后writeSuffix(generator, object);  然后flush即可~writeContent(stream, value);if (temporaryStream != null) {writeToResponse(response, temporaryStream);}}// 筛选Model并可选地将其包装在@link mappingjacksonvalue容器中protected Object filterAndWrapModel(Map<String, Object> model, HttpServletRequest request) {// filterModel抽象方法,从指定的model中筛选出不需要的属性值~~~~~Object value = filterModel(model);// 把这两个属性值,选择性的放进container容器里面  最终返回~~~~Class<?> serializationView = (Class<?>) model.get(JsonView.class.getName());FilterProvider filters = (FilterProvider) model.get(FilterProvider.class.getName());if (serializationView != null || filters != null) {MappingJacksonValue container = new MappingJacksonValue(value);if (serializationView != null) {container.setSerializationView(serializationView);}if (filters != null) {container.setFilters(filters);}value = container;}return value;}}
MappingJackson2JsonView

它是用于返回Json视图的(下面会介绍Spring MVC返回json的三种方式

// @since 3.1.2 可议看到它出现得还是比较早的~
public class MappingJackson2JsonView extends AbstractJackson2View {public static final String DEFAULT_CONTENT_TYPE = "application/json";@Nullableprivate String jsonPrefix;@Nullableprivate Set<String> modelKeys;private boolean extractValueFromSingleKeyModel = false;@Overrideprotected Object filterModel(Map<String, Object> model) {Map<String, Object> result = new HashMap<>(model.size());Set<String> modelKeys = (!CollectionUtils.isEmpty(this.modelKeys) ? this.modelKeys : model.keySet());// 遍历model所有内容~ model.forEach((clazz, value) -> {// 符合下列条件的会给排除掉~~~// 不是BindingResult类型 并且  modelKeys包含此key 并且此key不是JsonView和FilterProvider  这种key就排除掉~~~if (!(value instanceof BindingResult) && modelKeys.contains(clazz) &&!clazz.equals(JsonView.class.getName()) &&!clazz.equals(FilterProvider.class.getName())) {result.put(clazz, value);}});// 如果只需要排除singleKey,那就返回第一个即可,否则result全部返回return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result);}// 如果配置了前缀,把前缀写进去~~~@Overrideprotected void writePrefix(JsonGenerator generator, Object object) throws IOException {if (this.jsonPrefix != null) {generator.writeRaw(this.jsonPrefix);}}
}

此视图是专门来处理作为一个json视图格式进行返回的。那么接下里有必要举例说明一下,Spring MVC返回Json格式数据的多种方式:

Spring MVC返回json的三种方式

  1. 使用MappingJackson2JsonView,其实它是相对来说比较新的一种返回json数据的放置,主要是用到了这个视图的能力。

直接使用它相对来说还是比较麻烦点的,一般都需要结合内容协商视图解析器来使用(比如把它设置默认处理json的视图),但是本文就做一个Demo,所以还是简单的处理一下吧:使用BeanNameViewResolver执行我们定义的这个视图去即可:

    @RequestMapping(value = "/json")public String testView(Model model) {// 注意Model不添加数据,将会是一个空的JSON串model.addAttribute("name", "fsx");model.addAttribute("age", 18);return "mappingJackson2JsonView";}// 配置视图:
@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {// 此处为配置了一个前缀,发现前缀可以解决jsonp的问题~~~@Beanpublic MappingJackson2JsonView mappingJackson2JsonView() {MappingJackson2JsonView mappingJackson2JsonView = new MappingJackson2JsonView();mappingJackson2JsonView.setJsonPrefix("prefix");return mappingJackson2JsonView;}@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {BeanNameViewResolver viewResolver = new BeanNameViewResolver();viewResolver.setOrder(10); // 这样能保证在InternalResourceViewResolver之前执行registry.viewResolver(viewResolver);}
}

浏览器访问:http://localhost:8080/demo_war_war/json可看到如下:
在这里插入图片描述

它提供的前缀能力,在某些特殊的场景会有用

  1. 利用HttpServletResponse,然后获取response.getOutputStream()response.getWriter()自己写json串
    @RequestMapping(value = "/json")public void testView(PrintWriter printWriter) {printWriter.write("{\"name\":\"fsx\",\"age\":18}");}

这样啥处理器都不需要,直接写输出流即可。访问看浏览器:
在这里插入图片描述

显然这种方式最为原始的方式,一般情况下我是不推荐这么使用的~
插一句:我曾经看到过有项目在使用Spring MVC框架的时候,还大量的使用到了Servlet规范的东西,其实这个真的是非常非常不好的做法~~

  1. @ResponseBody这种方式是当下平时我们书写使用最多的方式
    相信这种方式我说一个字:“略”,应该没有人有意见吧~~~
MappingJackson2XmlView

它主要处理:

public static final String DEFAULT_CONTENT_TYPE = "application/xml";

大致逻辑是同上。只不过它用的是XmlMapper而已~~~

AbstractPdfView

处理PDF:"application/pdf"。依赖jar是com.lowagie

MarshallingView

Marshaller在国内使用非常少,忽略

AbstractXlsView

这个依赖于Apache的POI库,处理Excel等。
Spring MVC 中对于输出格式为pdf和xsl的view,提供了两个abstract的view类供继承分别为AbstractPdfView和AbstractXlsView。

AbstractFeedView

com.rometools包的WireFeed有关,忽略。


FastJsonJsonView

它不是位于Spring包内,位于aliabba包内。因为它也是一个json视图,所以没有太多可说的:

public class FastJsonJsonView extends AbstractView {public static final String DEFAULT_CONTENT_TYPE = "application/json;charset=UTF-8";// 这个是专门处理jsonp的public static final String DEFAULT_JSONP_CONTENT_TYPE = "application/javascript";private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");...@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, //HttpServletRequest request, //HttpServletResponse response) throws Exception {Object value = filterModel(model);String jsonpParameterValue = getJsonpParameterValue(request);if (jsonpParameterValue != null) {JSONPObject jsonpObject = new JSONPObject(jsonpParameterValue);jsonpObject.addParameter(value);value = jsonpObject;}ByteArrayOutputStream outnew = new ByteArrayOutputStream();// 它依赖的是这个静态方法,把value值写进去的~~~~int len = JSON.writeJSONString(outnew, //fastJsonConfig.getCharset(), //value, //fastJsonConfig.getSerializeConfig(), //fastJsonConfig.getSerializeFilters(), //fastJsonConfig.getDateFormat(), //JSON.DEFAULT_GENERATE_FEATURE, //fastJsonConfig.getSerializerFeatures());if (this.updateContentLength) {// Write content length (determined via byte array).response.setContentLength(len);}// Flush byte array to servlet output stream.ServletOutputStream out = response.getOutputStream();outnew.writeTo(out);outnew.close();out.flush();}
}


AbstractUrlBasedView

下面来到我们最为重要的一个分支:AbstractUrlBasedView。因为前面讲到过UrlBasedViewResolver这个分支是最重要的视图处理器,所以自然而然这个相关的视图也是最为重要的~~~

AbstractPdfStamperView

这个和AbstractPdfView有点类似,不过它出来相对较晚。因为它可以基于URL去渲染PDF,它也是个抽象类,Spring MVC并没有PDF的具体的视图实现~~

RedirectView(SmartView

这个视图和SmartView一起讲解一下。首先SmartView是一个子接口,增加了一个方法:

// @since 3.1 接口出来较晚,但是RedirectView早就有了的~~~
public interface SmartView extends View {boolean isRedirectView();
}

顾名思义RedirectView是用于页面跳转使用的。重定向我们都不陌生,因此我们下面主要看看RedirectView它的实现:

重定向在浏览器可议看到两个毫不相关的request请求。跳转的请求会丢失原请求的所有数据,一般的解决方法是将原请求中的数据放到跳转请求的URL中这样来传递,下面来看看RediectView是怎么优雅的帮我们解决这个问题的~~~

我们的重定向例子:

    @GetMapping("/index")public Object index(Model model) {RedirectView redirectView = new RedirectView("/index.jsp");redirectView.setContextRelative(true); //因为我们希望加上ServletContext  所以这个设置为true  并且以/打头redirectView.setHttp10Compatible(false); //不需要兼容http1.0  所以http状态码一般返回303// 给些参数 最终会拼接到URL后面去~model.addAttribute("name", "fsx");model.addAttribute("age", 18);return redirectView;}

效果如下:
在这里插入图片描述
源码分析:

public class RedirectView extends AbstractUrlBasedView implements SmartView {private static final Pattern URI_TEMPLATE_VARIABLE_PATTERN = Pattern.compile("\\{([^/]+?)\\}");private boolean contextRelative = false;// 是否兼容http1.0private boolean http10Compatible = true;private boolean exposeModelAttributes = true;// 如果你不设置,默认就是ISO-8859-1@Nullableprivate String encodingScheme;@Nullableprivate HttpStatus statusCode;private boolean expandUriTemplateVariables = true;// 当设置为@code true时,将追加当前URL的查询字符串,从而传播到重定向的URL。private boolean propagateQueryParams = false;@Nullableprivate String[] hosts;// 此处exposePathVariables设置为了truepublic RedirectView() {setExposePathVariables(false);}// 此处需要注意的是:给定的URL将被视为相对于Web服务器,而不是相对于当前Servletcontextpublic RedirectView(String url) {super(url);setExposePathVariables(false);}// contextRelative:true表示为将URL解释为相对于当前ServletContext上下文  它的默认这是falsepublic RedirectView(String url, boolean contextRelative) {super(url);this.contextRelative = contextRelative;setExposePathVariables(false);}...// 配置与应用程序关联的一个或多个主机。所有其他主机都将被视为外部主机。public void setHosts(@Nullable String... hosts) {this.hosts = hosts;}// 显然此复写 永远返回true@Overridepublic boolean isRedirectView() {return true;}// 父类ApplicationObjectSupport的方法// 此视图并不要求有ApplicationContext@Overrideprotected boolean isContextRequired() {return false;}// 这个就是吧Model里的数据  转换到 request parameters去~@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws IOException {// 构建目标URL,若以/开头并且contextRelative=true,那就自动会拼上getContextPath(request)前缀 否则不拼// encoding以自己set的为准,否则以request的为准,若都为null。那就取值:WebUtils.DEFAULT_CHARACTER_ENCODING// 2、从当前request里面拿到UriVariables,然后fill到新的url里面去~// 3、把当前request的url后的参数追加到新的url后面(默认是不会追加的~~~)  把propagateQueryParams属性值set为true就会追加了~~// 4、exposeModelAttributes默认值是true,会吧model里的参数都合理的拼接到URL后面去~~~(这步非常重要,处理逻辑也是较为复杂的)// 注意Bean的名字必须叫RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME  否则此处也不会执行的~~~String targetUrl = createTargetUrl(model, request);// 它主要是找Spring容器里是否有`RequestDataValueProcessor`的实现类,然后`processUrl`处理一下// 备注Spring环境默认没有它的实现,但是`Spring Security`对他是有实现的。比如大名鼎鼎的:`CsrfRequestDataValueProcessor`targetUrl = updateTargetUrl(targetUrl, model, request, response);// Save flash attributes// 此处因为request.getAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE)拿到的Map都是空的,所以此处也不会像里放了// FlashMap主要是用来解决`post/redrect/get`问题的,而现在都是ajax所以用得很少了~但Spring3.1之后提出了这个方案还是很优秀的RequestContextUtils.saveOutputFlashMap(targetUrl, request, response);// RedirectsendRedirect(request, response, targetUrl, this.http10Compatible);}protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,String targetUrl, boolean http10Compatible) throws IOException {// 这个isRemoteHost很有意思。若getHosts()为空,就直接返回false了// 然后看它是否有host,若没有host(相对路径)那就直接返回false// 若有host再看看这个host是否在我们自己的getHosts()里面,若在里面也返回fasle(表示还是内部的嘛)// 只有上面都没有return  就返回true// 比如此处值为:/demo_war_war/index.jspString encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeRedirectURL(targetUrl));// 这里是兼容Http1.0的做法   看一下即可~~~if (http10Compatible) {HttpStatus attributeStatusCode = (HttpStatus) request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE);if (this.statusCode != null) {response.setStatus(this.statusCode.value());response.setHeader("Location", encodedURL);}else if (attributeStatusCode != null) {response.setStatus(attributeStatusCode.value());response.setHeader("Location", encodedURL);}else {// Send status code 302 by default.// 大部分情况下我们都会走这里,所以我们看到的Http状态码都是302~~~~response.sendRedirect(encodedURL);}}// Http1.1else {// getHttp11StatusCode:若我们自己指定了status就以指定的为准// 否则看这里有没有:request.getAttribute(View.RESPONSE_STATUS_ATTRIBUTE)// 最后都没有,就是默认值HttpStatus.SEE_OTHER  303HttpStatus statusCode = getHttp11StatusCode(request, response, targetUrl);response.setStatus(statusCode.value());response.setHeader("Location", encodedURL);}}}

备注:若你方法只是:redirect:xxx这种形式,最终都会转换成一个RedirectView,所以不再去单独说明。参见:ViewNameMethodReturnValueHandler有这个转化过程

这样整个RedirectView就算是看完了。刚到这,就有小伙伴问:如何重定向到POST请求?
what还能这么玩?于是乎 我研究了一番:不可能

我在想为何为问出这样的问题呢?302属于浏览器客户端的行为,咋可能发POST请求呢?原来我百度了一下,是网上有不少误导性的文章,比如:
在这里插入图片描述
纠正:exposeModelAttributes属性表示是否吧model里的值拼接到URL后面,默认是true会拼接的。若你改成fasle,最多也就是不拼接而已,浏览器还是会给你发送一个GET请求的。

关于Spring MVC中的Flash Attribute,可参考文章:
Spring MVC Flash Attribute 的讲解与使用示例
但其实现在的ajax承担了很大一部分原来的工作,几乎没有post/redirect/get这种问题了~~~

提问:重定向传值普通值我们好解决,但如果是一个对象呢?比如User对象里面有很多属性?
方案一:序列化成json串传递
方案二:使用RedirectAttributes#addFlashAttribute + @ModelAttribute的方式(具体做法小伙伴们可议尝试尝试,其原理是基于FlashMapManagerFlashMap的)

提示一点,方案二默认是基于sesson的,所以分布式环境需谨慎使用。
其实像这种重定向还需要传大量数据的方案,一般本身就存在问题,建议遇上此问题的选手多思考,是否合理???



AbstractTemplateView

关于模版引擎渲染的抽象。它主要做两件事:

public abstract class AbstractTemplateView extends AbstractUrlBasedView {@Overrideprotected final void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {//1、exposeRequestAttributes,通过request.getAttributeNames()把请求域里面的attr都暴露出去//2、exposeSessionAttributes,session.getAttributeNames()把session域里面所有的attr都暴露出去//3、exposeSpringMacroHelpers,把RequestContext暴露出去(上两个默认值都是false,这个默认值是true)...renderMergedTemplateModel(model, request, response);}// 模版方法  各个模版自己去实现~~~protected abstract void renderMergedTemplateModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
FreeMarkerView

下面就以老牌模版引擎FreeMarker为例,窥探一下实现的思路:

public class FreeMarkerView extends AbstractTemplateView {// FreeMarker Configuration: "ISO-8859-1" if not specified otherwise@Nullableprivate String encoding;// FreeMarker的配置文件  里面极其多的配置信息~~比如文件后缀名、编码等@Nullableprivate Configuration configuration;@Nullableprivate TaglibFactory taglibFactory;@Nullableprivate ServletContextHashModel servletContextHashModel;// 就是检查这个模版存不存在~~~@Overridepublic boolean checkResource(Locale locale) throws Exception {String url = getUrl();Assert.state(url != null, "'url' not set");try {// Check that we can get the template, even if we might subsequently get it again.getTemplate(url, locale);return true;}catch (FileNotFoundException ex) {// Allow for ViewResolver chaining...return false;}catch (ParseException ex) {throw new ApplicationContextException("Failed to parse [" + url + "]", ex);}catch (IOException ex) {throw new ApplicationContextException("Failed to load [" + url + "]", ex);}}...// 最终会根据此模版去渲染~~~这是FreeMarker真正去做的事~~~~protected void processTemplate(Template template, SimpleHash model, HttpServletResponse response)throws IOException, TemplateException {template.process(model, response.getWriter());}}

此处我贴一个直接使用FreeMarker的使用案例,方便小伙伴对它的使用步骤有个感性的认识~~~

	@Testpublic void testFreeMarker() throws Exception{// 第0步,创建模板文件(自己找个目录创建,文件一般都以.ftl结尾)// 第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarker对于的版本号。Configuration configuration = new Configuration(Configuration.getVersion());// 第二步:设置模板文件所在的路径。configuration.setDirectoryForTemplateLoading(new File("D:\\workspace\\e3-item-web\\src\\main\\webapp\\WEB-INF\\ftl"));// 第三步:设置模板文件使用的字符集。一般就是utf-8.configuration.setDefaultEncoding("utf-8");// 第四步:加载一个模板,创建一个模板对象。Template template = configuration.getTemplate("hello.ftl");// 第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。Map data = new HashMap<>();//向数据集中添加数据data.put("hello", "this is my first freemarker test!");// 第六步:创建一个Writer对象,一般创建一FileWriter对象,指定生成的文件名。Writer out = new FileWriter(new File("D:\\Freemarker\\hello.txt"));// 第七步:调用模板对象的process方法输出文件,生成静态页面。template.process(data, out);// 第八步:关闭流。out.close();}
TilesView

XsltView

InternalResourceView:最重要的一个视图

Internal:内部的。所以该视图表示:内部资源视图。

// @since 17.02.2003  第一版就有了
public class InternalResourceView extends AbstractUrlBasedView {// 指定是否始终包含视图而不是转发到视图//默认值为“false”。打开此标志以强制使用servlet include,即使可以进行转发private boolean alwaysInclude = false;// 设置是否显式阻止分派回当前处理程序路径 表示是否组织循环转发,比如自己转发自己// 我个人认为这里默认值用true反而更好~~~因为需要递归的情况毕竟是极少数~// 其实可以看到InternalResourceViewResolver的buildView方法里是把这个属性显示的设置为true了的~~~private boolean preventDispatchLoop = false;public InternalResourceView(String url, boolean alwaysInclude) {super(url);this.alwaysInclude = alwaysInclude;}@Overrideprotected boolean isContextRequired() {return false;}// 请求包含、请求转发是它特有的~~~~~@Overrideprotected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {// Expose the model object as request attributes.// 把model里的数据都request.setAttribute里// 因为最终JSP里面取值其实都是从request等域对象里面取~exposeModelAsRequestAttributes(model, request);// Expose helpers as request attributes, if any.// JstlView有实现此protected方法~exposeHelpers(request);// Determine the path for the request dispatcher.String dispatcherPath = prepareForRendering(request, response);// Obtain a RequestDispatcher for the target resource (typically a JSP).  注意:此处特指JSP// 就是一句话:request.getRequestDispatcher(path)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.//useInclude:若alwaysInclude==true或者该request是incluse请求或者response.isCommitted()==true// 那就走incluse,否则走forward~~~~~if (useInclude(request, response)) {response.setContentType(getContentType());if (logger.isDebugEnabled()) {logger.debug("Including [" + getUrl() + "]");}rd.include(request, response);}else {// Note: The forwarded resource is supposed to determine the content type itself.if (logger.isDebugEnabled()) {logger.debug("Forwarding to [" + getUrl() + "]");}rd.forward(request, response);}}// 拿到URL,做一个循环检查~~~  若是循环转发就报错~~protected String prepareForRendering(HttpServletRequest request, HttpServletResponse response)throws Exception {String path = getUrl();Assert.state(path != null, "'url' not set");if (this.preventDispatchLoop) {String uri = request.getRequestURI();if (path.startsWith("/") ? uri.equals(path) : uri.equals(StringUtils.applyRelativePath(uri, path))) {throw new ServletException("Circular view path [" + path + "]: would dispatch back " +"to the current handler URL [" + uri + "] again. Check your ViewResolver setup! " +"(Hint: This may be the result of an unspecified view, due to default view name generation.)");}}return path;}
}

这样我们的InternalResourceView这个视图就渲染完成了,为何这么简单呢?因为它最终要么是include,要么forward掉了。交给别的Servlet去处理了。
而我们知道JSP的本质其实就是一个servlet,所以转发给它处理其实就是定位到了我们的JSP页面,它完成的对response写入动作。

比如:

    @GetMapping("/index")public Object index() {InternalResourceView view = new InternalResourceView();view.setUrl("/index.jsp");view.setPreventDispatchLoop(true);return view;}

注意:直接返回一个View是不会经过ViewResolver处理的

这样是能够正常展示出我们的jsp页面的。但是,但是,但是如果我们是一个html页面呢?比如如下:

    @GetMapping("/index")public Object index() {InternalResourceView view = new InternalResourceView();view.setUrl("/index.html");view.setPreventDispatchLoop(true);return view;}

访问直接报错:
在这里插入图片描述
原因很简单,因为你是HTML页面,所以它并没有对应的Servlet,所以你转发的时候肯定就报错了。所以接下里的问题变成了:
如何让我们的Controller跳转到HTML页面呢???其实这个涉及到Spring MVC中对静态资源的访问问题

说在前面:因为html属于静态数据,所以一般我们需要访问的话都是通过mvc:resources等这种配置去达到目的让可议直接访问。但是不乏业务中可能也存在通过controller方法跳转到html页面的需求(虽然你可以JSP里面全是html页面),本文就实现这个效果,能加深对此视图的了解~~

参考:【小家Spring】Spring MVC控制器中Handler的四种实现方式:Controller、HttpRequestHandler、Servlet、@RequestMapping
的最后半段来了解Spring MVC对静态资源的处理

JstlView

它继承自InternalResourceView,所以还是和JSP相关的。jstl相关的jar为:jstl.jar和standard.jar。它哥俩已经老久都没有更新过了,不过可以理解。毕竟JSP都快寿终正寝了。

它还可以和国际化有关,若使用Jstl的fmt标签,需要在SpringMVC的配置文件中配置国际化资源文件。

public class JstlView extends InternalResourceView {...public JstlView(String url, MessageSource messageSource) {this(url);this.messageSource = messageSource;}// 导出一些JSTL需要的东西@Overrideprotected void exposeHelpers(HttpServletRequest request) throws Exception {if (this.messageSource != null) {JstlUtils.exposeLocalizationContext(request, this.messageSource);}else {JstlUtils.exposeLocalizationContext(new RequestContext(request, getServletContext()));}}
}

因为JSTL技术比较古老了,现在很少人使用(当然JSP的使用人群还是有不少的,需要较重点的了解一下,毕竟是java嫡系技术,在历史进程中还是很重要的存在的),所以这里也不做演示了~

ScriptTemplateView

这个是脚本渲染引擎,从Spring4.2开始提供了一个ScriptTemplateView作为脚本模版视图。
脚本渲染引擎,据我目前了解,是为Kotlin而准备的,此处一个大写的:略

总结

视图就是展示给用户看的结果。可以是很多形式,例如:html、JSP、excel表单、Word文档、PDF文档、JSON数据、freemarker模板视图等等。

视图(解析器)作为Spring MVC设计中非常优秀的一环,最重要的是这种设计思想、作者的设计意图,值得我们深思和学习


关注A哥

AuthorA哥(YourBatman)
个人站点www.yourbatman.cn
E-mailyourbatman@qq.com
微 信fsx641385712
活跃平台
公众号BAT的乌托邦(ID:BAT-utopia)
知识星球BAT的乌托邦
每日文章推荐每日文章推荐

BAT的乌托邦

http://www.jmfq.cn/news/5293171.html

相关文章:

  • 自己做免费的网站/深圳排名seo公司
  • 嵊州做网站/今日特大新闻新事
  • 承包企业管理系统/企业网站seo排名
  • 住建城乡建设网站/互联网营销培训班
  • 网站远程数据库/如何做运营推广
  • wordpress入门建站/佛山外贸seo
  • 网站做区块链然后往里面投钱/百度地图在线查询
  • 永州做网站/广州网络营销选择
  • 京市保障性住房建设投资中心网站/盐城seo优化
  • 档案局网站的建设/seo值怎么提高
  • 网站租用服务器多少钱/爱链接
  • 珠海网站设计哪家好/百度账号申诉中心
  • 保定免费建站服务/怎么自己制作一个网站
  • 做网站会出现什么问题/想学销售去哪培训
  • 网站没排名的原因/百度公司的发展历程
  • 做网站什么公司/网站seo优化方法
  • 专门做宠物食品的网站/seo排名优化表格工具
  • 某网站开发项目成本估计/百度商家版下载
  • 自媒体主题wordpress/seozhun
  • 企业建站网站建站系统/软文写作技巧及范文
  • 男女做那种的视频网站/网站运营优化培训
  • 坐什么网站能用到html5/百度知道在线问答
  • 公司网站要什么做/百度一下搜索一下
  • 株洲第三方网站建设哪家好/天津网站快速排名提升
  • 建设公司网站需要什么/专业恶意点击软件
  • 高端网站建设公司联系电话/免费观看短视频的app软件推荐
  • 网络建设设计方案/小程序排名优化
  • 百度seo网站排名优化/刷钻业务推广网站
  • 帝国cms手机网站模板/西安网站关键词优化费用
  • 给别人做网站收8000贵不贵/seo关键词优化工具