Java进阶之 如何自动生成代码

一、前言:为什么要有代码的自动生成?
对于这个问题 最简洁直接的回答就是:代替手动编写代码、提高工作效率。

什么样的场景和代码适合用自动生成这种方式呢?
做过Java服务端的朋友一定都知道代码中我们需要编写与数据库表映射的Java实体类(Entity)、需要编写与实体对应的DAO类(XxDao.java类中有包含对应实体的增、删、改、查基本操作)。在这些实体类中通常都是一些属性方法以及属性对应的get/set方法、而实体对应的DAO类中也基本会包含有增、删、改、查这些与数据库操作相关的方法。在编写了那么多的实体类和Dao类的过程中 你是否发现了这些代码中有很多地方都相似或者差不多、只是名字不同而已呢?对、那么这个时候其实我们可以定义一个模板、通过模板我们来让代码自动生成去吧。

二、FreeMarker的简单介绍
在进入正文前,让我们首先简单、快速了解一下FreeMarker。
(做过Web开发的朋友肯定都是相当熟悉的、小吕当时 也是在做Web开发的时候第一次接触了FreeMarker)
1、概述:FreeMarker是一款模板引擎:即一种基于模板、用来生成输出文本的通用工具。更多的是被用来设计生成HTML页面。

简单说就是:FreeMarker是使用模板生成文本页面来呈现已经准备好的数据。如下图表述

\

FreeMarker官网:http://freemarker.org/

2、通过一个简单的例子来展示如何使用FreeMarker定义模板、绑定模型数据、生成最终显示的Html页面:
1>.新建项目 在项目根目录下新建template文件夹,用来存放我们的Template file,

如我们新建模板文件test.ftl 内容如下:

 

?
1
 

Welcome ${user}<#if user == Big Joe>, our beloved leader!

Our latest product: ${latestProduct.name}!

 

2>.项目引入freemarker.jar(下载地址:https://jarfiles.pandaidea.com/freemarker.html),
在Java类中使用FreeMarker API方法引用模板文件、创建数据模型、合并数据模型与模板文件最终输入,

代码如下:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
 
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
 
public class HtmlGeneratorClient {
 
     public static void main(String[] args) {
         try {
             Configuration cfg = new Configuration();
             // 指定模板文件从何处加载的数据源,这里设置成一个文件目录
             cfg.setDirectoryForTemplateLoading( new File(./template));
             cfg.setObjectWrapper( new DefaultObjectWrapper());
             
             // 获取或创建模板
             Template template = cfg.getTemplate(test.ftl);
             
             // 创建数据模型
             Map root = new HashMap();
             root.put(user, Big Joe);       
             Map latest = new HashMap();
             root.put(latestProduct, latest);
             latest.put(url, http: //blog.csdn.net/janice0529/article/details/products/greenmouse.html);
             latest.put(name, green mouse);
             
             // 将模板和数据模型合并 输出到Console
             Writer out = new OutputStreamWriter(System.out);
             template.process(root, out);
             out.flush();
             
         } catch (IOException e) {
             e.printStackTrace();
         } catch (TemplateException e) {
             e.printStackTrace();
         }
 
     }
 
}

 

3>.最终生成的HTML的页面代码如下:

 

?
1
 

Welcome Big Joe, our beloved leader!

Our latest product: green mouse!


 

三、如何使用FreeMerker完成Java类代码的自动生成
上面示例 我们的ftl模板文件定义的是HTML页面模板,那么我们将ftl模板定义为Java代码呢 通过数据模板的绑定不就可以生成Java类啦,
下面小吕将利用模板来自动创建实体对象的java类(编写实体类的模板文件相对逻辑简单,但简单归简单,最重要的还是我们要掌握它的思想)
1、属性类型的枚举类 PropertyType.java

?
1
2
3
4
5
6
7
8
/**
  * 属性类型枚举类
  * @author  lvzb.software@qq.com
  *
  */
public enum PropertyType {
     Byte, Short, Int, Long, Boolean, Float, Double, String, ByteArray, Date
}
2、实体对应的字段属性类 Property.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
  * 实体对应的属性类
  * @author  lvzb.software@qq.com
  *
  */
public class Property {
     // 属性数据类型
     private String javaType;
     // 属性名称
     private String propertyName;
     
     private PropertyType propertyType;
     
     public String getJavaType() {
         return javaType;
     }
 
     public void setJavaType(String javaType) {
         this .javaType = javaType;
     }
 
     public String getPropertyName() {
         return propertyName;
     }
 
     public void setPropertyName(String propertyName) {
         this .propertyName = propertyName;
     }
 
     public PropertyType getPropertyType() {
         return propertyType;
     }
 
     public void setPropertyType(PropertyType propertyType) {
         this .propertyType = propertyType;
     }
         
}
3、实体模型类 Entity.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import java.util.List;
 
/**
  * 实体类
  * @author  lvzb.software@qq.com
  *
  */
public class Entity {
     // 实体所在的包名
     private String javaPackage;
     // 实体类名
     private String className;
     // 父类名
     private String superclass;
     // 属性集合
     List<property> properties;
     // 是否有构造函数
     private boolean constructors;  
     
     public String getJavaPackage() {
         return javaPackage;
     }
     
     public void setJavaPackage(String javaPackage) {
         this .javaPackage = javaPackage;
     }
     
     public String getClassName() {
         return className;
     }
     
     public void setClassName(String className) {
         this .className = className;
     }
     
     public String getSuperclass() {
         return superclass;
     }
     
     public void setSuperclass(String superclass) {
         this .superclass = superclass;
     }
     
     public List<property> getProperties() {
         return properties;
     }
     
     public void setProperties(List<property> properties) {
         this .properties = properties;
     }
     
     public boolean isConstructors() {
         return constructors;
     }
     
     public void setConstructors( boolean constructors) {
         this .constructors = constructors;
     }  
 
}</property></property></property>
4、在项目根目录下新建template文件夹,用来存放我们的Template file, 新建实体模板entity.ftl 内容如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package ${entity.javaPackage};
 
/**
  * This code is generated by FreeMarker
  * @author lvzb.software@qq.com
  *
  */
public class ${entity.className}<# if entity.superclass?has_content> extends ${entity.superclass} <!--# if -->
{
     /********** attribute ***********/
<#list entity.properties as property>
     private ${property.javaType} ${property.propertyName};
     
<!--#list-->
     /********** constructors ***********/
<# if entity.constructors>
     public ${entity.className}() {
     
     }
 
     public ${entity.className}(<#list entity.properties as property>${property.javaType} ${property.propertyName}<# if property_has_next>, <!--# if --><!--#list-->) {
     <#list entity.properties as property>
         this .${property.propertyName} = ${property.propertyName};
     <!--#list-->
     }
<!--# if -->
 
     /********** get/set ***********/
<#list entity.properties as property>
     public ${property.javaType} get${property.propertyName?cap_first}() {
         return ${property.propertyName};
     }
 
     public void set${property.propertyName?cap_first}(${property.javaType} ${property.propertyName}) {
         this .${property.propertyName} = ${property.propertyName};
     }
     
<!--#list-->
}
5、自动生成实体类 客户端代码 EntityGeneratorClient.java
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
  * 自动生成实体类客户端
  * @author lvzb.software@qq.com
  *
  */
public class EntityGeneratorClient {
     
     private static File javaFile = null ;
 
     public static void main(String[] args) {
         Configuration cfg = new Configuration();   
         try {
             // 步骤一:指定 模板文件从何处加载的数据源,这里设置一个文件目录
             cfg.setDirectoryForTemplateLoading( new File(./template));
             cfg.setObjectWrapper( new DefaultObjectWrapper());
             
             // 步骤二:获取 模板文件
             Template template = cfg.getTemplate(entity.ftl);
             
             // 步骤三:创建 数据模型
             Map<string, object= "" > root = createDataModel();
             
             // 步骤四:合并 模板 和 数据模型
             // 创建.java类文件
             if (javaFile != null ){
                 Writer javaWriter = new FileWriter(javaFile);
                 template.process(root, javaWriter);
                 javaWriter.flush();
                 System.out.println(文件生成路径: + javaFile.getCanonicalPath());
                 
                 javaWriter.close();
             }
             // 输出到Console控制台
             Writer out = new OutputStreamWriter(System.out);
             template.process(root, out);
             out.flush();
             out.close();
             
         } catch (IOException e) {
             e.printStackTrace();
         } catch (TemplateException e) {
             e.printStackTrace();
         }
 
     }
 
     
     /**
      * 创建数据模型
      * @return
      */
     private static Map<string, object= "" > createDataModel() {
         Map<string, object= "" > root = new HashMap<string, object= "" >();
         Entity user = new Entity();
         user.setJavaPackage(com.study.entity); // 创建包名
         user.setClassName(User);  // 创建类名
         user.setConstructors( true ); // 是否创建构造函数
         // user.setSuperclass(person);
         
         List<property> propertyList = new ArrayList<property>();
         
         // 创建实体属性一
         Property attribute1 = new Property();
         attribute1.setJavaType(String);
         attribute1.setPropertyName(name);
         attribute1.setPropertyType(PropertyType.String);
         
         // 创建实体属性二
         Property attribute2 = new Property();
         attribute2.setJavaType( int );
         attribute2.setPropertyName(age);
         attribute2.setPropertyType(PropertyType.Int);
         
         propertyList.add(attribute1);
         propertyList.add(attribute2);
         
         // 将属性集合添加到实体对象中
         user.setProperties(propertyList);
         
         // 创建.java类文件
         File outDirFile = new File(./src-template);
         if (!outDirFile.exists()){
             outDirFile.mkdir();
         }
         
         javaFile = toJavaFilename(outDirFile, user.getJavaPackage(), user.getClassName());
         
         root.put(entity, user);
         return root;
     }
     
     
     /**
      * 创建.java文件所在路径 和 返回.java文件File对象
      * @param outDirFile 生成文件路径
      * @param javaPackage java包名
      * @param javaClassName java类名
      * @return
      */
     private static File toJavaFilename(File outDirFile, String javaPackage, String javaClassName) {
         String packageSubPath = javaPackage.replace( '.' , '/' );
         File packagePath = new File(outDirFile, packageSubPath);
         File file = new File(packagePath, javaClassName + .java);
         if (!packagePath.exists()){
             packagePath.mkdirs();
         }
         return file;
     }
 
}</property></property></string,></string,></string,></string,>
6、运行程序 我们将会在项目根目录下 生成文件夹 src-template以及自动生成的实体类User.java
效果图如下:
\  --- 运行后 ---> \

 

<程序运行前目录结构> <程序运行后目录结构>

自动生成的实体类User.java 代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.study.entity;
 
/**
  * This code is generated by FreeMarker
  * @author lvzb.software@qq.com
  *
  */
public class User
{
     /********** attribute ***********/
     private String name;
     
     private int age;
     
     /********** constructors ***********/
     public User() {
     
     }
 
     public User(String name, int age) {
         this .name = name;
         this .age = age;
     }
 
     /********** get/set ***********/
     public String getName() {
         return name;
     }
 
     public void setName(String name) {
         this .name = name;
     }
     
     public int getAge() {
         return age;
     }
 
     public void setAge( int age) {
         this .age = age;
     }
     
}

 

 

四、背后的思考

通过上面两个简单的示例我们了解到所谓的自动生成代码其实就是:

1、定义java类模板文件 2、定义模板数据 3、引用模板文件(.ftl)与模板数据合并生成Java类。

上面的示例中 有的朋友可能会问不就是要编写一个实体对象吗?干嘛搞那么麻烦、又建.ftl文件、又写了那么多类、定义模板数据的过程也是那么麻烦、我还不如手动去写、声明几个属性、set/get快捷键一下子就编写好啦。 真的是这样吗?
从一个辅助工具和软件架构的方面去思考,假设做成一个开发的辅助工具或是插件去完成实体类和对应DAO类的自动生成。假设需要建10个实体类和对应含有增删改查基本操作的DAO类。我在C/S客户端上填写包名、类名、属性字段等信息 然后一键生成,想想那是多么爽、多么痛快的一件事(当然 前提是你的模板类要编写的非常强大、通用),而你也许还在不停的 Ctrl+C、Ctrl+V。

五、其他
关于如何编写.ftl模板文件、就需要自己去翻阅资料自我学习啦!
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
主要讲述了计算机系统的开发领域。在每章中的漂亮代码都是来自独特解决方案的发现,而这种发现是来源于作者超越既定边界的远见卓识,并且识别出被多数人忽视的需求以及找出令人叹为观止的问题解决方案。 本书介绍了人类在一个奋斗领域中的创造性和灵活性:计算机系统的开发领域。在每章中的漂亮代码都是来自独特解决方案的发现,而这种发现是来源于作者超越既定边界的远见卓识,并且识别出被多数人忽视的需求以及找出令人叹为观止的问题解决方案。 本书33章,有33位作者,每位作者贡献一章。每位作者都将自己心目中对于“美丽的代码”的认识浓缩在一章当中,张力十足。33位大师,每个人对代码之美都 有自己独特的认识,现在一览无余的放在一起,对于热爱程序的每个人都不啻一场盛宴。 虽然本书的涉猎范围很广,但也只能代表一小部分在这个软件开发这个最令人兴奋领域所发生的事情。 本书收录的是软件设计领域中的一组大师级作品。每一章都是由一位或几位著名程序员针对某个问题给出的完美的解决方案,并且细述了这些解决方案的巧妙之处。 本书既不是一本关于设计模式的书,也不是一本关于软件工程的书,它告诉你的不仅仅是一些正确的方式或者错误的方式。它让你站在那些优秀软件设计师的肩膀上,从他们的角度来看待问题。 本书给出了38位大师级程序员在项目设计中的思路、在开发工作中的权衡,以及一些打破成规的决策。 第1章 正则表达式匹配器 。 1.1 编程实践 1.2 实现 1.3 讨论 1.4 其他的方法 1.5 构建 1.6 小结 第2章 Subversion中的增量编辑器:像本体一样的接口 2.1 版本控制与目录树的转换 2.2 表达目录树的差异 2.3 增量编辑器接口 2.4 但这是不是艺术? 2.5 像体育比赛一样的抽象 2.6 结论 第3章 我编写过的最漂亮代码 3.1 我编写过的最漂亮代码 3.2事倍功半 3.3 观点 3.4 本章的中心思想是什么? 3.5 结论 3.6致谢 第4章 查找 4.1. 耗时 4.2. 问题:博客数据 4.3. 问题:时间,人物,以及对象? 4.4. 大规模尺度的搜索 4.5. 结论 第5章 正确、优美、迅速(按重要性排序):从设计XML验证器中学到的经验 5.1 XML验证器的作用 5.2 问题所在 5.3 版本1:简单的实现 5.4 版本2:模拟BNF语法——复杂度O(N) 5.5 版本3:第一个复杂度O(log N)的优化 5.6 版本4:第二次优化:避免重复验证 5.7 版本5:第三次优化:复杂度 O(1) 5.8 版本 6:第四次优化:缓存(Caching) 5.9 从故事中学到的 第6章 集成测试框架:脆弱之美 6.1. 三个类搞定一个验收测试框架 6.2. 框架设计的挑战 6.3. 开放式框架 6.4. 一个HTML解析器可以简单到什么程度? 6.5. 结论 第7章 美丽测试 7.1 讨厌的二分查找 7.2 JUnit简介 7.3将二分查找进行到底 7.4 结论 第8章 图像处理中的即时代码生成 第9章 自顶向下的运算符优先级 9.1. JavaScript 9.2. 符号表 9.3. 语素 9.4. 优先级 9.5. 表达式 9.6. 中置运算符 9.7. 前置操作符 9.8. 赋值运算符 9.9. 常数 9.10. Scope 9.11. 语句 9.12. 函数 9.13. 数组和对象字面量 9.14. 要做和要思考的事 第 10章 追求加速的种群计数 10.1. 基本方法 10.2. 分治法 10.3. 其他方法 10.4. 两个字种群计数的和与差 10.5. 两个字的种群计数比较 10.6. 数组中的1位种群计数 10.7. 应用 第11章 安全通信:自由的技术 11.1 项目启动之前 11.2剖析安全通信的复杂性 11.3 可用性是关键要素 11.4 基础 11.5 测试集 11.6 功能原型 11.7 清理,插入,继续…… 11.8 在喜马拉雅山的开发工作 11.9 看不到的改动 11.10 速度确实重要 11.11 人权中的通信隐私 11.12 程序员与文明 第12章 在BioPerl里培育漂亮代码 12.1. BioPerl和Bio::Graphics模块 12.2. Bio::Graphics的设计流程 12.3. 扩展Bio::Graphics 12.4. 结束语和教训 第13章 基因排序器的设计 13.1 基因排序器的用户界面 13.2 通过Web跟用户保持对话 13.3. 多态的威力 13.4 滤除无关的基因 13.5 大规模美丽代码理论 13.6 结论 第14章 优雅代码随硬件发展的演化 14.1. 计算机体系结构对矩阵算法的影响 14.2 一种基于分解的方法 14.3 一个简单版本 14.4 LINPACK库中的DGEFA子程序 14.5 LAPACK DGETRF 14.6递归LU 14.7 ScaLAPACK PDGETRF 14.8 针对多核系统的多线程设计 14.9 误差分析与操作计数浅析 14.10 未来的研究方向 14.11 进一步阅读 第15章 漂亮的设计会给你带来长远的好处 15.1. 对于漂亮代码的个人看法 15.2. 对于CERN库的介绍 15.3. 外在美(Outer Beauty) 15.4. 内在美(Inner Beauty ) 15.5. 结论 第16章,Linux内核驱动模型:协作的好处 16.1 简单的开始 16.2 进一步简化 16.3 扩展到上千台设备 16.4 小对象的松散结合 第17章 额外的间接层 17.1. 从直接代码操作到通过函数指针操作 17.2. 从函数参数到参数指针 17.3. 从文件系统到文件系统层 17.4. 从代码到DSL(Domain-Specific Language) 17.5. 复用与分离 17.6.分层是永恒之道? 第18章 Python的字典类:如何打造全能战士 18.1. 字典类的内部实现 18.2. 特殊调校 18.3. 冲突处理 18.4. 调整大小 18.5. 迭代和动态变化 18.6. 结论 18.7. 致谢 第19章 NumPy中的多维迭代器 19.1 N维数组操作中的关键挑战 19.2 N维数组的内存模型 19.3NumPy迭代器的起源 19.4 迭代器的设计 19.5 迭代器的接口 19.6 迭代器的使用 19.7 结束语 第20章 NASA火星漫步者任务中的高可靠企业系统 20.1 任务与CIP 20.2 任务需求 20.3 系统架构 20.4 案例分析:流服务 20.5 可靠性 20.6 稳定性 20.7 结束语 第21章 ERP5:最大可适性的设计 21.1 ERP的总体目标 21.2 ERP5 21.3 Zope基础平台 21.4 ERP5 Project中的概念 21.5 编码实现ERP5 Project 21.6 结束语 第22章 一匙污水 第23章 MapReduce分布式编程 23.1 激动人心的示例 23.2 MapReduce编程模型 23.3 其他MapReduce示例 23.4 分布式MapReduce的一种实现 23.5 模型扩展 23.6 结论 23.7 进阶阅读 23.8 致谢 23.9 附录:单词计数解决方案 第24章 美丽的并发 24.2 软件事务内存 24.3 圣诞老人问题 24.4 对Haskell的一些思考 24.6 致谢 第25章 句法抽象:syntax-case 展开器 25.1. syntax-case简介 25.2. 展开算法 25.3. 例子 25.4. 结论 第26章 节省劳动的架构:一个面向对象的网络化软件框架 26.1 示例程序:日志服务 26.2 日志服务器框架的面向对象设计 26.3 实现串行化日志服务器 26.4 实现并行日志服务器 26.5 结论 第27章 以REST方式集成业务伙伴 27.1 项目背景 27.2 把服务开放给外部客户 27.3 使用工厂模式转发服务 27.4 用电子商务协议来交换数据 27.5 结束语 第28章 漂亮的调试 28.1 对调试器进行调试 28.2 系统化的过程 28.3 关于查找的问题 28.4 自动找出故障起因 28.5 增量调试 28.6 最小化输入 28.7 查找缺陷 28.8 原型问题 28.9 结束语 28.10 致谢 28.11 进一步阅读 第29章 把代码当作文章 第30章 当你与世界的联系只有一个按钮 30.1 基本的设计模型 30.2 输入界面 30.3 用户界面的效率 30.4 下载 30.5 未来的发展方向 第31章 Emacspeak:全功能音频桌面 31.1 产生语音输出 31.2 支持语音的Emacs 31.3 对于在线信息的简单访问 31.4 小结 31.5 致谢 第32章 变动的代码 32.1 像书本一样 32.2 功能相似的代码在外观上也保持相似 32.3 缩进带来的危险 32.4 浏览代码 32.5 我们使用的工具 32.6 DiffMerge的曲折历史 32.7 结束语 32.8 致谢 32.9 进一步阅读 第33章 为“The Book”编写程序 33.1 没有捷径 33.2 给Lisp初学者的提示 33.3 三点共线 33.4 不可靠的斜率 33.5 三角不等性 33.6 河道弯曲模型 33.7 “Duh!”——我的意思是“Aha!” 33.8 结束语 33.9 进一步阅读 后记

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值