Java下的框架编程(未完待續)

 http://calvin.javaeye.com/blog/91981

 

作者:江南白衣
                                                
   以Ruby为助力的Ruby on Rails 横空出世,又刷新了一次人们对Framework的认识:Closures、Maxin、Continuations、Metaprogramming、Reflection,又一堆名词砸得人悴不及防。
      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,动态语言嵌入”是一条明显的分界线,自己应该调度足够的业余时间,沉实的学习一遍。

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

 

Java下的框架编程(2)-对泛型的没心没肺的用法和为擦拭法站台

  作者: 江南白衣 

   Java 5的泛型语法已经有太多书讲了,这里不再打字贴书。但真要理解泛式编程的使用,还是要回到它的故乡C++ 去,靠Java书上的例子只能学到syntax。
   GP一定有用,不然Java和C#不会约好了似的同时开始支持GP。但大家也清楚,GP和Python式的动态OO语言属于不同的意识形态,如果是一人一票,我想大部分的平民程序员更热衷动态OO语言的平白自然。

   我对GP的没心没肺的用法:
   胡乱总结泛型的三点作用:
   第一是泛化,可以拿个T代表任意类型。 但GP是被C++严苛的静态性逼出来的,落到Java、C#这样的花语平原里,所有对象除几个原始类型外都派生于Object,一样能够很泛,所以Java的Collection类没有范型一样过得好好的。再加上Java的反射功能,在泛的方面GP不占多大优势。
   第二是收敛,就是增加了类型安全,减少了强制类型转换的代码。这点倒是Java Collection历来的弱项。
   第三是可以在编译期搞很多东西,比如MetaProgramming。但除非能完全封闭于框架内部,框架的使用者和扩展者都不用学习这些东西的用法,否则那就是自绝于人民的票房毒药。C++的MetaProgramming好厉害吧,但对比一下Python拿Meta Programming生造一个Class出来的简便语法,就明白什么才是真正的叫好又叫座。

     所以,作为一个架构设计师,只应该用Java泛型三样特质中的收敛特质,本着对人民负责的精神,用泛型使框架更加类型安全,更少强制类型转换。
     而且Java GP的擦拭法实现,使框架的范型特性对使用者保持了可用可不用的选择。

   擦拭法避免了Java的流血分裂 :
   大家经常骂Java GP的擦拭法实现,但我觉得多亏于它的中庸特性---如果你用就是范型,不用就是普通Object,避免了Java阵营又要经历一场to be or not to be的分裂。 
    最大的例子莫过Java 5的Collection 框架, 比如有些同学坚持认为自己不会白痴到类型出错,而且难以忍受每个定义的地方都要带一个泛型定义List〈Goods〉 ,不用强制类型转换所省下的代码还不够这里花的(对了,java里面还没有tyepdef.....),因此对范型十分不感冒,这时就要齐齐感谢这个搽拭法让你依然可以对一个泛型框架保持非泛型的用法了...

Java下的框架编程(3)--关于反射的碎话

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

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

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

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

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

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

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

Java下的框架编程(4)--Annotation vs XML vs Interface 最新一Round

作者: 江南白衣 

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

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

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

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

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

3.Annotation目前的弱点
 一是Hibernate、Struts、三姑妈、四姨婆的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,不关心。

Java下的框架编程(5)--cglib的应用

   作者: 江南白衣 

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

    cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。
    它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)

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

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

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

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();
       }
       
//默认 的拦截方法
       public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  throws  Throwable
      {
           log.info(
" 调用日志方法 " + method.getName());
           Object result
= proxy.invokeSuper(o,args);
           
return  result;
      }
   }

    应用的代码:
    LogDAOProxy proxy  =   new  LogDAOProxy();
    GoodsDAO  dao 
=  (GoodsDAO)proxy.getDAO(GoodsDAO. class );
    dao.insert(goods);

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


   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();
       }      
       
// 默认的拦截方法
       public  Object intercept(Object o,Method method,Object[] args,MethodProxy proxy)  throws  Throwable
      {
           log.info(
" 调用日志方法 " + method.getName());
           Object result
= proxy.invoke(dao, args);
           
return  result;
      }
  }

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

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

cglib,全称是Code Generation Library,它可以用来动态继承Java类或者实现接口,很多知名的开源项目中用到了它,譬如Hibernate,Spring之类用它来实现动态代理。

增强一个已有类

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
}

import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
 
public class Main {
 
	public static void main(String[] args) {
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallback( new MethodInterceptorImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			proxy.invokeSuper(obj, args);
 
			return null;
		}
	}
}

执行结果:

public void cglib_test.MyClass.method()
MyClass.method()

使用CallbackFilter

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
 
	public void method2() {
		System.out.println("MyClass.method2()");
	}
}

import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.NoOp;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
 
 
public class Main {
 
	public static void main(String[] args) {
 
		Callback[] callbacks =
			new Callback[] { new MethodInterceptorImpl(),  NoOp.INSTANCE };
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallbacks( callbacks );
		enhancer.setCallbackFilter( new CallbackFilterImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
		my.method2();
	}
 
	private static class CallbackFilterImpl implements CallbackFilter {
 
		public int accept(Method method) {
 
			if ( method.getName().equals("method2") ) {
				return 1;
 
			} else {
				return 0;
			}
		}
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			return proxy.invokeSuper(obj, args);
		}
	}
}

执行结果:

public void cglib_test.MyClass.method()
MyClass.method()
MyClass.method2()

使用Mixin

public interface MyInterfaceA {
 
	public void methodA();
}
 
public interface  MyInterfaceB {
	public void methodB();
}
 
public class MyInterfaceAImpl implements MyInterfaceA {
 
	public void methodA() {
		System.out.println("MyInterfaceAImpl.methodA()");
	}
}
 
public class MyInterfaceBImpl implements MyInterfaceB {
 
	public void methodB() {
		System.out.println("MyInterfaceBImpl.methodB()");
	}
}

import net.sf.cglib.proxy.Mixin;
 
public class Main {
 
	public static void main(String[] args) {
 
		Class[] interfaces =
			new Class[] { MyInterfaceA.class, MyInterfaceB.class };
		
		Object[] delegates =
			new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };
			
		
		Object obj = Mixin.create(interfaces, delegates);
 
 
		MyInterfaceA myA = (MyInterfaceA)obj;
		myA.methodA();
 
 
		MyInterfaceB myB = (MyInterfaceB)obj;
		myB.methodB();
	}
}

执行结果:

MyInterfaceAImpl.methodA()
MyInterfaceBImpl.methodB()

 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值