以Ruby為助力的Ruby on Rails 橫空出世,又刷新了一次人們對(duì)Framework的認(rèn)識(shí):Closures、Maxin、Continuations、Metaprogramming、Reflection,又一堆名詞砸得人悴不及防。
????? Java 雖然沒有動(dòng)態(tài)語言般暴起,但仍然天連天,水接水的生出好多框架技術(shù)---反射(reflection),泛型(generics),元數(shù)據(jù)(annotation),proxies(proxy/cglib),代碼動(dòng)態(tài)生成(asm),AOP(aspectJ),動(dòng)態(tài)語言嵌入(groovy/javascript/beanshell)。面對(duì)著這些,我們像一夜暴富的農(nóng)企,有點(diǎn)手足無措的樣子。
??? 第一感覺,應(yīng)該出現(xiàn)新的Design Pattern書籍了。
??? 多年前基于純C++語法寫就的那本經(jīng)典,很多模式都可以被更優(yōu)雅的實(shí)現(xiàn),還有更多的新模式,會(huì)隨著這些技術(shù)的推廣而出現(xiàn)。
??? 第二感覺,新的框架模式出現(xiàn)了。
??? 不斷升級(jí)自己的Spring,綜合運(yùn)用了除泛型外的大部分技術(shù),把編程界的想像力MFC/ EJB2.0 Container這樣傳統(tǒng)的template模式/鉤子函數(shù)的思路,向完全無侵入的標(biāo)準(zhǔn)過渡。
??? 當(dāng)你不顧性能狂用反射時(shí),C++下的遺憾感開始越來越遠(yuǎn)。
????
??? 第三感覺,自己應(yīng)該重新充電了。
??? 從C++下編寫框架的經(jīng)歷到盡用“反射,泛型,元數(shù)據(jù),proxy,代碼動(dòng)態(tài)生成,AOP,動(dòng)態(tài)語言嵌入”是一條明顯的分界線,自己應(yīng)該調(diào)度足夠的業(yè)余時(shí)間,沉實(shí)的學(xué)習(xí)一遍。
??? 這個(gè)系列開始一個(gè)個(gè)介紹上述的積木。
反射,所有教科書都正兒八經(jīng)的講了,這里只能再隨便講兩句。
?????反射是一種讓框架能夠根據(jù) "以字符串形式存在的信息" 來調(diào)用對(duì)象的屬性和函數(shù)的技術(shù),是Java對(duì)C++最大的進(jìn)步之一---讓框架編程真正走向平民化。MFC年代,無論侯捷如何深入淺出,還在念大學(xué)的我就是搞不懂那些注冊(cè)"消息--函數(shù)映射"的魔法宏。
?????不過Java的反射也就是對(duì)著C++比較自豪而以,因?yàn)镃#,Ruby,Python甚至php都標(biāo)配了反射的功能。而且,人家的反射語法都是內(nèi)嵌在基礎(chǔ)Object類的,拿最弱的php來看:
???? 而Java,卻搞出了Class,Methed, Field,Constructor這么一大堆類出來。本來這是Java設(shè)計(jì)師很嚴(yán)謹(jǐn),很cool的體現(xiàn),問題是它居然不提供一種集成的簡(jiǎn)便的寫法......相同的情形還出現(xiàn)在Java的I/O 類庫里。
????微軟這方面就做得好些,懂得討好開發(fā)人員。
??? 因?yàn)镴ava的無情,就搞得大家的項(xiàng)目里要自制BeanUtils了。幸虧Apache Jakarta Commons 已經(jīng)做了一個(gè)比較好的,可以直接使用--以前寫的介紹文章。
??? 另外Spring也做了一個(gè)。
????閑得沒事做的,還可以emule一本〈Relection in action〉回來看。
??? 而C++下面的"反射",見我偶像di文章。另還有一個(gè)比較BT的C++框架叫ACDK的,把自己整得和Java很像,有反射和垃圾收集,甚至和JSDK差不多的線程,Unicode,I/O,網(wǎng)絡(luò),XML API。可惜的是,即使到了C++0x, B大叔還是不準(zhǔn)備在語言級(jí)支持反射。
1.因?yàn)閮烧叩挠猛静煌訟nnotation與XML應(yīng)該并存
Anootation vs XML 到了現(xiàn)在,在Spring和EJB3各占一邊的持續(xù)演示下,各自的用途已清晰。
?? 雖然兩者之間完全可以相互替代,沒有絕對(duì)不能做的事情,但卻存在好不好的問題,兩者的用途不同,應(yīng)該并用:
?? "size=10,db_user=ito"這樣的參數(shù),仍然適合放在XML,YAML(ruby),properties,ini文件里。
??? 而Annotation中所放,是真正程序的一部分,是舊編程體系中所缺的那一塊東西。
看看編程語言的發(fā)展:
????一開始只是函數(shù)的封裝;
??? 到了OO語言,對(duì)象有了自己的屬性和方法;
??? 到了框架滿天飛的年代,這些屬性和方法又有了自己的屬性,來告訴框架如何為自己提供服務(wù)。比如Spring和EJB3,POJO就用配置信息來告訴框架無侵入的提供服務(wù)。整個(gè)進(jìn)化的過程非常自然。
????因?yàn)檫@些信息本來就是程序的一部分,所以應(yīng)該仍然放在Code里頭,Spring把它割裂到XML明顯影響了代碼的閱讀。
2.Anotation/XML?PK Interface,Spring 無侵入性的基礎(chǔ)
???切爾斯基的Blog講了Annotation/XML?對(duì)Interface的PK。這次PK,也可以認(rèn)為是Spring 對(duì) EJB2.1在框架無侵入性上的一次PK。
?? 在EJB2.1時(shí)代,POJO必須通過接口來向框架聲明一些東西,這就造成了框架的侵入性,強(qiáng)迫POJO實(shí)現(xiàn)一堆接口。而Spring,把這些元信息搬到了XML配置文件。
在Spring里隨便就可以舉個(gè)例子。比如一個(gè)POJO,在Spring為它進(jìn)行依賴注入A和B后,需要根據(jù)A和B來裝配一個(gè)內(nèi)部屬性C,這樣就需要再跑一個(gè)init()函數(shù)。Spring提供兩種方式,一種侵入性的,實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet()函數(shù)供Spring調(diào)用。???而另一種是無侵入性的,在Bean的xml節(jié)點(diǎn)里面自行定義init函數(shù)的名字。
3.Annotation目前的弱點(diǎn)
一是Hibernate、Struts、三姑媽、四姨婆的annotation如果一股腦兒的全堆在可憐的POJO上很沒人道,如果三姑六婆都搶Transation這個(gè)Anontation就更慘了。
??? 二是annoation的表達(dá)能力有限,不如XML的強(qiáng)。
4.JSR250 - Common Annotations for the Java
??? Final Draft已經(jīng)發(fā)布,八卦回來一看,也只是多定義了幾個(gè)Common Annotation而已。
?? 1.Generated? 自動(dòng)生成的代碼要用此聲明,而且必須說明工具的名稱,如@Generated(“com.sun.xml.rpc.AProcessor”)
???2.Resource/Resources 就是EJB3里面用的資源引用。
?? 3.PostConstruct / PreDestroy 聲明那些如題的函數(shù)。
??
?? 還有一些安全方面的annotation,不關(guān)心。
反射、Proxy和元數(shù)據(jù)是Java最強(qiáng)的三個(gè)特征,再加上CGLib (Code Generation Library)和ASM,使得Java雖然沒有Ruby,Python般后生可畏,一樣能做出強(qiáng)悍的框架。
???Proxy可以看作是微型的AOP,明白提供了在繼承和委托之外的第三個(gè)代碼封裝途徑,只要有足夠的想象力,可以做得非常好玩,Spring的源碼里用Proxy就用得很隨便,看得我非常眼紅。可惜Proxy必須基于接口。因此Spring的做法,基于接口的用proxy,否則就用cglib。AOP么,一般小事非compoent一級(jí)的就不麻煩AspectJ出手了。
????cglib的Enhancer說起來神奇,用起來一頁紙不到就講完了。
??? 它的原理就是用Enhancer生成一個(gè)原有類的子類,并且設(shè)置好callback到proxy, 則原有類的每個(gè)方法調(diào)用都會(huì)轉(zhuǎn)為調(diào)用實(shí)現(xiàn)了MethodInterceptor接口的proxy的intercept()?函數(shù):
?在intercept()函數(shù)里,你可以在執(zhí)行Object result=proxy.invokeSuper(o,args);來執(zhí)行原有函數(shù),在執(zhí)行前后加入自己的東西,改變它的參數(shù)值,也可以瞞天過海,完全干別的。說白了,就是AOP中的around advice。
???public?class?LogDAOProxy?implements?MethodInterceptor
???{
???????private?Logger?log=Logger.getLogger(LogDAOProxy.class);
???????private?Enhancer?enhancer=new?Enhancer();
????????//返回DAO的子類
???????public?Object?getDAO(Class?clz)
???????{
???????????enhancer.setSuperclass(clz);
???????????enhancer.setCallback(this);
???????????return?enhancer.create();
???????}
???????//默認(rèn)的攔截方法
??????public?Object?intercept(Object?o,Method?method,Object[]?args,MethodProxy?proxy)?throws?Throwable
??????{
???????????log.info("調(diào)用日志方法"+method.getName());
???????????Object?result=proxy.invokeSuper(o,args);
???????????return?result;
??????}
???}
??? 應(yīng)用的代碼:
2.而在Spring的管理下應(yīng)該略加修改的高級(jí)Decorator
?? 上面的例子用return?enhancer.create();創(chuàng)建子類實(shí)例,但在Spring管理下,一些Bean的實(shí)例必須由Spring來創(chuàng)建和管理,而不由enhancer來創(chuàng)建的。所以我對(duì)上述用法略加修改,使它真正當(dāng)一個(gè)Proxy的角色,請(qǐng)對(duì)比黑體字的部分
可見,原來模式里在getDao()時(shí)由enhancer創(chuàng)建dao,而?調(diào)用intercept時(shí)則將enhancer創(chuàng)建的dao以O(shè)bject o參數(shù)傳回。
而新模式里,dao在getDao()時(shí)從外面?zhèn)魅耄琫nhancer.create()返回的是一個(gè)proxy. 而調(diào)用intercept時(shí),實(shí)際會(huì)用之前傳入的dao進(jìn)行操作,而忽略O(shè)bject o參數(shù)傳入的proxy.
有點(diǎn)遺憾, intercept函數(shù)里MethodProxy的Signature是固定的 , 即客戶如果調(diào)用foo(String),你不可以用proxy.invoke偷換成foo(String,String);
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請(qǐng)注明來處和原文作者。非常感謝。
????? Java 雖然沒有動(dòng)態(tài)語言般暴起,但仍然天連天,水接水的生出好多框架技術(shù)---反射(reflection),泛型(generics),元數(shù)據(jù)(annotation),proxies(proxy/cglib),代碼動(dòng)態(tài)生成(asm),AOP(aspectJ),動(dòng)態(tài)語言嵌入(groovy/javascript/beanshell)。面對(duì)著這些,我們像一夜暴富的農(nóng)企,有點(diǎn)手足無措的樣子。
??? 第一感覺,應(yīng)該出現(xiàn)新的Design Pattern書籍了。
??? 多年前基于純C++語法寫就的那本經(jīng)典,很多模式都可以被更優(yōu)雅的實(shí)現(xiàn),還有更多的新模式,會(huì)隨著這些技術(shù)的推廣而出現(xiàn)。
??? 第二感覺,新的框架模式出現(xiàn)了。
??? 不斷升級(jí)自己的Spring,綜合運(yùn)用了除泛型外的大部分技術(shù),把編程界的想像力MFC/ EJB2.0 Container這樣傳統(tǒng)的template模式/鉤子函數(shù)的思路,向完全無侵入的標(biāo)準(zhǔn)過渡。
??? 當(dāng)你不顧性能狂用反射時(shí),C++下的遺憾感開始越來越遠(yuǎn)。
????
??? 第三感覺,自己應(yīng)該重新充電了。
??? 從C++下編寫框架的經(jīng)歷到盡用“反射,泛型,元數(shù)據(jù),proxy,代碼動(dòng)態(tài)生成,AOP,動(dòng)態(tài)語言嵌入”是一條明顯的分界線,自己應(yīng)該調(diào)度足夠的業(yè)余時(shí)間,沉實(shí)的學(xué)習(xí)一遍。
??? 這個(gè)系列開始一個(gè)個(gè)介紹上述的積木。
反射,所有教科書都正兒八經(jīng)的講了,這里只能再隨便講兩句。
?????反射是一種讓框架能夠根據(jù) "以字符串形式存在的信息" 來調(diào)用對(duì)象的屬性和函數(shù)的技術(shù),是Java對(duì)C++最大的進(jìn)步之一---讓框架編程真正走向平民化。MFC年代,無論侯捷如何深入淺出,還在念大學(xué)的我就是搞不懂那些注冊(cè)"消息--函數(shù)映射"的魔法宏。
?????不過Java的反射也就是對(duì)著C++比較自豪而以,因?yàn)镃#,Ruby,Python甚至php都標(biāo)配了反射的功能。而且,人家的反射語法都是內(nèi)嵌在基礎(chǔ)Object類的,拿最弱的php來看:
$func_name="helloworld";
$foo->$func_name;
$foo->$func_name;
???? 而Java,卻搞出了Class,Methed, Field,Constructor這么一大堆類出來。本來這是Java設(shè)計(jì)師很嚴(yán)謹(jǐn),很cool的體現(xiàn),問題是它居然不提供一種集成的簡(jiǎn)便的寫法......相同的情形還出現(xiàn)在Java的I/O 類庫里。
????微軟這方面就做得好些,懂得討好開發(fā)人員。
??? 因?yàn)镴ava的無情,就搞得大家的項(xiàng)目里要自制BeanUtils了。幸虧Apache Jakarta Commons 已經(jīng)做了一個(gè)比較好的,可以直接使用--以前寫的介紹文章。
??? 另外Spring也做了一個(gè)。
????閑得沒事做的,還可以emule一本〈Relection in action〉回來看。
??? 而C++下面的"反射",見我偶像di文章。另還有一個(gè)比較BT的C++框架叫ACDK的,把自己整得和Java很像,有反射和垃圾收集,甚至和JSDK差不多的線程,Unicode,I/O,網(wǎng)絡(luò),XML API。可惜的是,即使到了C++0x, B大叔還是不準(zhǔn)備在語言級(jí)支持反射。
1.因?yàn)閮烧叩挠猛静煌訟nnotation與XML應(yīng)該并存
Anootation vs XML 到了現(xiàn)在,在Spring和EJB3各占一邊的持續(xù)演示下,各自的用途已清晰。
?? 雖然兩者之間完全可以相互替代,沒有絕對(duì)不能做的事情,但卻存在好不好的問題,兩者的用途不同,應(yīng)該并用:
?? "size=10,db_user=ito"這樣的參數(shù),仍然適合放在XML,YAML(ruby),properties,ini文件里。
??? 而Annotation中所放,是真正程序的一部分,是舊編程體系中所缺的那一塊東西。
看看編程語言的發(fā)展:
????一開始只是函數(shù)的封裝;
??? 到了OO語言,對(duì)象有了自己的屬性和方法;
??? 到了框架滿天飛的年代,這些屬性和方法又有了自己的屬性,來告訴框架如何為自己提供服務(wù)。比如Spring和EJB3,POJO就用配置信息來告訴框架無侵入的提供服務(wù)。整個(gè)進(jìn)化的過程非常自然。
????因?yàn)檫@些信息本來就是程序的一部分,所以應(yīng)該仍然放在Code里頭,Spring把它割裂到XML明顯影響了代碼的閱讀。
2.Anotation/XML?PK Interface,Spring 無侵入性的基礎(chǔ)
???切爾斯基的Blog講了Annotation/XML?對(duì)Interface的PK。這次PK,也可以認(rèn)為是Spring 對(duì) EJB2.1在框架無侵入性上的一次PK。
?? 在EJB2.1時(shí)代,POJO必須通過接口來向框架聲明一些東西,這就造成了框架的侵入性,強(qiáng)迫POJO實(shí)現(xiàn)一堆接口。而Spring,把這些元信息搬到了XML配置文件。
在Spring里隨便就可以舉個(gè)例子。比如一個(gè)POJO,在Spring為它進(jìn)行依賴注入A和B后,需要根據(jù)A和B來裝配一個(gè)內(nèi)部屬性C,這樣就需要再跑一個(gè)init()函數(shù)。Spring提供兩種方式,一種侵入性的,實(shí)現(xiàn)InitializingBean接口的afterPropertiesSet()函數(shù)供Spring調(diào)用。???而另一種是無侵入性的,在Bean的xml節(jié)點(diǎn)里面自行定義init函數(shù)的名字。
3.Annotation目前的弱點(diǎn)
一是Hibernate、Struts、三姑媽、四姨婆的annotation如果一股腦兒的全堆在可憐的POJO上很沒人道,如果三姑六婆都搶Transation這個(gè)Anontation就更慘了。
??? 二是annoation的表達(dá)能力有限,不如XML的強(qiáng)。
4.JSR250 - Common Annotations for the Java
??? Final Draft已經(jīng)發(fā)布,八卦回來一看,也只是多定義了幾個(gè)Common Annotation而已。
?? 1.Generated? 自動(dòng)生成的代碼要用此聲明,而且必須說明工具的名稱,如@Generated(“com.sun.xml.rpc.AProcessor”)
???2.Resource/Resources 就是EJB3里面用的資源引用。
?? 3.PostConstruct / PreDestroy 聲明那些如題的函數(shù)。
??
?? 還有一些安全方面的annotation,不關(guān)心。
反射、Proxy和元數(shù)據(jù)是Java最強(qiáng)的三個(gè)特征,再加上CGLib (Code Generation Library)和ASM,使得Java雖然沒有Ruby,Python般后生可畏,一樣能做出強(qiáng)悍的框架。
???Proxy可以看作是微型的AOP,明白提供了在繼承和委托之外的第三個(gè)代碼封裝途徑,只要有足夠的想象力,可以做得非常好玩,Spring的源碼里用Proxy就用得很隨便,看得我非常眼紅。可惜Proxy必須基于接口。因此Spring的做法,基于接口的用proxy,否則就用cglib。AOP么,一般小事非compoent一級(jí)的就不麻煩AspectJ出手了。
????cglib的Enhancer說起來神奇,用起來一頁紙不到就講完了。
??? 它的原理就是用Enhancer生成一個(gè)原有類的子類,并且設(shè)置好callback到proxy, 則原有類的每個(gè)方法調(diào)用都會(huì)轉(zhuǎn)為調(diào)用實(shí)現(xiàn)了MethodInterceptor接口的proxy的intercept()?函數(shù):
public?Object?intercept(Object?o,Method?method,Object[]?args,MethodProxy?proxy)
?在intercept()函數(shù)里,你可以在執(zhí)行Object result=proxy.invokeSuper(o,args);來執(zhí)行原有函數(shù),在執(zhí)行前后加入自己的東西,改變它的參數(shù)值,也可以瞞天過海,完全干別的。說白了,就是AOP中的around advice。
??? AOP沒有出現(xiàn)以前,該領(lǐng)域經(jīng)典的設(shè)計(jì)模式是Decorator,像Java IO Stream的設(shè)計(jì)就是如此.不過,如果為每個(gè)DAO, 每個(gè)方法的寫Decorator函數(shù)會(huì)寫死人的,所以用上cglib的好處是一次過攔截所有方法。?
???? 另外,cglib除了Enhancer之外,還有BulkBean和Transform,都是Hibernate持久化的基礎(chǔ),但文檔貧乏,一時(shí)還沒去看怎么用。
1.AOP里講了一百遍阿一百遍的log aspect在cglib是這樣做的:
???public?class?LogDAOProxy?implements?MethodInterceptor
???{
???????private?Logger?log=Logger.getLogger(LogDAOProxy.class);
???????private?Enhancer?enhancer=new?Enhancer();
????????//返回DAO的子類
???????public?Object?getDAO(Class?clz)
???????{
???????????enhancer.setSuperclass(clz);
???????????enhancer.setCallback(this);
???????????return?enhancer.create();
???????}
???????//默認(rèn)的攔截方法
??????public?Object?intercept(Object?o,Method?method,Object[]?args,MethodProxy?proxy)?throws?Throwable
??????{
???????????log.info("調(diào)用日志方法"+method.getName());
???????????Object?result=proxy.invokeSuper(o,args);
???????????return?result;
??????}
???}
??? 應(yīng)用的代碼:
????LogDAOProxy?proxy?=?new?LogDAOProxy();
????GoodsDAO??dao?=?(GoodsDAO)proxy.getDAO(GoodsDAO.class);
????dao.insert(goods);
????GoodsDAO??dao?=?(GoodsDAO)proxy.getDAO(GoodsDAO.class);
????dao.insert(goods);
2.而在Spring的管理下應(yīng)該略加修改的高級(jí)Decorator
?? 上面的例子用return?enhancer.create();創(chuàng)建子類實(shí)例,但在Spring管理下,一些Bean的實(shí)例必須由Spring來創(chuàng)建和管理,而不由enhancer來創(chuàng)建的。所以我對(duì)上述用法略加修改,使它真正當(dāng)一個(gè)Proxy的角色,請(qǐng)對(duì)比黑體字的部分
??public?class?LogDAOProxy?implements?MethodInterceptor
??{
???????private?Logger?log=Logger.getLogger(LogDAOProxy.class);
???????private?Object?dao=null;
???????private?Enhancer?enhancer=new?Enhancer();
????????//返回DAO的子類
???????public?Object?getDAO(Class?clz,Object?dao)
???????{
???????????this.dao?=?dao;
???????????enhancer.setSuperclass(clz);
???????????enhancer.setCallback(this);
???????????return?enhancer.create();
???????}??????
???????//默認(rèn)的攔截方法
??????public?Object?intercept(Object?o,Method?method,Object[]?args,MethodProxy?proxy)?throws?Throwable
??????{
???????????log.info("調(diào)用日志方法"+method.getName());
???????????Object?result=proxy.invoke(dao,?args);
???????????return?result;
??????}
??}
??{
???????private?Logger?log=Logger.getLogger(LogDAOProxy.class);
???????private?Object?dao=null;
???????private?Enhancer?enhancer=new?Enhancer();
????????//返回DAO的子類
???????public?Object?getDAO(Class?clz,Object?dao)
???????{
???????????this.dao?=?dao;
???????????enhancer.setSuperclass(clz);
???????????enhancer.setCallback(this);
???????????return?enhancer.create();
???????}??????
???????//默認(rèn)的攔截方法
??????public?Object?intercept(Object?o,Method?method,Object[]?args,MethodProxy?proxy)?throws?Throwable
??????{
???????????log.info("調(diào)用日志方法"+method.getName());
???????????Object?result=proxy.invoke(dao,?args);
???????????return?result;
??????}
??}
可見,原來模式里在getDao()時(shí)由enhancer創(chuàng)建dao,而?調(diào)用intercept時(shí)則將enhancer創(chuàng)建的dao以O(shè)bject o參數(shù)傳回。
而新模式里,dao在getDao()時(shí)從外面?zhèn)魅耄琫nhancer.create()返回的是一個(gè)proxy. 而調(diào)用intercept時(shí),實(shí)際會(huì)用之前傳入的dao進(jìn)行操作,而忽略O(shè)bject o參數(shù)傳入的proxy.
有點(diǎn)遺憾, intercept函數(shù)里MethodProxy的Signature是固定的 , 即客戶如果調(diào)用foo(String),你不可以用proxy.invoke偷換成foo(String,String);
凡是有該標(biāo)志的文章,都是該blog博主Caoer(草兒)原創(chuàng),凡是索引、收藏
、轉(zhuǎn)載請(qǐng)注明來處和原文作者。非常感謝。