有这样子的需求,我想通过同一个url来返回不同的视图,可以通过url的后缀,或者直接给个参数,然后服务器就帮我返回不同的视图,例如
/task/taskList : 返回jsp页面
/task/taskList.json : 返回json数据
/task/taskList.xml : 返回xml数据
或者这样子
/task/taskList : 返回jsp页面
/task/taskList?format = json : 返回json数据
/task/taskList?format =xml : 返回xml数据
在方法级别上注解@Response可以根据请求信息头部的accept为application/xml或者application/json可以帮我实现这种需求,但是这种方式有缺点
1.需要显示设置accept,麻烦
2.很难通过指定url的后缀或者给个参数就直接返回不同视图
基于这样子的需求,springmvc给我们提供了ContentNegotiatingViewResolver,内容协商视图解析器,我们先来配置一下,然后再讲原理
<!--配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
<property name="viewClass" value = "org.springframework.web.servlet.view.JstlView"></property>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
<bean
class="org.springframework.web.servlet.view.xml.MappingJackson2XmlView" />
</list>
</property>
</bean>
注意不要把annotation-driven中的path-matching的suffix-pattern和trailing-slash设置false,例如下面,不然/task/taskList,/task/taskList.json,/task/taskList.xml不能映射到同一个方法中
<mvc:annotation-driven
content-negotiation-manager="contentNegotiationManager">
<mvc:path-matching
suffix-pattern="false"
trailing-slash="false"
/>
</mvc:annotation-driven>
ContentNegotiatingViewResolver的作用其实做为一个代理人,根据上下文信息选择一个合适的解析去负责解析视图,所以在所有的解析器中ContentNegotiatingViewResolver的优先级是最高的,居然他是一个代理,我们就需要专门配置视图解析器,在这里我们配置了,BeanNameViewResolver解析器还有InternalResourceViewResolver解析器
...
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
<property name="viewClass" value = "org.springframework.web.servlet.view.JstlView"></property>
</bean>
</list>
</property>
...
ContentNegotiatingViewResolver解析视图的步骤方法
1.根据请求的头部信息即accpet还有url后缀来获取客户端浏览器所需要的媒体类型
2.再根据所注册的视图解析器,BeanNameViewResolver和InternalResourceViewResolver解析出方法体返回值的视图,如果你还注册了默认的视图,例如我们这里还注册了MappingJackson2JsonView以及MappingJackson2XmlView,然后返回视图列表。
3.根据请求的媒体类型还有返回的视图列表,根据媒体类型匹配遍历获取最佳的视图,然后返回给客户端
ContentNegotiatingViewResolver可以配置属性ContentNegotiationManager,ContentNegotiationManager可以由ContentNegotiationManagerFactoryBean来具体实现
public class ContentNegotiationManagerFactoryBean
implements FactoryBean<ContentNegotiationManager>, ServletContextAware, InitializingBean {
private boolean favorPathExtension = true; //是否支持url后缀
private boolean favorParameter = false;//是否支持参数
private boolean ignoreAcceptHeader = false;
private Map<String, MediaType> mediaTypes = new HashMap<String, MediaType>(); //注册映射的媒体类型
private boolean ignoreUnknownPathExtensions = true;
private Boolean useJaf;
private String parameterName = "format";//可以通过参数来指定返回的视图,参数名默认是format
private ContentNegotiationStrategy defaultNegotiationStrategy; //默认的媒体类型解析策略
private ContentNegotiationManager contentNegotiationManager;
private ServletContext servletContext;
....
这里如果我还想指定,excel的媒体类型,我们可以配置下ContentNegotiationManager
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
html=text/html
excel=application/vnd.ms-excel <!--excel媒体类型-->
</value>
</property>
</bean>
然后把contentNegotiationManager注册到ContentNegotiatingViewResolver中
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
**<property name="contentNegotiationManager" ref="contentNegotiationManager" />**
<property name="viewResolvers">
<list>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
<property name="viewClass" value = "org.springframework.web.servlet.view.JstlView"></property>
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />
<bean
class="org.springframework.web.servlet.view.xml.MappingJackson2XmlView" />
</list>
</property>
</bean>
然后我们把定义个controller就可以通过指定后缀来生成excel,xml或者json视图了
@RequestMapping("/mvcTest10")
public String mvcTest10(ModelMap modelMap) {
List<Task> tasks = new ArrayList<Task>();
Task t = new Task();
t.setCreateTime(new Date());
t.setTaskDetail("taskDetail");
t.setTaskName("taskName");
Task t1 = new Task();
t1.setCreateTime(new Date());
t1.setTaskDetail("taskDetail");
t1.setTaskName("taskName");
tasks.add(t1);
tasks.add(t);
modelMap.addAttribute("tasks",tasks);
//return "task/task";
return "taskListExcelView";
}
taskListExcelView的视图需要自己定义
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.view.document.AbstractExcelView;
import com.liuxg.task.entity.Task;
/**
* excel视图
* @author liuxg
* @date 2016年4月9日 下午4:08:10
*/
@Component("taskListExcelView")
public class TaskListExcelView extends AbstractExcelView {
@SuppressWarnings("unchecked")
@Override
protected void buildExcelDocument(Map<String, Object> model, HSSFWorkbook workbook, HttpServletRequest request,
HttpServletResponse response) throws Exception {
List<Task> tasks = (List<Task>) model.get("tasks");
response.setHeader("Content-disposition", "inline;filename="+new String("任务列表".getBytes() , "iso8859-1") + ".xls");
HSSFSheet sheet = workbook.createSheet("tasks");
HSSFRow header = sheet.createRow(0);
header.createCell(0).setCellValue("任务名");
header.createCell(1).setCellValue("任务详细");
header.createCell(2).setCellValue("创建时间");
int rowNum = 1 ;
for(Task task : tasks){
HSSFRow row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(task.getTaskName());
row.createCell(1).setCellValue(task.getTaskDetail());
row.createCell(2).setCellValue(task.getCreateTime());
}
}
}
然后我们在客户端输入
http://localhost:8086/whats/task/mvcTest10.json返回json数据
http://localhost:8086/whats/task/mvcTest10.xml返回xml数据
http://localhost:8086/whats/task/mvcTest10.excel下载excel