闲来无聊,写了个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();
}
}