GreenDao-FreeMark 代码自动生成

  • greenDao代码生成相关源码在"这里",代码很简短。

  • DaoGenerator

    我们先简要回顾一下手动生成代码时编写的代码,主控代码如下,可以发现我们是通过DaoGenerator.generateAll开始执行生成的。

    public class MyDaoGenerator
    { 
        public static void main(String[] args)
        { 
            Schema schema = new Schema(1, "com.example.laixiaolong.greendaotraning.greenDao.db"); 
             addUser(schema);  
            new DaoGenerator().generateAll(schema, "./app/src/main/java");
        }
        // ...
    }
    复制代码
    

    于是我们找到DaoGenerator类,然后重现一下上面使用到的调用链,包括构造器和generateAll等。起初我去定位ConfigurationTemplate,发现没法直接定位,于是查看它的的包归属,发现它是来自Apache的叫做FreeMarker的模板引擎。

    import freemarker.template.Configuration;
    import freemarker.template.Template;
    复制代码
    
    • 这说明greenDao基于这个模板引擎来自动生成代码的,下面看看它的使用过程:
      • 创建Configuration实例,指定版本,并设置模板所在的文件夹
      • 然后通过Configuration#getTemplate它去加载模板,比如加载dao.ftl模板文件。
      • 最后通过Template#process执行生成引擎,这个过程会替换掉模板(如dao.ftl)中的模板变量,然后将替换后的模板数据写入到指定的文件夹中,也就是.java文件。
    • 构造器:
    public DaoGenerator() throws IOException {  
        patternKeepIncludes = compilePattern("INCLUDES");
        patternKeepFields = compilePattern("FIELDS");
        patternKeepMethods = compilePattern("METHODS");
        // 1\. 配置 Configuration
        Configuration config = getConfiguration("dao.ftl");
        // 2\. 加载模板
        templateDao = config.getTemplate("dao.ftl");
        templateDaoMaster = config.getTemplate("dao-master.ftl"); 
        // ...
    }
    复制代码
    
    • generateAll
    public void generateAll(Schema schema, String outDir, String outDirEntity, String outDirTest) throws Exception {
         // ... 
        List<Entity> entities = schema.getEntities();
        for (Entity entity : entities) {
            generate(templateDao, outDirFile, entity.getJavaPackageDao(), entity.getClassNameDao(), schema, entity); 
            // ... 
        }
        generate(templateDaoMaster, outDirFile, schema.getDefaultJavaPackageDao(),schema.getPrefix() + "DaoMaster", schema, null); 
        // ...
    } 
    复制代码
    
    • generate
    private void generate(Template template, File outDirFile, String javaPackage, String javaClassName, Schema schema, Entity entity, Map<String, Object> additionalObjectsForTemplate) throws Exception {
        // 构建模板数据
        Map<String, Object> root = new HashMap<>();
        root.put("schema", schema);
        root.put("entity", entity);
        if (additionalObjectsForTemplate != null) {
            root.putAll(additionalObjectsForTemplate);
        }
        try {
           // ...
            Writer writer = new FileWriter(file);
            try {
                // 3\. 执行模板引擎, 替换掉模板中的模板变量
                template.process(root, writer);
                writer.flush();
                System.out.println("Written " + file.getCanonicalPath());
            } finally {
                writer.close();
            }
        } catch (Exception ex) {//...}
    }
    复制代码
    

FreeMark模板引擎入门

  • 通过上面的分析,我们已经知道了greenDao是使用FreeMark模板引擎,并且知道了它的使用流程,那么这里我们也来用用,走一遍上面介绍的流程。

首先去官网下载jar包,然后按上述流程走一遍:

  • 配置Configuration
private static Configuration sConfiguration;
public static void initializeConfig() {

    sConfiguration = new Configuration(Configuration.VERSION_2_3_28);
    try {
        // 也就是放置模板的文件夹
        sConfiguration.setDirectoryForTemplateLoading(new File("src/templates/"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    sConfiguration.setDefaultEncoding("UTF-8");
    sConfiguration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER); 
} 
复制代码
  • 创建模板数据,也就是我们需要填充到模板上的真实数据,有两种方式:

    • Java Bean形式,虽说是Java bean形式,但是依然需要以Map为载体。
    public static Map<String, Object> createDataModelFromBean() {
        Author author = new Author();
        Person girlFriend = new Person()
                .setName("lalala")
                .setGender("女")
                .setAge("10");
    
        author.setGirlFriend(girlFriend)
                .setGender("男")
                .setAge("24")
                .setName("horseLai");
        // 虽说是Java bean形式,但是依然需要以 Map为载体
        Map<String, Object> root = new HashMap<>();
        root.put("author", author);
        return root;
    }
    复制代码
    
    • 全部使用Map的形式
    public static  Map<String, Object> createDataModelFromMap() {
    
        Map<String, Object> author = new HashMap<>();
    
        author.put("name", "horseLai");
        author.put("age", "100");
        author.put("gender", "男");
    
        Map<String, Object> girlFriend = new HashMap<>();
        girlFriend.put("name", "lalala");
        girlFriend.put("age", "10");
        girlFriend.put("gender", "女"); 
    
        author.put("girlFriend", girlFriend); 
        return author;
    }
    复制代码
    
  • 创建模板,后缀名为.ftl,比方说我们这里创建一个名为Author.ftl的模板,对应上面的两种模板数据,关于建立模板的更多规则可以查阅手册

    • 对应于Java bean形式,需要先申明一下变量,然后就可以引用了,这里可以类比一下DataBinding的使用方式;
    <#-- @ftlvariable name="author" type="Main.Author" -->
    
    大家好,我是${author.name},性别${author.gender},今年${author.age}岁.
    
    <#if author.girlFriend.name != "null">
    这是我姑娘${author.girlFriend.name},性别${author.girlFriend.gender},今年${author.girlFriend.age}岁.
    </#if> 
    复制代码
    
    • 对应于Map形式,则是这样的。
    大家好,我是${name},性别${gender},今年${age}岁.
    
    <#if girlFriend.name != "null">
    这是我姑娘${girlFriend.name},性别${girlFriend.gender},今年${girlFriend.age}岁.
    </#if>  
    复制代码
    
  • 最后就是让引擎执行起来

public static void go(){

    try (OutputStreamWriter writer = new OutputStreamWriter(System.out);){
        // 加载模板
        Template authorTemplate = sConfiguration.getTemplate("Author.ftl");
        // 执行模板引擎
        authorTemplate.process(createDataModelFromMap(), writer); 
    } catch (IOException e) {
        e.printStackTrace();
    } catch (TemplateException e) {
        e.printStackTrace();
    }
}
复制代码
  • 输出结果:
大家好,我是horseLai,性别男,今年100岁.

这是我姑娘lalala,性别女,今年10岁. 
复制代码
  • 以上便是FreeMark模板引擎的简单入门,具体更多特性还请自行查看文档,下面附上greenDaodao-session.ftl的模板代码,有助于我们在理解greenDao代码生成原理的同时学习如何制作模板:
package ${schema.defaultJavaPackageDao}; 
import java.util.Map; 
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.AbstractDaoSession;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.identityscope.IdentityScopeType;
import org.greenrobot.greendao.internal.DaoConfig;

<#list schema.entities as entity>
import ${entity.javaPackage}.${entity.className};
</#list>

<#list schema.entities as entity>
import ${entity.javaPackageDao}.${entity.classNameDao};
</#list>

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.

/**
 * {@inheritDoc}
 * 
 * @see org.greenrobot.greendao.AbstractDaoSession
 */
public class ${schema.prefix}DaoSession extends AbstractDaoSession {

<#list schema.entities as entity>
    private final DaoConfig ${entity.classNameDao?uncap_first}Config;
</#list>        

<#list schema.entities as entity>
    private final ${entity.classNameDao} ${entity.classNameDao?uncap_first};
</#list>        

    public ${schema.prefix}DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig>
            daoConfigMap) {
        super(db);

<#list schema.entities as entity>
        ${entity.classNameDao?uncap_first}Config = daoConfigMap.get(${entity.classNameDao}.class).clone();
        ${entity.classNameDao?uncap_first}Config.initIdentityScope(type);

</#list>        
<#list schema.entities as entity>
        ${entity.classNameDao?uncap_first} = new ${entity.classNameDao}<#--
-->(${entity.classNameDao?uncap_first}Config, this);
</#list>        

<#list schema.entities as entity>
        registerDao(${entity.className}.class, ${entity.classNameDao?uncap_first});
</#list>        
    }

    public void clear() {
<#list schema.entities as entity>
        ${entity.classNameDao?uncap_first}Config.clearIdentityScope();
</#list>    
    }

<#list schema.entities as entity>
    public ${entity.classNameDao} get${entity.classNameDao?cap_first}() {
        return ${entity.classNameDao?uncap_first};
    } 
</#list>        
} 
复制代码

综述

  • 通过以上分析,我们已经了解了greenDao生成代码的方式,对于greenDao而言,代码生成原理即事先对DaoMasterDaoSessionXxxDao等建立好模板,然后借助于FreeMark模板引擎,修改模板中设定好的变量即可达到指定的效果。

链接:https://juejin.im/post/5d5c1b54518825786938643e

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容