论坛首页 Java企业应用论坛

Non MVC ==> View First的框架Asta4D

浏览 8876 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2014-01-06   最后修改:2014-01-06
Asta4D是Java世界唯一的View First框架(Lift勉强算一个,因为它支持用Java开发,事实上本框架本身灵感也是来源与Lift)。

https://github.com/astamuse/asta4d

简单的讲,它提供远超MVC模式的开发效率,同时允许前端和后端工程师独立工作而互不干扰。

我的博客中发表了中文版的User Guide,欢迎大家鉴赏:

http://xzer.iteye.com/category/302126

暂时这只是一份User Guide,而介绍更多细节的reference性质的文档将会在本月中逐步补充。

在论坛发帖的目的,是希望能够听到一些意见和反馈。欢迎讨论。



   发表时间:2014-01-08   最后修改:2014-01-08
看完七篇的User Guide,个人有些想法总结如下:
1、Asta4D里的模板继承机制非常像facelets的模板,这个也是本人非常喜欢的,但Facelets的模板继承机制看起来更加的简洁明了,无冗余代码。或许Asta4D是view first 所以,保留一些html body等等冗余标签(子模板)。其实一旦应用了这种模板继承机制,个人觉得已经破坏了User Guide里提到的“设计友好度”原则。起码现在没有这种前端IDE可以完美显示这种继承的模板页面。
2、Asta4D里的函数式的编程是不是指afd的几个标签,这几个标签看起来也像是MVC 1.0时代的“jsp:useBean”一样,当然不是说“jsp:useBean”不好,而是本人总觉得,是把设计师的工作移给程序员去做了,需要程序员去手动渲染html标签,就像要用servlet.write("<div>")一样。
3、在URL映射方面,Asta4D提供了UrlMappingRuleInitializer接口,把所有的映射都写在一个类方法里,总感觉就是把所有Spring MVC里的Controller的@RequestMapping集合到一个类方法里进行声明似的,这其实就相当于硬编码的效果。还不如把映射写到XML文件里,其实个人更喜欢用在相应的Controller里标注映射URL,就是目前的Spring MVC注解方式,“职责单一”嘛。当然Asta4D这里有个优点,我觉得可以直接导向到模板文件,不需要经过Controller也是Asta4D里提到的handler。因为有时候确实只是为了一个简单的页面跳转,可Spring MVC不得不在Controller里写一个对应的空方法来处理。
0 请登录后投票
   发表时间:2014-01-09   最后修改:2014-01-09
谢谢semmy的留言,我来解释一下semmy的几个评论。

semmy 写道

1、Asta4D里的模板继承机制非常像facelets的模板,这个也是本人非常喜欢的,但Facelets的模板继承机制看起来更加的简洁明了,无冗余代码。或许Asta4D是view first 所以,保留一些html body等等冗余标签(子模板)。其实一旦应用了这种模板继承机制,个人觉得已经破坏了User Guide里提到的“设计友好度”原则。起码现在没有这种前端IDE可以完美显示这种继承的模板页面。


关于这个问题,我觉得既然有了继承和引用的机制,除非特别定制的编辑器,否则的确没有任何IDE可以完美显示。另外,关于冗余标签的问题,我觉得也许我应该修改一下我的示例,冗余标签不是必须的,这里本来想说明的是,无论是child还是embed,框架只认body部分。也许这个内容放到reference里面去说明更好。另外,冗余的标签从另一个角度多少可以改善一些可视性。带完整html标记的文件,至少就这个文件单独来说,浏览器是可以正常识别的。

关于继承和引用的机制,对设计师是否友好的问题,我们有这样一些实践的结果:

1. 对设计师来说,html代码就等于页面,他们甚至不需要浏览器,仅凭看到的html片段,就能够在大脑中构建出90%以上的细节。
2. 就具体的开发流程来说,设计师自行配置好url映射,就可以开始工作了,因为所有的继承关系和引用关系,都可以通过框架来解释并进行组装。当然,设计师配置的url是最简单的view first映射关系,之后后端程序员可能会根据需要做必要的修改。我们的流程,允许设计师在UrlMappingRuleInitializer中添加自己的代码,当然,也可以反过来,先由程序员配置好规则并建立stub文件,然后再把工作移交给设计师。


semmy 写道

2、Asta4D里的函数式的编程是不是指afd的几个标签,这几个标签看起来也像是MVC 1.0时代的“jsp:useBean”一样,当然不是说“jsp:useBean”不好,而是本人总觉得,是把设计师的工作移给程序员去做了,需要程序员去手动渲染html标签,就像要用servlet.write("<div>")一样。


Asta4D的函数式编程不是指嵌入html的标记,而是指通过requenst handler封装副作用。这里你可能有一个误解,程序员不需要手动渲染html标签,所有的html标签是由设计师完成的,程序员做的事情是将数据通过css选择器输出到指定的位置:

render.add("#someIdForInt", 12345);  
render.add("#someIdForLong", 12345L);  
render.add("#someIdForBool", true);  
render.add("#someIdForStr", "a str"); 


框架的确提供了一个允许后端程序员直接输出html标签的接口:
ElementUtil.parseAsSingle("<div></div>");  


但这个接口本身只是作为最终手段提供给程序员用来应付极端万一的情况的,我们的开发实践中,整个网站代码中,只有一处用该接口来输出了一段动态定制的javascript代码。另外这个接口本身也是存在cross-site隐患的,因此在user guide中也特别说明了,看管好这个api的调用。

因此从设计友好的角度讲,所有的html代码都是由设计师产出的,按照我们的流程,即使是一个简单的span标签,也必须由设计师添加并提交代码后后端程序员才能介入并填入数据。设计师在编写前端的模板文件的时候,可以填入dummy数据,即使在没有后端程序员介入的情况下,前端设计师可以完全独立的调式并完成所有前端的工作。于此同时,程序员只关心数据,不再关心前端会如何显示。

semmy 写道

3、在URL映射方面,Asta4D提供了UrlMappingRuleInitializer接口,把所有的映射都写在一个类方法里,总感觉就是把所有Spring MVC里的Controller的@RequestMapping集合到一个类方法里进行声明似的,这其实就相当于硬编码的效果。还不如把映射写到XML文件里,其实个人更喜欢用在相应的Controller里标注映射URL,就是目前的Spring MVC注解方式,“职责单一”嘛。


这个是一个见仁见智的理解,就我们的理解来说,在controller中映射url,导致系统完整的site-map是不可见的,这对于可维护性来说是相当糟糕的体验。当然,你也提到,可以把映射写到xml中,就Asta4D来说,UrlMappingRuleInitializer的实现类就等同于Spring MVC的url mapping xml。至于为什么是java类硬编码的形式而不是xml文件,我们有以下几点考虑:

1. 即使是xml文件,修改了映射规则,代码也是要重新打包发布的,从这个意义上说,这个xml文件并不是配置文件,而是代码,只不过编码语言是特定的xml tag而已。与其使用功能贫乏的自定义xml tag language,现成的java语言应该是更好的选择。

2. 基于xml的配置,没法引用代码中的常量,我们觉得这是一个相当糟糕的味道

3. 在xml配置中,如果想实现一些简单的逻辑判断和额外的处理逻辑,是相当麻烦的事情,而基于Java编码的实现,就相当简单了,不需要额外的学习,java代码该怎么写就怎么写了。特别的,在request handler的配置中已经介绍过了,我们允许下面的代码:

rules.add("/app/handler")  
     .handler(new Object(){  
        @RequestHandler  
        public void handle(){  
            //  
        }  
     }); 

基于xml的配置是没法提供这样的灵活性的。
0 请登录后投票
   发表时间:2014-01-09  
其实URL映射在XML中配置也不是我推崇的,因为XML配置也确实如你如说的那些缺点,我更推崇的是目前Spring MVC注解的配置方式“职责单一”,但我不明白你所说的“在controller中映射url,导致系统完整的site-map是不可见的,这对于可维护性来说是相当糟糕的体验”。在开发界有个不成文规定“约定优于配置”,也就是我从一个前端的URL就可以知道后端的对应的Controller,这样我不觉得会很难维护。相反,当一个项目的URL映射规模相当大时,UrlMappingRuleInitializer的实现类也是变得非常庞大,如果再在其实现类加上request handler处理,那么UrlMappingRuleInitializer的实现类的维护才真的可怕。想想一个有几千行代码的类就已经够怕了。
0 请登录后投票
   发表时间:2014-01-09   最后修改:2014-01-09
semmy 写道

在开发界有个不成文规定“约定优于配置”,也就是我从一个前端的URL就可以知道后端的对应的Controller,这样我不觉得会很难维护。


嗯,这就是为什么用MVC来做网站很麻烦的原因之一。对于一个网站来说,基于SEO的原因URL的结构是经常调整的,我们的网站最初的handler和snippe类的构成,也是基本和URL构造一致的,但经过半年的SEO之后,现在大约只有一半还保持一致了,所以,对于一个网站来说,约定是没法优于配置的。换句话说,我们网站的内部逻辑结构,和外部逻辑结构,并不一致。更进一步的,因为SEO的原因,我们即使更改了链接,也必须保留历史链接有效,因此我们有大量的不同的URL指向相同的页面,这些都可以在UrlMappingRuleInitializer通过简单的remap来实现(remap功能在user guide中没有介绍,计划在reference中说明)。

而对于企业业务系统来说,通常改URL就意味着用户需求发生变更了,只要客户肯给钱,啥都好办了

更进一步的,我们的设计中handler(或者说MVC的Controller)并不是必须的,因此我们必须有一个地方用来配置url与模板的关系,既然我们觉得xml不是一个好的选择,那么一个集中式的UrlMappingRuleInitializer就是必然的选择了。


semmy 写道

相反,当一个项目的URL映射规模相当大时,UrlMappingRuleInitializer的实现类也是变得非常庞大,如果再在其实现类加上request handler处理,那么UrlMappingRuleInitializer的实现类的维护才真的可怕。想想一个有几千行代码的类就已经够怕了。


理论上,这个类的确可能有几千行,但实际上不会出现这种情况,因为我们可以简单的把内容拆分到不同的类中,我们的实际编码是按照网站的一级目录拆分成了大约7-8个类,每个类内部再按照二级目录拆分成不同的方法,而UrlMappingRuleInitializer的实现,只是简单的调用一级目录类的方法而已。顺便说一下,这个拆分并不需要复杂的接口设计之类的,简单的静态方法就可以了,唯一的目的就是把这几千行内容分散到不同的文件中去而已。 当然,你也可以为这个小事情设计一个定制化的接口来让代码看起来更不明觉厉一些:D

你看,基于java的配置代码,有无限可能,只有想不到,没有做不到。
0 请登录后投票
   发表时间:2014-01-09   最后修改:2014-01-09
关于约定大于配置的问题,我再补充一点,Asta4D是基于View First的,因此URL是优先映射到模板文件而不是handler(controller)的,我们也的确有这样的case,就是某个路径下的所有下级url,都是简单映射到固定的目录中的相同文件名的模板文件的,我们的应对方法是,提供了一个“GenericPathTemplateHandler”的request handler,在URL配置中简单的指定上级目录,就可以把所有的请求映射到同名的模板文件上去。

比如为了方便设计师调试新页面,我们特意定义了下面的rule:


if(debugMode){
  rules.add("/debug").handler(new GenericPathTemplateHandler());
}



特别注意一点,因为是基于java的代码,我们可以很简单的让这个路径,只在开发模式有效

也许,我应该在user guide中就说明一下有这么个功能,本来是考虑放到reference中去的。
0 请登录后投票
   发表时间:2014-01-14   最后修改:2014-01-14
1、如何扩展Asta4D目前已有的标签,比如使其权限控制渲染的功能?
2、一个网站如果需要生成静态页面,Asta4D的渲染引擎可以完成吗?如何实现?
0 请登录后投票
   发表时间:2014-01-15   最后修改:2014-01-15
semmy 写道

1、如何扩展Asta4D目前已有的标签,比如使其权限控制渲染的功能?

我不太明白你说的扩展是什么意思,但就具体的说权限控制来说,可以有如下方法可以做到:

1. 因为snippet的调用是从外到内允许嵌套的,因此可以在需要权限控制的snippet外围嵌套一个PermitSnippet,在该snippet的方法中判断权限,如果没有权限,把下层的内容替换掉就行了。
2. 框架提供了snippet interceptor接口,该interceptor在每个snippet方法被调用前和调用后被执行,因此,也可以在before snippet的时候判断权限。
3. 更进一步的,系统允许你配置自定义的snippet resolver,比如通过spring容器获得的snippet实例,可以很轻松的通过spring aop功能来拦截snippet方法。

另外,我们目前正在考虑为snippet增加缓存的功能,在实现方法上仍然在讨论中,有可能会增加一个允许扩展自定义标签的功能,或者允许在snippet标签上更多的定制化动作。

semmy 写道

2、一个网站如果需要生成静态页面,Asta4D的渲染引擎可以完成吗?如何实现?

就静态页面来说,其实是分为两类的,一类是真正的静态页面,事先做好的html文件,就原样传递给客户端,另外一类,可以勉强称为伪静态页面,是指它的内容的确是静态的,但是一般来说因为一个系统的所有页面要求统一的页面布局,比如要求统一的header和footer,在这种情况下,这个即使的静态的页面,也会从系统的parent template继承之后在content block中写入自己的静态内容,对这种页面来讲,需要用到框架的模板合并的功能,因此,大概可以视之为伪静态页面。

对于静态页面来说,可以和css,js一样处理,框架提供了一个StaticResourceHandler的预定义request handler,和上面举例的GenericPathTemplateHandler一样的方法声明URL匹配规则。StaticResourceHandler只是简单的读入文件内容,然后按照二进制写入response,并根据文件扩展名写入mime type。另外,StaticResourceHandler也允许你继承之后定制化应答内容。

对于伪静态页面来讲,其实就用GenericPathTemplateHandler就可以了。框架会解释文件并进行模板合并,最后将结果输出。

另外,如果你指的是进最后的生成的页面结果静态化的话,Asta4D并不提供这样的功能,因为这个可以简单的用apache或者nginx的反向代理来实现,事实上我们就是这样做的。另外,在计划中的snippet cache功能可以提供近似于静态化的能力。
0 请登录后投票
   发表时间:2014-01-15   最后修改:2014-01-15
1、我说的标签扩展是指比如如下:
<afd:snippet render="SimpleSnippet:setProfile" permit="权限ID"> <div>有权限才显示</div></afd:snippet>

可不可以通过扩展在标签在自定义属性,然后自定义是否渲染子标签及内容。


2、不好意思关于第2点静态化没看明白。我的意思做网站往往可能需要SEO(搜索优化),那么很多人会生成静态的html文件,我的意思就是Asta4D模板引擎里,如何可以实现生成html文件,这种静态页面。就像freemarker可以生成静态页面那样。
3、Asta4D好像一个URL必须映射到一个模板文件,是吗?那如果是AJAX请求时,往往是只需要数据,不需要指定页面的,那么如何处理?
0 请登录后投票
   发表时间:2014-01-15   最后修改:2014-01-15
semmy 写道
1、我说的标签扩展是指比如如下:
<afd:snippet render="SimpleSnippet:setProfile" permit="权限ID"> <div>有权限才显示</div></afd:snippet>

可不可以通过扩展在标签在自定义属性,然后自定义是否渲染子标签及内容。


严格的说,介于支持和不支持之间。我上面提到了,你可以有三种方案来实现这个权限检查,如果你用下面的代码来做,虽然稍显麻烦,但却是得到静态配置的`permit`值的最佳办法:

<afd:snippet render="PermitSnippet" permit="权限ID">
  <afd:snippet render="SimpleSnippet:setProfile"> <div>有权限才显示</div></afd:snippet>
</afd:snippet>



class PermitSnippet{  
  
  public Renderer render(String permit){  
     if(check(permit)){  
        //do nothing  
        return Renderer.create();  
        //or  
        //return new GothroughRenderer();  
     }else{  
        return Renderer.create("*", Clear);  
     }  
  }  
  
}  



这里,你在html中静态声明的属性,可以被自动注入到snippet方法中。另外,在声明snippet的时候,不声明方法就是缺省的名字“render”。

然后,如果按照你提示的代码来写的话,那你就必须使用我提到的另外两个方法,框架的interceptor,或者aop,但很遗憾你都没法直接得到这个静态配置的属性值,因此你必须调用一个稍微底层些的api来得到这个值,然后你就可以做同样的检查了。这个地方算是一个缺陷。

就我们目前正在规划的snippet cache的功能来说,一个最低限度会做的改进就是,在snippet intercepor中,会允许你简单的访问到静态配置的snippet属性。

semmy 写道

2、不好意思关于第2点静态化没看明白。我的意思做网站往往可能需要SEO(搜索优化),那么很多人会生成静态的html文件,我的意思就是Asta4D模板引擎里,如何可以实现生成html文件,这种静态页面。就像freemarker可以生成静态页面那样。


我仍然不太明白你说的生成静态页面是什么意思,如果说是将生成的页面静态缓存起来以便提高性能的话,如我上面所解释的,框架最后生成并输出到客户端的必然是一个静态页面,但框架并不提供接口让你将生成的页面缓存起来,页面的静态缓存功能,我们现在是交给nginx的反向代理来做的,同样我们认为,这个事情没有必要在框架中来解决,因为nginx可以做得更好。

另外,规划中的对snippet执行结果的缓存功能,可以在很大程度上解决性能问题,因为框架拼接模板,解析DOM所耗费的CPU成本,在现代服务器上是非常轻微的。

semmy 写道

3、Asta4D好像一个URL必须映射到一个模板文件,是吗?那如果是AJAX请求时,往往是只需要数据,不需要指定页面的,那么如何处理?


很好的问题,答案是request handler。在request handler中,可以简单的返回header only的应答。更进一步的,如果你希望在body部分带上数据,比如json,有两个选择:
1. 自己在request handler中将json写入response
2. request handler返回pojo,在url rule中声明一个json转换:

rules.add("/ajax/getJson")
     .handler(new Object(){
          @RequestHandler
          public SomePojo handle(){
              return new SomePojo();
          } 
      }).json();


鉴于我们目前的系统只有返回json的case而没有返回xml的case,因此我们暂时还没有实现在rule上直接声明xml转换 ,但你仍然可以自己在request handler中直接写response的outputstream。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics