美文网首页
GreenDao-FreeMark 代码自动生成

GreenDao-FreeMark 代码自动生成

作者: 小小的coder | 来源:发表于2020-01-06 09:33 被阅读0次
    • 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

    相关文章

      网友评论

          本文标题:GreenDao-FreeMark 代码自动生成

          本文链接:https://www.haomeiwen.com/subject/kguhactx.html