JAVA项目实战-实现生成固定格式PDF文件和打包成zip压缩包并在浏览器中输出
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>