
1. preface
I wrote an article aboutMybatis Plus Code GeneratorIn my article, many students privately asked me how this code generator works and why I need to use some template engines, so today I will explain the code generator process.
2. Usage scenarios for code generators
We have a lot of boilerplate code in our coding. The format is relatively fixed, the structure is relatively stable with the iteration of the project, and the quantity is huge. If we write too much of this code, it will not have any technical content. In this case, the code generator can effectively improve our efficiency, but it is not suitable to use a code generator in other situations.
3. Code generator production process
First of all, we need to make a template and extract the fixed format of the boilerplate code. Then bind dynamic attributes to the template, just like filling in the blanks. So the template engine is the most suitable in this process. We use the template engine's syntax to dynamically parse the data into static templates, and then export it to the corresponding file in programming.
In addition, the template engine has a rich instruction set for binding data, which allows us to dynamically bind data to templates based on conditions. toFreemarkerTake an example:
ternary expression:
${true ? 'checked': ''}
There is also the traversal list we will use later:
<#list fields as field>
private ${field.fieldType} ${field.fieldName};
</#list>
The template engines we commonly use in Java development are:Freemarker、Velocity、Thymeleaf , withWebThe usage scenarios of the popular template engine that separates the front and back ends in development are being compressed, but it is still a useful technology.
4. Code generator demonstration
Next, we useFreemarkerWrite a simple code generator for an example to generatePOJOClass. need to introduceFreemarkerdependence.
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
4.1 formwork fabrication
POJOThe structure can be divided into the following parts:
java.lang
Packages do not need to be imported.
So encapsulate these rules into configuration classes:
public class JavaProperties {
// 包名
private final String pkg;
// 类名
private final String entityName;
// 属性集合 需要改写 equals hash 保证名字可不重复 类型可重复
private final Set<Field> fields = new LinkedHashSet<>();
// 导入类的不重复集合
private final Set<String> imports = new LinkedHashSet<>();
public JavaProperties(String entityName, String pkg) {
this.entityName = entityName;
this.pkg = pkg;
}
public void addField(Class<?> type, String fieldName) {
// 处理 java.lang
final String pattern = "java.lang";
String fieldType = type.getName();
if (!fieldType.startsWith(pattern)) {
// 处理导包
imports.add(fieldType);
}
Field field = new Field();
// 处理成员属性的格式
int i = fieldType.lastIndexOf(".");
field.setFieldType(fieldType.substring(i + 1));
field.setFieldName(fieldName);
fields.add(field);
}
public String getPkg() {
return pkg;
}
public String getEntityName() {
return entityName;
}
public Set<Field> getFields() {
return fields;
}
public Set<String> getImports() {
return imports;
}
/**
* 成员属性封装对象.
*/
public static class Field {
// 成员属性类型
private String fieldType;
// 成员属性名称
private String fieldName;
public String getFieldType() {
return fieldType;
}
public void setFieldType(String fieldType) {
this.fieldType = fieldType;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
/**
* 一个类的成员属性 一个名称只能出现一次
* 我们可以通过覆写equals hash 方法 然后放入Set
*
* @param o 另一个成员属性
* @return 比较结果
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Field field = (Field) o;
return Objects.equals(fieldName, field.fieldName);
}
@Override
public int hashCode() {
return Objects.hash(fieldType, fieldName);
}
}
}
Then there is the static templateentity.ftl
package ${pkg};
<#list imports as impt>
import ${impt};
</#list>
/**
* the ${entityName} type
* @author felord.cn
*/
public class ${entityName} {
<#list fields as field>
private ${field.fieldType} ${field.fieldName};
</#list>
}
Here we useFreemarkerThe syntax for binding data, such asList
Iterative rendering.
4.2 Generator writing
FreemarkerConfigure and obtain template objects through declarationsfreemarker.template
, the objectprocess
The method can bind dynamic data into a template and export it as a file, and finally implements a code generator. The core code is as follows:
/**
* Simple code generator.
*
* @param rootPath maven's java directory
* @param templatePath Folder where templates are stored
* @param templateName the name of the template
* @param javaProperties requires encapsulation of the rendered object
* @throws IOException the io exception
* @throws TemplateException the template exception
*/
public static void autoCodingJavaEntity(String rootPath,
String templatePath,
String templateName,
JavaProperties javaProperties) throws IOException, TemplateException {
// freemarker configuration
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
configuration.setDefaultEncoding("UTF-8");
//Specify the path to the template
configuration.setDirectoryForTemplateLoading(new File(templatePath));
//Get the template under the path according to the template name
Template template = configuration.getTemplate(templateName);
//Handle path issues
final String ext = ".java";
String javaName = javaProperties.getEntityName().concat(ext);
String packageName = javaProperties.getPkg();
String out = rootPath.concat(Stream.of(packageName.split("\\."))
.collect(Collectors.joining("/", "/", "/" + javaName)));
//Define an output stream to export code files
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(out));
// freemarker engine exports dynamic data-bound templates into files
template.process(javaProperties, outputStreamWriter);
}
You can generate one by executing the following codeUserEntity
thePOJO:
//The path is adjusted according to the characteristics of your project
String rootPath = "C:\\Users\\felord\\IdeaProjects\\codegenerator\\src\\main\\java";
String packageName = "cn.felord.code";
String templatePath = "C:\\Users\\felord\\IdeaProjects\\codegenerator\\src\\main\\resources\\templates";
String templateName = "entity.ftl";
JavaProperties userEntity = new JavaProperties("UserEntity", packageName);
userEntity.addField(String.class, "username");
userEntity.addField(LocalDate.class, "birthday");
userEntity.addField(LocalDateTime.class, "addTime");
userEntity.addField(Integer.class, "gender");
userEntity.addField(Integer.class, "age");
autoCodingJavaEntity(rootPath, templatePath, templateName, userEntity);
Is the generated effect similar to the handwritten one:
5. summary
This is the mechanism of most code generators, and I hope it can answer some netizens 'questions.
Comments0