1.工具
// 生成PDF自定義模板內容
(1) Adobe Acrobat Pro9
2.操作步驟
(1)利用Adobe Acrobat Pro9 生成一張根據業務場景的PDF,設置每個內容的字段(這款軟件功能比較強大,可以設置條形碼和二維碼的參數)
(3)JAVA實現代碼
1 import com.itextpdf.text.pdf.PdfReader; 2 import lombok.extern.slf4j.Slf4j; 3 import org.springframework.beans.factory.annotation.Value; 4 import org.springframework.stereotype.Component; 5 import org.springframework.util.ClassUtils; 6 import org.springframework.util.CollectionUtils; 7 import org.springframework.util.ResourceUtils; 8 9 import javax.servlet.ServletOutputStream; 10 import javax.servlet.http.HttpServletResponse; 11 import java.io.*; 12 import java.util.HashMap; 13 import java.util.List; 14 import java.util.UUID; 15 16 /** 17 * @description: PDF下載 18 * @author: ZhuCJ 19 * @date: 2020-05-20 15:36 20 */ 21 @Slf4j 22 @Component 23 public class PdfDownUtils { 24 25 /** 最終存放pdf位置 */ 26 @Value("${pdf.savePath}") 27 private String savePath; 28 29 /** 讀取模板,生成的復制pdf位置 */ 30 @Value("${pdf.cachePath}") 31 private String cacheTempPath; 32 33 /**讀取模板的位置 */ 34 @Value("${pdf.tempPath}") 35 private String tempPath; 36 37 /** 38 * 讀取的模板名字 39 */ 40 public static String TEMPLATE_NAME = "temp.pdf"; 41 42 43 /** 44 * 下載單張PDF 45 * @param mapPDF 46 * @param fileName 47 * @param filePath 48 * @param response 49 * @throws IOException 50 */ 51 public void pdfCompress(HashMap<String,String> mapPDF,String fileName 52 ,String filePath, HttpServletResponse response) throws IOException{ 53 54 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 55 //項目下模板路徑 56 //String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 57 //讀取模板文件 58 PdfReader reader = new PdfReader(tempPath + File.separator + TEMPLATE_NAME); 59 //生成pdf 60 InputStream pdfStream = null; 61 try { 62 pdfStream = this.print(reader, mapPDF,fileName,filePath); 63 } catch (IOException e) { 64 e.printStackTrace(); 65 } 66 ServletOutputStream op = null; 67 try { 68 op = response.getOutputStream(); 69 } catch (IOException e) { 70 e.printStackTrace(); 71 } 72 response.setContentType("application/pdf"); 73 response.setHeader("Content-Disposition", "inline; filename=\"" 74 + new String(fileName.getBytes("gb18030"), "ISO8859-1") + ".pdf" + "\""); 75 int length = 0; 76 byte[] bytes = new byte[1024]; 77 while((pdfStream != null) && ((length = pdfStream.read(bytes)) != -1)) { 78 op.write(bytes, 0, length); 79 } 80 op.close(); 81 reader.close(); 82 response.flushBuffer(); 83 } 84 85 86 87 /** 88 * 生成pdf打成ZIP包下載 89 * @param orderZips 模板參數 90 * @param filePath pdf 保存文件上級文件夾名 91 * @param response 92 * @throws IOException 93 */ 94 public void pdfCompressZip(List<List<HashMap<String,Object>>> orderZips, 95 String filePath, HttpServletResponse response) throws IOException{ 96 if (CollectionUtils.isEmpty(orderZips)){ 97 return; 98 } 99 100 for (List orderZip:orderZips){ 101 if (CollectionUtils.isEmpty(orderZip)){ 102 continue; 103 } 104 for (Object orderPdf:orderZip){ 105 HashMap<String,Object> mapPDF =(HashMap<String,Object>) orderPdf; 106 //獲取生成pdf的文件名 107 String fileName = null; 108 if (mapPDF.containsKey("fileName")){ 109 fileName = mapPDF.get("fileName").toString(); 110 }else { 111 //默認隨機生成一個,保證唯一性避免覆蓋 112 fileName = UUID.randomUUID().toString(); 113 } 114 try { 115 this.printFilePath(mapPDF, fileName, filePath); 116 } catch (IOException e) { 117 log.error("生成Pdf文件IO異常:{}",e.getMessage()); 118 } 119 } 120 } 121 //本次操作保存pdf文件路徑,用於壓縮成Zip包 122 String pdfFilePath = savePath+File.separator+filePath+File.separator; 123 log.info("待壓縮zip包文件名:{}",pdfFilePath); 124 File file = new File(pdfFilePath); 125 String zipFile = null; 126 File ftp = null; 127 try { 128 zipFile = CompressZipUtil.zipFile(file,"zip"); 129 } catch (Exception e) { 130 e.printStackTrace(); 131 } 132 response.setContentType("APPLICATION/OCTET-STREAM"); 133 response.setHeader("Content-Disposition","attachment; filename=listDown.zip"); 134 OutputStream out = null; 135 InputStream in = null; 136 try { 137 out = response.getOutputStream(); 138 // 139 ftp = ResourceUtils.getFile(zipFile); 140 in = new FileInputStream(ftp); 141 // 循環取出流中的數據 142 byte[] b = new byte[100]; 143 int len; 144 while ((len = in.read(b)) !=-1) { 145 out.write(b, 0, len); 146 } 147 } catch (Exception e) { 148 e.printStackTrace(); 149 log.error("文件讀取異常:{}",e.getMessage()); 150 151 }finally { 152 if (in !=null){ 153 in.close(); 154 } 155 if (out !=null){ 156 out.close(); 157 } 158 log.info("zip下載完成,進行刪除本地zip包"); 159 //刪除保存的Pdf文件 160 DeleteFileUtil.deleteFile(file); 161 //刪除保存的壓縮包 162 if (ftp!=null){ 163 ftp.delete(); 164 } 165 } 166 } 167 168 /** 169 * 170 * @param map 171 * @param fileName 172 * @return 所在文件地址 173 * @throws IOException 174 */ 175 private String printFilePath(HashMap<String,Object> map 176 ,String fileName,String filePath) throws IOException { 177 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 178 //項目下模板路徑 179 // String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 180 //判斷是否存在文件目錄,不存在創建 181 createFile(savePath,cacheTempPath); 182 //保存路徑+隨機文件名 183 String save = savePath+ File.separator+filePath+File.separator; 184 PdfFormater pdf = new PdfFormater(tempPath, save,cacheTempPath,TEMPLATE_NAME,map); 185 pdf.doTransform(fileName); 186 return save; 187 } 188 189 190 /** 191 * 打印,以PDF為模板 192 * @param templateName String 模板名字 193 * @param map 模板數據HashMap 194 * @return InputStream 195 * @throws IOException 196 */ 197 private InputStream print(PdfReader reader, HashMap<String,String> map, String fileName, String filePath) throws IOException { 198 InputStream is = null; 199 String sysPath = ClassUtils.getDefaultClassLoader().getResource("").getPath(); 200 //項目下模板路徑 201 //String readTempPath = sysPath +"data"+File.separator+"hanzt"+File.separator+"temp"; 202 //判斷是否存在文件目錄,不存在創建 203 createFile(savePath,cacheTempPath); 204 //保存路徑+隨機文件名 205 String save = savePath+ File.separator+filePath+File.separator; 206 PdfFormater pdf = new PdfFormater(tempPath,save,cacheTempPath,TEMPLATE_NAME, map); 207 String PdfFilePath = pdf.doTransform(fileName); 208 is = new FileInputStream(PdfFilePath); 209 return is; 210 } 211 212 213 214 /** 215 * 判斷文件夾是否存在,不存在創建一個 216 * @param filePaths 217 * @return 218 */ 219 public void createFile(String ... filePaths){ 220 for (String filePath:filePaths){ 221 File file = new File(filePath); 222 if (!file.exists()){ 223 file.mkdirs(); 224 } 225 } 226 } 227 228 }
1 import com.itextpdf.text.BadElementException; 2 import com.itextpdf.text.DocumentException; 3 import com.itextpdf.text.Image; 4 import com.itextpdf.text.Rectangle; 5 import com.itextpdf.text.pdf.*; 6 import com.sf.vsolution.hb.sfce.util.string.StringUtils; 7 import lombok.extern.slf4j.Slf4j; 8 9 import java.io.File; 10 import java.io.FileOutputStream; 11 import java.io.IOException; 12 import java.lang.reflect.Field; 13 import java.util.Iterator; 14 import java.util.List; 15 import java.util.Map; 16 import java.util.Objects; 17 18 /** 19 * @description: 20 * @author: ZhuCJ 21 * @date: 2020-05-27 10:50 22 */ 23 @Slf4j 24 public class PdfFormater { 25 /** 26 * pdf模板路徑 27 */ 28 private String templatePath; 29 /** 30 * 下載完成的pdf路徑 31 */ 32 private String savePath; 33 /** 34 * 緩存pdf路徑 35 */ 36 private String cachePath; 37 38 /** 39 * 讀取模板對象 40 */ 41 private String templateName; 42 43 /** 44 * 需要填充的數據 45 */ 46 private Map dataMap; 47 48 private String cacheFileName; 49 50 //新的PDF文件名稱 51 private String resultFileName; 52 //動態數據 53 private List dynData; 54 55 56 /** 57 * 構造器,生成PDF引擎實例,並引入相應模板文件XXX.FO、路徑和報表數據HashMap 58 * 59 * @param templateDir 60 * 模板文件所在目錄 61 * @param basePath 62 * 模板文件工作副本及結果PDF文件所在工作目錄 63 * @param templateFileFo 64 * 模板文件名,推薦格式為“XXXTemplate.FO”, 其文件由word模板文檔在設計時轉換而成 65 * @param dataMap 66 * 對應模板的數據HashMap,由調用該打印引擎的里程根據模板格式和約定進行准備 67 */ 68 public PdfFormater(String templatePath, String savePath, String cachePath, 69 String templateName, Map dataMap) { 70 this.templatePath = templatePath; 71 this.savePath = savePath; 72 this.templateName = templateName; 73 this.cachePath = cachePath; 74 this.dataMap = dataMap; 75 } 76 77 /** 78 * 設置字體 79 * @param font 80 * @return 81 */ 82 private BaseFont getBaseFont(String font) { 83 // 需要根據不同的模板返回字體 84 BaseFont bf = null; 85 try { 86 bf = BaseFont.createFont( StringUtils.isEmpty(font)?"STSong-Light":font, "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED); 87 } catch (DocumentException e) { 88 e.printStackTrace(); 89 } catch (IOException e) { 90 e.printStackTrace(); 91 } 92 return bf; 93 } 94 95 /** 96 * 避免出現多線程重復讀 97 * @param fileName 98 * @return 99 */ 100 public String doTransform(String fileName) { 101 long name = System.currentTimeMillis(); 102 //緩存模板名字 103 cacheFileName = name + ".pdf"; 104 //最后保存模板名字 105 resultFileName = fileName + ".pdf"; 106 try { 107 PdfReader reader; 108 PdfStamper stamper; 109 //讀取PDF模板對象 110 reader = new PdfReader(templatePath + File.separator + templateName); 111 //生成新的PDF模板對象 112 stamper = new PdfStamper(reader, new FileOutputStream(cachePath + File.separator + cacheFileName)); 113 AcroFields form = stamper.getAcroFields(); 114 form.addSubstitutionFont(getBaseFont("")); 115 transformRegular(form,stamper); 116 stamper.setFormFlattening(true); 117 stamper.close(); 118 reader.close(); 119 postProcess(); 120 } catch (Exception e) { 121 e.printStackTrace(); 122 } 123 return savePath + File.separator + resultFileName; 124 } 125 126 /** 127 * 填充規整的表單域 128 * @param form 129 */ 130 private void transformRegular(AcroFields form,PdfStamper stamper) { 131 if (dataMap == null || dataMap.size() == 0) {return;} 132 String key = ""; 133 Iterator ekey = dataMap.keySet().iterator(); 134 Object obj = null ; 135 while (ekey.hasNext()) { 136 key = ekey.next().toString(); 137 try { 138 obj = dataMap.get(key); 139 if(obj instanceof List){ 140 //map中放的是list,為動態字段 141 dynData = (List)obj; 142 transformDynTable(form); 143 }else{ 144 //非空放入 145 if( dataMap.get(key) != null) { 146 if (Objects.equals(key,"code1") || Objects.equals(key,"code2") ){ 147 //key = code1或code2 進行生成條形碼; 148 createBarCode(form,stamper,key,dataMap.get(key)); 149 }else if (Objects.equals(key,"qrCode")){ 150 //key = qrCode 進行生成二維碼 151 createQrCode(form,stamper,key,dataMap.get(key)); 152 }else { 153 form.setField(key, dataMap.get(key).toString()); 154 } 155 } 156 157 } 158 } catch (Exception e) { 159 log.error("pdf賦值異常:{}",e.getMessage()); 160 } 161 } 162 } 163 164 /** 165 * 動態table的填充 166 * @param form 167 */ 168 private void transformDynTable(AcroFields form) { 169 if (dynData == null || dynData.size() == 0) 170 {return;} 171 Object obj = null; 172 String name = ""; 173 String value = ""; 174 for (int x = 0; x < dynData.size(); x++) { 175 obj = dynData.get(x); 176 Field[] fld = obj.getClass().getDeclaredFields(); 177 for (int i = 0; i < fld.length; i++) { 178 name = fld[i].getName(); 179 value = (String) ReflectUtils.getFieldValue(obj, name); 180 try { 181 form.setField(name + x, value); 182 } catch (IOException e) { 183 e.printStackTrace(); 184 } catch (DocumentException e) { 185 e.printStackTrace(); 186 } 187 } 188 } 189 } 190 191 /** 192 * 對生成的pdf文件進行后處理 193 * 194 * @throws RptException 195 */ 196 private synchronized void postProcess() throws Exception { 197 FileOutputStream fosRslt = null; 198 PdfStamper stamper = null; 199 PdfReader reader = null; 200 try { 201 reader = new PdfReader(cachePath + File.separator + cacheFileName); 202 String save = savePath+File.separator+resultFileName; 203 File file = new File(save); 204 File parentFile = file.getParentFile(); 205 if (!parentFile.exists()){ 206 parentFile.mkdirs(); 207 } 208 fosRslt = new FileOutputStream(savePath + File.separator + resultFileName); 209 stamper = new PdfStamper(reader, fosRslt); 210 211 Rectangle pageSize = reader.getPageSize(1); 212 float width = pageSize.getWidth(); 213 BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",BaseFont.NOT_EMBEDDED); 214 PdfContentByte over; 215 int total = reader.getNumberOfPages() + 1; 216 for (int i = 1; i < total; i++) { 217 over = stamper.getOverContent(i); 218 if (total <= 2){break;} 219 over.beginText(); 220 over.setFontAndSize(bf, 10); 221 over.setTextMatrix(width - 92f, 32); 222 over.showText("第 " + i + " 頁"); 223 over.endText(); 224 } 225 } catch (Exception ie) { 226 ie.printStackTrace(); 227 } finally { 228 if (stamper != null) { 229 try { 230 stamper.close(); 231 } catch (DocumentException e) { 232 e.printStackTrace(); 233 } catch (IOException e) { 234 e.printStackTrace(); 235 } 236 } 237 if (fosRslt != null) { 238 try { fosRslt.close(); 239 } catch (IOException e) { 240 e.printStackTrace(); 241 } 242 } 243 if (reader != null) { 244 reader.close(); 245 } 246 File pdfFile = new File(cachePath+File.separator + cacheFileName); 247 pdfFile.delete(); 248 } 249 250 } 251 252 /** 253 * PDF中繪制條形碼 254 * @param form 255 * @param stamper 256 * @param key 257 * @param value 258 */ 259 public void createBarCode(AcroFields form, PdfStamper stamper,String key,Object value){ 260 261 // 獲取屬性的類 262 if (value != null && form.getField(key) != null) { 263 //獲取位置(左上右下) 264 AcroFields.FieldPosition fieldPosition = form.getFieldPositions(key).get(0); 265 //繪制條碼 266 Barcode128 barcode128 = new Barcode128(); 267 //字號 268 barcode128.setSize(6); 269 //條碼高度 270 if (key.equals("code1")){ 271 barcode128.setBarHeight(19.88f); 272 barcode128.setBaseline(9); 273 }else { 274 barcode128.setBarHeight(16.24f); 275 //條碼與數字間距 276 barcode128.setBaseline(8); 277 } 278 //條碼值 279 barcode128.setCode(value.toString()); 280 barcode128.setStartStopText(false); 281 barcode128.setExtended(true); 282 //繪制在第一頁 283 PdfContentByte cb = stamper.getOverContent(1); 284 //生成條碼圖片 285 Image image128 = barcode128.createImageWithBarcode(cb, null, null); 286 //條碼位置 287 float marginLeft = (fieldPosition.position.getRight() - fieldPosition.position.getLeft() - image128.getWidth()) / 2; 288 if (key.equals("code2")){ 289 //條碼位置 290 image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 395.67f); 291 }else { 292 image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 157.8f ); 293 } 294 //加入條碼 295 try { 296 cb.addImage(image128); 297 } catch (DocumentException e) { 298 log.error("創建條碼異常:{}",e.getMessage()); 299 } 300 301 } 302 303 } 304 305 /** 306 * 繪制二維碼 307 * @param form 308 * @param stamper 309 * @param key 310 * @param value 311 */ 312 public void createQrCode(AcroFields form, PdfStamper stamper,String key,Object value){ 313 // 獲取屬性的類型 314 if(value != null && form.getField(key) != null){ 315 //獲取位置(左上右下) 316 AcroFields.FieldPosition fieldPosition = form.getFieldPositions(key).get(0); 317 //繪制二維碼 318 float width = fieldPosition.position.getRight() - fieldPosition.position.getLeft(); 319 BarcodeQRCode pdf417 = new BarcodeQRCode(value.toString(), (int)width, (int)width, null); 320 //生成二維碼圖像 321 Image image128 = null; 322 try { 323 image128 = pdf417.getImage(); 324 } catch (BadElementException e) { 325 log.error("創建二維碼異常:{}",e.getMessage()); 326 } 327 //繪制在第一頁 328 PdfContentByte cb = stamper.getOverContent(1); 329 //左邊距(居中處理) 330 float marginLeft = (fieldPosition.position.getRight() - fieldPosition.position.getLeft() - image128.getWidth()) / 2; 331 //二維碼位置 332 image128.setAbsolutePosition(fieldPosition.position.getLeft() + marginLeft, 300); 333 image128.setBorderWidth(1000); 334 //加入二維碼 335 try { 336 cb.addImage(image128); 337 } catch (DocumentException e) { 338 log.error("加入二維碼異常:{}",e.getMessage()); 339 } 340 } 341 } 342 343 }
import lombok.extern.log4j.Log4j2; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipOutputStream; import java.io.*; /** * @description: 文件打ZIP包工具類 * @author: ZhuCJ * @date: 2020-05-27 14:43 */ @Log4j2 public class CompressZipUtil { /** * 壓縮文件(文件夾) * @param path 目標文件流 * @param format zip 格式 | rar 格式 * @throws Exception */ public static String zipFile(File path, String format) throws Exception { String generatePath = ""; if (path.isDirectory()) { generatePath = path.getParent().endsWith(File.separator) == false ? path.getParent() + File.separator + path.getName() + "." + format : path.getParent() + path.getName() + "." + format; } else { generatePath = path.getParent().endsWith(File.separator) == false ? path.getParent() + File.separator : path.getParent(); generatePath += path.getName().substring(0, path.getName().lastIndexOf(".")) + "." + format; } // 輸出流 FileOutputStream outputStream = new FileOutputStream(generatePath); // 壓縮輸出流 ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(outputStream)); zip(out, path, ""); out.flush(); out.close(); return generatePath; } /** * @param sourcePath 要壓縮的文件路徑 * @param suffix 生成的格式后最(zip、rar) */ public static void generateFile(String sourcePath, String suffix) throws Exception { File file = new File(sourcePath); // 壓縮文件的路徑不存在 if (!file.exists()) { throw new Exception("路徑 " + sourcePath + " 不存在文件,無法進行壓縮..."); } // 用於存放壓縮文件的文件夾 String generateFile = file.getParent() + File.separator + "CompressFile"; File compress = new File(generateFile); // 如果文件夾不存在,進行創建 if (!compress.exists()) { compress.mkdirs(); } // 目的壓縮文件 String generateFileName = compress.getAbsolutePath() + File.separator + "AAA" + file.getName() + "." + suffix; // 輸入流 表示從一個源讀取數據 // 輸出流 表示向一個目標寫入數據 // 輸出流 FileOutputStream outputStream = new FileOutputStream(generateFileName); // 壓縮輸出流 ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(outputStream)); generateFile(zipOutputStream, file, ""); System.out.println("源文件位置:" + file.getAbsolutePath() + ",目的壓縮文件生成位置:" + generateFileName); // 關閉 輸出流 zipOutputStream.close(); } /** * @param out 輸出流 * @param file 目標文件 * @param dir 文件夾 * @throws Exception */ private static void generateFile(ZipOutputStream out, File file, String dir) { FileInputStream inputStream = null; try { // 當前的是文件夾,則進行一步處理 if (file.isDirectory()) { //得到文件列表信息 File[] files = file.listFiles(); //將文件夾添加到下一級打包目錄 out.putNextEntry(new ZipEntry(dir + File.separator)); dir = dir.length() == 0 ? "" : dir + File.separator; //循環將文件夾中的文件打包 for (int i = 0; i < files.length; i++) { generateFile(out, files[i], dir + files[i].getName()); } } else { // 當前是文件 // 輸入流 inputStream = new FileInputStream(file); // 標記要打包的條目 out.putNextEntry(new ZipEntry(dir)); // 進行寫操作 int len = 0; byte[] bytes = new byte[1024]; while ((len = inputStream.read(bytes)) > 0) { out.write(bytes, 0, len); } } } catch (Exception e) { log.error("generateFile異常:", e); } finally { // 關閉輸入流 try { if (inputStream != null) { inputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } /** * 遞歸壓縮文件 * * @param output ZipOutputStream 對象流 * @param file 壓縮的目標文件流 * @param childPath 條目目錄 */ private static void zip(ZipOutputStream output, File file, String childPath) { FileInputStream input = null; try { // 文件為目錄 if (file.isDirectory()) { // 得到當前目錄里面的文件列表 File list[] = file.listFiles(); childPath = childPath + (childPath.length() == 0 ? "" : File.separator) + file.getName(); // 循環遞歸壓縮每個文件 for (File f : list) { zip(output, f, childPath); } } else { // 壓縮文件 childPath = (childPath.length() == 0 ? "" : childPath + File.separator) + file.getName(); output.putNextEntry(new ZipEntry(childPath)); input = new FileInputStream(file); int readLen = 0; byte[] buffer = new byte[1024 * 8]; while ((readLen = input.read(buffer, 0, 1024 * 8)) != -1) { output.write(buffer, 0, readLen); } } } catch (Exception ex) { ex.printStackTrace(); } finally { // 關閉流 if (input != null) { try { input.close(); } catch (IOException ex) { ex.printStackTrace(); } } } } }
import lombok.extern.slf4j.Slf4j; import java.io.File; /** * @description: 刪除指定文件或文件夾下所有內容 * @author: ZhuCJ * @date: 2020-06-02 0:18 */ @Slf4j public class DeleteFileUtil { public static void deleteFile(File file){ //取得這個目錄下的所有子文件對象 File[] files = file.listFiles(); //遍歷該目錄下的文件對象 for (File f: files){ //判斷子目錄是否存在子目錄,如果是文件則刪除 if (f.isDirectory()){ deleteFile(f); }else { f.delete(); } } //刪除空文件夾 for循環已經把上一層節點的目錄清空。 file.delete(); } }
import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.lang.reflect.*; import java.util.Date; /** * @description: 反射設置對象屬性值 * @author: ZhuCJ * @date: 2020-05-27 11:16 */ public class ReflectUtils { private static final String SETTER_PREFIX = "set"; private static final String GETTER_PREFIX = "get"; private static final String CGLIB_CLASS_SEPARATOR = "$$"; private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); /** * 調用Getter方法. * 支持多級,如:對象名.對象名.方法 */ public static Object invokeGetter(Object obj, String propertyName) { Object object = obj; for (String name : StringUtils.split(propertyName, ".")){ String getterMethodName = GETTER_PREFIX + org.springframework.util.StringUtils.capitalize(name); object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); } return object; } /** * 調用Setter方法, 僅匹配方法名。 * 支持多級,如:對象名.對象名.方法 */ public static void invokeSetter(Object obj, String propertyName, Object value) { Object object = obj; String[] names = StringUtils.split(propertyName, "."); for (int i=0; i<names.length; i++){ if(i<names.length-1){ String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); }else{ String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); invokeMethodByName(object, setterMethodName, new Object[] { value }); } } } /** * 直接讀取對象屬性值, 無視private/protected修飾符, 不經過getter函數. */ public static Object getFieldValue(final Object obj, final String fieldName) { Field field = getAccessibleField(obj, fieldName); if (field == null) { throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]"); } Object result = null; try { result = field.get(obj); } catch (IllegalAccessException e) { logger.error("不可能拋出的異常{}", e.getMessage()); } return result; } /** * 直接設置對象屬性值, 無視private/protected修飾符, 不經過setter函數. */ public static void setFieldValue(final Object obj, final String fieldName, final Object value) { Field field = getAccessibleField(obj, fieldName); if (field == null) { throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]"); } try { field.set(obj, value); } catch (IllegalAccessException e) { logger.error("不可能拋出的異常:{}", e.getMessage()); } } /** * 直接調用對象方法, 無視private/protected修飾符. * 用於一次性調用的情況,否則應使用getAccessibleMethod()函數獲得Method后反復調用. * 同時匹配方法名+參數類型, */ public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] parameterTypes, final Object[] args) { Method method = getAccessibleMethod(obj, methodName, parameterTypes); if (method == null) { throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); } try { return method.invoke(obj, args); } catch (Exception e) { throw convertReflectionExceptionToUnchecked(e); } } /** * 直接調用對象方法, 無視private/protected修飾符, * 用於一次性調用的情況,否則應使用getAccessibleMethodByName()函數獲得Method后反復調用. * 只匹配函數名,如果有多個同名函數調用第一個。 */ public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) { Method method = getAccessibleMethodByName(obj, methodName); if (method == null) { throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]"); } try { return method.invoke(obj, args); } catch (Exception e) { throw convertReflectionExceptionToUnchecked(e); } } /** * 循環向上轉型, 獲取對象的DeclaredField, 並強制設置為可訪問. * * 如向上轉型到Object仍無法找到, 返回null. */ public static Field getAccessibleField(final Object obj, final String fieldName) { Validate.notNull(obj, "object can't be null"); Validate.notBlank(fieldName, "fieldName can't be blank"); for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { try { Field field = superClass.getDeclaredField(fieldName); makeAccessible(field); return field; } catch (NoSuchFieldException e) {//NOSONAR // Field不在當前類定義,繼續向上轉型 continue;// new add } } return null; } /** * 循環向上轉型, 獲取對象的DeclaredMethod,並強制設置為可訪問. * 如向上轉型到Object仍無法找到, 返回null. * 匹配函數名+參數類型。 * * 用於方法需要被多次調用的情況. 先使用本函數先取得Method,然后調用Method.invoke(Object obj, Object... args) */ public static Method getAccessibleMethod(final Object obj, final String methodName, final Class<?>... parameterTypes) { Validate.notNull(obj, "object can't be null"); Validate.notBlank(methodName, "methodName can't be blank"); for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { try { Method method = searchType.getDeclaredMethod(methodName, parameterTypes); makeAccessible(method); return method; } catch (NoSuchMethodException e) { // Method不在當前類定義,繼續向上轉型 continue;// new add } } return null; } /** * 循環向上轉型, 獲取對象的DeclaredMethod,並強制設置為可訪問. * 如向上轉型到Object仍無法找到, 返回null. * 只匹配函數名。 * * 用於方法需要被多次調用的情況. 先使用本函數先取得Method,然后調用Method.invoke(Object obj, Object... args) */ public static Method getAccessibleMethodByName(final Object obj, final String methodName) { Validate.notNull(obj, "object can't be null"); Validate.notBlank(methodName, "methodName can't be blank"); for (Class<?> searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { Method[] methods = searchType.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals(methodName)) { makeAccessible(method); return method; } } } return null; } /** * 改變private/protected的方法為public,盡量不調用實際改動的語句,避免JDK的SecurityManager抱怨。 */ public static void makeAccessible(Method method) { if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) { method.setAccessible(true); } } /** * 改變private/protected的成員變量為public,盡量不調用實際改動的語句,避免JDK的SecurityManager抱怨。 */ public static void makeAccessible(Field field) { if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) || Modifier .isFinal(field.getModifiers())) && !field.isAccessible()) { field.setAccessible(true); } } /** * 通過反射, 獲得Class定義中聲明的泛型參數的類型, 注意泛型必須定義在父類處 * 如無法找到, 返回Object.class. * eg. * public UserDao extends HibernateDao<User> * * @param clazz The class to introspect * @return the first generic declaration, or Object.class if cannot be determined */ @SuppressWarnings("unchecked") public static <T> Class<T> getClassGenricType(final Class clazz) { return getClassGenricType(clazz, 0); } /** * 通過反射, 獲得Class定義中聲明的父類的泛型參數的類型. * 如無法找到, 返回Object.class. * * 如public UserDao extends HibernateDao<User,Long> * * @param clazz clazz The class to introspect * @param index the Index of the generic ddeclaration,start from 0. * @return the index generic declaration, or Object.class if cannot be determined */ public static Class getClassGenricType(final Class clazz, final int index) { Type genType = clazz.getGenericSuperclass(); if (!(genType instanceof ParameterizedType)) { logger.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType"); return Object.class; } Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); if (index >= params.length || index < 0) { logger.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + params.length); return Object.class; } if (!(params[index] instanceof Class)) { logger.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); return Object.class; } return (Class) params[index]; } public static Class<?> getUserClass(Object instance) { Validate.notNull(instance, "Instance must not be null"); Class clazz = instance.getClass(); if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { Class<?> superClass = clazz.getSuperclass(); if (superClass != null && !Object.class.equals(superClass)) { return superClass; } } return clazz; } /** * 將反射時的checked exception轉換為unchecked exception. */ public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) { if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException || e instanceof NoSuchMethodException) { return new IllegalArgumentException(e); } else if (e instanceof InvocationTargetException) { return new RuntimeException(((InvocationTargetException) e).getTargetException()); } else if (e instanceof RuntimeException) { return (RuntimeException) e; } return new RuntimeException("Unexpected Checked Exception.", e); } /** * 判斷屬性是否為日期類型 * * @param clazz * 數據類型 * @param fieldName * 屬性名 * @return 如果為日期類型返回true,否則返回false */ public static <T> boolean isDateType(Class<T> clazz, String fieldName) { boolean flag = false; try { Field field = clazz.getDeclaredField(fieldName); Object typeObj = field.getType().newInstance(); flag = typeObj instanceof Date; } catch (Exception e) { // 把異常吞掉直接返回false } return flag; } /** * 根據類型將指定參數轉換成對應的類型 * * @param value * 指定參數 * @param type * 指定類型 * @return 返回類型轉換后的對象 */ public static <T> Object parseValueWithType(String value, Class<?> type) { Object result = null; try { // 根據屬性的類型將內容轉換成對應的類型 if (Boolean.TYPE == type) { result = Boolean.parseBoolean(value); } else if (Byte.TYPE == type) { result = Byte.parseByte(value); } else if (Short.TYPE == type) { result = Short.parseShort(value); } else if (Integer.TYPE == type) { result = Integer.parseInt(value); } else if (Long.TYPE == type) { result = Long.parseLong(value); } else if (Float.TYPE == type) { result = Float.parseFloat(value); } else if (Double.TYPE == type) { result = Double.parseDouble(value); } else { result = (Object) value; } } catch (Exception e) { // 把異常吞掉直接返回null } return result; } }
4.使用的maven依賴
<!--文件轉成PDF-->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<!--打成zip壓縮包-->
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.5</version>
</dependency>