2006-11-08
侵入,无侵入? Annotation vs Interface
1. Interface
使用Interface 定义对象的类型,框架根据对象的接口来提供服务,这种模式是古而有之的Java框架设计者必习之法,从最重量的EJB到最轻量的Spring,都离不开这种方式,也的确解决了很多问题。比如在大家熟悉的Spring里:
等等,最后一个InitializingBean接口很邪恶阿!!硬要POJO实现一个名字怪怪的afterPropertiesSet()函数,侵入得不能再侵入阿。
2. 无侵入
所以,Spring提供了另一种方式,在配置文件里定义初始方法的名字:
一种是像Quartz,JMX,WebService这些模块,POJO啥都不用做,Spring使用AOP,FactoryBean这样的机制使它们白日飞升变为WebService,或者具有定时执行的能力。
一种是像init-method的例子,POJO并不很明显的实现Spring的接口,知觉Spring的存在,自己实现自己的函数,然后由Spring的配置文件去调用它们。
前一种无侵入的作用非常明显,后一种的意义就看具体的场景了,有时候实现一下Spring的接口也没什么所谓,但有时候各种原因会希望不显式的实现接口。
3. Annotation
在JDK5新增了Annotation的模式。Annotation 是XML配置模式的竞争者,但题目太大本篇就不讨论了,个人观点是主要看数据属于配置数据还是代码元数据。
本文forcus Interface vs Annotation来定义对象类型和行为。最开始用Annotation 使Java代码不再那么受传统模式局限的是TestNG,然后JUnit 4紧跟其后,在默认机制之外,可以用annotation 来灵活定义setup函数和测试函数。
4. 对比演示
下面,我们以SpringSide里的UndeleteEntity接口和@Undelete 标识的实现,具体演示一下两者。
项目需求是如果把Entity对象定义为不能删除,Dao基类的remove函数就不会真正删除该对象,而是设置状态列为"无效"。
Interface是这样实现的:
Interface 定义:
Interface 在框架中使用:
大家都很熟悉的写法,就不解释了。
Annotation是这样实现的:
Annotation 定义:
@Target 定义Annotation 可用在什么地方,比如类型,函数,函数参数等。Type代表Class,Interface...
@Retention(RetentionPolicy.RUNTIME)表明是个运行期的annotation,否则后面的代码就不会起作用了。
String status() 定义了annotation有个status的属性,而且可以定义默认值为 "status"。
Annotation在Entity使用:
在框架中的判断:
可见,annotation的模式,比interface要少一点侵入性,不规定死status列的名称,而且还可以灵活定义更多元属性,比如定义无效时的值为"-1","unvalid"。
但是,这种模式也和所有动态的东西向一样,失去了编译期校验的优势,POJO如果没有setStatus() 这个函数在编译期也检查不出来。
BTW.标题太长了,其实也属于幼学琼林系列,show一下用interface 和 annotation 定义元数据,交给框架去反射的基本写法。
使用Interface 定义对象的类型,框架根据对象的接口来提供服务,这种模式是古而有之的Java框架设计者必习之法,从最重量的EJB到最轻量的Spring,都离不开这种方式,也的确解决了很多问题。比如在大家熟悉的Spring里:
- BeanFactoryAware接口,框架看到它就会调用Bean的setBeanFactory(BeanFactory beanFactory) 函数,将BeanFactory传给它,让它自行用BeanFactory获得更多Bean,而不仅限于依赖注入的Bean。
- FactoryBean接口,框架看到它就不会把这个Bean放入自己的Context,而是调用它的getObject()函数,将返回的结果作为Bean放入。Spring里很多功能依赖于这个扩展机制,因为它可以用Factory模式返回任意类型对象,而不是<bean class="foo">中class指定类型。
- InitializingBean接口,因为很多Bean在各setter注入后,还需要一个总的init函数进行初始化,比如需要同时根据注入的属性A和 B,来初始化属性C的值。这时候框架就会调用afterPropertiesSet() 初始化。
等等,最后一个InitializingBean接口很邪恶阿!!硬要POJO实现一个名字怪怪的afterPropertiesSet()函数,侵入得不能再侵入阿。
2. 无侵入
所以,Spring提供了另一种方式,在配置文件里定义初始方法的名字:
<bean class="foo" init-method="init"/>其实Spring提倡的无侵入有两种层次:
一种是像Quartz,JMX,WebService这些模块,POJO啥都不用做,Spring使用AOP,FactoryBean这样的机制使它们白日飞升变为WebService,或者具有定时执行的能力。
一种是像init-method的例子,POJO并不很明显的实现Spring的接口,知觉Spring的存在,自己实现自己的函数,然后由Spring的配置文件去调用它们。
前一种无侵入的作用非常明显,后一种的意义就看具体的场景了,有时候实现一下Spring的接口也没什么所谓,但有时候各种原因会希望不显式的实现接口。
3. Annotation
在JDK5新增了Annotation的模式。Annotation 是XML配置模式的竞争者,但题目太大本篇就不讨论了,个人观点是主要看数据属于配置数据还是代码元数据。
本文forcus Interface vs Annotation来定义对象类型和行为。最开始用Annotation 使Java代码不再那么受传统模式局限的是TestNG,然后JUnit 4紧跟其后,在默认机制之外,可以用annotation 来灵活定义setup函数和测试函数。
4. 对比演示
下面,我们以SpringSide里的UndeleteEntity接口和@Undelete 标识的实现,具体演示一下两者。
项目需求是如果把Entity对象定义为不能删除,Dao基类的remove函数就不会真正删除该对象,而是设置状态列为"无效"。
Interface是这样实现的:
Interface 定义:
public interface UndeletableEntity {
void setStatus(String status);
}Interface 在Entity使用:
public class Book implements UndeletableEntity {
private String status; public void setStatus(String status) {
this.status = status;
}
}
Interface 在框架中使用:
//根据Class 判断
if(UndeletableEntity.class.isAssignableFrom(Book.class)){
...
}
//根据Bean实体判断
if(bookinstanceof UndeletableEntity{
((UldeletableEntity)bean).setStatus("-1");
}
大家都很熟悉的写法,就不解释了。
Annotation是这样实现的:
Annotation 定义:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Undeletable {
String status() default "status";
} 原本怪Sun怎么这么抠门,用@interface 而不另外搞一个关键字,原来Sun的意思就是把Interface和Annotation的定位一样呀。
@Target 定义Annotation 可用在什么地方,比如类型,函数,函数参数等。Type代表Class,Interface...
@Retention(RetentionPolicy.RUNTIME)表明是个运行期的annotation,否则后面的代码就不会起作用了。
String status() 定义了annotation有个status的属性,而且可以定义默认值为 "status"。
Annotation在Entity使用:
@Undeletable
public class Book{
private String status;
public void setStatus(String status) {
this.status = status;
}
}
@Undeletable(status = "status2")
public class BookWithNewStatus {
private String status2;
public void setStatus2(String status2) {
this.status2 = status2;
}
}
在框架中的判断:
if (entityClass.isAnnotationPresent(Undeletable.class)) {
Undeletable anno = (Undeletable) entityClass.getAnnotation(Undeletable.class);
statusProperty = anno.status();
}
可见,annotation的模式,比interface要少一点侵入性,不规定死status列的名称,而且还可以灵活定义更多元属性,比如定义无效时的值为"-1","unvalid"。
但是,这种模式也和所有动态的东西向一样,失去了编译期校验的优势,POJO如果没有setStatus() 这个函数在编译期也检查不出来。
BTW.标题太长了,其实也属于幼学琼林系列,show一下用interface 和 annotation 定义元数据,交给框架去反射的基本写法。
评论
Godlikeme
2007-04-29
是否集中配置是 xml与annotation的重要区别么?
sue0927
2007-04-29
引用
@Undeletable(status = "status2")
public class BookWithNewStatus {
private String status2;
public void setStatus2(String status2) {
this.status2 = status2;
}
}
public class BookWithNewStatus {
private String status2;
public void setStatus2(String status2) {
this.status2 = status2;
}
}
是不是要把太多的信息明文写到了程序中各个角落了...
f_sunboy
2007-04-19
Annotation很好用,在我的T2框架中用来定义基础业务,省事
manmoon
2007-04-19
性能与好的设计本来就是一对矛盾
看你的侧重点在哪里了
看你的侧重点在哪里了
roc8633284
2006-12-14
何谓完美? 我觉得80%的工作能用20%的投入来实现,就已经很完美了。节约了成本,提高了效率。难道非得做到100%
yimlin
2006-11-29
貌似又冒出新的方案了!看起来不错啊!
xml和annotation都是元数据了,只是管理上的需要。各自看项目了,只有合适的,对吧?
xml和annotation都是元数据了,只是管理上的需要。各自看项目了,只有合适的,对吧?
江南白衣
2006-11-29
Google和互联网将永远记录你说过的每句话。
现在回看以前的博客,总有好些说错的地方,但一来懒得逐篇修正,二来好些被转载出去的也没法改了....就这样继续误导...
现在回看以前的博客,总有好些说错的地方,但一来懒得逐篇修正,二来好些被转载出去的也没法改了....就这样继续误导...
taowen
2006-11-29
Morgan0916 写道
partech 写道
Feiing 写道
Annotation, J2EE 衰落的标志?
基于Aspect的Declare Annotation,才能做到彻底的无侵入。AO复兴的标志?
引用
AspectJ不是正确的道路- -
小陶:AspectJ是正确的道路
http://www.blogdriver.com/showBlog.do?bloggerID=21367&diaryID=159117
加入不加入语言支持,这不是关键。关键是,是否方便调试,是否方便TDD(当然,首先必须满足必要的功能,否则就不用说了)。照这个标准,AspectJ是个糟糕的方向。AspectJ增强后的代码,不但debugger跟踪不到,而且异常堆栈的行号都和原来的源代码对不上(那是当然了),调试起来会头大如斗。AspectJ让“编辑-构建-发布”周期变长了,修改源码之后必须先做预编译,才能执行单元测试。
更加糟糕的是,用AspectJ你不能动态决定是否加载aspect。想想吧,小陶,当一个class被预编译增强之后,你又打算不给它加载aspect了,该怎么办?恐怕你必须重新预编译整个系统。那么,如果这个class在某些时候需要aspect、某些时候不需要呢?“是否需要AOP能力”、“需要哪些aspect”应该、必须是在配置文件中声明的,不然你甚至不能独立地测试一个component。所以现在基于动态代理的AOP才是主流。
原文:http://gigix.blogdriver.com/gigix/159329.html
我最近在关注这方面的问题,想对公司现有的平台做些增强.请问partech,怎么解决gigix所列的这些问题呢? 谢谢
oh, my god。被人翻旧账了。我收回我的话行不……Aspect J代表的静态(载入时)元编程是强类型语法突围的唯一的道路。
江南白衣
2006-11-29
complystill 写道
、
如果基于 Ableverse Meta Facility http://meta.ableverse.org, 还可以这样写:
Book.meta
在框架中的判断:
这样运行期不用像Annotation那样需要重新编译才能修改元数据, 只要改XML就可以了.
而且这个 undeletable/@status 可以在包的级别设置, 下面的类可以继承这个元数据, 这样当一个包下面的大部分Entity类都是 status2 的时候只要写这个包的 .meta 文件包含上面的XML内容, 有个别的是 status3 的话, 写在它自己的 .meta 文件里就可以了.
我刚写完教程, 大家有兴趣可以看看: http://www.ableverse.org/tutorials/meta/
如果基于 Ableverse Meta Facility http://meta.ableverse.org, 还可以这样写:
Book.meta
<?xml version="1.0" encoding="UTF-8"?> <java> <undeletable status="status2" /> </java>
在框架中的判断:
try
{
MetaBundle meta = MetaBundle.get(entityClass);
String statusProperty = meta.getString("undeletable/@status");
// use it
}
catch (MetaNotFoundException e)
{
// Not an undeletable
}
这样运行期不用像Annotation那样需要重新编译才能修改元数据, 只要改XML就可以了.
而且这个 undeletable/@status 可以在包的级别设置, 下面的类可以继承这个元数据, 这样当一个包下面的大部分Entity类都是 status2 的时候只要写这个包的 .meta 文件包含上面的XML内容, 有个别的是 status3 的话, 写在它自己的 .meta 文件里就可以了.
我刚写完教程, 大家有兴趣可以看看: http://www.ableverse.org/tutorials/meta/
不错不错,又多一种选择。
XML or Annotation,一直有集中配置(不想散弹式修改)和可动态配置的两难,如果是XML的时候,Java开源社区之前并没有提供一个公共的快速将XML Data影射到Class MetaData的框架,像Spring的Bean Defintion还没达到函数,函数参数,函数返回值级元数据这个要求,自己写一个的话多数人都嫌麻烦不愿干。
现在有猛人share一个出来就最好了。
Book.meta是不是和XFire的aegis一样,要求默认和Book.java放在同一个目录?这样的确最COC了。
RyanPoy
2006-11-29
个人感觉annotation是以后的趋势
mltp
2006-11-28
劳民伤财啊。。。
pioneer21th
2006-11-10
我们只能说,Annotation和配置文件各有各的最适宜的使用环境,就像汽车分为公交车长途车货车等等一样,如果你对annotation不感冒或者是其他的,完全可以不用,相反,有的人也许会觉得比较好.再比如就像Weblogic这样的服务器,有很多高级的功能,很多项目可能都用不到,但BEA必须得提供,因为它是一种面向所有的人产品.
歆渊
2006-11-09
江南白衣 写道
@Undeletable
public class Book{
private String status;
public void setStatus(String status) {
this.status = status;
}
}
@Undeletable(status = "status2")
public class BookWithNewStatus {
private String status2;
public void setStatus2(String status2) {
this.status2 = status2;
}
}
在框架中的判断:
if (entityClass.isAnnotationPresent(Undeletable.class)) {
Undeletable anno = (Undeletable) entityClass.getAnnotation(Undeletable.class);
statusProperty = anno.status();
}
如果基于 Ableverse Meta Facility http://meta.ableverse.org, 还可以这样写:
Book.meta
<?xml version="1.0" encoding="UTF-8"?> <java> <undeletable status="status2" /> </java>
在框架中的判断:
try
{
MetaBundle meta = MetaBundle.get(entityClass);
String statusProperty = meta.getString("undeletable/@status");
// use it
}
catch (MetaNotFoundException e)
{
// Not an undeletable
}
这样运行期不用像Annotation那样需要重新编译才能修改元数据, 只要改XML就可以了.
而且这个 undeletable/@status 可以在包的级别设置, 下面的类可以继承这个元数据, 这样当一个包下面的大部分Entity类都是 status2 的时候只要写这个包的 .meta 文件包含上面的XML内容, 有个别的是 status3 的话, 写在它自己的 .meta 文件里就可以了.
我刚写完教程, 大家有兴趣可以看看: http://www.ableverse.org/tutorials/meta/
Morgan0916
2006-11-09
谢谢partech,看来我需要对AspejctJ5多一些研究
partech
2006-11-09
Morgan0916 写道
引用
AspectJ不是正确的道路- -
小陶:AspectJ是正确的道路
http://www.blogdriver.com/showBlog.do?bloggerID=21367&diaryID=159117
加入不加入语言支持,这不是关键。关键是,是否方便调试,是否方便TDD(当然,首先必须满足必要的功能,否则就不用说了)。照这个标准,AspectJ是个糟糕的方向。AspectJ增强后的代码,不但debugger跟踪不到,而且异常堆栈的行号都和原来的源代码对不上(那是当然了),调试起来会头大如斗。AspectJ让“编辑-构建-发布”周期变长了,修改源码之后必须先做预编译,才能执行单元测试。
更加糟糕的是,用AspectJ你不能动态决定是否加载aspect。想想吧,小陶,当一个class被预编译增强之后,你又打算不给它加载aspect了,该怎么办?恐怕你必须重新预编译整个系统。那么,如果这个class在某些时候需要aspect、某些时候不需要呢?“是否需要AOP能力”、“需要哪些aspect”应该、必须是在配置文件中声明的,不然你甚至不能独立地测试一个component。所以现在基于动态代理的AOP才是主流。
原文:http://gigix.blogdriver.com/gigix/159329.html
我最近在关注这方面的问题,想对公司现有的平台做些增强.请问partech,怎么解决gigix所列的这些问题呢? 谢谢
这些问题在最新版本的AspejctJ5中,已经得到解决。
DEBUG,动态织入都没有问题。
ray_linn
2006-11-09
总体说来,学究式的讨论。
ray_linn
2006-11-09
Feiing 写道
Allen 写道
Feiing 写道
Annotation, J2EE 衰落的标志?
这个,为什么这样说呢?我入行尚浅,看不太明白……
开玩笑的, 只是我个人看 Annotation 不太舒服
因为C#有了,所以java也得有,你看看java 6.0吧,又学了不少呢。
从C# 2.0 和C# 3.0 preview之后,大概已经没人敢再说,C#象java了。
Morgan0916
2006-11-09
partech 写道
Feiing 写道
Annotation, J2EE 衰落的标志?
基于Aspect的Declare Annotation,才能做到彻底的无侵入。AO复兴的标志?
引用
AspectJ不是正确的道路- -
小陶:AspectJ是正确的道路
http://www.blogdriver.com/showBlog.do?bloggerID=21367&diaryID=159117
加入不加入语言支持,这不是关键。关键是,是否方便调试,是否方便TDD(当然,首先必须满足必要的功能,否则就不用说了)。照这个标准,AspectJ是个糟糕的方向。AspectJ增强后的代码,不但debugger跟踪不到,而且异常堆栈的行号都和原来的源代码对不上(那是当然了),调试起来会头大如斗。AspectJ让“编辑-构建-发布”周期变长了,修改源码之后必须先做预编译,才能执行单元测试。
更加糟糕的是,用AspectJ你不能动态决定是否加载aspect。想想吧,小陶,当一个class被预编译增强之后,你又打算不给它加载aspect了,该怎么办?恐怕你必须重新预编译整个系统。那么,如果这个class在某些时候需要aspect、某些时候不需要呢?“是否需要AOP能力”、“需要哪些aspect”应该、必须是在配置文件中声明的,不然你甚至不能独立地测试一个component。所以现在基于动态代理的AOP才是主流。
原文:http://gigix.blogdriver.com/gigix/159329.html
我最近在关注这方面的问题,想对公司现有的平台做些增强.请问partech,怎么解决gigix所列的这些问题呢? 谢谢
Tin
2006-11-09
就是说对于Undeletable这样一个语义约定,使用Interface或者Annotation都是有侵入的。例子中,status获取的只是方法不同名的灵活性,但是却失去了静态检查的恩惠,这里是不划算的。
看来真的是declare annotation这样的东西才可以,这是个什么样的东西呢?要学习一下呀。
BTW:我觉得Junit4是个例子。Annotation并没有带来立竿见影的甜头,反倒是增加的BeforeClass和AfterClass非常有用,它们实际上可以不依赖于Annotation这种方式。我觉得这可以说明Anno这种显示的元数据有时不一定比基于规则的反射出的元数据(我觉得动态的AOP都是这个路子吧)读取要效果好。
看来真的是declare annotation这样的东西才可以,这是个什么样的东西呢?要学习一下呀。
BTW:我觉得Junit4是个例子。Annotation并没有带来立竿见影的甜头,反倒是增加的BeforeClass和AfterClass非常有用,它们实际上可以不依赖于Annotation这种方式。我觉得这可以说明Anno这种显示的元数据有时不一定比基于规则的反射出的元数据(我觉得动态的AOP都是这个路子吧)读取要效果好。
dogstar
2006-11-08
加载期动态织入感觉有些动态语言的意思了,呵呵







评论排行榜