バックエンドフレームワークの構築-Velocityテンプレートエンジンの応用


開発効率を向上させるためには、通常、いくつかのパターンが固定された重複的な労働を抽出して、後で使用するときに、主義を持っていけばいいのです.これにより、開発効率が向上し、エラーのリスクも低減できます.
この考えは私たちの日常の仕事の中でどこにでもあると言えます.私たちは複雑な工事を完成して、何でも自分で書く必要はありません.第三者のjarバッグを利用して、よく使うapcheのcommons-lang 3バッグのような仕事の半分の効果を達成することができます.Javaの継承、私たち自身がカプセル化したツールクラスなどです.一方、ソースファイルについては、会社が成熟したフレームワークを持っていれば、私たちの開発はフレームワーク制定の約束に従って開発されています.私たちはある業務の制御層、業務層、持続層を作成する際に、実際にはかなりの部分の作業が重複しています.
では、ソースファイルの作成について、私たちはこっそり怠けることができますか?答えは間違いなくできます.テンプレートエンジン技術を利用して、不変の部分をテンプレートファイルに書き、可変の部分を変数としてテンプレートエンジンのコンテキストに渡し、最終的に私たちが望んでいるソースファイルを生成することができます.
テンプレートエンジンの製品はたくさんあります.例えば、フロントエンドテンプレートartTemplate、バックエンドテンプレートVelocity、FreeMarkerなどです.本稿では、Velocityを例に、実戦での応用をまとめてみましょう.
1.基礎知識
構築プロセスに関する基礎知識には、Maven、Velocity、工場モデル、構築者モデル、ユニットテストの基礎に慣れていない学生が含まれています.次の2つの文章Velocityの基礎Velocity文法の要約を見ることをお勧めします.
2.建設工事
2.1モジュールディレクトリ
コード生成機能は、私が設計したバックグラウンドフレームワークでは、独立したモジュールとして存在し、Mavenを使用して構築されています.builderディレクトリ:コンストラクタモードアプリケーション.テーブル構造を表すTableエンティティはやや複雑なため、コンストラクタモードを使用してTableオブジェクトを構築します.実は使わなくてもいいです.Tableは複雑ではありませんから、習った設計モードの知識factoryディレクトリ:工場モードの応用を復習するためです.ソースファイルを構築する際には、Controller、Service、Dao、Domainといったいくつかのタイプのファイルに関わるため、異なるタイプのファイルに対して対応する処理クラスを使用するため、ファクトリモードhandlerディレクトリを使用する:ソースファイルを生成するコアコードmodelディレクトリ:domainを生成する際、フィールドはデータベースのテーブルから読み取る必要があるため、したがって、テーブルに対応するエンティティクラスの処理が容易なutilsディレクトリ:ツールクラスGeneratorが構築する.JAva:プログラムプライマリファイル、呼び出しエントリtestディレクトリ:ユニットテスト
.
├── generator.iml
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── wt
    │   │           └── master
    │   │               └── generator
    │   │                   ├── Generator.java
    │   │                   ├── builder
    │   │                   │   ├── MySqlTableBuilder.java
    │   │                   │   └── TableBuilder.java
    │   │                   ├── factory
    │   │                   │   └── GeneratorFactory.java
    │   │                   ├── handler
    │   │                   │   ├── BaseGenerator.java
    │   │                   │   ├── ControllerGeneratorHandler.java
    │   │                   │   ├── DomainGeneratorHandler.java
    │   │                   │   ├── MapperGeneratorHandler.java
    │   │                   │   └── ServiceGeneratorHandler.java
    │   │                   ├── model
    │   │                   │   └── Table.java
    │   │                   └── util
    │   │                       ├── JdbcUtils.java
    │   │                       ├── SpringContextUtils.java
    │   │                       ├── TableColumnUtils.java
    │   │                       └── TableInfoUtils.java
    │   └── resources
    │       ├── config
    │       │   ├── applicationContext.xml
    │       │   └── db.properties
    │       └── template
    │           ├── controller.java.vm
    │           ├── dao.java.vm
    │           ├── domain.java.vm
    │           ├── service.java.vm
    │           └── serviceimpl.java.vm
    └── test
        └── com.wt.master.generator
            └── GeneratorTest.java

2.2導入依存


    
        j2ee
        com.wt.master
        1.0-SNAPSHOT
        ../version/
    
    4.0.0

    generator
    
        
        
            org.apache.velocity
            velocity
            1.7
        
        
        
            junit
            junit
            4.12
            test
        
        
            com.wt.master
            core
            1.0-SNAPSHOT
        
        
        
            com.mchange
            c3p0
            0.9.5.4
        
    

3.コアコード
3.1テンプレートファイルの定義
コントロール層生成テンプレートを例として不変の部分を直接書く.vmファイルではテンプレートファイルの中で、変化する可能性のある部分を変数として抽出し、変数の値をVelocityContextからVelocityアーキテクチャに取得し、コンテキストの定義があり、コンテキストを通じてプログラムは変数をコンテキストオブジェクトに入れる.テンプレートはコンテキストから対応する変数の値を取得します.取得方法は${変数名}です.Velocityテンプレートファイルの構文については、上記の2つの記事を参照してください.
package ${packagePath}.controller;

import ${packagePath}.domain.${moduleName};
import ${packagePath}.service.${moduleName}Service;
import com.wt.master.core.base.BaseController;
import com.wt.master.core.helper.QueryHelper;
import com.wt.master.core.request.HttpResultEntity;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ${moduleNameCN}   
 *
 * @author
 * @date
 */
@Api(value = "${moduleNameCN}   ", tags = "${moduleName}Controller", description = "${moduleNameCN}   " )
@RestController
@RequestMapping("/${moduleName}" )
@Slf4j
public class ${moduleName}Controller extends BaseController {
    @Autowired
    private ${moduleName}Service ${lowerModuleName}Service;

    @Override
    protected ${moduleName}Service getService() {
        return ${lowerModuleName}Service;
    }

}

3.2工場クラス定義
ソースファイルの種類によって、異なる処理クラスが定義され、ファクトリモードで対応する処理クラスが返されます.
package com.wt.master.generator.factory;

import com.wt.master.generator.Generator;
import com.wt.master.generator.handler.*;

/**
 *      
 *
 * @author [email protected]
 * @date Jun 18, 2019 at 4:02:23 PM
 */
public class GeneratorFactory {
    public static BaseGenerator create(Generator.GenerateItem item) {
        BaseGenerator baseGenerator = null;
        switch (item) {
            case service:
                baseGenerator = new ServiceGeneratorHandler();
                break;
            case controller:
                baseGenerator = new ControllerGeneratorHandler();
                break;
            case mapper:
                baseGenerator = new MapperGeneratorHandler();
                break;
            case domain:
                baseGenerator = new DomainGeneratorHandler();
                break;
            default:
                baseGenerator = new ControllerGeneratorHandler();
        }
        return baseGenerator;
    }
}

3.3ソース生成処理クラス定義
コントローラ処理クラスを例に抽象クラスを定義し、ベースクラスとして抽象メソッドgenerateを定義し、ソースファイルを生成する処理メソッド抽象メソッドgetFilePathを定義し、生成ファイルを取得するパスメソッドの実現は具体的な実装クラスによって実現される
package com.wt.master.generator.handler;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;

/**
 *      
 *
 * @author [email protected]
 * @date May 12, 2019 at 10:44:53 AM
 */
public abstract class BaseGenerator {
    /**
     *     
     *
     * @param tableName      
     * @param moduleName        
     * @param moduleNameCN      
     * @param packagePath     
     * @return
     */
    public abstract BaseGenerator generate(String tableName, String moduleName, String moduleNameCN,
                                           String packagePath);

    /**
     *       
     * @param packagePath
     * @return
     */
    public abstract String getFilePath(String packagePath,String moduleName);

    /**
     *      
     *
     * @param templateName       
     * @return
     */
    Template getTemplate(String templateName) {
        VelocityEngine ve = new VelocityEngine();
        ve.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath" );
        ve.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
        ve.setProperty("input.encoding","utf-8");
        ve.setProperty("output.encoding","utf-8");
        ve.init();
        Template t = ve.getTemplate("/template/" + templateName);
        return t;
    }

    protected void merge(Template template, VelocityContext ctx, String path) {
        File file = new File(path);
        if(!file.exists()){
            new File(file.getParent()).mkdirs();
        }else{
            System.out.println("    "+file.getAbsolutePath());
        }

        PrintWriter writer = null;
        try {
            writer = new PrintWriter(path);
            template.merge(ctx, writer);
            writer.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            writer.close();
        }
    }

    /**
     *      
     * @return
     */
    protected String getRootPath(){
        String rootPath = "";
        try {
            File file = new File(BaseGenerator.class.getResource("/").getFile());
            rootPath = file.getParent();
            rootPath = java.net.URLDecoder.decode(rootPath.substring(0, rootPath.indexOf("target") - 1), "utf-8");
            return rootPath+"/src/main/java";
        } catch (Exception e) {
            e.printStackTrace();
        }
        return rootPath;
    }

    /**
     *           
     * @param packagePath
     * @return
     */
    protected String convertPackagePathToFilePath(String packagePath){
        StringBuilder path = new StringBuilder();
        path.append("/" );
        path.append(packagePath.replace(".", "/" ));
        path.append("/");
        return path.toString();
    }

}

3.4ツールクラスの定義
このクラスは主にテーブルの情報と対応するフィールド情報を取得する
package com.wt.master.generator.util;

import com.wt.master.generator.model.Table;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *     
 *
 * @author [email protected]
 * @date Apr 23, 2019 at 11:36:30 PM
 */
public class TableInfoUtils {

    public static final String JDBC_TEMPLATE = "jdbcTemplate";

    public static Table getTableColumnList(String tableName) {
        JdbcTemplate jdbcTemplate = (JdbcTemplate) SpringContextUtils.getBean(JDBC_TEMPLATE);

        List> tableInfo = jdbcTemplate.queryForList(getTableStructureSql(tableName));
        if (CollectionUtils.isEmpty(tableInfo)) {
            throw new RuntimeException(" :" + tableName + "   " );
        }

        List> columns = jdbcTemplate.queryForList(getColumnStructureSql(tableName));

        return TableColumnUtils.convertToColumn(columns, tableInfo.get(0));
    }

    /**
     *           SQL
     *
     * @param tableName   
     * @return
     */
    private static String getColumnStructureSql(String tableName) {
        StringBuilder sql = new StringBuilder();
        sql.append("select column_name, data_type,column_comment,column_key " );
        sql.append("from information_schema.columns " );
        sql.append("where table_name = '" + tableName + "'" );
        return sql.toString();
    }

    /**
     *       
     * @param tableName
     * @return
     */
    private static String getTableStructureSql(String tableName) {
        StringBuilder sql = new StringBuilder();
        sql.append("select table_name,table_comment " );
        sql.append("from information_schema.tables " );
        sql.append("where table_name= '" + tableName + "'" );
        return sql.toString();
    }


}

3.5アプリケーション入口
createメソッドを使用してソースファイルを生成する
package com.wt.master.generator;

import com.wt.master.generator.builder.MySqlTableBuilder;
import com.wt.master.generator.builder.TableBuilder;
import com.wt.master.generator.factory.GeneratorFactory;
import com.wt.master.generator.handler.BaseGenerator;
import com.wt.master.generator.model.Table;
import com.wt.master.generator.util.TableInfoUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;

import java.util.List;

/**
 *       
 *
 * @author [email protected]
 * @date Apr 23, 2019 at 10:41:51 PM
 */
public class Generator {

    /**
     *       
     *
     * @param tableName      
     * @param moduleName        
     * @param moduleNameCN      
     * @param packagePath      
     * @param item             
     */
    public static void create(String tableName, String moduleName, String moduleNameCN, String packagePath,
                              GenerateItem... item) {
        if (StringUtils.isBlank(tableName) || StringUtils.isBlank(moduleName) || StringUtils.isBlank(moduleNameCN) || StringUtils.isBlank(packagePath)) {
            throw new IllegalArgumentException("    !" );
        }

        for (GenerateItem generateItem : item) {
            BaseGenerator baseGenerator = GeneratorFactory.create(generateItem);
            baseGenerator.generate(tableName, moduleName, moduleNameCN, packagePath);
        }
    }


    public enum GenerateItem {
        controller, service, mapper, domain
    }

}

4.ユニットテスト
package com.wt.master.generator;

import org.junit.Test;
import org.junit.Before;
import org.junit.After;

/**
 * Generator Tester.
 *
 * @author 
 * @version 1.0
 * @since 
Jun 18, 2019
*/
public class GeneratorTest {
@Test
public void testCreate() throws Exception {
//TODO: Test goes here...
Generator.create(「SecurityRolet」,「SecurityRole」,「ロール 」,「com.wt.common.security」,
Generator.GenerateItem.controller, Generator.GenerateItem.service, Generator.GenerateItem.mapper,
Generator.GenerateItem.domain);
}
}
ソースgithubアドレス