《开源框架那些事儿26》:“最好的模板引擎”Beetl剖析及与Tiny模板引擎对比

查找最好的模板引擎,发现这个搜索词出来的是beetl,于是就仔细学习了Beetl,试图找寻“最好的”三个字表现在哪里?于是搭建环境,阅读代码,与鄙人所做的TinyTemplate进行了粗略的对比,在征得beetl作者@闲.大赋 的同意后,编写了此对比文章。由于时间关系,对Beetl的认知深度还有不足,分析不当之处在所难免,还请广大同学纠正,定当有错误和不当必改。

点滴悟透设计思想,加入框架设计兴趣小组:http://bbs.tinygroup.org/group-113-1.html

Beetl的环境搭建
输入命令

1 [WARNING] Some problems were encountered while[INFO] Scanning for[ERROR]   
[ERROR]     Non-resolvable parent POM: Could not find[ERROR]  
[ERROR] For more[INFO] -------------------------------------------------------------
[INFO] beetl-core ......................................... FAILURE [ 44.926[ERROR]  
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[INFO] beetl-core ......................................... SUCCESS [03:52 min]
[INFO] ------------------------------------------------------------------------

从这里看,整体来说还可以,把一些bak文件上传上来,稍嫌不严谨,另外有些jpg文件直接放在根目录也有一点点乱,如果整理一下就更好了。
接下来比较关心core

这里面有几个东东,就有点难理解了,为什么这里放了个jar文件?为什么这里放了个lib目录?为什么这里放了个performance工程?性能评测的代码怎么会放到core工程中??


上面这个应该就是关键工程了?core应该就是引擎核心代码所在的位置,ext应该是它对各种开源框架方面的扩展或支持。有这些扩展还是非常不错的,方便使用者上手,赞一个。但是把ext和core放在一个工程里还是有点随意了,如果能把ext单独开个工程就更好了。


从上面的目录结构看还是不错的,但是很显然下面的一些类和接口看起来就比较乱了,应该相当有改进的空间。 相对应的,可以看看Tiny模板引擎的目录结构:


就简洁清爽多了。
再来看看beetl模板的代码行数:


可以看到core工程中的java代码是20291行,不算空行,不算注释行。


Tiny模板引擎的代码行数,纯纯的java代码只有4944行,也就是beetl的代码整整是Tiny模板引擎4倍多。


上面是Beetl的sonar检查情况


上面的统计数据是Tiny模板引擎的统计数据:
这里的数据和上面用Statistics统计的数据稍有区别,但是基本上差别不大。
从上面的数据可以看出:

项目 Beetl Tiny模板引擎
代码行数 23087 4944
文件数 230 171  
重复 3.3% 0.0%
复杂度 2.8/方法 1.9/方法
包耦合指数 31.5% 31.6%
包耦合循环 >35 >18

从代码规模来说,Tiny完胜,只有Beetl的不到1/4。代码重复率方面Beetl也相当不错了,当然Tiny的更好一点。复杂度Beetl方面也不错,当然 Tiny的要更好一点。包耦合指数方面差不多,但是包耦合循环方面,tiny只有Beetl的一半。 从上面的数据来看,Tiny的方法更小,包依赖的长度更短,更容易维护。
OK,从上面的静态分析来看,Beetl的包结构组织有进步的空间,有一定的代码重复,整体代码质量还不错,但是包耦合度有点高,所以其可维护性较Tiny稍弱。
Beetl语法 到main/antlr中查找Beetl语法定义文件,居然没有找到,最后终于在下面的位置main/java/org/beetl/core/parser/BeetlParser.g4找到了,为什么不能完全遵循Maven规范呢?


Tiny模板引擎则完全遵守规范。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    :   block   #blockSt    |   constantsTextStatment #staticOutputSt

|   If parExpression statement (Else statement)? #ifSt    |   While parExpression statement   #whileSt    |   Select g_switchStatment #selectSt    |   Return expression? END  #returnSt    |   Continue END    #continueSt    |   Directive  directiveExp #directiveSt     |   functionTagCall #functionTagSt     |   Ajax Identifier COLON block   #ajaxSt     |   END   #end

上面是Beetl支持的语法。 tiny模板引擎支持的语法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
            |   if_directive
            |   for_directive
            |   import_directive
            |   stop_directive
            |   macro_directive
            |   layout_impl_directive
            |   call_directive
            |   blank_directive
            |   indent_directive
            |   call_macro_directive
            |   bodycontent_directive
            ;

二者做个对比: 语法体系的差异,Beetl采用的是类似jsp的方式,而Tiny模板引擎采用的是Velocity的方式,二者各有优缺点,因此并无好坏之分,只是萝卜青菜上的差异。从我本人来说,是非常讨厌类似于<% ... %>来方式圈定脚本,而更喜欢Velocity的直接用指令嵌入的方式来进行使用,所以我选择了类 Velocity的方式。因此语法体系方面没有什么好比较的。
对于常规指令Beetl和Tiny模板引擎都有良好的支持

  • 循环指令两者都支持for和while,都支持break,contine,stop/return等。同时也都支持else,也就是当循环次数为0时,执行一些操作,比如:有数据的时候在循环体内展示数据,没有数据的时候显示else中的默认内容。
  • 在条件判断方面Beetl支持了if、switch、select等指令,而tiny模板引擎则是由强大的#if() ... #elseif()... #else...#end指令格式来完成所有的条件判断,两者功能都可以互相覆盖。
项目 Beetl Tiny
定义临时变量 var number=1 #set(number=1)
定义页面变量 template.binding("number",1) #!set(number=1)
属性引用 ${user.wife.name} ${user.wife.name}
算述表达式 <%
var a1 = 12;
var b1 = (a1+15)/3-2*a1;
var bc = -1-b1;
%>
${bc}
#set(a1=12,b1 = (a1+15)/3-2*a1,bc = -1-b1)
${bc}
当然,#set指令也可以一行写一个赋值指令
逻辑表达式 <%
var a1 = 12;
var b1 = a1==12;
var b2 = a1!=12;
%>
${b1}
${b2}
#set(a1 = 12,b1 = a1==12,b2 = a1!=12)
${b1}
${b2}
循环语句 <%
print("总共"+userList.~size+"<br>");
for(user in userList){
%>
${userLP.index}    ${user.name} <br>
<%}%>
总共${userList.size()}
#for(user in userList)
${userFor.index}    ${user.name}
#end
条件语句 <%
var user = map["001"];
if(user.name=="lijz"){
print(user.name);
}else{
return ;
}
%>
#set(user = map."001")
#if(user.name=="lijz")
     ${user.name}
#else
    #return
#end
函数调用 <%
print("hello");
println("hello");
printf("hello,%s,your age is %s","lijz",12+"");
%>
${format("hello")}
${format("hello\n")}

${format("hello,%s,your age is %s","lijz",12)}

格式化 <%
var now = date();
var date = date("2013-1-1","yyyy-MM-dd");
%>
now=${now,dateFormat=‘yyyy年MM月dd日‘}
date=${date,dateFormat=‘yyyy年MM月dd日‘}
or
now=${now,‘yyyy年MM月dd日‘}
tiny模板引擎不允许动态创建对象,但是允许通过自定义函数或SpringBean来获取对象。
假设,这里在上下文中在now和date两个变量
now=${format(now,‘yyyy年MM月dd日 HH:mm:SS‘)}
date=${format(date,‘yyyy年MM月dd日‘)}
成员方法调用 <%
var list = [5,2,4];
%>
${ @java.util.Collections.max(list)}
#set( list = [5,2,4])
${list.get(1)}
安全输出 <%
var user1 = null;
var user2 = null;
var user3 = {"name":"lijz",wife:{‘name‘:‘lucy‘}};
%>
${user1.wife.name!"单身"}
${user2.wife.name!}
${user3.wife.name!"单身"}

#set(user1 = null,user2 = null,user3 = {"name":"lijz",wife:{‘name‘:‘lucy‘}})
%>

${user1?.wife?.name?:"单身"}
${user2?.wife?.name?:"单身"}
${user3?.wife?.name?:"单身"}

注释 <%
//最大值是12;
/*最大值是12*/
var max = 12;
%>

##最大值是12;
#*最大值是12*#
#set( max = 12)

上面做了两个模板引擎的常规指令的示例和对比,基本上采用Beetl在线示例中的示例然后用Tiny模板引擎的语法来同样实现的功能。
下面来说说一些有意思的高级功能

项目 Beetl Tiny模板引擎
异常处理 <%
try{
        callOtherSystemView()
}catch(error){
        print("暂时无数据");
}
%>
Tiny模板引擎的设计者认为如果让模板引擎来处理异常,实际上是有点过度设计的意味,而应该是系统的异常处理框架去处理之。模板只参与展示层的处理,不参与业务逻辑处理。
虚拟属性
${user.~genderShowName}
${user.toJson()}
Tiny支持为某种类增加一些扩展的成员函数,和Beetl的虚拟属性的意思是相同的,但是在函数调用过程中,使用方式与原生成员函数没有区别。如果扩展的方法是getXxx,那么就可以直接调用object.xxx的方式按属性的方式来进行调用。
函数扩展 <%
var date = date();
var len = strutil.len("cbd");
println("len="+len);
%>
Tiny也提供了函数扩展体系,也完全可以添加类似的函数扩展,调用方式也差不多。
#set(date =date(),len=strutil.len("cbd"))
标签的支持 public class CmsContentTag extends GeneralVarTagBinding {
public void render(){
Object id= this.getAttributeValue("id");
try
{ctx.byteWriter.writeString("当前定义了一个窜上:"+id.toString());
}catch (IOException e){
e.printStackTrace();
}
}
}
Tiny没有提供标签的扩展功能,却提供了强大的宏定义功能
简单宏定义

1
2
3
//content.html内容如下:
this is 正文
..........
<%%}%>

layout.html 是布局文件

1
2
3
4
5
6
<%include("/inc/header.html"){} %>
this is content {layoutContent}
this is footer:

<%%}%>

运行结果:
运行content.html模板文件后,,正文文件的内容将被替换到layoutContent的地方,变成如下内容

1
2
3
4
this is content:#pageContent
var a = 1;
%>

错误提示如下:

1
2
3
4
5
1|<%
3|var b = a/0;
4|%>

beetl只给出了具体的位置在哪一行,以及整个模板(或者比较近位置的模板)内容。

1 #set(a=1,b=1/0)

错误提示如下:

1
2
3
4
5
路径:/a.page
位置[1行11列]-[1行13列]
===================================================================
1/0
===================================================================

Tiny则明确给出了精确的坐标,x1,y1-x2,y2,同时还给出了具体出问题的内容,相对来说程序员查找问题更加迅捷。

     

工具的支持 beetl的插件功能 Beetl插件如约而来!

安装说明:
本插件是beetl模板语言插件,请放到dropins目录下重启即可。如果以前安装过,需要删除以前保本
如果文件以.btl结尾,则自动以插件方式打开,否则,可以通过右键此文件,选择open-with,并选择beetl editor,不建议使用btl结尾,请尽量使用原有编辑器,参考使用说明4快捷使用beetl editor

使用说明:

1 工程属性里有个beetl属性,可以指定定界符号等,默认是<%%> ${}。也可以指定模板根目录(可选,不必手工填写,在模板单击定位里会提示你选择)2 ctrl-2 定位到下一个beetl 块3 ctrl-3 定位到上一个beetl块4 ctrl-4 将普通文件以beetl editor方式打开,并保持同步编辑 5 ctrl-5 静态文本全部折叠和打开静态文本折叠6 可以ctrl+单击字符串定位到字符串对应的模板文件,第一次使用的时候,需要选择模板根目录,随后,也可以在project属性的beetl配置里配置模板根目录7 alt-/ 进行上下文提示。也可以键入此快速输入定界符号和占位符号8 alt-shift-p 从{ 快速移动到 匹配的},或者反之亦然。如果只单击{ 则会框选住匹配的} 而光标不移动9 选中任何id,都能全文框选住同样的id。10 ctrl-/ 单行注释,或者取消注释11 通常eclipse具有的快捷操作方式,beetl仍然予以保留不变 12 具备一定的错误提示,目前只提示第一个发现的错误。Tiny模板引擎的插件功能

  • 大纲支持:支持在大纲当中显示一些关键内容,并可以快速定位
  • 语法高亮:支持在编辑器中,根据语法进行着色,使得代码更容易阅读和排错
  • 错误提示:如果模板语言存在错误,则可以在工程导航、错误视图及编辑窗口进行错误提示
  • 代码折叠:支持对代码块进行代码折叠,方便查阅
  • 语法提示:支持Tiny模板引擎语法提示及Html语法提示方便快速录入
  • 快速定位:支持Tiny模板中开始语句与结束语句间快速切换
  • 变量快速提示:点鼠标点击某变量时,会高亮显示文件中的所有同名变量
  • 宏定义对应位置显示:在tiny块处理的标签头部按ctrl时,会高亮显示与其对应的#end,反之亦然
  • 格式化:可以按快捷键ctrl+shift+F进行格式化了
  • 注释处理:可以按快捷键ctrl+/来进行快速设置单行注释或取消单行注释,可以按ctrl+shift+/来进行快速设置块注释或取消块注释

由于篇幅太长,因此这里不贴完整内容,详细请看链接:http://my.oschina.net/tinyframework/blog/365370
OK,工具上完全不在一个等级上。
代码质量对比
代码质量这个本身没有唯一标准,这里贴一下类似的功能的代码对比,不做评论:
for语句实现  Beetl版

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
Expression idNode;
Expression exp;
Statement forPart;
Statement elseforPart;
boolean    public    public
     * for(idNode in exp) {forPath}elsefor{elseforPart}
     * @param exp
     * @param elseforPart
     */
ForStatement(VarDefineNode idNode, Expression exp, boolean    {
        this.idNode = idNode;
        this.hasSafe = hasSafe;
        this.forPart = forPart;
    public        // idNode 是其后设置的                Object collection = exp.evaluate(ctx);
        if            if                BeetlException ex = new                ex.pushToken(exp.token);
ex;
            else
                it = new            }
        else
            it = IteratorStatus.getIteratorStatusByType(collection, itType);
(it == null)
                BeetlParserException ex = new                ex.pushToken(exp.token);
ex;
        }
        // loop_index        //      ctx.vars[varIndex+3] = it.getSize();        if
(it.hasNext())
                ctx.vars[varIndex] = it.next();
                switch                    case                    case                        continue;
IGoto.RETURN:
                    case                        return;
            }
(!it.hasData())
                if            }
        }
        {
(it.hasNext())
                ctx.vars[varIndex] = it.next();

if                if            }
    }
    public        // TODO Auto-generated method stub            }
    public        this.hasGoto = occour;
    @Override
void        exp.infer(inferCtx);
(exp.getType().types != null)
            if                idNode.type = Type.mapEntryType;
            else
                //list or array                            }
        else
            idNode.type = Type.ObjectType;
        int        inferCtx.types[index] = idNode.type;
Type(IteratorStatus.class, idNode.type.cls);
        if            elseforPart.infer(inferCtx);
    }
    public        return    }
booleanfalse;
    public        Object values = interpreter.interpretTree(engine, templateFromContext, parseTree.for_expression().expression(),pageContext, context, writer,fileName);
ForIterator(values);

hasItem = false;
(forIterator.hasNext()) {
TemplateContextDefault();
            hasItem = true;
            forContext.put(name, value);
{
            } catch            } catch            }
        if            if            }
        return    }
查看源码
Tiny版

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
TerminalNodeProcessor[200];
HashMap<Class<arserRuleContext>, ContextProcessor>();

OtherTerminalNodeProcessor();
void    }
void    }
TinyTemplateParser.TemplateContext parserTemplateTree(String sourceName, String templateString) {
        ANTLRInputStream is = new        // set source file name, it will be displayed in error report.                TinyTemplateParser parser = new        return    }
void        writer.flush();

void(int            interpretTree(engine, templateFromContext, templateParseTree.getChild(i), pageContext, context, writer,fileName );
    }
Object interpretTree(TemplateEngineDefault engine, TemplateFromContext templateFromContext, ParseTree tree, TemplateContext pageContext, TemplateContext context, Writer writer, String fileName) throws         if            TerminalNodeProcessor processor = terminalNodeProcessors[terminalNode.getSymbol().getType()];
(processor != null) {
            } else            }
if{
                if                }
(processor == null                    for                        Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer,fileName );
(value != null) {
                        }
                }
(StopException se) {
se;
(TemplateException te) {
(te.getContext() == null) {
                }
te;
(Exception e) {
new            }
{
(int                Object value = interpretTree(engine, templateFromContext, tree.getChild(i), pageContext, context, writer,fileName );
(returnValue == null                    returnValue = value;
            }
        return    }
static(object != null) {
        }
}

嗯嗯,不到100行的规模
当然整个通读下来,就会慢慢发现为什么Tiny的代码行数这么少功能却又多的原因之所在了。
总结
Beetl算得上是较好的模板语言框架和不错的开源项目,但是距离“最好的”三个字还是有一定差距的,作为@闲.大赋 的粉丝,偶会持续支持他,也希望他能再积再累,真正当得起“最好的”三个字。
补充说明
beetl里面有4014行由antlr生成的代码,实际统计中,由于Beetl的目录结构没有按标准化的来,导致统计中包含了这部分代码,因此实际上,应该是在16000+,因此规模是Tiny模板引擎的3倍左右,特此纠正。



欢迎访问开源技术社区:http://bbs.tinygroup.org。本例涉及的代码和框架资料,将会在社区分享。《自己动手写框架》成员QQ群:228977971,一起动手,了解开源框架的奥秘!或点击加入QQ群:http://jq.qq.com/?_wv=1027&k=d0myfX

时间: 2024-10-11 21:28:32

《开源框架那些事儿26》:“最好的模板引擎”Beetl剖析及与Tiny模板引擎对比的相关文章

《开源框架那些事儿12》:框架2.0的设计梳理

前面从各个角度,讲了大概11篇了.言归正传,这里讲解一个完整的框架设计实例.这里不是一个空白的描述,而是基于V1.0之后的建构.因此,整个设计过程,也会尽量少走一些弯路.一起来看看吧! 方法论 方法论决定了可以达到的高度 方法论,就是人们认识世界.改造世界的根本方法. 它是人们用什么样的方式.方法来观察事物和处理问题.概括地说,世界观主要解决世界"是什么"的问题,方法论主要解决"怎么办"的问题. 方法论是一种以解决问题为目标的体系或系统,通常涉及对问题阶段.任务.工

《开源框架那些事儿21》:巧借力与借巧力

同样做前端UI,为什么有人花了一点力气,就可以做好?而有的人费尽全力,仍然错误百出?我们可以先看看几个故事. 故事1:巧借力,乌鸦也可以吃核桃 有一个盛产核桃的村子,每年秋末冬初,成群的乌鸦总会来到这里,到果园里捡拾那些被果农们遗落的核桃. 核桃仁虽然美味,但是外壳那么坚硬,乌鸦怎么才能吃到呢?原来乌鸦先把核桃叼起,然后飞到高高的树枝上,再将核桃摔下去,核桃落到坚硬的地面上,被撞破了,于是,乌鸦就得到了那美味的核桃仁. 可是,让核桃从高空坠落,核桃壳破裂的概率很低,很多时候,乌鸦都是望而兴叹.然

《开源框架那些事儿27》悠然乱弹:一段SQL引发的性能危机及其背后隐藏的设计缺

有个同学,说是系统中出现性能问题了,说是让我帮助诊断一下.本来是不想花这时间的,结果耐不住对方的死缠乱打,只要答应帮看看.  故事发生的背景是,在文件上传的时候,有时间会有人上传了文件,但是最后没有使用上传的文件,这样就会产生一些垃圾文件. 原来软件作者就想写一个后台定时任务程序,来清除这些垃圾文件? 由于作者坚定的不让我发她的SQL语句(这个我也理解,这么丑陋的SQL),所以这里就不发源代码了,发伪代码. 123456789101112131415 void deleteMissLinkFil

《开源框架那些事儿22》:UI框架设计实战

UI是User Interface的缩写,通常被认为是MVC中View的部分,作用是提供跟人机交互的可视化操作界面.MVC中Model提供内容给UI进行渲染,用户通过UI框架产生响应,一般而言会由控制层调用业务逻辑进行处理,并把处理结果以Model方式返回View,再次渲染.UI框架的大致过程就是如此,按实现方式可以分为RIA和瘦客户端方式,目前基于B/S的瘦客户端方式比较流行.UI框架套路上很简单,但是想要做好可就不容易了.目前基于MVC的框架灿若繁星,不客气的说是个软件公司就有自己的技术框架

《开源框架那点事儿23》:Tiny模板语言(VelocityPlus)初步入门

2 Tiny模板语言概述 3 Tiny模板语言能为您做什么? 4 Tiny模板语言简介 5 Hello Tiny! 6 注释 7 引用(References) 7.1 变量(variables) 7.2 属性(properties) 7.3 方法(methods) 8 表达式(Expression) 8.1 取值表达式 8.2 Map常量 8.3 数组常量 8.4 其他表达式 9 索引表示法 10 渲染 11 与Java无缝对接 12 模板布局 13 指令集 13.1 #set指令 13.2 条

Tiny模板语言(VelocityPlus)初步入门

1 关于用户手册 本文主要介绍如何在模板中使用Tiny模板语言,通过查阅本手册,可以对Tiny模板语言 TTL(Tiny Template Language)的用法有一个较全面的认识,并学会如何有效地使用Tiny模板语言.同时,本文提供了较多的例子帮您来学习并掌握它. 2 Tiny模板语言概述 Tiny 模板语言是一个参考Velocity语法的模板语言,它对Velocity模板语言中一些功能不太完全及使用过程中比较不方便的地方进行全面的扩展和升级,同时为了更好的适应Web界面层的开发,还提供了强

Tiny模板引擎之开发工具

要想做一流的模板引擎,当得一流的模板编辑器是必须的. Tiny模板引擎推出已经有一段时间了,但是由于其语法是独立的,因此编辑只能采用一般的文本编辑器,但是编辑效率自然就低.还有一种是采用Velocity编辑器进行编辑,但是带来的问题经常会有错误的语法提示,有些语法无法正确提示,总而言之言而总之,就是不好用.那问题已经来了,咋办?答案当然是最好有一个自己的了. 现在,Tiny模板引擎的专有编辑器来了! 特性介绍 大纲支持:支持在大纲当中显示一些关键内容,并可以快速定位 语法高亮:支持在编辑器中,根

《开源框架那点事儿25》:对框架模板引擎实现方式的改造实录

点滴悟透设计思想,Tiny模板引擎优化实录! 增加框架设计兴趣小组:http://bbs.tinygroup.org/group-113-1.html Tiny模板引擎的实现方式原来是採用的编译方式,近期发生了一些问题.因此我认为有必要把编译方式调整为解释方式,为此就開始了此次实现活动. 编译方式存在的问题 当时採用编译方式.主要是考虑到编译方式在执行时不必再去遍历语法树.因此就採用了编译方式. 可是在实际应用其中,出现了例如以下问题: 文件路径冲突的问题 因为採用的是编译方式,这个时候就存在在

开源框架那点事儿25》:对框架模板引擎实现方式的改造实录

点滴悟透设计思想,Tiny模板引擎优化实录! Tiny模板引擎的实现方式原来是采用的编译方式,最近发生了一些问题,因此我觉得有必要把编译方式调整为解释方式,为此就开始了此次实现活动. 编译方式存在的问题 当时采用编译方式,主要是考虑到编译方式在运行时不必再去遍历语法树,因此就采用了编译方式.但是在实际应用当中,出现了如下问题: 文件路径冲突的问题 由于采用的是编译方式,这个时候就存在在一个选择,即:Java源代码落地或不落地的选择.如果Java文件不落地,则在有问题的时候,如果想要进行代码调试(