资讯专栏INFORMATION COLUMN

SpringMVC之源码分析--ViewResolver(四)

jcc / 2407人阅读

摘要:概述本章继续学习另一个实现类解析器,该类的主要作用是根据同一请求的某些策略,选择对应的进行渲染。可以把理解为适配器,对不同类型进行适配。值得注意的是处理的为同一个。本系列文章是基于。实战需求目标实现后缀名或参数控制,显示不同的视图。

概述

本章继续学习ViewResolver另一个实现类ContentNegotiatingViewResolver解析器,该类的主要作用是根据同一请求的某些策略,选择对应的View进行渲染。可以把ContentNegotiatingViewResolver理解为适配器,对不同类型View进行适配。值得注意的是处理的handler为同一个。

ContentNegotiatingViewResolver本身不解析视图,它将委托其他视图解析器进行视图解析。

请求的策略包括:请求后缀、请求头的Accept、使用参数等等。

本系列文章是基于Spring5.0.5RELEASE。

流程概述

使用此视图解析器时,调用resolverViewName(viewName,locale)方法,首先调用本类的getMediaType(reuqest)获取请求的媒体类型mediaType(根据策略),然后调用getCandidateViews()方法解析归并到View集合,最后调用getBestView()方法,根据contentType选择出合适的视图并返回。

源码分析

ContentNegotiatingViewResolver

该类主要完成

public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
    implements ViewResolver, Ordered, InitializingBean {

// 判断请求mediaType,内部包含使用的策略集合
@Nullable
private ContentNegotiationManager contentNegotiationManager;
// 用于创建ContentNegotiationManager实例
private final ContentNegotiationManagerFactoryBean cnmFactoryBean = new ContentNegotiationManagerFactoryBean();
// 控制为查找到时的处理
private boolean useNotAcceptableStatusCode = false;
// 存储View实例,可在此集合查询符合条件的View实例进行视图渲染
@Nullable
private List defaultViews;
// 视图解析器集合,用于解析视图
@Nullable
private List viewResolvers;
// 排序属性
private int order = Ordered.HIGHEST_PRECEDENCE;

... ...
/**
 *启动时从上下文中加载ViewResolver
 */
@Override
protected void initServletContext(ServletContext servletContext) {
    // 从上下文中获取所有视图解析器
    Collection matchingBeans =
            BeanFactoryUtils.beansOfTypeIncludingAncestors(obtainApplicationContext(), ViewResolver.class).values();
    if (this.viewResolvers == null) {
        // 将上下文配置的视图解析器添加到属性viewResolvers中,以供后续使用
        this.viewResolvers = new ArrayList<>(matchingBeans.size());
        for (ViewResolver viewResolver : matchingBeans) {
            if (this != viewResolver) {
                this.viewResolvers.add(viewResolver);
            }
        }
    }
    else {
        // 初始化viewResolvers属性中配置的视图解析器
        for (int i = 0; i < this.viewResolvers.size(); i++) {
            ViewResolver vr = this.viewResolvers.get(i);
            if (matchingBeans.contains(vr)) {
                continue;
            }
            String name = vr.getClass().getName() + i;
            obtainApplicationContext().getAutowireCapableBeanFactory().initializeBean(vr, name);
        }

    }
    if (this.viewResolvers.isEmpty()) {
        logger.warn("Did not find any ViewResolvers to delegate to; please configure them using the " +
                ""viewResolvers" property on the ContentNegotiatingViewResolver");
    }
    // 排序视图解析器
    AnnotationAwareOrderComparator.sort(this.viewResolvers);
    this.cnmFactoryBean.setServletContext(servletContext);
}

/*
 *启动时调用,如果没有配置ContentNegotiationManager,启动时进行创建初始化该属性
 */
@Override
public void afterPropertiesSet() {
    if (this.contentNegotiationManager == null) {
        this.contentNegotiationManager = this.cnmFactoryBean.build();
    }
}

/*
 *请求到来时匹配核实的View并返回
 */
@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
    RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
    Assert.state(attrs instanceof ServletRequestAttributes, "No current ServletRequestAttributes");
    // 获取请求的mediaType
    List requestedMediaTypes = getMediaTypes(((ServletRequestAttributes) attrs).getRequest());
    if (requestedMediaTypes != null) {
        // 解析出所有视图View和配置的默认View合并,供后面从中匹配选择
        List candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);
        // 根据contentType匹配选择出合适的View
        View bestView = getBestView(candidateViews, requestedMediaTypes, attrs);
        // 返回
        if (bestView != null) {
            return bestView;
        }
    }
    // 未匹配到合适的View,并且把参数useNotAcceptableStatusCode设置为true时,返回406
    if (this.useNotAcceptableStatusCode) {
        if (logger.isDebugEnabled()) {
            logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
        }
        return NOT_ACCEPTABLE_VIEW;
    }
    else {
        logger.debug("No acceptable view found; returning null");
        return null;
    }

    ... ...
}

以上是ContentNegotiatingViewResolver类的主要代码,具体调用的方法再此不再展开,有兴趣的童鞋可以自行查看,下面我们来使用这个解析器做个例子,通过例子再加深下理解。

实战

需求目标

实现后缀名或参数控制,显示不同的视图。如:

http://localhost:8088/user jsp视图显示

http://localhost:8088/user.json(http://localhost:8088/user?format=json) json视图显示

http://localhost:8088/user.xml(http://localhost:8088/user?format=xml) xml视图显示

项目结构

新建maven web项目,最终目录结构如下:

pom文件

通过maven引入jar依赖,代码如下:




    4.0.0

    com.github.dalianghe
    spring-mvc-viewresolver-03
    1.0-SNAPSHOT
    war

    spring-mvc-viewresolver-03 Maven Webapp
    
    http://www.example.com

    
        UTF-8
        1.8
        1.8
    

    
        
        
            org.springframework
            spring-webmvc
            5.0.5.RELEASE
        
        
        
            javax.servlet
            javax.servlet-api
            3.1.0
            provided
        
        
        
            javax.servlet
            jstl
            1.2
        
        
        
            com.fasterxml.jackson.core
            jackson-core
            2.9.5
        
        
            com.fasterxml.jackson.core
            jackson-databind
            2.9.5
        
        
        
            com.fasterxml.jackson.dataformat
            jackson-dataformat-xml
            2.9.5
        
    
    
        spring-mvc-viewresolver-03
        
            
                
                    org.apache.tomcat.maven
                    tomcat7-maven-plugin
                    2.2
                    
                        /
                        8088
                    
                
            
        
    

spring配置文件

配置视图解析器等,代码如下:




    
    

    
    
        
        
        
        
        
        
        
        
            
                
                
                
                
            
        
    

    
    
        

        
            
                
                    
                    
                    
                
            
        
        
        
            
                
                    
                    
                
                
                    
                
            
        
        
    


部署描述文件

配置DispatcherServlet,代码如下:



  Archetype Created Web Application

  
    
    dispatcher
    
    org.springframework.web.servlet.DispatcherServlet
    
    
      contextConfigLocation
      classpath:spring-servlet.xml
    
    
    1
    true
  
  
  
    
    dispatcher
    
    /
  


User实体

简单的实体类,代码如下:

public class User{

    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

handler处理器

编写Controller,代码如下:

import com.github.dalianghe.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class DemoController {
    @GetMapping("/user")
    public String demo(ModelMap model){

        User user = new User("hedaliang", "123456");
        model.addAttribute(user);
        return "user";
    }
}

jsp页面

jsp视图(user.jsp),代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    My Frist JSP


username:${user.username}


password:${user.password}

以上代码编写结束,下面来进行测试。

测试

启动应用,访问地址:http://localhost:8080/user,此时应使用jsp进行渲染,结果如下:

访问http://locahost:8088/user.json或http://localhost:8088/user?format=json,结果如下:

访问http://localhost:8088/user.xml或http://localhost:8088/user?format=xml,结果如下:

OK!跟预期一致,测试通过,至此我们就实现了需求功能。

总结

本章介绍了ContentNegotiatingViewResolver类,并通过开发小demo验证了此类的功能,里面细节很多,有兴趣的朋友可以再深入了解,希望本文能给大家一写启发。

最后创建了qq群方便大家交流,可扫描加入,同时也可加我qq:276420284,共同学习、共同进步,谢谢!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69659.html

相关文章

  • SpringMVC源码分析--ViewResolver(三)

    摘要:概述本节学习下的功能,简单来说,该类的作用就是把多个视图解析器进行组装,内部使用存储配置使用的视图解析器。总结本章介绍了类,根据测试,了解到属性不影响中配置使用的视图解析器顺序。 概述 本节学习下ViewResolverComposite的功能,简单来说,该类的作用就是把多个ViewResolver视图解析器进行组装,内部使用list存储配置使用的视图解析器。 本系列文章是基于Spri...

    fox_soyoung 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(二)

    摘要:概述上篇学习了视图解析器作用及处理流程,为我们提供了丰富的视图解析器见下图本系列文章是基于。该视图解析器是根据处理器返回的逻辑视图名称,在应用上下文中查找该名称的视图对象视图对象就是的对象。 概述 上篇学习了Spring MVC ViewResolver视图解析器作用及处理流程,Spring为我们提供了丰富的视图解析器(见下图):showImg(https://segmentfault...

    jas0n 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(一)

    摘要:概述本章开始进入另一重要的组件,即视图组件,处理视图组件使用两个主要的接口是和。接口的作用是用于处理视图进行渲染。延用之前的介绍流程,本章分两部分进行阐述启动初始化和请求处理。 概述 本章开始进入另一重要的组件,即视图组件,Spring MVC处理视图组件使用两个主要的接口是ViewResolver和View。根据名称可知,ViewResolver即视图解析器,其作用是把逻辑视图名称解...

    pf_miles 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(五)

    摘要:此解析器与差不多,更改下配置文件中的类全路径即可。总结本章介绍了以及三个视图解析器。这部分内容有点儿多,我会尽快结束。 概述 通过上几篇的学习,我们分析了并试验了ViewResolverComposite、BeanNameViewResolver和ContentNegotiatingViewResolver,这三个类都直接实现ViewResolver接口。Spring MVC提供了很多...

    klinson 评论0 收藏0
  • SpringMVC源码分析--ViewResolver(六)

    摘要:与一样,该类继承抽象类,并且通过外部的属性文件定义逻辑视图名称与真正的视图对象的关系,属性文件默认是下的,可以通过或属性来指定,该属性指的是文件的基名称,也就是说以属性值开头的属性文件。 概述 本章再学习另外两个ViewResolver,分别是XmlViewResolver和ResourceBundleViewResolver,从功能上说,这两个视图解析器都是从外部资源文件中查找视图V...

    alighters 评论0 收藏0

发表评论

0条评论

jcc

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<