最近項目中遇到需要excle導出的需求,百度搜索關鍵詞,excle和spring mvc出來的結果都是代碼及其長,並且還是直接設置response的原始方法
不得不說國內的程序員大都只追求能夠勉強實現功能,很少想到優雅,我在bing上搜索(沒有vpn哭)同樣關鍵詞,結果至少是返回modelandview對象,但是需要自己實現視圖的基礎上,還要實現視圖解析器
話不多說,上正菜:
本文使用注解和自定義視圖的方式實現excle導出功能,不需要實現自定義視圖解析器,只要用默認視圖解析器InternalResourceViewResolver就可以,
現在網上還沒有看到使用注解來簡化excle導出的文章,本文算是首創,是我目前水平下想到的最好的實現方式,希望大家可以多多指教
本文是在使用Spring MVC和Poi的前提
- 先定義注解,大部分注釋內容我直接放在代碼中
1 @Target(ElementType.FIELD)//標注用來注解field類型 2 @Retention(RetentionPolicy.RUNTIME)//標注運行時注解 3 public @interface Export { 4 //不設置default表示必填 5 String title();//title表示導出時的標題 6 int index();//inde表示導出時改字段的列號 7 8 Class format() default String.class;//默認使用String.class只是標注,自定義的不可能是String.class 9 }
其中 :title表示導出時的標題,inde表示導出時改字段的列號,format用於格式化一些字段,由於是非必填項,所以要指定默認值,我這里指定了String.class。
2.自定義一個需要導出的model類,也就是bean類,在需要導出的字段上加入注解
public class Company extends BaseModel{ @Export(title = "企業名稱",index = 1) private String name; @Export(title = "統一社會信用代碼",index = 2) private String uniformSocialCreditCode; //1:規模以上;0:規模以下 @Export(title = "規模",index = 3,format = Scale.class) private Byte scale; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUniformSocialCreditCode() { return uniformSocialCreditCode; } public void setUniformSocialCreditCode(String uniformSocialCreditCode) { this.uniformSocialCreditCode = uniformSocialCreditCode; } public Byte getScale() { return scale; } public void setScale(Byte scale) { this.scale = scale; } }
解析注解需要用到反射
//生成標題列,生成內容列是一樣的,只是多出一個解析字段值
private void generateHead(Field[] fields) { currentSheetHeadRow = currentSheet.createRow(0); //這里我學習lambda表達式,讀者也可以直接用for循環 Arrays.stream(fields)
.filter(field -> field.isAnnotationPresent(Export.class))//找出有Export注解的field,也就是需要導出的字段
.forEach((field) -> { String title = field.getAnnotation(Export.class).title(); int index = field.getAnnotation(Export.class).index(); createHeadTitle(title, index); }); } //指定的index位置加入標題 private void createHeadTitle(String name, int index) { Cell c1 = currentSheetHeadRow.createCell(index); c1.setCellValue(name); }
3.我的例子中有一個需要規模字段,它是比較典型的一類,就是導出的時候與實際的值並不相同,需要轉換,此時定義一個枚舉類最為合適,這里只是供大家參考
我的Scale枚舉類,用來解析字段值,
public enum Scale { ABOVE("規模以上",1),BLOW("規模以下",0),NULL("",-1); private String name; private Integer index; Scale(String name, Integer index) { this.name = name; this.index = index; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getIndex() { return index; } public void setIndex(Integer index) { this.index = index; } }
此時導出功能的基本的注解部分已經實現
接下來自定義一個exle的視圖,到時候在Controller中返回modelAndView的時候加入這個視圖類即可
/**
*AbstractXlsxView是Spring內置的類是2007版之后的excle版本
*如果你想要2003版本,這里只需要繼承AbstractXlsView即可,其他不需要任何改動
*/
public class ExcleView extends AbstractXlsxView{
//導出文件名 private String fileName; public ExcleView(String excleName){ super(); this.fileName = excleName; } @Override protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { List<BaseModel> list = (List<BaseModel>)model.get("list"); Sheet currentSheet = workbook.createSheet(); for (BaseModel baseModel : list) { //....省略具體代碼
//可以循環的添加cell } String name = URLEncoder.encode(fileName+".xlsx", "UTF-8"); response.setHeader("Content-Disposition", "attachment; filename="+name); } }
Controller中的方法示例
public ModelAndView exportExcel(@ModelAttribute CompanyBasic model) throws IOException{ List<Company> list = new ArrayList<>(); //示例創建一個list列表 for (int i = 0; i <3 ; i++) { Company company = new Company(); company.setName("a"); company.setUniformSocialCreditCode("124521"); company.setScale((byte)1); list.add(company); }
//將list放入Map類型的對象中,即可 Map<String, Object> map = new HashMap<>(); map.put("list",list); return new ModelAndView(new ExcleView("企業列表"),map); }
只要返回的時候 return new ModelAndView(new ExcleView("企業列表"),map); 其中企業列表是導出的文件名
自此,以后再有其他需要導出的類,只要在對應的字段加上titie和index注解,在對應Controller中,查詢中list,再new ExcleVIew(filename)的時候傳入文件名即可
大功告成