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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 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);
    }
}

相关文章
|
5天前
|
搜索推荐 Java Spring
Spring Filter深度解析
【10月更文挑战第21天】Spring Filter 是 Spring 框架中非常重要的一部分,它为请求处理提供了灵活的控制和扩展机制。通过合理配置和使用 Filter,可以实现各种个性化的功能,提升应用的安全性、可靠性和性能。还可以结合具体的代码示例和实际应用案例,进一步深入探讨 Spring Filter 的具体应用和优化技巧,使对它的理解更加全面和深入。
|
18天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
18天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
23天前
|
人工智能 缓存 Java
深入解析Spring AI框架:在Java应用中实现智能化交互的关键
【10月更文挑战第12天】Spring AI 是 Spring 框架家族的新成员,旨在满足 Java 应用程序对人工智能集成的需求。它支持自然语言处理、图像识别等多种 AI 技术,并提供与云服务(如 OpenAI、Azure Cognitive Services)及本地模型的无缝集成。通过简单的配置和编码,开发者可轻松实现 AI 功能,同时应对模型切换、数据安全及性能优化等挑战。
|
14天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
42 0
|
19天前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
34 0
|
19天前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
60 0
|
19天前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
39 0
|
19天前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
29 0
|
19天前
|
存储 Java C++
Collection-PriorityQueue源码解析
Collection-PriorityQueue源码解析
33 0

推荐镜像

更多