导出jxls所涉及到的技能点
- servlet规范
- http协议的了解
- view ViewResolver 接口理解
- oop
为了应对复杂的excel导出,比如跨行,跨列,合并单元格等复杂操作.再使用poi操作,会很麻烦,现集成jxls进行导出操作.
使用
新建一个@RequestMappping方法,并指定exportFileName
@RequestMapping("1")
public ModelAndView test(){
return new ModelAndView("jxls")
.addObject("good",2)
.addObject("title","一二三四")
.addObject("exportFileName","测试");
}
jxls导出使用模板view接口进行处理,之前的导出配置表头,数据等已经在模板中定义了,但是导出的文件名因为字符编码的问题,最好通过addObject("exportFileName","测试");
进行指定,如果不指定,取ModelAndView 的viewName 作为导出文件名(如果有路径,会进行处理) 比如上例的代码(没有最后一行exportFileName
key的添加) 导出的文件名将会是 jxls.xls
.
新建一个excel模板
A | B | |
---|---|---|
1 | ${title} | |
2 | ${good} | ${ss} |
在A1单元格添加批注jx:area(lastCell="D3")
设置单元格模板范围
浏览器访问
浏览器访问http(s)://ip:port/.../1.sp
.这样访问是会报错的,需要添加参数值export=jxls
.
浏览器提示下载文件 文件名是测试.xls
导出的文件查看
A | B | |
---|---|---|
1 | 一二三四 | |
2 | 2 |
这是模板替换以后生成的文件内容.因为在ModelAndView的model里没有提供变量ss
,所以会在控制台报错,这种错误不影响文件的生成,但应该避免.
实现原理
view & ViewResolver
这两接口干嘛的请自行百度或者查看最后一个参考链接
eap 系统 所有的 ViewResolver
ViewResolver | order | 说明 | SUFFIX | ! |
---|---|---|---|---|
EditorViewResolver | 1 | 开发模式 | .jsp.view | ?dev |
JxlsExcelViewResolver | 5 | jxls导出 | .xls | ?export=jxls |
QInternalResourceViewResolver | 10 | 默认的jsp | .jsp | |
QThymeleafViewResolver | Integer.MAX_VALUE | Thymeleaf html模板 | .html |
这些解析器是需要配置成bean 在应用启动spring mvc会自动加载他们 持有其引用
JxlsExcelViewResolver & JxlsExcelView
开始扒源码
DispatcherServlet.doService(req,res);
进入doService()
↓
doDispatch(request, response);
进入doDispatch()
↓
servlet3.1支持的异步 可以极大的提升应用的并发 但不能提升响应速度
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
↓
判断是否是文件上传请求
processedRequest = checkMultipart(request);
↓
确定 handler for the current request. -->待扒
mappedHandler = getHandler(processedRequest);
↓
查找该请求所对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
↓
判断是否是get请求 并根据Last-Modified做出响应(缓存的一种最基本策略)
↓
执行拦截器链上定义的所有PreHandle
mappedHandler.applyPreHandle(processedRequest, response)
↓
执行经过aop字节码增强的@controller下的@RequestMapping方法(自己写的controller),返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
↓
如果返回的modelAndView没有viewName 根据提供的策略生成一个
applyDefaultViewName(processedRequest, mv);
↓
执行拦截器链上定义的所有PostHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
↓
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
进入processDispatchResult()
↓
根据返回的modelAndView对象,req,res 渲染出对应的响应写入res
render(mv, request, response);
进入render()
↓
根据viewName 数据 国际化信息 确定到底用哪个view进行渲染
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
进入resolveViewName()
↓
根据上一小节的order排序 进行判断哪个视图解析器可以处理这个modelandview 如果可以处理返回一个view对象
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
返回上一级
↓
调用render方法完成真正的数据渲染,将流 flush close
view.render(mv.getModelInternal(), request, response);
返回上一级
↓
执行拦截器链上定义的所有AfterCompletion
mappedHandler.triggerAfterCompletion(request, response, null);
JxlsExcelViewResolver
JxlsExcelViewResolver
为了提高性能并不是直接实现的ViewResolver接口
而是继承的InternalResourceViewResolver
,InternalResourceViewResolver
继承了AbstractCachingViewResolver
提供了易用的缓存功能,提高了性能必须覆盖三个方法
@Override
protected View createView(String viewName, Locale locale) throws Exception {
if (!canHandle(viewName, locale)) {
return null;
}
return loadView(viewName, locale);
}
@Override
protected boolean canHandle(String viewName, Locale locale) {}
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
return new JxlsExcelView(viewName);
}
JxlsExcelView
JxlsExcelView
继承了AbstractUrlBasedView
覆盖它的renderMergedOutputModel
方法即可,也可以直接实现view接口实现render()
,看自己了,想怎么搞怎么搞.
方法里面的逻辑就是根据viewName 找到模板 由jxls组件将数据渲染到 excel file 将file 写入response流中 flush and close
就是javase的东西了