原JavaBean类
package collection;
public class TestBean {
private String name;
private String[] attributes;
private int num;
private int[] map;
private int[][] maps;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getAttributes() {
return attributes;
}
public void setAttributes(String[] attributes) {
this.attributes = attributes;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public int[] getMap() {
return map;
}
public void setMap(int[] map) {
this.map = map;
}
public int[][] getMaps() {
return maps;
}
public void setMaps(int[][] maps) {
this.maps = maps;
}
}
生成的JavaBean派生类
package collection;
import java.util.Optional;
import java.lang.String;
public class TestBeanOpt extends TestBean {
public Optional optName() {
return Optional.ofNullable(getName());
}
public Optional optAttributes() {
return Optional.ofNullable(getAttributes());
}
public Optional<int[]> optMap() {
return Optional.ofNullable(getMap());
}
public Optional<int[][]> optMaps() {
return Optional.ofNullable(getMaps());
}
}
工具代码,使用方法见注释
package collection;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
/**
* 功能描述:自动生成带Optional方法的派生类
* 注意事项:不会生成getClass名称的opt方法,不会生成返回8种基本类型的方法
* 创建:JRZ
* 创建时间:2017/9/13 16:01
*/
public class OptionalClassBuilder {
private String filePath;
private List> list = new ArrayList<>();
private String packageName;
private String className;
private Set importNames = new HashSet<>();
private List methodCells = new ArrayList<>();
private Set filterMethodSet;
private Set filterReturnSet;
private String view;
/**
* 构造 Optional对象返回类 生成器
* 使用这个结构的原因是,能力有限,做不出文本解析器,只能曲线使用字节码对象实现
* 如果我要对cn.es.bean包下的文件进行opt返回值加强,那就需要将rootPath设置为java编译后class根路径
* 就是不需要包含/cn/es/bean的路径
*
* @param filePath 文件生成位置
* @param rootPath 目标项目class文件所在的根路径
* @param packageName 要对哪个包下的class文件进行生成
* @throws Exception 发生异常时
*/
public OptionalClassBuilder(String filePath, String rootPath, String packageName) throws Exception {
this.filePath = filePath;
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);//开启访问权限
URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
//将目标环境根路径加入运行环境
method.invoke(classLoader, new File(rootPath).toURI().toURL());
File dir = new File(rootPath + "/" + packageName.replace('.', File.separatorChar));
File[] files = dir.listFiles();
if (files == null) {
throw new RuntimeException("目录不包含java文件");
}
for (File file : files) {
String fileName = file.getName();
//不是class文件,或者是已经生成的class文件
if (!fileName.endsWith(".class") || fileName.endsWith("Opt.class")) {
continue;
}
// 文件名称
String className = file.getAbsolutePath();
className = className.substring(rootPath.length() + 1, className.length() - 6);
className = className.replace(File.separatorChar, '.');
// 加载Class类
Class> cls = Class.forName(className);
list.add(cls);
}
}
/**
* 构造 Optional对象返回类 生成器
* 推荐使用这种方法,这种方法是可控的,出错几率也小
*
* @param filePath 文件生成位置
* @param packageName 包名
* @param cs 要构造文件的字节码对象数组
*/
public OptionalClassBuilder(String filePath, String packageName, Class... cs) {
this.filePath = filePath;
this.packageName = packageName;
Arrays.stream(cs).forEach(list::add);
}
/**
* 执行文件生成
*/
public void build() {
for (Class> cls : list) {
init();
parserObject(cls);
viewClass();
saveFile();
}
}
/**
* 初始化
*/
private void init() {
importNames.clear();
importNames.add("java.util.Optional");
methodCells.clear();
//构造符合get条件,但不想生成opt的方法名列表
if (filterMethodSet == null) {
filterMethodSet = new HashSet<>();
filterMethodSet.add("getClass");
}
//对以下类型的返回值,不进行方法生成,不导包
if (filterReturnSet == null) {
filterReturnSet = new HashSet<>();
filterReturnSet.add("boolean");
filterReturnSet.add("byte");
filterReturnSet.add("short");
filterReturnSet.add("char");
filterReturnSet.add("int");
filterReturnSet.add("float");
filterReturnSet.add("long");
filterReturnSet.add("double");
}
}
/**
* 将对象解析为中间态
*
* @param cls 要解析的字节码对象
*/
private void parserObject(Class> cls) {
Package pag = cls.getPackage();
if (pag != null) {
packageName = pag.getName();
}
className = cls.getSimpleName();
Method[] methods = cls.getMethods();
for (Method method : methods) {
//输入参数不为空,去掉
if (method.getParameterCount() > 0) {
continue;
}
//过滤以get为方法名开头,去掉(boolean类型,将不会返回null,不支持is;Boolean是get)
String name = method.getName();
if (!name.startsWith("get")) {
continue;
}
//过滤一些自定义的方法,例如:getClass
if (filterMethodSet.contains(name)) {
continue;
}
//过滤基本返回类型
Class> returnType = method.getReturnType();
if (filterReturnSet.contains(returnType.getSimpleName())) {
continue;
}
//返回类型是数组的情况
if (returnType.isArray()) {
Class> componentType = returnType;
//循环处理多维数组
while (componentType.isArray()) {
componentType = componentType.getComponentType();
}
//不是基本类型,加入导包
if (!filterReturnSet.contains(componentType.getSimpleName())) {
importNames.add(componentType.getName());
}
} else {
//是对象类型,直接加入导包
importNames.add(returnType.getName());
}
MethodCell methodCell = new MethodCell();
methodCell.returnValue = returnType.getSimpleName();
methodCell.name = method.getName();
methodCells.add(methodCell);
}
}
/**
* 将中间态解析为文本
*/
private void viewClass() {
StringBuilder sb = new StringBuilder();
//解析包名
sb.append("package ").append(packageName).append(";\n");
sb.append("\n");
//解析导入的包
for (String name : importNames) {
sb.append("import ").append(name).append(";\n");
}
sb.append("\n");
//解析类名,并继承原Bean对象
sb.append("public class ").append(className).append("Opt extends ").append(className).append(" {\n");
//解析方法
for (MethodCell cell : methodCells) {
sb.append("\n");
sb.append(" public Optional<").append(cell.returnValue).append("> ")
.append(cell.name.replaceFirst("get", "opt")).append("() {\n");
sb.append(" return Optional.ofNullable(").append(cell.name).append("());\n");
sb.append(" }\n");
}
sb.append("\n}\n");
view = sb.toString();
}
/**
* 将文本保存
*/
private void saveFile() {
File file = new File(filePath + "/" + className + "Opt.java");
//同名文件已存在时,不覆盖
if (file.exists()) {
System.err.println("同名文件已存在,请手动处理:file=" + file.getAbsolutePath());
return;
}
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)))) {
bw.write(view);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("生成文件:" + file.getName());
}
private class MethodCell {
private String returnValue;
private String name;
}
}
使用方法,
* @param filePath 文件生成位置,直接到包就好
* 例如 F:\Workspace\IdeaProjects\Scala\src\main\java\bean
* @param packageName 包名,就是上面目录下文件的包
* 例如 bean
* @param cs 要构造文件的字节码对象数组
* 最简单的方式,new Object().getClass()