生成Java Bean的Builder的工具类GenerateBuilderUtil

闲来无聊,写了个Java Bean的Builder类的生成工具。

其实Java Bean的Builder类,一般应该放在Java Bean类中,作为Java Bean的一个静态内部类。不过也有一些情况,希望将Java Bean单独作为一个类。

写这个工具类的目的,就是为了偷懒,不想手工写Java Bean的Builder类了!

package com.test.generatecode;

import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.apache.commons.text.TextStringBuilder;

/**
 * 生成某个Java Bean的Builder类。
 */
public final class GenerateBuilderUtil {
    private GenerateBuilderUtil() {}
    /**
     * 四个空格,以替代制表位TAB
     */
    private static final String FOUR_SPACES = "    ";
    private static final FastDateFormat DATE_FORMAT = FastDateFormat.getInstance("yyyy年MM月dd日");
    private static final String AUTHOR_NAME = "Author name";
    /**
     * 一些不需要导入的类
     */
    private static final Set<Class<?>> IGNORE_IMPORT_CLASSES = Sets.newHashSet();
    /**
     * 不需要导入的类的全路径类名
     */
    private static final Set<String> IGNORE_IMPORT_CLASS_NAMES = IGNORE_IMPORT_CLASSES.stream()
            .map(Class::getCanonicalName).collect(Collectors.toSet());
    /**
     * java.lang包的正则匹配
     */
    private static final Pattern JAVA_LANG_PATTERN = Pattern.compile("java\\.lang\\.[a-zA-Z]{1,256}");

    public static void main(String[] args) throws Exception {
        List<Class<?>> clazzList = Lists.newArrayList(Student.class);
        generate(clazzList);
    }

    public static void generate(List<Class<?>> clazzList) throws Exception {
        for (Class<?> clazz : clazzList) {
            String packageName = clazz.getPackage().getName();// 类的包名
            String classFullName = clazz.getCanonicalName();// Java Bean类的全路径类名
            String className = StringUtils.substringAfterLast(classFullName, ".");// Java Bean类的类名
            String uncapClassName = StringUtils.uncapitalize(className);// 类名首字母小写

            // 得到Java Bean的所有Field
            List<Field> fields = Lists.newArrayList();
            Arrays.stream(clazz.getDeclaredFields()).forEach(fields::add);
            while ((clazz = clazz.getSuperclass()) != Object.class) {
                Arrays.stream(clazz.getDeclaredFields()).forEach(fields::add);
            }

            List<Map<String, String>> fieldList = Lists.newArrayListWithExpectedSize(fields.size());
            Set<String> shouldImportClasses = Sets.newHashSet();
            for (Field f : fields) {
                String fieldName = f.getName();
                Class<?> fieldType = f.getType();
                String fieldTypeName = fieldType.getCanonicalName();// 属性类型的全路径类名
                String genericTypeName = f.getGenericType().getTypeName();// 属性泛型的字符串表示,比如java.util.List<java.util.Map<java.lang.String, java.lang.String>>

//                System.out.println(String.format("%s -> %s", fieldTypeName, genericTypeName));

                String fieldTypeGenericType = null;// 属性类型的泛型的简化字符串形式,比如List<Map<String, String>>
                if (!fieldTypeName.equals(genericTypeName)) {
                    fieldTypeGenericType = generateGenericTypeStr(genericTypeName, fieldTypeName, shouldImportClasses);
                }
                
                fieldList.add(
                        ImmutableMap.of("fieldName", fieldName, 
                                "capFieldName", StringUtils.capitalize(fieldName), 
                                "fieldTypeName", fieldTypeName.contains(".") ? StringUtils.substringAfterLast(fieldTypeName, ".") : fieldTypeName,
                                "fieldTypeGenericType", Strings.nullToEmpty(fieldTypeGenericType)));
                if (shouldImport(fieldTypeName)) {
                    shouldImportClasses.add(fieldTypeName);
                }
            }
            
            try (PrintWriter pw = new PrintWriter(
                    new OutputStreamWriter(
                            new FileOutputStream("C:\\Users\\Administrator\\Desktop\\" + className + "Builder.java"), StandardCharsets.UTF_8))) {
                pw.println("package " + packageName + ";");
                pw.println("");
                shouldImportClasses.stream().forEach(importClass -> pw.println("import " + importClass + ";"));
                pw.println("");
                pw.println("/**");
                pw.println(" * " + className + "的Builder。<br>");
                pw.println(" * 每次" + className + "有大的变动的时候(比如增加了一个属性),都应该适当更新该Builder。");
                pw.println(" * ");
                pw.println(" * @author " + AUTHOR_NAME);
                pw.println(" * @date " + DATE_FORMAT.format(System.currentTimeMillis()));
                pw.println(" */");
                pw.println("public final class " + className + "Builder {");
                pw.println(String.format("%sprivate %s %s;", FOUR_SPACES, className, uncapClassName));
                pw.println(String.format("%sprivate %sBuilder() {", FOUR_SPACES, className));
                pw.println(String.format("%s%sthis.%s = new %s();", FOUR_SPACES, FOUR_SPACES, uncapClassName, className));
                pw.println(FOUR_SPACES + "}");
                pw.println("");
                pw.println(String.format("%spublic static %sBuilder newBuilder() {", FOUR_SPACES, className));
                pw.println(String.format("%s%sreturn new %sBuilder();", FOUR_SPACES, FOUR_SPACES, className));
                pw.println(FOUR_SPACES + "}");
                pw.println("");
                pw.println(String.format("%spublic %s build() {", FOUR_SPACES, className));
                pw.println(String.format("%s%sreturn this.%s;", FOUR_SPACES, FOUR_SPACES, uncapClassName));
                pw.println(FOUR_SPACES + "}");
                pw.println("");
                for (Map<String, String> fieldMap : fieldList) {
                    String fieldName = fieldMap.get("fieldName");
                    String capFieldName = fieldMap.get("capFieldName");
                    String fieldTypeName = fieldMap.get("fieldTypeName");
                    String fieldTypeGenericType = fieldMap.get("fieldTypeGenericType");
                    if (StringUtils.isEmpty(fieldTypeGenericType)) {// 字段类型没有泛型
                        pw.println(String.format("%spublic %sBuilder set%s(%s %s) {", FOUR_SPACES, className,
                                capFieldName, fieldTypeName, fieldName));
                    } else {
                        pw.println(String.format("%spublic %sBuilder set%s(%s<%s> %s) {", FOUR_SPACES, className,
                                capFieldName, fieldTypeName, fieldTypeGenericType, fieldName));
                    }
                    pw.println(String.format("%s%sthis.%s.set%s(%s);", FOUR_SPACES, FOUR_SPACES, uncapClassName, capFieldName, fieldName));
                    pw.println(FOUR_SPACES + FOUR_SPACES + "return this;");
                    pw.println(FOUR_SPACES + "}");
                }
                pw.println("}");
            }
        }
    }

    /**
     * 根据类名判断是否应该导入。
     *
     * @param classFullName
     * @return
     */
    private static boolean shouldImport(String classFullName) {
        if (!classFullName.contains(".")) {// 比如int等基本类型
            return false;
        }
        // java.lang包下的类不需要导入
        if ("java.lang".equals(StringUtils.substringBeforeLast(classFullName, "."))) {
            return false;
        }

        return !IGNORE_IMPORT_CLASS_NAMES.contains(classFullName);
    }

    /**
     * 属性类型的泛型的简化字符串形式。
     *
     * @param genericTypeName
     * @param fieldTypeName
     * @param shouldImportClasses
     * @return
     */
    public static String generateGenericTypeStr(String genericTypeName, String fieldTypeName, Set<String> shouldImportClasses) {
        String genericTypeStr = new TextStringBuilder(genericTypeName)
                .deleteCharAt(genericTypeName.lastIndexOf(">"))
                .deleteFirst(fieldTypeName)
                .deleteFirst('<')
                .build();

        List<String> matchList = Lists.newArrayList();
        // java.lang包下的类不需要导入
        Matcher matcher = JAVA_LANG_PATTERN.matcher(genericTypeStr);
        while (matcher.find()) {
            matchList.add(matcher.group());
        }
        // shouldImportClasses中的类会使用import进行导入
        for (String className : shouldImportClasses) {
            Matcher m = Pattern.compile(className).matcher(genericTypeStr);
            while (m.find()) {
                matchList.add(m.group());
            }
        }

        TextStringBuilder builder = new TextStringBuilder(genericTypeStr);
        for (String matchStr : matchList) {
            String replacement = StringUtils.substringAfterLast(matchStr, ".");
            builder.replaceFirst(matchStr, replacement);
        }
        return builder.toString();
    }
}

猜你喜欢

转载自blog.csdn.net/frankingly/article/details/89515778