自动生成带Optional方法的派生Bean类,对Mybatis反向工程结果的加强

效果展示

原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()

你可能感兴趣的:(编程工具)