Java下的框架编程


Ruby为助力的Ruby on Rails 横空出世,又刷新了一次人们对Framework的认识:ClosuresMaxinContinuationsMetaprogrammingReflection,又一堆名词砸得人悴不及防。
Java
虽然没有动态语言般暴起,但仍然天连天,水接水的生出好多框架技术---反射(reflection),泛型(generics),元数据(annotation)proxies(proxy/cglib),代码动态生成(asm)AOP(aspectJ),动态语言嵌入(groovy/javascript/beanshell)。面对着这些,我们像一夜暴富的农企,有点手足无措的样子。

第一感觉,应该出现新的Design Pattern书籍了。
多年前基于纯C++语法写就的那本经典,很多模式都可以被更优雅的实现,还有更多的新模式,会随着这些技术的推广而出现。

第二感觉,新的框架模式出现了。
不断升级自己的Spring,综合运用了除泛型外的大部分技术,把编程界的想像力MFC/ EJB2.0 Container这样传统的template模式/钩子函数的思路,向完全无侵入的标准过渡。
当你不顾性能狂用反射时,C++下的遗憾感开始越来越远。

第三感觉,自己应该重新充电了。
C++下编写框架的经历到尽用反射,泛型,元数据,proxy,代码动态生成,AOP,动态语言嵌入是一条明显的分界线,自己应该调度足够的业余时间,沉实的学习一遍。

这个系列开始一个个介绍上述的积木。

关于反射的碎话

反射,所有教科书都正儿八经的讲了,这里只能再随便讲两句。

反射是一种让框架能够根据 "以字符串形式存在的信息" 来调用对象的属性和函数的技术,是JavaC++最大的进步之一---让框架编程真正走向平民化。MFC年代,无论侯捷如何深入浅出,还在念大学的我就是搞不懂那些注册"消息--函数映射"的魔法宏。

不过Java的反射也就是对着C++比较自豪而以,因为C#,Ruby,Python甚至php都标配了反射的功能。而且,人家的反射语法都是内嵌在基础Object类的,拿最弱的php来看:

$func_name="helloworld";
$foo->$func_name;


Java,却搞出了ClassMethed, FieldConstructor这么一大堆类出来。本来这是Java设计师很严谨,很cool的体现,问题是它居然不提供一种集成的简便的写法......相同的情形还出现在JavaI/O 类库里。
微软这方面就做得好些,懂得讨好开发人员。

因为Java的无情,就搞得大家的项目里要自制BeanUtils了。幸亏Apache Jakarta Commons 已经做了一个比较好的,可以直接使用--以前写的介绍文章
另外Spring也做了一个。

闲得没事做的,还可以emule一本〈Relection in action〉回来看。

C++下面的"反射",见我偶像di文章。另还有一个比较BTC++框架叫ACDK的,把自己整得和Java很像,有反射和垃圾收集,甚至和JSDK差不多的线程,UnicodeI/O,网络,XML API。可惜的是,即使到了C++0x, B大叔还是不准备在语言级支持反射。

Annotation vs XML vs Interface 最新一Round

1.因为两者的用途不同,所以AnnotationXML应该并存

 Anootation vs XML 到了现在,在SpringEJB3各占一边的持续演示下,各自的用途已清晰。
虽然两者之间完全可以相互替代,没有绝对不能做的事情,但却存在好不好的问题,两者的用途不同,应该并用:

"size=10
db_user=ito"这样的参数,仍然适合放在XMLYAML(ruby)propertiesini文件里。
Annotation中所放,是真正程序的一部分,是旧编程体系中所缺的那一块东西。
 看看编程语言的发展:
一开始只是函数的封装;
到了OO语言,对象有了自己的属性和方法;
到了框架满天飞的年代,这些属性和方法又有了自己的属性,来告诉框架如何为自己提供服务。比如SpringEJB3POJO就用配置信息来告诉框架无侵入的提供服务。整个进化的过程非常自然。

因为这些信息本来就是程序的一部分,所以应该仍然放在Code里头,Spring把它割裂到XML明显影响了代码的阅读。

2.Anotation/XMLPK Interface
Spring 无侵入性的基础
切尔斯基的Blog讲了Annotation/XMLInterfacePK
。这次PK,也可以认为是Spring EJB2.1在框架无侵入性上的一次PK
EJB2.1时代,POJO必须通过接口来向框架声明一些东西,这就造成了框架的侵入性,强迫POJO实现一堆接口。而Spring,把这些元信息搬到了XML配置文件。
 在Spring里随便就可以举个例子。比如一个POJO,在Spring为它进行依赖注入AB后,需要根据AB来装配一个内部属性C,这样就需要再跑一个init()函数。Spring提供两种方式,一种侵入性的,实现InitializingBean接口的afterPropertiesSet()函数供Spring调用。而另一种是无侵入性的,在Beanxml节点里面自行定义init函数的名字。

.Annotation目前的弱点
 一是HibernateStruts、三姑妈、四姨婆的annotation如果一股脑儿的全堆在可怜的POJO上很没人道,如果三姑六婆都抢Transation这个Anontation就更惨了。
二是annoation的表达能力有限,不如XML的强。

4.JSR250 - Common Annotations for the Java
Final Draft已经发布
,八卦回来一看,也只是多定义了几个Common Annotation而已。
1.Generated
自动生成的代码要用此声明,而且必须说明工具的名称,如@Generated(“com.sun.xml.rpc.AProcessor”)

2.Resource/Resources
就是EJB3里面用的资源引用。

3.PostConstruct / PreDestroy
声明那些如题的函数。

还有一些安全方面的annotation,不关心。

cglib的应用

反射、Proxy和元数据是Java最强的三个特征,再加上CGLib (Code Generation Library)ASM,使得Java虽然没有RubyPython般后生可畏,一样能做出强悍的框架。
Proxy
可以看作是微型的AOP,明白提供了在继承和委托之外的第三个代码封装途径,只要有足够的想象力,可以做得非常好玩,Spring的源码里用Proxy就用得很随便,看得我非常眼红。可惜Proxy必须基于接口。因此Spring的做法,基于接口的用proxy,否则就用cglibAOP么,一般小事非compoent一级的就不麻烦AspectJ出手了。

cglib
Enhancer说起来神奇,用起来一页纸不到就讲完了。
它的原理就是用Enhancer生成一个原有类的子类,并且设置好callbackproxy 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxyintercept()函数:

publicObjectintercept(Objecto,Methodmethod,Object[]args,MethodProxyproxy)


intercept()函数里,你可以在执行Object result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全干别的。说白了,就是AOP中的around advice

AOP没有出现以前,该领域经典的设计模式是Decorator,像Java IO Stream的设计就是如此.不过,如果为每个DAO, 每个方法的写Decorator函数会写死人的,所以用上cglib的好处是一次过拦截所有方法。

另外,cglib除了Enhancer之外,还有BulkBeanTransform,都是Hibernate持久化的基础,但文档贫乏,一时还没去看怎么用。

1.AOP
里讲了一百遍阿一百遍的log aspectcglib是这样做的:


publicclassLogDAOProxyimplementsMethodInterceptor
{
privateLoggerlog=Logger.getLogger(LogDAOProxy.class);
privateEnhancerenhancer=newEnhancer();
//返回DAO的子类
publicObjectgetDAO(Classclz)
{
enhancer.setSuperclass(clz);
enhancer.setCallback(
this);
returnenhancer.create();
}
//默认的拦截方法
publicObjectintercept(Objecto,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable
{
log.info("
调用日志方法"+method.getName());
Objectresult=proxy.invokeSuper(o,args);
returnresult;
}
}


应用的代码:

LogDAOProxyproxy=newLogDAOProxy();
GoodsDAOdao=(GoodsDAO)proxy.getDAO(GoodsDAO.
class);
dao.insert(goods);


2.而在Spring的管理下应该略加修改的高级Decorator
上面的例子用returnenhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分

publicclassLogDAOProxyimplementsMethodInterceptor
{
privateLoggerlog=Logger.getLogger(LogDAOProxy.class);
privateObjectdao=null;
privateEnhancerenhancer=newEnhancer();
//返回DAO的子类
publicObjectgetDAO(Classclz,Objectdao)
{
this.dao=dao;
enhancer.setSuperclass(clz);
enhancer.setCallback(
this);
returnenhancer.create();
}
//默认的拦截方法
publicObjectintercept(Objecto,Methodmethod,Object[]args,MethodProxyproxy)throwsThrowable
{
log.info("
调用日志方法"+method.getName());
Objectresult=proxy.invoke(dao,args);
returnresult;
}
}


可见,原来模式里在getDao()时由enhancer创建dao,而调用intercept时则将enhancer创建的daoObject o参数传回。
而新模式里,daogetDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy.

有点遗憾, intercept函数里MethodProxySignature是固定的 , 即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String);

你可能感兴趣的:(java)