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);
    }
}

相关文章
|
18天前
|
Java 数据库连接 开发者
Java中的异常处理机制深度解析
【8月更文挑战第13天】本文旨在深入探讨Java编程语言中一个至关重要的组成部分——异常处理机制。我们将从基本概念入手,逐步展开讨论异常处理在Java语言设计中的角色和重要性,以及如何正确利用这一机制来提高代码的健壮性和可维护性。文章将通过分析异常处理的最佳实践,揭示如何在复杂的应用程序中有效地管理和处理错误情况。
|
2月前
|
设计模式 监控 Java
解析Spring Cloud中的断路器模式原理
解析Spring Cloud中的断路器模式原理
|
2月前
|
安全 Java 数据安全/隐私保护
解析Spring Security中的权限控制策略
解析Spring Security中的权限控制策略
|
2天前
|
Java 微服务 Spring
Spring Cloud全解析:配置中心之解决configserver单点问题
但是如果该configserver挂掉了,那就无法获取最新的配置了,微服务就出现了configserver的单点问题,那么如何避免configserver单点呢?
|
15天前
|
XML Java 数据格式
Spring Cloud全解析:注册中心之zookeeper注册中心
使用ZooKeeper作为Spring Cloud的注册中心无需单独部署服务器,直接利用ZooKeeper服务端功能。项目通过`spring-cloud-starter-zookeeper-discovery`依赖实现服务注册与发现。配置文件指定连接地址,如`localhost:2181`。启动应用后,服务自动注册到ZooKeeper的`/services`路径下,形成临时节点,包含服务实例信息。
|
15天前
|
存储 SQL 关系型数据库
深入解析MySQL事务机制和锁机制
深入解析MySQL事务机制和锁机制
|
15天前
|
存储 缓存 NoSQL
深入解析Memcached:内部机制、存储结构及在大数据中的应用
深入解析Memcached:内部机制、存储结构及在大数据中的应用
|
23天前
|
Java 开发者
深入解析Java中的异常处理机制
在Java的世界中,异常处理是维护程序健壮性的基石之一。本文将通过实例演示和理论分析相结合的方式,探讨Java异常处理机制的工作原理及其最佳实践。我们将从异常的基本概念出发,逐步深入到异常类的层次结构、捕获异常的策略以及自定义异常的使用场景,旨在为读者提供一个全面而深入的视角来理解和应用Java中的异常处理。
15 2
|
2天前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
|
2月前
|
Java Spring 容器
Spring Boot 启动源码解析结合Spring Bean生命周期分析
Spring Boot 启动源码解析结合Spring Bean生命周期分析
67 11

推荐镜像

更多
下一篇
云函数