ContentNegotiation内容协商机制(三)---在视图View上的应用:ContentNegotiatingViewResolver深度解析【享学Spring MVC】(中)

简介: ContentNegotiation内容协商机制(三)---在视图View上的应用:ContentNegotiatingViewResolver深度解析【享学Spring MVC】(中)

如何使用?


我们已经知道,默认情况下Spring MVC可没有使用此内容协商视图解析器,因此若有同一资源,多视图展示的需求,我们是需要手动配置(开启)支持的。


通过检索可以看到ViewResolverRegistry它为我们提供了便捷使用的方式:


当然你也可以通过单独配置一个ContentNegotiatingViewResolver @Bean的方式来做,原理也很简单很好解释。本文我就给个最佳实践作为参考示例


public class ViewResolverRegistry {
  ...
  public void enableContentNegotiation(View... defaultViews) {
    initContentNegotiatingViewResolver(defaultViews);
  }
  public void enableContentNegotiation(boolean useNotAcceptableStatus, View... defaultViews) {
    ContentNegotiatingViewResolver vr = initContentNegotiatingViewResolver(defaultViews);
    vr.setUseNotAcceptableStatusCode(useNotAcceptableStatus);
  }
  // 初始化一个内容协商视图解析器
  private ContentNegotiatingViewResolver initContentNegotiatingViewResolver(View[] defaultViews) {
    // ContentNegotiatingResolver in the registry: elevate its precedence!
    // 请保证它是最高优先级的:在所有视图解析器之前执行
    // 这样即使你配置了其它的视图解析器  也会先执行这个(后面的被短路掉)
    this.order = (this.order != null ? this.order : Ordered.HIGHEST_PRECEDENCE);
    // 调用者自己已经配置好了一个contentNegotiatingResolver,那就用他的
    if (this.contentNegotiatingResolver != null) {
      // 若存在defaultViews,那就处理一下把它放进contentNegotiatingResolver里面
      if (!ObjectUtils.isEmpty(defaultViews) && !CollectionUtils.isEmpty(this.contentNegotiatingResolver.getDefaultViews())) {
        List<View> views = new ArrayList<>(this.contentNegotiatingResolver.getDefaultViews());
        views.addAll(Arrays.asList(defaultViews));
        this.contentNegotiatingResolver.setDefaultViews(views);
      }
    } else { // 若没配置就自己new一个 并且设置好viewResolvers
      this.contentNegotiatingResolver = new ContentNegotiatingViewResolver();
      this.contentNegotiatingResolver.setDefaultViews(Arrays.asList(defaultViews));
      // 注意:这个viewResolvers是通过此ViewResolverRegistry配置进来的
      // 若仅仅是容器内的Bean,这里可捕获不到。所以如果你有特殊需求建议你自己set
      // 若仅仅是jsp()/tiles()/freeMarker()/groovy()/beanName()这些,内置的支持即可满足要求儿聊
      // ViewResolverRegistry.viewResolver()可调用多次,因此可以多次指定  若有需要个性化,可以调用此方法
      this.contentNegotiatingResolver.setViewResolvers(this.viewResolvers);
      if (this.contentNegotiationManager != null) {
        this.contentNegotiatingResolver.setContentNegotiationManager(this.contentNegotiationManager);
      }
    }
    return this.contentNegotiatingResolver;
  }
}


说明一点:虽然这里有些视图解析器是new出来的,但不用担心最后都会执行InitializingBean、ApplicationContextAware…等等的一些接口方法的。因为这些都是交给ViewResolverComposite统一代劳的~(因此并不需要放进Spring容器里亦可,减少容器的负担也是一种优化)


上面"复习"的时候提到了,Spring MVC准备好ViewResolverRegistry后会回调我们,因此实际使用中可以通过此入口进行配置(最佳实践):

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(); // 开启内容协商视图解析器
    }
}


在我准备介绍案例时,为了便于对小伙伴对整个内容协商流程的把控和理解,我提供如下这张执行原理流程图作为辅助理解(若图有错误可留言指出,多谢):

image.png


使用示例


是骡子是马,总归还是要拉出来溜溜。下面我用一个工作中非常具象的案例,来演示一下它的用法。


需求:同一个RESTful的URL,我希望得到一个PDF视图、JSON视图、Html视图???

实现代码


因为是同一个URL,并且还要求是有不同视图的,因此这里用ContentNegotiatingViewResolver来做内容协商就非常得心应手了。


1、准备针对于处理这三种视图的ViewResolver实现类:

    // 自定义三个视图分别用于处理对应的视图需求
    private final ViewResolver pdf_viewresolver= (viewName, locale) -> new View() {
        @Override
        public String getContentType() {
            return MediaType.APPLICATION_PDF_VALUE;
        }
        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getWriter().write("<html><body style='color:red'>this is pdf view</body></html>");
        }
    };
    private final ViewResolver excel_viewresolver= (viewName, locale) -> new View() {
        @Override
        public String getContentType() {
            return MediaType.APPLICATION_JSON_UTF8_VALUE;
        }
        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getWriter().write("<html><body style='color:yellow'>this is json view</body></html>");
        }
    };
    private final ViewResolver html_viewresolver= (viewName, locale) -> new View() {
        @Override
        public String getContentType() {
            return MediaType.TEXT_HTML_VALUE;
        }
        @Override
        public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
            response.getWriter().write("<html><body style='color:green'>this is html view</body></html>");
        }
    };


请注意:三者的getContentType()、渲染内容、颜色都是不一样的


说明:因为此处我只是模拟,所以我全部以匿名类来实现,各位小伙伴理解起来理论上应该都没有啥障碍吧(有问题可给我留言~)


2、开启Spring MVC在视图上对ContentNegotiation内容协商的支持:


@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.viewResolver(pdf_viewresolver);
        registry.viewResolver(excel_viewresolver);
        registry.viewResolver(html_viewresolver);
        // 上面三个注册方法必须在此方法之上执行
        registry.enableContentNegotiation(false);
    }
}

相关文章
|
4天前
|
存储 缓存 安全
第二章 HTTP请求方法、状态码详解与缓存机制解析
第二章 HTTP请求方法、状态码详解与缓存机制解析
|
5天前
|
前端开发 Java 测试技术
Java一分钟之Spring MVC:构建Web应用
【5月更文挑战第15天】Spring MVC是Spring框架的Web应用模块,基于MVC模式实现业务、数据和UI解耦。常见问题包括:配置DispatcherServlet、Controller映射错误、视图解析未设置、Model数据传递遗漏、异常处理未配置、依赖注入缺失和忽视单元测试。解决这些问题可提升代码质量和应用性能。注意配置`web.xml`、`@RequestMapping`、`ViewResolver`、`Model`、`@ExceptionHandler`、`@Autowired`,并编写测试用例。
289 3
|
5天前
|
设计模式 存储 前端开发
MVC(模型-视图-控制器)是一种在Web应用程序开发中广泛使用的软件设计模式
【5月更文挑战第12天】MVC模式是Web应用开发中的常见设计模式,将逻辑、数据和界面分离,提升代码可维护性和重用性。模型处理数据逻辑,视图展示数据,控制器协调用户输入与模型视图交互。优点包括代码分离、易维护、可扩展和组件重用,促进高效灵活的开发。
9 2
|
5天前
|
存储 机器学习/深度学习 搜索推荐
深入解析矢量数据库的数据模型与索引机制
【4月更文挑战第30天】本文深入探讨了矢量数据库的数据模型和索引机制。向量数据库以高维向量表示数据,采用稀疏或密集向量形式,并通过数据编码和组织优化存储与检索。索引机制包括基于树的(如KD-Tree和Ball Tree)、基于哈希的(LSH)和近似方法(PQ),加速相似性搜索。理解这些原理有助于利用矢量数据库处理大规模高维数据,应用于推荐系统、图像搜索等领域。随着技术发展,矢量数据库将扮演更重要角色。
|
2天前
|
存储 Java 程序员
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
【5月更文挑战第18天】Python内存管理关乎程序性能与稳定性,包括变量存储和垃圾回收。变量存储时,如`x = 10`,`x`指向内存中值的引用。垃圾回收通过引用计数自动回收无引用对象,防止内存泄漏。了解此机制可优化内存使用,避免循环引用等问题,提升程序效率和稳定性。深入学习内存管理对成为优秀Python程序员至关重要。
【Python 的内存管理机制专栏】深入解析 Python 的内存管理机制:从变量到垃圾回收
|
3天前
|
iOS开发 Python
mac:python安装路径,带你全面解析Python框架体系架构view篇
mac:python安装路径,带你全面解析Python框架体系架构view篇
|
4天前
|
前端开发 JavaScript 开发者
深入理解MVC和MVVM:构建现代Web应用的利器
深入理解MVC和MVVM:构建现代Web应用的利器
|
5天前
|
PHP 开发者
深入解析PHP的命名空间与自动加载机制
【4月更文挑战第30天】 在现代PHP开发实践中,命名空间和自动加载机制是模块化和代码复用的关键。本文旨在提供一个全面的视角来理解这两个概念如何协同工作以优化项目结构。我们将探讨命名空间解决代码冲突的方式,以及自动加载机制如何智能地按需加载类,从而减少内存占用和提升性能。
|
5天前
|
算法 安全 Linux
深度解析:Linux内核内存管理机制
【4月更文挑战第30天】 在操作系统领域,内存管理是核心功能之一,尤其对于多任务操作系统来说更是如此。本文将深入探讨Linux操作系统的内核内存管理机制,包括物理内存的分配与回收、虚拟内存的映射以及页面替换算法等关键技术。通过对这些技术的详细剖析,我们不仅能够理解操作系统如何高效地利用有限的硬件资源,还能领会到系统设计中的性能与复杂度之间的权衡。
|
5天前
|
缓存 Java Python
Python 弱引用全解析:深入探讨对象引用机制!
Python 弱引用全解析:深入探讨对象引用机制!
22 3

推荐镜像

更多