三、利用AOP来解决无缝注入
虽然上面的方法2是一种很滥的方法,但是却给我们很大的启发,我们可以通过手工的方法将接口注入到domain object,但是由于这种方法的注入分散在不同类的里,使得开发和维护都很不方便。方法2最大的问题是:注入的关注点分散。
仔细想一下,“注入的关注点分散”这句话让我们想起了什么?对,是AOP,AOP的作用不就把分散的关注点集到一个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容器
Spring对aspectJ提供了很好的支持,这意味着spring里配置好的Aspect可以直接访问容器的对象,以下是Spring里对aspectJ的配置:
<bean id="injectionAspect" class="edu.zju.tcm.aspects.InjectionAspect" factory-method="aspectOf"/>
然后在我们定义的aspect里implements BeanFactoryAware,如下所示:
public aspect InjectionAspect implements BeanFactoryAware{
BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException{
this.beanFactory=beanFactory;
}
}
这样我们的aspect就具备了访问Spring的BeanFactory的能力。
2、2、 定义pointcut
定义Domain Object的pointcut是比较难的事,因为我们无法控制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 Johnson在expert one on on J2EE design and development里甚至说任何时候都用前一种方法。而且这是完全non-invasive的。
这样我们的pointcut就很好定义了,我们要在Domain Object第一次使用Dao或Service时进行注入(这其实是一种lazy load,假如我们的domain object没有调用相关业务逻辑时系统甚至不会去inject,这对系统的性能也有帮助):
pointcut domainGet() : execution (* edu.zju.tcm.domain..*.get*DAO())||execution (* edu.zju.tcm.domain..*.get*Service());
以上将domain包里所有的类里有方法的开头是get末尾是DAO或Service的类都定义为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啦。接下来用反射机制和Spring的BeanWrapper将BeanFactory里读出来的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那一章是请人写的,因为他自己是不用的)。
2、AspectJ的编译速度
以前我只在玩具项目里用过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 |
|---|
|
|
|
|
|
|
|
|
|
|
|
|
| 技术站点 |
|---|
|
|
|
|
| 我的联系方式 |
|---|
|
|
|
|
| 友情链接 |
|---|
|
|
|
|
|
|
| 主页RSS |
|---|
|
|
|
|
|
|
