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 that
greenDao
isAutomatically generate code based on this template engineHere’s how it works:- create
Configuration
Example, specify the version, and set the folder where the template resides - Then through
Configuration#getTemplate
It loads the template, like loaddao.ftl
Template file. - At last,
Template#process
Execute the build engine, which replaces the template (e.gdao.ftl
), and writes the replaced template data to the specified folder, i.e.java
File.
- create
- 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
- This suggests that
FreeMark Template engine getting started
- From the above analysis, we already know that
greenDao
Is the use ofFreeMark
Template 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:
- configuration
Configuration
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 Bean
In the form of, although in Java bean form, but still need toMap
As 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 use
Map
In 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 the
Java bean
The form, you have to declare the variable, and then you can reference it, so here’s the analogyDataBinding
The 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 the
Map
The 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
- Corresponding to the
-
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 is
FreeMark
A simple introduction to the template engine, for more details please see the documentation, attached belowgreenDao
In thedao-session.ftl
The template code helps us in understandinggreenDao
Code 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 know
greenDao
The way the code is generated, forgreenDao
In terms of code generation, the principle isPrior toDaoMaster
,DaoSession
,XxxDao
When the template is set up, and then withFreeMark
Template engine, modify the template set variablesCan achieve the specified effect. - At this point, from”Basic Use”to”Core Class Understanding”, to this article, yes
greenDao
That’s the end of the analysis, limited level, welcome correction.