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>