Java之利用Freemarker模板引擎實現代碼生成器,提升效率

https://blog.csdn.net/huangwenyi1010/article/details/71249258

版權聲明:我已委託「維權騎士」(rightknights.com)爲個人文章進行維權行動 https://blog.csdn.net/huangwenyi1010/article/details/71249258

開心一笑

【1.你覺得我會眼睜睜的看着你去送死?我會閉着眼睛。2.給你講個故事,從前有個笨蛋,他很是笨,別人問他問題他只會回答「沒有」,這個故事你聽過嗎?】html

視頻教程

你們好,我錄製的視頻《Java之優雅編程之道》已經在CSDN學院發佈了,有興趣的同窗能夠購買觀看,相信你們必定會收穫到不少知識的。謝謝你們的支持……java

視頻地址:http://edu.csdn.net/lecturer/994web

提出問題

如何利用 Java + Freemarker 實現代碼生成器???spring

解決問題

* 前言 *

作業務開發的時候,常常要根據創建好的數據庫表,生成相關的 Model , DTO , Service, Controller , DAO 等等。包括基本的增刪改查。而這些細活每每比較簡單且沒有挑戰性,純粹苦力活。所以,根據公司的框架,開發一個代碼生成器是頗有必要的。sql

這裏: 
假如你有必定的java基礎; 
假如你熟悉freemarker模板引擎; 
假如你熟悉MVC框架; 
假如你熟悉Spring Data 框架數據庫

* 技術選型 *

因爲代碼生成器是要生成不少文件的,包 Test.java , TestDTO.java ,TestController.java , TestServiceImpl.java , ITestService.java , TestDAO.java 等等這些文件。全部考慮用 freemarker 強大的模板引擎,製做相關的模板。apache

* 實現思路 *

首先,假如在數據庫中有一張表 ay_test.

咱們首先要獲取數據庫的鏈接,這裏我只貼出相關的代碼。CREATE TABLE "public"."ay_test" (
    "id" varchar(32) COLLATE "default" NOT NULL,
    "name" varchar(10) COLLATE "default",
    "birth_date" timestamp(6),
    "remark" text COLLATE "default",
    CONSTRAINT "ay_test_pkey" PRIMARY KEY ("id")
)
private final String URL = "jdbc:postgresql://192.168.3.160:10655/cibpm";
private final String USER = "postgres";
private final String PASSWORD = "888888";
private final String DRIVER = "org.postgresql.Driver";

public Connection getConnection() throws Exception{
    Class.forName(DRIVER);
    Connection connection= DriverManager.getConnection(URL, USER, PASSWORD);
    return connection;
}

獲取數據庫表的元數據

private final String changeTableName = replaceUnderLineAndUpperCase(tableName);

Connection connection = getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
ResultSet resultSet = databaseMetaData.getColumns(null,"%", tableName,"%");

最後根據元數據獲取表字段,註釋等等,生成相關的文件

代碼實現

Java代碼實現

package com.evada.inno.pm.code.generate.util;
import com.evada.inno.pm.code.generate.model.ColumnClass;

import freemarker.template.Template;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 描述:代碼生成器
 * Created by Ay on 2017/5/1.
 */
public class CodeGenerateUtils {

    private final String AUTHOR = "Ay";
    private final String CURRENT_DATE = "2017/05/03";
    private final String tableName = "tm_project_quality_problem";
    private final String packageName = "com.evada.pm.process.manage";
    private final String tableAnnotation = "質量問題";
    private final String URL = "jdbc:postgresql://192.168.3.160:10655/cibpm";
    private final String USER = "postgres";
    private final String PASSWORD = "888888";
    private final String DRIVER = "org.postgresql.Driver";
    private final String diskPath = "D://";
    private final String changeTableName = replaceUnderLineAndUpperCase(tableName);

    public Connection getConnection() throws Exception{
        Class.forName(DRIVER);
        Connection connection= DriverManager.getConnection(URL, USER, PASSWORD);
        return connection;
    }

    public static void main(String[] args) throws Exception{
        CodeGenerateUtils codeGenerateUtils = new CodeGenerateUtils();
        codeGenerateUtils.generate();
    }

    public void generate() throws Exception{
        try {
            Connection connection = getConnection();
            DatabaseMetaData databaseMetaData = connection.getMetaData();
            ResultSet resultSet = databaseMetaData.getColumns(null,"%", tableName,"%");
            //生成Mapper文件
            generateMapperFile(resultSet);
            //生成Dao文件
            generateDaoFile(resultSet);
            //生成Repository文件
            generateRepositoryFile(resultSet);
            //生成服務層接口文件
            generateServiceInterfaceFile(resultSet);
            //生成服務實現層文件
            generateServiceImplFile(resultSet);
            //生成Controller層文件
            generateControllerFile(resultSet);
            //生成DTO文件
            generateDTOFile(resultSet);
            //生成Model文件
            generateModelFile(resultSet);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally{

        }
    }

    private void generateModelFile(ResultSet resultSet) throws Exception{

        final String suffix = ".java";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "Model.ftl";
        File mapperFile = new File(path);
        List<ColumnClass> columnClassList = new ArrayList<>();
        ColumnClass columnClass = null;
        while(resultSet.next()){
            //id字段略過
            if(resultSet.getString("COLUMN_NAME").equals("id")) continue;
            columnClass = new ColumnClass();
            //獲取字段名稱
            columnClass.setColumnName(resultSet.getString("COLUMN_NAME"));
            //獲取字段類型
            columnClass.setColumnType(resultSet.getString("TYPE_NAME"));
            //轉換字段名稱,如 sys_name 變成 SysName
            columnClass.setChangeColumnName(replaceUnderLineAndUpperCase(resultSet.getString("COLUMN_NAME")));
            //字段在數據庫的註釋
            columnClass.setColumnComment(resultSet.getString("REMARKS"));
            columnClassList.add(columnClass);
        }
        Map<String,Object> dataMap = new HashMap<>();
        dataMap.put("model_column",columnClassList);
        generateFileByTemplate(templateName,mapperFile,dataMap);

    }

    private void generateDTOFile(ResultSet resultSet) throws Exception{
        final String suffix = "DTO.java";
        final String path = "D://" + changeTableName + suffix;
        final String templateName = "DTO.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);
    }

    private void generateControllerFile(ResultSet resultSet) throws Exception{
        final String suffix = "Controller.java";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "Controller.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);
    }

    private void generateServiceImplFile(ResultSet resultSet) throws Exception{
        final String suffix = "ServiceImpl.java";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "ServiceImpl.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);
    }

    private void generateServiceInterfaceFile(ResultSet resultSet) throws Exception{
        final String prefix = "I";
        final String suffix = "Service.java";
        final String path = diskPath + prefix + changeTableName + suffix;
        final String templateName = "ServiceInterface.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);
    }

    private void generateRepositoryFile(ResultSet resultSet) throws Exception{
        final String suffix = "Repository.java";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "Repository.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);
    }

    private void generateDaoFile(ResultSet resultSet) throws Exception{
        final String suffix = "DAO.java";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "DAO.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);

    }

    private void generateMapperFile(ResultSet resultSet) throws Exception{
        final String suffix = "Mapper.xml";
        final String path = diskPath + changeTableName + suffix;
        final String templateName = "Mapper.ftl";
        File mapperFile = new File(path);
        Map<String,Object> dataMap = new HashMap<>();
        generateFileByTemplate(templateName,mapperFile,dataMap);

    }

    private void generateFileByTemplate(final String templateName,File file,Map<String,Object> dataMap) throws Exception{
        Template template = FreeMarkerTemplateUtils.getTemplate(templateName);
        FileOutputStream fos = new FileOutputStream(file);
        dataMap.put("table_name_small",tableName);
        dataMap.put("table_name",changeTableName);
        dataMap.put("author",AUTHOR);
        dataMap.put("date",CURRENT_DATE);
        dataMap.put("package_name",packageName);
        dataMap.put("table_annotation",tableAnnotation);
        Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"),10240);
        template.process(dataMap,out);
    }

    public String replaceUnderLineAndUpperCase(String str){
        StringBuffer sb = new StringBuffer();
        sb.append(str);
        int count = sb.indexOf("_");
        while(count!=0){
            int num = sb.indexOf("_",count);
            count = num + 1;
            if(num != -1){
                char ss = sb.charAt(count);
                char ia = (char) (ss - 32);
                sb.replace(count , count + 1,ia + "");
            }
        }
        String result = sb.toString().replaceAll("_","");
        return StringUtils.capitalize(result);
    }

}
FreeMarkerTemplateUtils工具類

FreeMarkerTemplateUtils工具類用來配置模板所在的路徑編程

package com.evada.inno.pm.code.generate.util;

import com.evada.inno.core.exception.BusinessException;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.NullCacheStorage;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;

import java.io.IOException;

/**
 * Created by Ay on 2016/7/27.
 */
public class FreeMarkerTemplateUtils {

    private FreeMarkerTemplateUtils(){}
    private static final Configuration CONFIGURATION = new Configuration(Configuration.VERSION_2_3_22);

    static{
        //這裏比較重要,用來指定加載模板所在的路徑
        CONFIGURATION.setTemplateLoader(new ClassTemplateLoader(FreeMarkerTemplateUtils.class, "/templates"));
        CONFIGURATION.setDefaultEncoding("UTF-8");
        CONFIGURATION.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        CONFIGURATION.setCacheStorage(NullCacheStorage.INSTANCE);
    }

    public static Template getTemplate(String templateName) throws IOException {
        try {
            return CONFIGURATION.getTemplate(templateName);
        } catch (IOException e) {
            throw e;
        }
    }

    public static void clearCache() {
        CONFIGURATION.clearTemplateCache();
    }
}
實體類 ColumnClass

ColumnClass 用來封裝 數據庫表元數據的信息,如字段名稱,字段類型,字段註釋等等。api

package com.evada.inno.pm.code.generate.model;

/**
 * 數據庫字段封裝類
 * Created by Ay on 2017/5/3.
 */
public class ColumnClass {

    /** 數據庫字段名稱 **/
    private String columnName;
    /** 數據庫字段類型 **/
    private String columnType;
    /** 數據庫字段首字母小寫且去掉下劃線字符串 **/
    private String changeColumnName;
    /** 數據庫字段註釋 **/
    private String columnComment;

    public String getColumnComment() {
        return columnComment;
    }

    public void setColumnComment(String columnComment) {
        this.columnComment = columnComment;
    }

    public String getColumnName() {
        return columnName;
    }

    public void setColumnName(String columnName) {
        this.columnName = columnName;
    }

    public String getColumnType() {
        return columnType;
    }

    public void setColumnType(String columnType) {
        this.columnType = columnType;
    }

    public String getChangeColumnName() {
        return changeColumnName;
    }

    public void setChangeColumnName(String changeColumnName) {
        this.changeColumnName = changeColumnName;
    }
}

freemarker 模板文件

Freemarker的模板文件,後綴都是以ftl結尾的。markdown

Model.ftl模型模板

Model.ftl能夠生成字段的屬性,而且能夠生成字段屬性對應的 set 和 get 方法,包括字段在數據庫中對應的註釋,以及一些該引入的包和類註釋。

package ${package_name}.model;
import com.evada.inno.common.domain.BaseModel;
import com.evada.inno.common.listener.ICreateListenable;
import com.evada.inno.common.listener.IDeleteListenable;
import com.evada.inno.common.listener.IModifyListenable;
import org.hibernate.annotations.Where;
import javax.persistence.*;
import java.util.Date;

/**
* 描述:${table_annotation}模型
* @author ${author}
* @date ${date}
*/
@Entity
@Table(name="${table_name_small}")
@Where(clause = "status > '0'")
@Inheritance(strategy= InheritanceType.SINGLE_TABLE)
public class ${table_name} extends BaseModel implements ICreateListenable,IModifyListenable,IDeleteListenable {

    <#if model_column?exists>
        <#list model_column as model>
    /**
    *${model.columnComment!}
    */
    <#if (model.columnType = 'varchar' || model.columnType = 'text')>
    @Column(name = "${model.columnName}",columnDefinition = "VARCHAR")
    private String ${model.changeColumnName?uncap_first};

    </#if>
    <#if model.columnType = 'timestamp' >
    @Column(name = "${model.columnName}",columnDefinition = "TIMESTAMP")
    private Date ${model.changeColumnName?uncap_first};

    </#if>
        </#list>
    </#if>

<#if model_column?exists>
<#list model_column as model>
<#if (model.columnType = 'varchar' || model.columnType = 'text')>
    public String get${model.changeColumnName}() {
        return this.${model.changeColumnName?uncap_first};
    }

    public void set${model.changeColumnName}(String ${model.changeColumnName?uncap_first}) {
        this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
    }

</#if>
<#if model.columnType = 'timestamp' >
    public Date get${model.changeColumnName}() {
        return this.${model.changeColumnName?uncap_first};
    }

    public void set${model.changeColumnName}(Date ${model.changeColumnName?uncap_first}) {
        this.${model.changeColumnName?uncap_first} = ${model.changeColumnName?uncap_first};
    }

</#if>
</#list>
</#if>

}
DTO.ftl模板

DTO.ftl 文件用來生產 DTO 值對象,該對象繼承 Model.ftl 文件中的對象。

package ${package_name}.dto;

import ${package_name}.model.${table_name};

/**
* 描述:${table_annotation}DTO
* @author ${author}
* @date ${date}
*/
public class ${table_name}DTO extends ${table_name}{

}
Service.ftl模板

Service.ftl 模板用來生成服務層實現類,在模板中已經添加了增 ,刪,改,查等方法,同時能夠注入DAO和repository 到service中,一旦文件生成,就不須要咱們去寫了,很方便,提升開發的效率。

package ${package_name}.service.impl;
import com.evada.inno.core.service.impl.BaseServiceImpl;
import ${package_name}.model.${table_name};
import ${package_name}.repository.${table_name}Repository;
import ${package_name}.service.I${table_name}Service;
import ${package_name}.repository.mybatis.${table_name}DAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ${package_name}.dto.${table_name}DTO;
import org.apache.commons.beanutils.BeanUtils;
import com.evada.inno.core.enums.StatusEnum;

/**
* 描述:${table_annotation} 服務實現層
* @author ${author}
* @date ${date}
*/
@Service
public class ${table_name}ServiceImpl extends BaseServiceImpl<${table_name}, String> implements I${table_name}Service {

    @Autowired
    private ${table_name}DAO ${table_name?uncap_first}DAO;

    @Autowired
    private ${table_name}Repository ${table_name?uncap_first}Repository;

    @Override
    public ${table_name}DTO findDTOById(String id) throws Exception {
        ${table_name}DTO ${table_name?uncap_first}DTO = ${table_name?uncap_first}DAO.findDTOById(id);
        return ${table_name?uncap_first}DTO;
    }

    @Override
    public ${table_name}DTO create${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception {
        ${table_name} ${table_name?uncap_first} = new ${table_name}();
        BeanUtils.copyProperties(${table_name?uncap_first},${table_name?uncap_first}DTO);
        ${table_name?uncap_first}.setStatus(StatusEnum.ENABLE.toString());
        ${table_name?uncap_first} = ${table_name?uncap_first}Repository.saveAndFlush(${table_name?uncap_first});
        return this.findDTOById(${table_name?uncap_first}.getId());
    }

    @Override
    public ${table_name}DTO update${table_name}(${table_name}DTO ${table_name?uncap_first}DTO)throws Exception {
        ${table_name} ${table_name?uncap_first} = new ${table_name}();
        BeanUtils.copyProperties(${table_name?uncap_first},${table_name?uncap_first}DTO);
        ${table_name?uncap_first} = ${table_name?uncap_first}Repository.saveAndFlush(${table_name?uncap_first});
        return this.findDTOById(${table_name?uncap_first}.getId());
    }
Interface.ftl 模板

Interface.ftl 用來生成服務層接口,接口中定義了增,刪,改,查等接口。

package ${package_name}.service;
import com.evada.inno.core.service.IBaseService;
import ${package_name}.model.${table_name};
import ${package_name}.dto.${table_name}DTO;
/**
* 描述:${table_annotation} 服務實現層接口
* @author ${author}
* @date ${date}
*/
public interface I${table_name}Service extends IBaseService<${table_name},String> {

    /**
    * 描述:根據Id獲取DTO
    * @param id
    */
    ${table_name}DTO findDTOById(String id)throws Exception;

    ${table_name}DTO create${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception;

    void delete${table_name}(String id) throws Exception;

    ${table_name}DTO update${table_name}(${table_name}DTO ${table_name?uncap_first}DTO) throws Exception;

}
Respontory.ftl 模板

Respontory.ftl 用來生成 Repository 文件,這一塊是 Spring Data的內容,可能不一樣的公司,使用的框架不同。

package ${package_name}.repository;
import com.evada.inno.core.repository.BaseJpaRepository;
import ${package_name}.model.${table_name};

/**
* 描述:${table_annotation} Repository接口
* @author ${author}
* @date ${date}
*/
public interface ${table_name}Repository extends BaseJpaRepository<${table_name}, String> {

}
Mappter.ftl 模板

Mappter.ftl 用來生成 MyBatis 使用到的 mappter 文件,在文件中,定義了查詢的sql語句。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="${package_name}.repository.mybatis.${table_name}DAO">

    <resultMap id="${table_name}DTOResultMap" type="${package_name}.dto.${table_name}DTO"></resultMap>

    <sql id="findDtoSql">
        select * from (
        select * from  ${table_name_small} temp
        ) t
    </sql>

    <select id="findDTOById" parameterType="String" resultMap="${table_name}DTOResultMap">
        <include refid="findDtoSql"></include>
        <where>
            and t.id = ${r'#{id}'}
        </where>
    </select>

    <select id="find${table_name}Page" parameterType="${package_name}.dto.${table_name}DTO" resultMap="${table_name}DTOResultMap">
        <include refid="findDtoSql" />
        <where>

        </where>
    </select>

</mapper>
Controller.ftl 模板

Controller.ftl 文件用來生成控制層類,類中已經幫咱們生成了,增,刪,改,查等路由。同時能夠注入接口道控制層中。

package ${package_name}.controller;
import com.evada.inno.core.annotation.Rest;
import ${package_name}.service.I${table_name}Service;
import ${package_name}.model.${table_name};
import ${package_name}.dto.${table_name}DTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.http.MediaType;
import com.evada.inno.common.domain.ResultData;
import com.evada.inno.core.util.AssertUtils;

/**
* 描述:${table_annotation}控制層
* @author ${author}
* @date ${date}
*/
@Rest(${table_name}.class)
public class ${table_name}Controller {

    @Autowired
    private I${table_name}Service ${table_name?uncap_first}Service;

    /**
    * 描述:根據Id 查詢
    * @param id  ${table_annotation}id
    */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultData findById(@PathVariable("id") String id)throws Exception {
        ${table_name}DTO ${table_name?uncap_first}DTO = ${table_name?uncap_first}Service.findDTOById(id);
        AssertUtils.checkResourceFound(${table_name?uncap_first}DTO);
        return new ResultData(${table_name}DTO.class, ${table_name?uncap_first}DTO);
    }

    /**
    * 描述:建立${table_annotation}
    * @param ${table_name?uncap_first}DTO  ${table_annotation}DTO
    */
    @RequestMapping(value = "", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultData create(@RequestBody ${table_name}DTO ${table_name?uncap_first}DTO) throws Exception {
        return new ResultData(${table_name}.class,${table_name?uncap_first}Service.create${table_name}(${table_name?uncap_first}DTO));
    }

    /**
    * 描述:刪除${table_annotation}
    * @param id ${table_annotation}id
    */
    @RequestMapping(value = "/{id}/bulk", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void deleteById(@PathVariable("id") String id) throws Exception {
        ${table_name?uncap_first}Service.deleteById(id);
    }

    /**
    * 描述:更新${table_annotation}
    * @param id ${table_annotation}id
    */
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public ResultData update${table_name}(@PathVariable("id") String id,@RequestBody ${table_name}DTO ${table_name?uncap_first}DTO) throws Exception {
        ${table_name?uncap_first}DTO.setId(id);
        return new ResultData(${table_name}.class,${table_name?uncap_first}Service.update${table_name}(${table_name?uncap_first}DTO));
    }

}

經過上面的代碼生成,咱們就能夠把生成的文件複製到相關的目錄,從新啓動系統,這樣,基本的增,刪,改,查就實現了。

讀書感悟

來自東野圭吾《信》

  • 「大多數人都想置身於遠離罪犯的地方。和犯罪者,特別是犯下搶劫殺人這樣噁心犯罪的人,哪怕是間接的關係也不想有。由於稍微有點什麼關係,沒準也會被捲入莫名其妙的事情中去。排斥犯罪者或是與其近似的人,是很是正當的行爲,也能夠說是正當防衛的本能。」
  • 犯罪者必需要有這樣的思想準備,就是本身犯罪的同時也抹殺了本身親屬在社會上的存在。
  • 所謂偏見,就是不平等看待,其產生的根源就在於人的自私本性。這裏的不平等看待,其實能夠視爲一種處理各類社會關係時的態度平衡缺失,而老是向有利於本身的一方傾斜,除非不一樣的立場之間不存在厲害衝突。通常狀況下,人都是首先從本身的角度來看問題,而不會力圖站在他人的立場來考量,徹底意義上的中立是不可能的,由己及人是必然的思惟定勢,偏見遂天然而生。

經典故事

  
【一隻小青蛙厭倦了常年生活的小水溝——水溝的水愈來愈少,它已經沒有什麼食物了。小青蛙天天都不停地蹦,想要逃離這個地方。而它的同伴整日懶洋洋地蹲在渾濁的水窪裏,說:「如今不是還餓不死 
嗎?你着什麼急?」終於有一天,小青蛙縱身一躍,跳進了旁邊的一個大河塘,那裏面有不少好吃的,它能夠自由遊弋。小青蛙呱呱地呼喚本身的夥伴:「你快過來吧,這邊簡直是天堂!」可是它的同伴說:「我在這裏已經習慣了,我從小就生活在這裏,懶得動了!」 不久,水溝裏的水乾了,小青蛙的同伴活活餓死了。】

大神文章

【1】Freemarker官網 
【2】可用於企業級開發的JAVA代碼生成器 
【3】 一個java代碼生成器的簡單實現

其餘

若是有帶給你一絲絲小快樂,就讓快樂繼續傳遞下去,歡迎點贊、頂、歡迎留下寶貴的意見、多謝支持!

相關文章
相關標籤/搜索