天天看點

Java進階之 如何自動生成代碼 Welcome ${user}<#if user == Big Joe>, our beloved leader! Welcome Big Joe, our beloved leader!

一、前言:為什麼要有代碼的自動生成?

對于這個問題 最簡潔直接的回答就是:代替手動編寫代碼、提高工作效率。

什麼樣的場景和代碼适合用自動生成這種方式呢?

做過Java服務端的朋友一定都知道代碼中我們需要編寫與資料庫表映射的Java實體類(Entity)、需要編寫與實體對應的DAO類(XxDao.java類中有包含對應實體的增、删、改、查基本操作)。在這些實體類中通常都是一些屬性方法以及屬性對應的get/set方法、而實體對應的DAO類中也基本會包含有增、删、改、查這些與資料庫操作相關的方法。在編寫了那麼多的實體類和Dao類的過程中 你是否發現了這些代碼中有很多地方都相似或者差不多、隻是名字不同而已呢?對、那麼這個時候其實我們可以定義一個模闆、通過模闆我們來讓代碼自動生成去吧。

二、FreeMarker的簡單介紹

在進入正文前,讓我們首先簡單、快速了解一下FreeMarker。

(做過Web開發的朋友肯定都是相當熟悉的、小呂當時 也是在做Web開發的時候第一次接觸了FreeMarker)

1、概述:FreeMarker是一款模闆引擎:即一種基于模闆、用來生成輸出文本的通用工具。更多的是被用來設計生成HTML頁面。

簡單說就是:FreeMarker是使用模闆生成文本頁面來呈現已經準備好的資料。如下圖表述

Java進階之 如何自動生成代碼 Welcome ${user}<#if user == Big Joe>, our beloved leader! Welcome Big Joe, our beloved leader!

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

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

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;

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};

public

class

${entity.className}<#

if

entity.superclass?has_content>

extends

${entity.superclass} <!--#

if

-->

{

<#list entity.properties as property>

private

${property.javaType} ${property.propertyName};

<!--#list-->

<#

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

-->

<#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;

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();

}

}

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;

}

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

效果圖如下:

Java進階之 如何自動生成代碼 Welcome ${user}&lt;#if user == Big Joe&gt;, our beloved leader! Welcome Big Joe, our beloved leader!

 --- 運作後 --->

Java進階之 如何自動生成代碼 Welcome ${user}&lt;#if user == Big Joe&gt;, our beloved leader! Welcome Big Joe, our beloved leader!

<程式運作前目錄結構> <程式運作後目錄結構>

自動生成的實體類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;

public

class

User

{

private

String name;

private

int

age;

public

User() {

}

public

User(String name,

int

age) {

this

.name = name;

this

.age = age;

}

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模闆檔案、就需要自己去翻閱資料自我學習啦!