The introduction

  • Before that, I wrote two articles on “Basic Usage” and “Core Class Understanding” of greenDao, for those who are interested.

  • Gradle generates DaoMaster, Dao, Session, etc. Java files in the specified folder during compilation. There is also a handwritten code generator that automatically generates a.Java file including Entity after filling in the relevant properties. Well, it’s no wonder that greenDao is so efficient because it generates.Java code files at compile time that are consistent with what we wrote manually, and then compiles to.class, so that the runtime is consistent with running a normal static program.

  • After all, it’s worth learning. That’s the purpose of this article: to analyze the greenDao code generation process and learn how to automatically generate code.

Source code analysis

  • GreenDao code generation related source code “here”, the code is very short.

  • DaoGenerator

    Let’s start with a brief review of the code we wrote when we generated the code manually. The master code is shown below, and you can see that we start the build with 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");
        }
        // ...
    }
    Copy the code

    So let’s go to the DaoGenerator class and recreate the call chain used above, including the constructor and generateAll. Initially I tried to locate Configuration and Template, but I couldn’t locate it directly, so I checked its package ownership and found that it was a Template engine from Apache called FreeMarker.

    import freemarker.template.Configuration;
    import freemarker.template.Template;
    Copy the code
    • This suggests thatgreenDaoisAutomatically generate code based on this template engineHere’s how it works:
      • createConfigurationExample, specify the version, and set the folder where the template resides
      • Then throughConfiguration#getTemplateIt loads the template, like loaddao.ftlTemplate file.
      • At last,Template#processExecute the build engine, which replaces the template (e.gdao.ftl), and writes the replaced template data to the specified folder, i.e.javaFile.
    • The constructor:
    public DaoGenerator() throws IOException {  
        patternKeepIncludes = compilePattern("INCLUDES");
        patternKeepFields = compilePattern("FIELDS");
        patternKeepMethods = compilePattern("METHODS"); Configuration Configuration config = getConfiguration("dao.ftl");
        // 2. 加载模板
        templateDao = config.getTemplate("dao.ftl");
        templateDaoMaster = config.getTemplate("dao-master.ftl"); 
        // ...
    }
    Copy the code
    • 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); 
        // ...
    } 
    Copy the code
    • generate
    private void generate(Template template, File outDirFile, String javaPackage, String javaClassName, Schema schema, The Entity, the Entity Map < String, Object > additionalObjectsForTemplate) throws the Exception {/ / build template data 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); Run the template engine to replace the template variable template.process(root, writer) in the template. writer.flush(); System.out.println("Written " + file.getCanonicalPath());
            } finally {
                writer.close();
            }
        } catch (Exception ex) {//...}
    }
    Copy the code

FreeMark Template engine getting started

  • From the above analysis, we already know thatgreenDaoIs the use ofFreeMarkTemplate engine, and know how to use it, so here we also use, go through the process introduced above.

First go to the official website to download the JAR package, and then follow the above process:

  • configurationConfiguration
private static Configuration sConfiguration;
public static void initializeConfig() { sConfiguration = new Configuration(Configuration.VERSION_2_3_28); Try {/ / is placed template folder sConfiguration setDirectoryForTemplateLoading (new File ("src/templates/"));
    } catch (IOException e) {
        e.printStackTrace();
    }
    sConfiguration.setDefaultEncoding("UTF-8");
    sConfiguration.setTemplateExceptionHandler(TemplateExceptionHandler.DEBUG_HANDLER); 
} 
Copy the code
  • There are two ways to create template data, that is, the real data we need to populate the template:

    • Java BeanIn the form of, although in Java bean form, but still need toMapAs the carrier.
    public static Map<String, Object> createDataModelFromBean() {
        Author author = new Author();
        Person girlFriend = new Person()
                .setName("lalala")
                .setGender("Female")
                .setAge("10");
    
        author.setGirlFriend(girlFriend)
                .setGender("Male")
                .setAge("24")
                .setName("horseLai"); Map<String, Object> root = new HashMap<>(); root.put("author", author);
        return root;
    }
    Copy the code
    • All useMapIn the form of
    public static  Map<String, Object> createDataModelFromMap() {
    
        Map<String, Object> author = new HashMap<>();
    
        author.put("name"."horseLai");
        author.put("age"."100");
        author.put("gender"."Male");
    
        Map<String, Object> girlFriend = new HashMap<>();
        girlFriend.put("name"."lalala");
        girlFriend.put("age"."10");
        girlFriend.put("gender"."Female"); 
        
        author.put("girlFriend", girlFriend); 
        return author;
    }
    Copy the code
  • For example, we create a template named author.ftl, which corresponds to the above two types of template data. For more rules on template creation, see the manual:

    • Corresponding to theJava beanThe form, you have to declare the variable, and then you can reference it, so here’s the analogyDataBindingThe use of;
    <#-- @ftlvariable name="author" type="Main.Author" -->Hello, I am${author.name}, gender${author.gender}This year,${author.age}Years old."#if author.girlFriend.name ! = "null">This is my girl${author.girlFriend.name}, gender${author.girlFriend.gender}This year,${author.girlFriend.age}Years old. < /#if> 
    Copy the code
    • Corresponding to theMapThe form looks like this.
    Hello, I am${name}, gender${gender}This year,${age}Years old."#if girlFriend.name ! = "null">This is my girl${girlFriend.name}, gender${girlFriend.gender}This year,${girlFriend.age}Years old. < /#if>  
    Copy the code
  • The last thing is to get the engine going

public static void go(){ try (OutputStreamWriter writer = new OutputStreamWriter(System.out);) {/ / loaded Template Template authorTemplate = sConfiguration. GetTemplate ("Author.ftl"); Authortemplate.process (createDataModelFromMap(), writer); } catch (IOException e) { e.printStackTrace(); } catch (TemplateException e) { e.printStackTrace(); }}Copy the code
  • Output result:
Hi, I'm horseLai, male, 100 years old. This is my girl Lalala. She is 10 years old.Copy the code
  • The above isFreeMarkA simple introduction to the template engine, for more details please see the documentation, attached belowgreenDaoIn thedao-session.ftlThe template code helps us in understandinggreenDaoCode generation principles while learning how to create templates:
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>        
} 
Copy the code

review

  • From the above analysis, we already knowgreenDaoThe way the code is generated, forgreenDaoIn terms of code generation, the principle isPrior toDaoMaster,DaoSession,XxxDaoWhen the template is set up, and then withFreeMarkTemplate engine, modify the template set variablesCan achieve the specified effect.
  • At this point, from”Basic Use”to”Core Class Understanding”, to this article, yesgreenDaoThat’s the end of the analysis, limited level, welcome correction.