天堂的阶梯
Domain injection with AOP(下)
08月30日(星期二)

三、利用AOP来解决无缝注入

虽然上面的方法2是一种很滥的方法,但是却给我们很大的启发,我们可以通过手工的方法将接口注入到domain object,但是由于这种方法的注入分散在不同类的里,使得开发和维护都很不方便。方法2最大的问题是:注入的关注点分散。


仔细想一下,“注入的关注点分散”这句话让我们想起了什么?对,是AOPAOP的作用不就把分散的关注点集到一个aspect里吗?我们只要将这种domain object的注入逻辑集中在一个aspect里,我们代码量和可读性不都会变得很好了?而且除了这个aspect之外,所有的其它类都不需要知道domain object的接口是怎么注入的,这不是完全地实现我们的non-invasive的目的吗?如图所示:

下面我用aspectJ实现的对domain object的自动注入。(Why not Spring AOP? Spring AOP的实现机制是dynamics proxy,这意味着返回的domain object是一个新创建代理对象。但是由于我们无法控制domain object的生命周期,如我们直接从hibernate里查出几百个对象,没有容器的支持我们根本没有机会将这些对象转化代理对象)。

1、1、 aspect可以访问IOC容器

SpringaspectJ提供了很好的支持,这意味着spring里配置好的Aspect可以直接访问容器的对象,以下是Spring里对aspectJ的配置:

<bean id="injectionAspect" class="edu.zju.tcm.aspects.InjectionAspect" factory-method="aspectOf"/>

然后在我们定义的aspectimplements BeanFactoryAware,如下所示:

public aspect InjectionAspect implements BeanFactoryAware{

BeanFactory beanFactory;

public void setBeanFactory(BeanFactory beanFactory) throws BeansException{

this.beanFactory=beanFactory;

}

}

这样我们的aspect就具备了访问SpringBeanFactory的能力。

2、2、 定义pointcut

定义Domain Objectpointcut是比较难的事,因为我们无法控制domain object的生命周期,因此我们无法将pointcut定义在对象创建的时候。domain object除了Object外没有共同的父类,每个domain object又没有共同的方法,确实是一件头痛的事。

因此我做了一个唯一假设,这也是我们在使用aspect之后,我们正常代码唯一要遵从的事(如用了annotation就什么都不用了,但我觉得annotation反而增加了工作量):我们的Dao类或Service类有一个统一的后缀名,然后我们的domain object调用这些类时用的是get方法而不是instance变量。如:

private LcyjDAO lcyjDAO;

private LcyjDAO getLcyjDAO() {

return lcyjDAO;

}

public void setLcyjDAO(LcyjDAO lcyjDAO) {

this.lcyjDAO = lcyjDAO;

}

我们以后在domain object里调用这个类时用getLcyjDAO(),而不是lcyjDAO

事实这是很好的编程习惯,Rod Johnsonexpert one on on J2EE design and development里甚至说任何时候都用前一种方法。而且这是完全non-invasive的。

这样我们的pointcut就很好定义了,我们要在Domain Object第一次使用DaoService时进行注入(这其实是一种lazy load,假如我们的domain object没有调用相关业务逻辑时系统甚至不会去inject,这对系统的性能也有帮助)

pointcut domainGet() : execution (* edu.zju.tcm.domain..*.get*DAO())||execution (* edu.zju.tcm.domain..*.get*Service());

以上将domain包里所有的类里有方法的开头是get末尾是DAOService的类都定义为pointcut

3、3、 实现Advice

前面都讲清楚之后,Advice的实现倒反而很轻而易举了,以下是它的代码:

Object around(Object domainObj) : domainGet() && this(domainObj){

Object returnObj=proceed(domainObj);

if (null!=returnObj){

return returnObj;

}

BeanWrapper beanWrapper=new BeanWrapperImpl();

beanWrapper.setWrappedInstance(domainObj);

MutablePropertyValues pvs=new MutablePropertyValues();

Field[] fields=domainObj.getClass().getDeclaredFields();

for (int i=0;i<fields.length;i++){

String fieldName=fields[i].getName();

if (isDaoClass(fieldName) || isServiceClass(fieldName)){

PropertyValue pv=new PropertyValue(fieldName,getBeanFactory().getBean(fieldName));

pvs.addPropertyValue(pv);

}

}

beanWrapper.setPropertyValues(pvs);

return proceed(domainObj);

}

首先我们判断一下这个domain object有没有被注入过,也就是get回来的结果是否为空,如果不为空就不用inject啦。接下来用反射机制和SpringBeanWrapperBeanFactory里读出来的interface设置成这个domainObject的属性,注射完成之后返回就行了。

Domain object里定义的field名字必须与Spring配置文件里的bean名字一致。这根本不是限制条件,而是每个用Spring的人都必须遵从的习惯。

代码是如果出奇的简单,简直令xiecc都不敢相信,但是无论如何,它成功了啦!

四、遗留问题

1、事务处理

用了aspectJ之后,在domain object里实现透明的事务处理也会变得非常容易。但是xiecc没有实现这一部分,因为xiecc仍然认为在domain object是不应该有存储逻辑的,更不要说参与事务处理了。但是也许有人真的想那么干,毕竟在Account里写个deposit之类的方法也不算错。而且在Martin Fowler的世界里似乎是不需要Service层的(它的Patterns of enterprise application Architecture里的Service Pattern那一章是请人写的,因为他自己是不用的)。

2AspectJ的编译速度

以前我只在玩具项目里用过AspectJ,感觉好爽。这次把它加到我去年做的一个项目里,其实也只有9000多行代码,结果速度慢得要死,而且很容易out of memory。我每次保存一个aspect系统都要停半天。也许采用编译时织入的方法对开发效率还是有影响的,也许与AspectWerkz合并的Aspect5里会有更好的方法。

文章分类: java
前篇(05-08-30): Domain injection with AOP(上)
后篇(05-09-02): Floyd Marinescu离开TSS了

最新回复(1件)
主题/内容 作者/日时
Re: Domain injection with AOP(下)

radiumce
07-12-07 15:46

发表评论
标题:
称呼:
内容:

引用链接
您可以按照以下步骤引用本文.本站收到您的引用通知后, 将自动链接您的文章, 以方便别人阅览 .
1. 启动您自己的博客管理页面, 并进入发表新文章的画面, 输入文章的内容. (如果您是ITPUB的博客请点这里.)
2. 复制下面虚线框里的连接字串, 把它们粘贴到您的文章中, 按照您的喜好修改一下表示文字.
3. 确认您选择了"发送引用通知"的选项.
4. 发表您的文章.
好啦, 您的文章就可以被自动链接到本站啦.

« 八月 2008 »
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

国内技术blog
  • 透明思考
  • 花钱的年华

  • 国外技术blog
  • Bile blog
  • Cedric Beust
  • Cameron Purdy
  • Mike Spille
  • Ted Neward
  • Alef Arendsen

  • 技术站点
  • theserverside
  • Java视线论坛

  • 我的联系方式
  • email1
  • email2

  • 友情链接
  • 车东--搞搜索引擎的牛人
  • 除却巫山不是云--实验室的小MM
  • 奔--未来的大牛

  • 主页RSS
  • RSS 2.0
  • RSS 1.0
  • RSS 0.9


  • Creative Commons License 本站全部著作均采用CC授权. Plog 1.0 is powered by: plogworld.net.
    Itpub BLOG is provided by: itpub.net.
    This temlate(named Happy-Life's SunShine) is designed by lodge@itpub(肥猫猫).