How to Build a Java Code Generator with Freemarker Templates

This article explains the scenarios, workflow, and implementation details of creating a Java code generator using Freemarker templates, including template design, data binding, and sample code that produces POJO classes automatically.

Programmer DD
Programmer DD
Programmer DD
How to Build a Java Code Generator with Freemarker Templates

1. Introduction

A few days ago I wrote an article about code generators; many readers asked how the generator works and why template engines are needed, so this article explains the code generator process.

2. When to Use a Code Generator

When a project contains a large amount of repetitive, fixed‑format boilerplate code whose structure remains stable across iterations, a code generator can significantly improve efficiency; other situations are not suitable for a generator.

3. Code Generator Creation Process

First, create a template by extracting the fixed parts of the boilerplate code. Then bind dynamic attributes to the template, similar to filling in blanks, which makes a template engine ideal. Using the template engine syntax, data is parsed into the static template and exported as the corresponding source file.

Template engines provide rich directives for conditional data binding. For example, with Freemarker you can use a ternary expression: ${true ? 'checked' : ''} and iterate over a list:

<#list  fields as field>
    private ${field.fieldType}  ${field.fieldName};
</#list>

Common Java template engines include Freemarker , Velocity , and Thymeleaf . Although front‑end/back‑end separation reduces some use cases, they remain useful technologies.

4. Code Generator Demonstration

Using Freemarker , we create a simple generator to produce a POJO class. First, add the Freemarker dependency:

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.28</version>
</dependency>

4.1 Template Creation

The POJO structure consists of several parts:

Java class basic structure
Java class basic structure
java.lang package does not need to be imported.

These rules are encapsulated in a configuration class:

public class JavaProperties {
    // package name
    private final String pkg;
    // class name
    private final String entityName;
    // field collection, need to override equals/hash to ensure unique names
    private final Set<Field> fields = new LinkedHashSet<>();
    // import set to avoid duplicates
    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) {
        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);
    }

    // getters omitted for brevity

    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; }
        @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); }
    }
}

The static template file entity.ftl looks like:

package ${pkg};

<#list imports as impt>
import ${impt};
</#list>

/**
 * the ${entityName} type
 */
public class ${entityName} {

<#list fields as field>
    private ${field.fieldType}  ${field.fieldName};
</#list>

}

Freemarker binds data using directives such as #list for iteration.

4.2 Generator Implementation

Freemarker configuration and template processing are performed as follows:

/**
 * Simple code generator.
 */
public static void autoCodingJavaEntity(String rootPath,
                                         String templatePath,
                                         String templateName,
                                         JavaProperties javaProperties) throws IOException, TemplateException {
    Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
    configuration.setDefaultEncoding("UTF-8");
    configuration.setDirectoryForTemplateLoading(new File(templatePath));
    Template template = configuration.getTemplate(templateName);
    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)));
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(out));
    template.process(javaProperties, outputStreamWriter);
}

Running the following code generates a UserEntity POJO:

// Adjust paths to 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);

The generated Java POJO looks like:

Generated Java POJO
Generated Java POJO

5. Summary

This covers the core mechanisms of most code generators and should answer common questions about their operation.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaCode GenerationAutomationTemplate EngineFreemarkerpojo
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.