# How to Customize Code Generator Templates in ContiNew-Admin: A Complete Guide

> Learn to customize ContiNew-Admin code generator templates. Control file placement with YAML and code structure with Freemarker .ftl files for efficient development.

- Repository: [OpenContiNew/continew-admin](https://github.com/continew-org/continew-admin)
- Tags: how-to-guide
- Published: 2026-02-28

---

**ContiNew-Admin's code generator uses a two-layer customization system: YAML configuration defines which templates to render and where to place files, while Freemarker .ftl files control the actual code skeleton.**

ContiNew-Admin includes a powerful code generator plugin that automates the creation of backend Java classes and frontend Vue components from database metadata. Understanding how to customize these code generator templates allows you to tailor the output to your specific project standards without modifying core generator logic. The system separates configuration from presentation, enabling template customization through simple YAML edits and Freemarker file modifications.

## Understanding the Two-Layer Architecture

The generator operates through two independent layers that work together during the code generation process.

### Template Configuration Layer

The **template configuration** controls which templates are rendered, their output destinations, file extensions, and whether they target the backend or frontend. This configuration lives in [`application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/application-generator.yml) (or any Spring Boot YAML file that merges into the `generator` prefix).

The `GeneratorProperties` class binds these settings into a usable structure:

```java
@ConfigurationProperties(prefix = "generator")
public class GeneratorProperties { 
    // Binds generator.templateConfigs map from YAML
}

```

Located at [`continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/config/properties/GeneratorProperties.java`](https://github.com/continew-org/continew-admin/blob/main/continew-plugin/continew-plugin-generator/src/main/java/top/continew/admin/generator/config/properties/GeneratorProperties.java), this POJO captures the `generator.*` section and makes it available to the generation service.

### Freemarker Template Execution Layer

The **Freemarker template files** define the actual source code skeleton using placeholders filled with values from `InnerGenConfigDO`. These templates reside in:

- Backend templates: `continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/*.ftl`
- Frontend templates: `continew-plugin/continew-plugin-generator/src/main/resources/templates/frontend/*.ftl`

In [`GeneratorServiceImpl.java`](https://github.com/continew-org/continew-admin/blob/main/GeneratorServiceImpl.java) (lines 341-342), the service creates a `TemplateEngine` instance and renders the template:

```java
engine.getTemplate(templatePath).render(BeanUtil.beanToMap(innerGenConfig))

```

The rendered text becomes both the preview content displayed in the UI and the eventual file written to disk.

## The Code Generation Flow in Detail

Understanding the internal flow helps you customize templates effectively. The process follows these exact steps as implemented in `GeneratorServiceImpl`:

1. **Load configuration** – `GeneratorProperties` binds the YAML configuration into a Java map accessible via `generatorProperties.getTemplateConfigs()`.

2. **Collect metadata** – `MetaUtils.getTables()` and `MetaUtils.getColumns()` pull schema information from the configured `DataSource`.

3. **Prepare template data** – `InnerGenConfigDO` aggregates class name prefixes, package paths, import statements, and field lists. The `convertToFieldConfigDO` method resolves dictionary enums and adds necessary imports.

4. **Iterate and render** – Inside the `preview(String tableName)` method, the service loops through `templateConfigMap.entrySet()`. For each template:
   - Filters fields listed in `templateConfig.excludeFields`
   - Sets sub-package and class name via `innerGenConfig.setSubPackageName()`
   - Renders the template using the Freemarker engine with `BeanUtil.beanToMap(innerGenConfig)` converting the data object to a `Map<String, Object>`

5. **Write output** – The `generateCode()` or `downloadCode()` methods write the rendered string to disk. Backend paths resolve to `continew-admin/continew-system/src/main/java/...` while frontend components land under `continew-admin-ui/src/views/...`.

Because the template engine receives a plain `Map<String, Object>`, any new field added to `InnerGenConfigDO` automatically becomes available as a `${placeholder}` in your `.ftl` files.

## Practical Customization: Adding a Backend DTO Template

To add a custom DTO template, modify both the YAML configuration and create the corresponding `.ftl` file.

**Step 1: Configure the template in [`application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/application-generator.yml)**

```yaml
generator:
  templateConfigs:
    Dto:
      templatePath: backend/Dto.ftl
      packageName: model.dto
      excludeFields:
        - id
        - createUser
        - createTime
        - updateUser
        - updateTime
      extension: .java
      suffix: Dto

```

**Step 2: Create the template file at `continew-plugin/continew-plugin-generator/src/main/resources/templates/backend/Dto.ftl`**

```ftl
package ${innerGenConfig.subPackageName};

import lombok.Data;
<#if innerGenConfig.imports?has_content>
<#list innerGenConfig.imports as imp>
import ${imp};
</#list>
</#if>

@Data
public class ${innerGenConfig.className} {

    <#list innerGenConfig.fieldConfigs as field>
    /** ${field.columnComment} */
    private ${field.fieldType} ${field.fieldName};
    </#list>
}

```

When generating code for a table named `user`, this produces [`UserDto.java`](https://github.com/continew-org/continew-admin/blob/main/UserDto.java) in the `model.dto` package, excluding audit fields like `createTime` and `updateUser`.

## Practical Customization: Creating a Frontend Search Panel

Frontend customization follows the same pattern, using the `backend: false` flag.

**Step 1: Add the configuration**

```yaml
generator:
  templateConfigs:
    SearchPanel:
      templatePath: frontend/SearchPanel.ftl
      packageName: src/views
      extension: .vue
      backend: false

```

**Step 2: Create the Vue template**

```ftl
<template>
  <div class="search-panel">
    <el-form :model="query">
      <#list innerGenConfig.fieldConfigs as field>
      <#if field.showInQuery>
      <el-form-item label="${field.columnComment}">
        <el-input v-model="query.${field.fieldName}" placeholder="请输入${field.columnComment}" />
      </el-form-item>
      </#if>
      </#list>
      <el-form-item>
        <el-button type="primary" @click="search">搜索</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script setup>
import { ref } from 'vue';
const query = ref({});
function search() {
  console.log('search params:', query.value);
}
</script>

<style scoped>
.search-panel { padding: 16px; }
</style>

```

This generates [`SearchPanel.vue`](https://github.com/continew-org/continew-admin/blob/main/SearchPanel.vue) under `continew-admin-ui/src/views/<moduleName>/`, including only fields marked for query display.

## Triggering Generation via the REST API

The generator exposes REST endpoints through [`GeneratorController.java`](https://github.com/continew-org/continew-admin/blob/main/GeneratorController.java) for programmatic access:

**Preview generated files:**

```bash
curl -X POST "http://localhost:8080/api/generator/preview" \
     -H "Content-Type: application/json" \
     -d '{"tableNames":["user"]}'

```

**Download as ZIP:**

```bash
curl -X POST "http://localhost:8080/api/generator/download" \
     -H "Content-Type: application/json" \
     -d '{"tableNames":["user"]}' \
     -o continew-code.zip

```

**Generate directly to disk:**

```bash
curl -X POST "http://localhost:8080/api/generator/generate" \
     -H "Content-Type: application/json" \
     -d '{"tableNames":["user"]}'

```

These endpoints accept the table names and execute the full generation flow using your customized templates.

## Summary

- **Two-layer architecture**: YAML configuration controls rendering logic while Freemarker `.ftl` files define code structure.
- **Configuration location**: Edit [`application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/application-generator.yml) to add or modify template entries, defining paths, extensions, and exclusions.
- **Template location**: Place backend templates in `templates/backend/` and frontend templates in `templates/frontend/` within the generator plugin module.
- **Data model**: `InnerGenConfigDO` provides the data map; extending it adds new placeholders for templates.
- **Hot reload**: Template changes apply immediately on the next generation request without recompiling the Java code.
- **Entry points**: Use `GeneratorController` endpoints or the admin UI to trigger generation with custom templates.

## Frequently Asked Questions

### Where are the default generator templates located in the source code?

Default templates reside in `continew-plugin/continew-plugin-generator/src/main/resources/templates/`, separated into `backend/` for Java files (entities, services, controllers) and `frontend/` for Vue components. The default configuration mapping these templates is defined in [`continew-server/src/main/resources/config/application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/continew-server/src/main/resources/config/application-generator.yml).

### Do I need to restart the application after modifying templates?

No restart is required for Freemarker template changes (`.ftl` files), as they are read at request time. However, if you modify [`application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/application-generator.yml) to add new template configurations, you must restart the Spring Boot application to rebind the `GeneratorProperties` configuration class.

### How do I add custom variables to my Freemarker templates?

Add fields to the `InnerGenConfigDO` class located in the generator plugin module. Since `GeneratorServiceImpl` converts this object to a Map using `BeanUtil.beanToMap()`, any new getter methods automatically become available as `${variableName}` placeholders in your templates.

### Can I exclude specific database columns from generated code?

Yes. Use the `excludeFields` list in your template configuration within [`application-generator.yml`](https://github.com/continew-org/continew-admin/blob/main/application-generator.yml). Specify the Java field names (such as `id`, `createTime`, or `updateUser`) to remove them from that specific template's output while retaining them in other generated files.