1. 程式人生 > >Jeesite開發,Excel匯入匯出的功能

Jeesite開發,Excel匯入匯出的功能

一. 匯入匯出的公共工具:

  1. /** 
  2.  * Copyright &copy; 2012-2013 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved. 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  */
  6. package com.cyou.seal.common.utils.excel.annotation;  
  7. import java.lang.annotation.ElementType;  
  8. import java.lang.annotation.Retention;  
  9. import java.lang.annotation.RetentionPolicy;  
  10. import java.lang.annotation.Target;  
  11. /** 
  12.  * Excel註解定義 
  13.  * @author ThinkGem 
  14.  * @version 2013-03-10 
  15.  */
  16. @Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})  
  17. @Retention(RetentionPolicy.RUNTIME)  
  18. public@interface
     ExcelField {  
  19.     /** 
  20.      * 匯出欄位名(預設呼叫當前欄位的“get”方法,如指定匯出欄位為物件,請填寫“物件名.物件屬性”,例:“area.name”、“office.name”) 
  21.      */
  22.     String value() default"";  
  23.     /** 
  24.      * 匯出欄位標題(需要新增批註請用“**”分隔,標題**批註,僅對匯出模板有效) 
  25.      */
  26.     String title();  
  27.     /** 
  28.      * 欄位型別(0:匯出匯入;1:僅匯出;2:僅匯入) 
  29.      */
  30.     int type() default0;  
  31.     /** 
  32.      * 匯出欄位對齊方式(0:自動;1:靠左;2:居中;3:靠右)
     
  33.      *  
  34.      * 備註:Integer/Long型別設定居右對齊(align=3) 
  35.      */
  36.     int align() default0;  
  37.     /** 
  38.      * 匯出欄位欄位排序(升序) 
  39.      */
  40.     int sort() default0;  
  41.     /** 
  42.      * 如果是字典型別,請設定字典的type值 
  43.      */
  44.     String dictType() default"";  
  45.     /** 
  46.      * 反射型別 
  47.      */
  48.     Class<?> fieldType() default Class.class;  
  49.     /** 
  50.      * 欄位歸屬組(根據分組匯出匯入) 
  51.      */
  52.     int[] groups() default {};  
  53. }  

反射工具類:

  1. /** 
  2.  * 反射工具類. 
  3.  * 提供呼叫getter/setter方法, 訪問私有變數, 呼叫私有方法, 獲取泛型型別Class, 被AOP過的真實類等工具函式. 
  4.  * @author calvin 
  5.  * @version 2013-01-15 
  6.  */
  7. @SuppressWarnings("rawtypes")  
  8. publicclass Reflections {  
  9.     privatestaticfinal String SETTER_PREFIX = "set";  
  10.     privatestaticfinal String GETTER_PREFIX = "get";  
  11.     privatestaticfinal String CGLIB_CLASS_SEPARATOR = "$$";  
  12.     privatestatic Logger logger = LoggerFactory.getLogger(Reflections.class);  
  13.     /** 
  14.      * 呼叫Getter方法. 
  15.      * 支援多級,如:物件名.物件名.方法 
  16.      */
  17.     publicstatic Object invokeGetter(Object obj, String propertyName) {  
  18.         Object object = obj;  
  19.         for (String name : StringUtils.split(propertyName, ".")){  
  20.             String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);  
  21.             object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});  
  22.         }  
  23.         return object;  
  24.     }  
  25.     /** 
  26.      * 呼叫Setter方法, 僅匹配方法名。 
  27.      * 支援多級,如:物件名.物件名.方法 
  28.      */
  29.     publicstaticvoid invokeSetter(Object obj, String propertyName, Object value) {  
  30.         Object object = obj;  
  31.         String[] names = StringUtils.split(propertyName, ".");  
  32.         for (int i=0; i<names.length; i++){  
  33.             if(i<names.length-1){  
  34.                 String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);  
  35.                 object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {});  
  36.             }else{  
  37.                 String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);  
  38.                 invokeMethodByName(object, setterMethodName, new Object[] { value });  
  39.             }  
  40.         }  
  41.     }  
  42.     /** 
  43.      * 直接讀取物件屬性值, 無視private/protected修飾符, 不經過getter函式. 
  44.      */
  45.     publicstatic Object getFieldValue(final Object obj, final String fieldName) {  
  46.         Field field = getAccessibleField(obj, fieldName);  
  47.         if (field == null) {  
  48.             thrownew IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");  
  49.         }  
  50.         Object result = null;  
  51.         try {  
  52.             result = field.get(obj);  
  53.         } catch (IllegalAccessException e) {  
  54.             logger.error("不可能丟擲的異常{}", e.getMessage());  
  55.         }  
  56.         return result;  
  57.     }  
  58.     /** 
  59.      * 直接設定物件屬性值, 無視private/protected修飾符, 不經過setter函式. 
  60.      */
  61.     publicstaticvoid setFieldValue(final Object obj, final String fieldName, final Object value) {  
  62.         Field field = getAccessibleField(obj, fieldName);  
  63.         if (field == null) {  
  64.             thrownew IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");  
  65.         }  
  66.         try {  
  67.             field.set(obj, value);  
  68.         } catch (IllegalAccessException e) {  
  69.             logger.error("不可能丟擲的異常:{}", e.getMessage());  
  70.         }  
  71.     }  
  72.     /** 
  73.      * 直接呼叫物件方法, 無視private/protected修飾符. 
  74.      * 用於一次性呼叫的情況,否則應使用getAccessibleMethod()函式獲得Method後反覆呼叫. 
  75.      * 同時匹配方法名+引數型別, 
  76.      */
  77.     publicstatic Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes,  
  78.             final Object[] args) {  
  79.         Method method = getAccessibleMethod(obj, methodName, parameterTypes);  
  80.         if (method == null) {  
  81.             thrownew IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");  
  82.         }  
  83.         try {  
  84.             return method.invoke(obj, args);  
  85.         } catch (Exception e) {  
  86.             throw convertReflectionExceptionToUnchecked(e);  
  87.         }  
  88.     }  
  89.     /** 
  90.      * 直接呼叫物件方法, 無視private/protected修飾符, 
  91.      * 用於一次性呼叫的情況,否則應使用getAccessibleMethodByName()函式獲得Method後反覆呼叫. 
  92.      * 只匹配函式名,如果有多個同名函式呼叫第一個。 
  93.      */
  94.     publicstatic Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {  
  95.         Method method = getAccessibleMethodByName(obj, methodName);  
  96.         if (method == null) {  
  97.             thrownew IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");  
  98.         }  
  99.         try {  
  100.             return method.invoke(obj, args);  
  101.         } catch (Exception e) {  
  102.             throw convertReflectionExceptionToUnchecked(e);  
  103.         }  
  104.     }  
  105.     /** 
  106.      * 迴圈向上轉型, 獲取物件的DeclaredField, 並強制設定為可訪問. 
  107.      *  
  108.      * 如向上轉型到Object仍無法找到, 返回null. 
  109.      */
  110.     publicstatic Field getAccessibleField(final Object obj, final String fieldName) {  
  111.         Validate.notNull(obj, "object can't be null");  
  112.         Validate.notBlank(fieldName, "fieldName can't be blank");  
  113.         for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {  
  114.             try {  
  115.                 Field field = superClass.getDeclaredField(fieldName);  
  116.                 makeAccessible(field);  
  117.                 return field;  
  118.             } catch (NoSuchFieldException e) {//NOSONAR
  119.                 // Field不在當前類定義,繼續向上轉型
  120.                 continue;// new add
  121.             }  
  122.         }  
  123.         returnnull;  
  124.     }  
  125.     /** 
  126.      * 迴圈向上轉型, 獲取物件的DeclaredMethod,並強制設定為可訪問. 
  127.      * 如向上轉型到Object仍無法找到, 返回null. 
  128.      * 匹配函式名+引數型別。 
  129.      *  
  130.      * 用於方法需要被多次呼叫的情況. 先使用本函式先取得Method,然後呼叫Method.invoke(Object obj, Object... args) 
  131.      */
  132.     publicstatic Method getAccessibleMethod(final Object obj, final String methodName,  
  133.             final Class<?>... parameterTypes) {  
  134.         Validate.notNull(obj, "object can't be null");  
  135.         Validate.notBlank(methodName, "methodName can't be blank");  
  136.         for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {  
  137.             try {  
  138.                 Method method = searchType.getDeclaredMethod(methodName, parameterTypes);  
  139.                 makeAccessible(method);  
  140.                 return method;  
  141.             } catch (NoSuchMethodException e) {  
  142.                 // Method不在當前類定義,繼續向上轉型
  143.                 continue;// new add
  144.             }  
  145.         }  
  146.         returnnull;  
  147.     }  
  148.     /** 
  149.      * 迴圈向上轉型, 獲取物件的DeclaredMethod,並強制設定為可訪問. 
  150.      * 如向上轉型到Object仍無法找到, 返回null. 
  151.      * 只匹配函式名。 
  152.      *  
  153.      * 用於方法需要被多次呼叫的情況. 先使用本函式先取得Method,然後呼叫Method.invoke(Object obj, Object... args) 
  154.      */
  155.     publicstatic Method getAccessibleMethodByName(final Object obj, final String methodName) {  
  156.         Validate.notNull(obj, "object can't be null");  
  157.         Validate.notBlank(methodName, "methodName can't be blank");  
  158.         for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) {  
  159.             Method[] methods = searchType.getDeclaredMethods();  
  160.             for (Method method : methods) {  
  161.                 if (method.getName().equals(methodName)) {  
  162.                     makeAccessible(method);  
  163.                     return method;  
  164.                 }  
  165.             }  
  166.         }  
  167.         returnnull;  
  168.     }  
  169.     /** 
  170.      * 改變private/protected的方法為public,儘量不呼叫實際改動的語句,避免JDK的SecurityManager抱怨。 
  171.      */
  172.     publicstaticvoid makeAccessible(Method method) {  
  173.         if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))  
  174.                 && !method.isAccessible()) {  
  175.             method.setAccessible(true);  
  176.         }  
  177.     }  
  178.     /** 
  179.      * 改變private/protected的成員變數為public,儘量不呼叫實際改動的語句,避免JDK的SecurityManager抱怨。 
  180.      */
  181.     publicstaticvoid makeAccessible(Field field) {  
  182.         if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier  
  183.                 .isFinal(field.getModifiers())) && !field.isAccessible()) {  
  184.             field.setAccessible(true);  
  185.         }  
  186.     }  
  187.     /** 
  188.      * 通過反射, 獲得Class定義中宣告的泛型引數的型別, 注意泛型必須定義在父類處 
  189.      * 如無法找到, 返回Object.class. 
  190.      * eg. 
  191.      * public UserDao extends HibernateDao<User> 
  192.      * 
  193.      * @param clazz The class to introspect 
  194.      * @return the first generic declaration, or Object.class if cannot be determined 
  195.      */
  196.     @SuppressWarnings("unchecked")  
  197.     publicstatic <T> Class<T> getClassGenricType(final Class clazz) {  
  198.         return getClassGenricType(clazz, 0);  
  199.     }  
  200.     /** 
  201.      * 通過反射, 獲得Class定義中宣告的父類的泛型引數的型別. 
  202.      * 如無法找到, 返回Object.class. 
  203.      *  
  204.      * 如public UserDao extends HibernateDao<User,Long> 
  205.      * 
  206.      * @param clazz clazz The class to introspect 
  207.      * @param index the Index of the generic ddeclaration,start from 0. 
  208.      * @return the index generic declaration, or Object.class if cannot be determined 
  209.      */
  210.     publicstatic Class getClassGenricType(final Class clazz, finalint index) {  
  211.         Type genType = clazz.getGenericSuperclass();  
  212.         if (!(genType instanceof ParameterizedType)) {  
  213.             logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");  
  214.             return Object.class;  
  215.         }  
  216.         Type[] params = ((ParameterizedType) genType).getActualTypeArguments();  
  217.         if (index >= params.length || index < 0) {  
  218.             logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
  219.                     + params.length);  
  220.             return Object.class;  
  221.         }  
  222.         if (!(params[index] instanceof Class)) {  
  223.             logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");  
  224.             return Object.class;  
  225.         }  
  226.         return (Class) params[index];  
  227.     }  
  228.     publicstatic Class<?> getUserClass(Object instance) {  
  229.         Assert.notNull(instance, "Instance must not be null");  
  230.         Class clazz = instance.getClass();  
  231.         if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {  
  232.             Class<?> superClass = clazz.getSuperclass();  
  233.             if (superClass != null && !Object.class.equals(superClass)) {  
  234.                 return superClass;  
  235.             }  
  236.         }  
  237.         return clazz;  
  238.     }  
  239.     /** 
  240.      * 將反射時的checked exception轉換為unchecked exception. 
  241.      */
  242.     publicstatic RuntimeException convertReflectionExceptionToUnchecked(Exception e) {  
  243.         if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException  
  244.                 || e instanceof NoSuchMethodException) {  
  245.             returnnew IllegalArgumentException(e);  
  246.         } elseif (e instanceof InvocationTargetException) {  
  247.             returnnew RuntimeException(((InvocationTargetException) e).getTargetException());  
  248.         } elseif (e instanceof RuntimeException) {  
  249.             return (RuntimeException) e;  
  250.         }  
  251.         returnnew RuntimeException("Unexpected Checked Exception.", e);  
  252.     }  
  253. }  


關於自定義註解:

(1)@Target:說明了Annotation所修飾的物件範圍,Annotation可以用於packages、types(類、介面、列舉、Annotation型別),型別成員(方法、構造方法、成員變數、列舉值)、方法引數和本地變數(如迴圈變數、catch引數)。在Annotation型別的宣告中使用了target可更加明晰其修飾的目標。取值(ElementType)有:

1.CONSTRUCTOR:用於描述構造器
    2.FIELD:用於描述域
    3.LOCAL_VARIABLE:用於描述區域性變數
    4.METHOD:用於描述方法
    5.PACKAGE:用於描述包
    6.PARAMETER:用於描述引數
    7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告
    

(2)@Retention定義了該Annotation被保留的時間長短:

某些Annotation僅出現在原始碼中,而被編譯器丟棄;而另一些卻被編譯在class檔案中;
編譯在class檔案中的Annotation可能會被虛擬機器忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。
使用這個meta-Annotation可以對 Annotation的“生命週期”限制。
取值(RetentionPoicy)有:
    1.SOURCE:在原始檔中有效(即原始檔保留)
    2.CLASS:在class檔案中有效(即class保留)
    3.RUNTIME:在執行時有效(即執行時保留)

這個工具類,可以幫註解可以幫我我們定義匯出的時候的欄位名,標題,對齊方式,欄位排序,反射的是哪個類,是匯入還是匯出。

二.  Excel匯出

我們在Excel匯出的時候,基本思路就是,(1)獲取要匯出的資料   (2)通過反射獲取對應的class的欄位  (3)將匯出的資料通過反射放到list (4)繪製Excel

匯出工具類:

  1. /** 
  2.  * 匯出Excel檔案(匯出“XLSX”格式,支援大資料量匯出   @see org.apache.poi.ss.SpreadsheetVersion) 
  3.  * @author ThinkGem 
  4.  * @version 2013-04-21 
  5.  */
  6. publicclass ExportExcel {  
  7.     privatestatic Logger log = LoggerFactory.getLogger(ExportExcel.class);  
  8.     /** 
  9.      * 工作薄物件 
  10.      */
  11.     private SXSSFWorkbook wb;  
  12.     /** 
  13.      * 工作表物件 
  14.      */
  15.     private Sheet sheet;  
  16.     /** 
  17.      * 樣式列表 
  18.      */
  19.     private Map<String, CellStyle> styles;  
  20.     /** 
  21.      * 當前行號 
  22.      */
  23.     privateint rownum;  
  24.     /** 
  25.      * 註解列表(Object[]{ ExcelField, Field/Method }) 
  26.      */
  27.     List<Object[]> annotationList = Lists.newArrayList();  
  28.     /** 
  29.      * 建構函式 
  30.      * @param title 表格標題,傳“空值”,表示無標題 
  31.      * @param cls 實體物件,通過annotation.ExportField獲取標題 
  32.      */
  33.     public ExportExcel(String title, Class<?> cls){  
  34.         this(title, cls, 2);  
  35.     }  
  36.     /** 
  37.      * 建構函式 
  38.      * @param title 表格標題,傳“空值”,表示無標題 
  39.      * @param cls 實體物件,通過annotation.ExportField獲取標題 
  40.      * @param type 匯出型別(1:匯出資料;2:匯出模板) 
  41.      * @param groups 匯入分組 
  42.      */
  43.     public ExportExcel(String title, Class<?> cls, int type, int... groups){  
  44.         // Get annotation field 
  45.         //getDeclaredFields:通過反射獲取對應class的全部欄位,包括私有欄位,但是不包括父類申明的欄位
  46.         Field[] fs = cls.getDeclaredFields();  
  47.         for (Field f : fs){  
  48.             //獲取欄位中帶有@ExcelField的標籤
  49.             ExcelField ef = f.getAnnotation(ExcelField.class);  
  50.             //標籤不為空並且匯出型別是匯入匯出或者匯出模板
  51.             if (ef != null && (ef.type()==0 || ef.type()==type)){  
  52.                 if (groups!=null && groups.length>0){  
  53.                     boolean inGroup = false;  
  54.                     for (int g : groups){  
  55.                         if (inGroup){  
  56.                             break;  
  57.                         }  
  58.                         for (int efg : ef.groups()){  
  59.                             if (g == efg){  
  60.                                 inGroup = true;  
  61.                                 annotationList.add(new Object[]{ef, f});  
  62.                                 break;  
  63.                             }  
  64.                         }  
  65.                     }  
  66.                 }else{  
  67.                     annotationList.add(new Object[]{ef, f});  
  68.                 }  
  69.             }  
  70.         }  
  71.         // Get annotation method
  72.         //獲取對應類中的全部方法,包括pulbic,protected,private.但是不包括繼承的方法,當然也包括他所實現介面的方法
  73.         Method[] ms = cls.getDeclaredMethods();  
  74.         for (Method m : ms){  
  75.             ExcelField ef = m.getAnnotation(ExcelField.class);  
  76.             //欄位:匯入匯出或者欄位匯出
  77.             if (ef != null && (ef.type()==0 || ef.type()==type)){  
  78.                 if (groups!=null && groups.length>0){  
  79.                     boolean inGroup = false;  
  80.                     for (int g : groups){  
  81.                         if (inGroup){  
  82.                             break;  
  83.                         }  
  84.                         for (int efg : ef.groups()){  
  85.                             if (g == efg){  
  86.                                 inGroup = true;  
  87.                                 annotationList.add(new Object[]{ef, m});  
  88.                                 break;  
  89.                             }  
  90.                         }  
  91.                     }  
  92.                 }else{  
  93.                     annotationList.add(new Object[]{ef, m});  
  94.                 }  
  95.             }  
  96.         }  
  97.         // Field sorting 
  98.         Collections.sort(annotationList, new Comparator<Object[]>() {  
  99.             publicint compare(Object[] o1, Object[] o2) {  
  100.                 returnnew Integer(((ExcelField)o1[0]).sort()).compareTo(  
  101.                         new Integer(((ExcelField)o2[0]).sort()));  
  102.             };  
  103.         });  
  104.         // Initialize
  105.         List<String> headerList = Lists.newArrayList();  
  106.         for (Object[] os : annotationList){  
  107.             String t = ((ExcelField)os[0]).title();  
  108.             // 如果是匯出,則去掉註釋
  109.             if (type==1){  
  110.                 String[] ss = StringUtils.split(t, "**"2);  
  111.                 if (ss.length==2){  
  112.                     t = ss[0];  
  113.                 }  
  114.             }  
  115.             headerList.add(t);  
  116.         }  
  117.         initialize(title, headerList);  
  118.     }  
  119.     /** 
  120.      * 建構函式 
  121.      * @param title 表格標題,傳“空值”,表示無標題 
  122.      * @param headers 表頭陣列 
  123.      */
  124.     public ExportExcel(String title, String[] headers) {  
  125.         initialize(title, Lists.newArrayList(headers));  
  126.     }  
  127.     /** 
  128.      * 建構函式 
  129.      * @param title 表格標題,傳“空值”,表示無標題 
  130.      * @param headerList 表頭列表 
  131.      */
  132.     public ExportExcel(String title, List<String> headerList) {  
  133.         initialize(title, headerList);  
  134.     }  
  135.     /** 
  136.      * 初始化函式 
  137.      * @param title 表格標題,傳“空值”,表示無標題 
  138.      * @param headerList 表頭列表 
  139.      */
  140.     privatevoid initialize(String title, List<String> headerList) {  
  141.         //SXSSFWorkbook專門用來處理大資料寫入Excel2007的問題
  142.         this.wb = new SXSSFWorkbook(500);  
  143.         this.sheet = wb.createSheet("Export");  
  144.         this.styles = createStyles(wb);  
  145.         // Create title
  146.         if (StringUtils.isNotBlank(title)){  
  147.             Row titleRow = sheet.createRow(rownum++);  
  148.             titleRow.setHeightInPoints(30);//設定行高
  149.             Cell titleCell = titleRow.createCell(0);  
  150.             titleCell.setCellStyle(styles.get("title"));//將已經寫好的單元格屬性呼叫
  151.             titleCell.setCellValue(title);//設定單元格的值
  152.             //addMergedRegion:設定列寬,設定列寬的方法在HSSFSheet中,方法引數:1:第幾列,2:寬度
  153.             //單元格合併方法也是在HSSFSheet中,方法引數:一個CellRangeAddress,該類建構函式的4個引數分別表示為:合併開始行,合併結束行,合併開始列,合併結束列
  154.             sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(),  
  155.                     titleRow.getRowNum(), titleRow.getRowNum(), headerList.size()-1));  
  156.         }  
  157.         // Create header
  158.         if (headerList == null){  
  159.             thrownew RuntimeException("headerList not null!");  
  160.         }  
  161.         Row headerRow = sheet.createRow(rownum++);  
  162.         headerRow.setHeightInPoints(16);  
  163.         for (int i = 0; i < headerList.size(); i++) {  
  164.             Cell cell = headerRow.createCell(i);  
  165.             cell.setCellStyle(styles.get("header"));  
  166.             String[] ss = StringUtils.split(headerList.get(i), "**"2);  
  167.             if (ss.length==2){  
  168.                 cell.setCellValue(ss[0]);  
  169.                 Comment comment = this.sheet.createDrawingPatriarch().createCellComment(  
  170.                         new XSSFClientAnchor(0000, (short33, (short56));  
  171.                 comment.setString(new XSSFRichTextString(ss[1]));  
  172.                 cell.setCellComment(comment);  
  173.             }else{  
  174.                 cell.setCellValue(headerList.get(i));  
  175.             }  
  176.             sheet.autoSizeColumn(i);  
  177.         }  
  178.         for (int i = 0; i < headerList.size(); i++) {    
  179.             int colWidth = sheet.getColumnWidth(i)*2;  
  180.             sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth);    
  181.         }  
  182.         log.debug("Initialize success.");  
  183.     }  
  184.     /** 
  185.      * 建立表格樣式 
  186.      * @param wb 工作薄物件 
  187.      * @return 樣式列表 
  188.      */
  189.     private Map<String, CellStyle> createStyles(Workbook wb) {  
  190.         Map<String, CellStyle> styles = new HashMap<String, CellStyle>();  
  191.         CellStyle style = wb.createCellStyle();  
  192.         style.setAlignment(CellStyle.ALIGN_CENTER);  
  193.         style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);  
  194.         Font titleFont = wb.createFont();  
  195.         titleFont.setFontName("Arial");  
  196.         titleFont.setFontHeightInPoints((short16);  
  197.         titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);  
  198.         style.setFont(titleFont);  
  199.         styles.put("title", style);  
  200.         style = wb.createCellStyle();  
  201.         style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);  
  202.         style.setBorderRight(CellStyle.BORDER_THIN);  
  203.         style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());  
  204.         style.setBorderLeft(CellStyle.BORDER_THIN);  
  205.         style.setLeftBorderColor(Indexed