首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

展示逻辑AOP, 复杂页面逻辑Craker - fastm. .

2012-10-27 
显示逻辑AOP, 复杂页面逻辑Craker -- fastm. .....这段时间在javaeye的关于fastm的讨论,令我受益良多。特此

显示逻辑AOP, 复杂页面逻辑Craker -- fastm. .....
这段时间在javaeye的关于fastm的讨论,令我受益良多。特此感谢大家。非常感谢。
很多人鼓励我的精神,很多人耐心指正我的错误,也有很多人提出尖锐深刻的问题。最感谢的是,甚至有些朋友深入研究了fastm,并提出了改进方案。
并且很多朋友也讨论了fastm之外的话题,令我的知识面开阔。

我这里把这几天针对fastm的一些尖锐深刻的问题列出来。并感谢大家对fastm的深入思考、详细讨论和尖锐抨击。

(1)格式化信息分布在Template DOM和ValueSet DOM两个地方
<c:choose> <c:when test="${copyMade}"> here we add a line. <h4><fmt:message key="copy.ok"/></h4> </c:when> <c:otherwise> here we add a line too. <h4><fmt:message key="copy.nok"/></h4> </c:otherwise> </c:choose>

因为逻辑嵌套只有两层,那么对应的fastm DOM也只有两层。
fastm DOM其实没有增加复杂度。而且,fastm DOM还简化了复杂度。
这是fastm的一个优点。
ValueSet DOM是fastm实现显示逻辑AOP和显示逻辑重用的关键结构。
也是Pipeline式处理过程的数据交换中枢。

不在这里讨论了。:-)  以后证明。

(5)fastm的代码长

其实,这个问题也有些误会。
只是“看起来”,fastm的代码比较长。
我本来是把fastm代码短作为一个优势来宣传的。

其它模板技术的代码分布在模板和Java Code两端。
即使不计算TagLib的代码。fastm代码其实还要比其它的模板技术短。

而且,fastm代码只存在于Java Code里,而且很多都是公用方法 -- 页面逻辑AOP。

不在这里讨论了。:-)  以后证明。public class ViewObject { private Object entity public ViewObject(Object entity);{ this.entity = entity; }}public interface ViewObjectFactory { public void ViewObject createViewObject(Obejct entity);;}

拦截createViewObject方法, pointcut使用正则表达式, 使ViewObject的get*Date()被拦截, 变成return format(entity.get*Date()); 而其他的getter都只是简单地代理调用entity.getter

另外这种格式化的代码用AOP来实现,  纯粹只是一个噱头, AOP, AOP, 只不过是炒冷饭而已......

:-) 我喜欢这种直截了当的直观交流方式。多谢你的代码。还有一点需要请教。
你采用的是Dynamic Proxy, 还是AspectJ的静态织入?
“拦截createViewObject方法”,然后在里面用reflection判断get*Date(), 还是用pointcut定义get*Date()?
我的理解是用reflection做到比较直观和容易。
用pointcut定义get*Date()的方法,能具体一点吗?

在我的印象中,Dynamic Proxy只能够截获Interface的方法。
我前面讲过了,不可能为不同的Entity,都声明一个getter, setter interface.
那么get*Date()是怎么被截获的?
hibernate用了cglib继承(extends)VO,动态实现PO。这种方法到是可能把一些代码加入到子类的实现当中去。但同样要为每一个entity建立一个配置问文件。

你给出的代码用的是哪一种AOP技术?
JDK Dynamic Proxy? CGLIB?  AspectJ? Spring AOP? JBoss AOP?

sorry, 我的理解能力有限,希望能够多指教。

先向几位唐大哥请安了, 偶又回来了, 下面是正文:

偶知道的几种Dynamic Proxy技术如你所说只能支持interface, 面向interface, OOP的第一守则了吧, 这个就不用多说了, 拿cglib来举例:
SimpleInvocationHandler.java

import java.lang.reflect.Method;import java.text.SimpleDateFormat;import java.util.Date;import net.sf.cglib.proxy.InvocationHandler;public class SimpleInvocationHandler implements InvocationHandler {    public static final SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");;        private Object o = null;    public SimpleInvocationHandler(Object o); {        this.o = o;    }    public Object invoke(Object proxy, Method m, Object[] args); throws Throwable {        Object r = m.invoke(o, args);;        if(m.getName();.matches("get.*Date");); {            return (new FDate((Date);r););;        }        return r;    }        private class FDate extends Date {        public FDate (Date date); {            super(date.getTime(););;        }                public String toString(); {            return f.format(this);;        }    }}

Unit test code:
import java.util.Date;import junit.framework.TestCase;import net.sf.cglib.proxy.InvocationHandler;import net.sf.cglib.proxy.Proxy;public class TestProxy extends TestCase {    public interface IFoo {        public Date getBarDate();;        public String getBarString();;    }        public class Foo implements IFoo {        private Date barDate;        private String barString;        public Foo(Date date, String string); {            barDate = date;            barString = string;        }        public Date getBarDate(); {            return barDate;        }                public String getBarString(); {            return barString;        }    }    public void testGetProxyInstance(); throws Exception {        Foo foo = new Foo(new Date(0);, "huluhulu");;                InvocationHandler handler = new SimpleInvocationHandler(foo);;        IFoo proxy = (IFoo); Proxy.newProxyInstance(TestProxy.class.getClassLoader();, new Class[] { IFoo.class }, handler);;        assertEquals("huluhulu", proxy.getBarString(););;        assertEquals("1970-01-01", proxy.getBarDate();.toString(););;            }}


偶知道AspectWerkz ( http://aspectwerkz.codehaus.org/features.html ) 这种半静态的方式则可支持POJO, 不过从来没有用过, 你自己去看文档玩吧.

AOP只是嗡嗡作响的苍蝇, 一个设计良好的OO架构, 完全可以让这只苍蝇滚蛋, 而格式化用AOP来处理更是吃力不讨好.

眼看着fastm将在四不象的道路上堕落, 可惜, 可惜是别人家的孩子, 各位唐大哥们就少说两句, 让这个孩子耳根清净些吧
import java.lang.reflect.Method;import java.text.SimpleDateFormat;import java.util.Date;import net.sf.cglib.proxy.InvocationHandler;public class SimpleInvocationHandler implements InvocationHandler { public static final SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");; private Object o = null; public SimpleInvocationHandler(Object o); { this.o = o; } public Object invoke(Object proxy, Method m, Object[] args); throws Throwable { Object r = m.invoke(o, args);; if(m.getName();.matches("get.*Date");); { return (new FDate((Date);r););; } return r; } private class FDate extends Date { public FDate (Date date); { super(date.getTime(););; } public String toString(); { return f.format(this);; } }}
Unit test code:
import java.util.Date;import junit.framework.TestCase;import net.sf.cglib.proxy.InvocationHandler;import net.sf.cglib.proxy.Proxy;public class TestProxy extends TestCase {    public interface IFoo {        public Date getBarDate();;        public String getBarString();;    }        public class Foo implements IFoo {        private Date barDate;        private String barString;        public Foo(Date date, String string); {            barDate = date;            barString = string;        }        public Date getBarDate(); {            return barDate;        }                public String getBarString(); {            return barString;        }    }    public void testGetProxyInstance(); throws Exception {        Foo foo = new Foo(new Date(0);, "huluhulu");;                InvocationHandler handler = new SimpleInvocationHandler(foo);;        IFoo proxy = (IFoo); Proxy.newProxyInstance(TestProxy.class.getClassLoader();, new Class[] { IFoo.class }, handler);;        assertEquals("huluhulu", proxy.getBarString(););;        assertEquals("1970-01-01", proxy.getBarDate();.toString(););;            }}


偶知道AspectWerkz ( http://aspectwerkz.codehaus.org/features.html ) 这种半静态的方式则可支持POJO, 不过从来没有用过, 你自己去看文档玩吧.

AOP只是嗡嗡作响的苍蝇, 一个设计良好的OO架构, 完全可以让这只苍蝇滚蛋, 而格式化用AOP来处理更是吃力不讨好.

眼看着fastm将在四不象的道路上堕落, 可惜, 可惜是别人家的孩子, 各位唐大哥们就少说两句, 让这个孩子耳根清净些吧


多谢你的代码。很漂亮的实现。
我前面的意思是说,每个需要显示的Bean都不同。因为我们需要显示的Bean不一定每个都有interface。 那么为每个Bean都额外声明一个interface,代价还是挺大的。

<% for ..... { %><mytag:access screenId="XXX" ><a href="the url to the screen XXX"></mytag: access><%= dictionary.find(obj.column1); %> -- obj.column1<mytag:access screenId="XXX" ></a></mytag: access>....<%} // end for %>

access tag用来判断当前用户(in session)是否有访问“XXX”的权限。如果有,那么显示那个 link <a href = >  </a>。如果没有访问这个权限,那么不显示那个link。用户看不到那里的 link.

至于显示字段的值,通常也没有那么直接。
数据库里存放的都是整数,由一个自定义的数据字典翻译为显示字符串。
比如(不是真实业务数据),
1   ---- One.
2   ---- Two


这种JSP方案的缺点:
(1)那个<mytag: access> 权限判断在循环里面,判断多次。其实只要判断一次就可以。但如何把这个部分抽取出来,是个难题。
(2)很难进行局部Cache。我调查过的那个Apache Cache TagLib, 也满足不了要求。那个Apache Cache TagLib不够强大和灵活,不能根据不同的请求进行get, set, 或invalidate Cache。

如果用Velocity, freemarker,如何解决这两个缺点?for each.... if(access_column1); show column1 link ... if(access_column2); show column2 link ...end for

一般来说,产品设计人员根据用户需求,一旦定下了UI,用户界面方面基本就没有商量的余地了。有的时候,产品设计人员提出很多动态交互的要求,需要大量的动态Java Script支持,但是也必须做到。
开发人员根据这些UI和业务要求,可以自主的定义DB Schema,这样也能够从一定程度缓解实现的复杂度。这个确实要非常详细准确地分析理解业务需求。

我做的项目中的页面,千篇一律,全是同样的需求模式。除了上面说的需求模式,还有头表数据列表和子表数据列表都需要的统一的分页需求。用户的目的是尽量在同一个页面中,显示尽量多的关联信息,免得他到处去找。

关于缓存的问题,一种是你说的方法,把相关的数据缓存起来,并可以缓存相关的数据(比如,access_column1, access_column2,还有其它的从Dictionary中取出来的显示数据)。这时缓存的相当于View Object。这种缓存方法对任何模板技术都适用。页面处理逻辑几乎不需要额外的计算,只要把View Object取出来显示就可以了。
一种是直接缓存部分结果页面,就是我说的方法。这种方式只比前一种方式节省了一点处理时间,从空间上来说,一段连续的HTML String也不比一个View Object列表大多少。从缓存的调用方法来说,这种方法也要简单一些,只需要考虑一个String类型的数据就够了。这种缓存方案,只有一些高级的Cache TagLib可以做到,或者是fastm, 或者XMLC这种DOM模板技术。
缓存View Object, 或缓存HTML,这两种方案的实际的空间和时间差别不会很大。

在这类的UI要求中,显示逻辑和后台逻辑的界限不是很清楚,比如,权限判断的部分,可以放在后端的Java Code,也可以放在前端的模板脚本中。如果放在后端的Java Code中,还可以想办法把一些通用操作抽取出来作为公用方法,增加结构性,和可重用性。这也是我写fastm的初衷,fastm就是被这种需求逼迫出来的。fastm走向的一个极端就是,把逻辑完全从模板中驱逐了出去。

对于显示逻辑简单的页面,fastm确实没有用武之地。硬把简单的for each,if else从模板脚本中移出来,放到Java Code里面,反而更不直观,更难维护。

我一直在考虑fastm适用的范围:
(1)显示逻辑需要复杂到一定程度,HTML长到一定程度,一段for each, 或 if else 的跨度很大,超过一个屏幕的时候,程序员才会对HTML里面的脚本感到抱怨。

(2)显示逻辑的需求模式重复性很大的时候(比如前面说的格式化,权限判断,字典查询,分页等),程序员才会抱怨HTML的脚本的结构不好,和不能重用等问题。

(3)对动态生成页面的速度要求比较高的情况。由于fastm使用java code处理显示逻辑,速度比脚本快。但这个优势的用处却不是很明显。如你所说,大型网站通常都是用静态Cache之类的技术缓解大访问量,对动态要求不好。而对动态要求高的webapp网站,页面通常又不会很复杂,用户量也不会很大,而且速度瓶颈通常都在于大数据量查询,和后台业务计算上。

我一直在试图找到一个Portal/CMS类 和 Webapp(如ERP/CRM )类 这两个领域之间的一个结合点:页面处理逻辑足够复杂,动态速度要求又很高的一个领域。那才是fastm的用武之地。
按照查询条件来缓存,是更通用,命中率高,用户间共享Cache的一个方法,但实现难度比较大。那就是你说的,以SQL(或查询条件)为key的数据缓存。这种方案应该在DAO数据访问层实现,也许需要考虑DAO函数的signature,Cache的管理也相应复杂,还要考虑数据刷新问题。考虑到这个代价,一般不轻易使用这种方案。

另外,你说的“甲方”,是指购买产品的客户方?

我处在的角色是开发人员,中间有产品设计人员和客户直接打交道。
我考虑的主要是,如何在规定时间内多快好省地完成规定的项目,最好这一期项目中的技术积累能够用在其它的项目中。<select name="state"> <!-- BEGIN DYNAMIC: state--> <option value="{value}" {selected}>{display}</option> <!-- END DYNAMIC: state--></select>

java

List options = makeOptions(all options, selected option value);;valueSet.setDynamicValueSets("state", options);;makeOptions method is a common function. It looks like.List makeOptions(String[][] options,  String selectedOption);{   List valueSets = new ArrayList(options.length);;   for(int i =0; i < options.length; i++);{      String[] pair = options[i];      String value = pair[0];      String display = pair[1];      IValueSet valueSet = new ValueSet();;      valueSet.setVariable("{value}", value);;      valueSet.setVariable("{display}", display);;      valueSet.setVariable("{selected}",           value.equals(selectedOption); ? "selected" : "" );;      valueSets.add(valueSet);;   }   return valueSets;}


2. checkbox

html

<input name="a" value="1" {a_checked}/>


java

valueSet.setVariable("{a_checked}", "checked");;

热点排行