查看: 1437|回复: 24
|
来谈谈 AOP(Aspect Oriented Programming)
[复制链接]
|
|
鲜少有人提起这个东西,我就来开个头,希望可以和各位一起学习。
简介: Aspect Oriented Programming (aka AOP) 或是 面向侧面程序设计(听起来好吓人)
(简介就是这样,基本上没有一个适合的简单介绍,越简单反而越混乱,还是从现实情况来分析AOP的运用会比较好)
AOP中有许多术语,例如join point, pointcut, aspect, advice等,我也不多加解释,一切由实例中慢慢分析。不过,可以确定一点就是,AOP的运用可以非常广泛。因为可以在不植入代码的情况下实现Interceptor, Listener, Filter等Design Pattern,所以常常会被用在选择性的功能如Logging或Authentication上。
我将会用Sping+AspectJ(开源的AOP语言框架)来制作一个简单的例子; 在分享例子之前,希望各位可以分享一下看法。 |
|
|
|
|
|
|
|
发表于 10-11-2007 05:42 PM
|
显示全部楼层
这个AOP,背后的典故是什么?
[ 本帖最后由 tensaix2j 于 10-11-2007 05:46 PM 编辑 ] |
|
|
|
|
|
|
|

楼主 |
发表于 10-11-2007 08:06 PM
|
显示全部楼层
回复 #2 tensaix2j 的帖子
从wikipedia抄的
Separation of concerns entails breaking down a program into distinct parts that overlap in functionality as little as possible. All programming methodologies—including procedural programming and object-oriented programming—support some separation and encapsulation of concerns (or any area of interest or focus) into single entities. For example, procedures, packages, classes, and methods all help programmers encapsulate concerns into single entities. But some concerns defy these forms of encapsulation. Software engineers call these crosscutting concerns, because they cut across multiple modules in a program.
aspect指的是横切面,从程序中的个体的某个切点执行所需的功能。其意义即不遵守OOP的封装,也不是Procedural,所以就自立门户叫AOP罗 |
|
|
|
|
|
|
|

楼主 |
发表于 10-11-2007 09:21 PM
|
显示全部楼层
AOP实例 -- 简单的Logging
概况
有一个Worker类,只拥有一个doWork()方法。根据客户的需求,在不修改Worker类代码的情况下,每次Worker.doWork()指令发出之后做一个Logging。
第一步骤 -- 写一个Worker类,再交给Spring Container管理- //Worker.java
- package org.kugua.aopdemo;
- //Worker类声明
- public class Worker {
-
- //简单的工作指令
- public void doWork(){
- //工作内容非常简单
- System.out.println("I'm working...");
- }
- }
- }
复制代码 弄一个Spring Container(springAop.xml), 生成一个Worker Instance, 备用- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="[代码省略]">
- <bean id="workerBean" class="org.kugua.aopdemo.Worker"/>
- </beans>
复制代码 第二步骤 -- 启动Spring Container, 加载Worker Bean- //Worker.java
- [code]
- import ...
- public class Worker {
-
- //简单的工作指令
- ... [代码省略]
-
- public static void main(String[] args){
- //载入Spring Context
- ApplicationContext ctx = new ClassPathXmlApplicationContext("org/kugua/aopdemo/springAop.xml");
-
- //加载worker bean
- Worker workerBean = (Worker)ctx.getBean("workerBean");
-
- //重复工作1000次
- for(int i = 0;i<1000;i++){
- workerBean.doWork();
- }
- }
- }
复制代码 执行一下main,你可以看到这样的结果...
.....
I'm working...
I'm working...
I'm working...
.....
第三步骤 -- 加入AOP!
基本上前两个步骤只是Spring的实例,并还未加入AOP。Spring支持声明式的AOP,即大部分功能都在springAop.xml内。
修改springAop.xml- ....
- <aop:config>
- <aop:aspect ref="workAspect">
- <aop:pointcut id="workerDoWork"
- expression="execution(* org.kugua.aopdemo.Worker.doWork())" />
- <aop:after-returning method="logWork"
- pointcut-ref="workerDoWork"/>
- </aop:aspect>
- </aop:config>
- <bean id="workAspect" class="org.kugua.aopdemo.WorkAspect" />
- <bean id="workerBean" class="org.kugua.aopdemo.Worker" />
- ....
复制代码 写一个logging class,WorkerAspect,这里将包涵logging的功能- //WorkerAspect
- [code]
- package org.kugua.aopdemo;
- public class WorkAspect {
- //记录Worker工作次数
- private long time;
-
- //加入日志
- public void logWork(){
- time++;
- System.out.println("AspectJ: doWork() called "+String.valueOf(time)+" times");
- }
- }
复制代码 执行一下main,看一看结果...
.....
I'm working...
AspectJ: doWork() called 982 times
I'm working...
AspectJ: doWork() called 983 times
I'm working...
AspectJ: doWork() called 984 times
....
总结
符合顾客要求,不修改Worker Class的情况下,用AOP介入Worker.doWork()并执行Logging的功能。
接下来会解析以上的实例,累了,明天继续....
下载代码
[ 本帖最后由 苦瓜汤 于 10-11-2007 09:24 PM 编辑 ] |
|
|
|
|
|
|
|
发表于 11-11-2007 02:25 PM
|
显示全部楼层
原帖由 苦瓜汤 于 10-11-2007 08:06 PM 发表 
从wikipedia抄的
aspect指的是横切面,从程序中的个体的某个切点执行所需的功能。其意义即不遵守OOP的封装,也不是Procedural,所以就自立门户叫AOP罗
“从程序中的个体的某个切点执行所需的功能,”
不是很明白
为何不把该功能包装在某sub procedure,然后必要时再呼唤那个 sub procedure。 |
|
|
|
|
|
|
|

楼主 |
发表于 11-11-2007 04:16 PM
|
显示全部楼层
回复 #5 tensaix2j 的帖子
当然没有什么是不可以的,只不过在灵活性上AOP是技高一筹的。其实,AOP的实现方式是靠代理(Proxy),就拿我发的例子来说,在加入了AOP后,程序其实是间接执行doWork()这个method,在这之前这个method call 得先通过针对Worker类生成的Proxy,Proxy类会检察所有对其的method call,要是有需要的话,Proxy类将会在某个点上执行所需的功能。
我之前尝试过Spring AOP,其对于Proxy的配置会比较明显,可以知道AOP的魔术是怎么回事,但是配置方法会比AspectJ复杂。至于AspectJ,我到目前其实还搞不懂它是怎么做到这个功能的 |
|
|
|
|
|
|
|

楼主 |
发表于 12-11-2007 11:12 AM
|
显示全部楼层
Join Point与Pointcut
Join Point与Pointcut是AOP最重要的概念。在AOP视野,所有的程序个体上有几个切入点,也就是Join Point,例如method execution/call, constructor call/execution, pre-initialization等等。例如例子内的doWork() 执行就是一个join point。
Pointcut基本上最容易让人和Join Point混淆,不过,两者关系是密切的,Pointcut可以理解为Join Point的集合,也就是切入点的集合,例子内的AspectJ的Pointcut Expression -- execution(* org.kugua.aopdemo.Worker.doWork())就是一个Pointcut的表达式,即符合public/private/protected modifier,或任何的return type, 例如 private void doWork() , public String doWork(), protected void doWork()都符合以上的pointcut表达式。
更多AspectJ Pointcut Expression的例子
* org.kugua.aopdemo.Worker.*()
-符合Worker类所有的方法 (无Parameter)
* org.kugua.aopdemo.Worker.*(..)
-符合Worker类所有的方法 (有Parameter)
* org.kugua.aopdemo.Worker.set*(..)
-符合Worker类所有的以set开头方法
public * org.kugua.*.*.doWork()
-符合org.kugua包内所有public的doWork()方法 |
|
|
|
|
|
|
|
发表于 12-11-2007 11:27 AM
|
显示全部楼层
|
|
|
|
|
|
|

楼主 |
发表于 12-11-2007 03:08 PM
|
显示全部楼层
|
|
|
|
|
|
|
发表于 12-11-2007 03:25 PM
|
显示全部楼层
小章鱼的理解能力有限,
看完后,觉得不就是呼叫另一个程式的某个 method 然后执行?
有个 helper class 去读取 XML 设置文件,然后自动载入和执行。
其它语言小章鱼不清楚,不过 .Net 似乎可以实现。
之前有做过一个 Plugin 的功能,就是在程式执行中载入其它的插件(DLL),然后对插件传入资料+执行+回传处理后的资料。
不知道这个 AOP 和这个有何不同?请指教 |
|
|
|
|
|
|
|

楼主 |
发表于 12-11-2007 03:49 PM
|
显示全部楼层
回复 #10 sson 的帖子
各位不必对其所达到的目的感到迷惑,这些目的可以用现有的Design Pattern如Filter,Listener,Proxy等实现,不过AOP的不同之处在于可以不必修改已经compiled的程序,而且随意在程序内寻找所要切入的点进行拦截。
举个Listener的例子,对于一个Event的Trigger我可以用一个以上的Listener来完成不同的callback.所以,在element上你需要addEventListener这个方法来call你的listener(s)。但对于AOP来说,我可以在element.fireEvent()执行上定义一个pointcut,然后用advice来完成listener的工作,完全不需要用上addEventListener这个方法。
不好意思,我的表达能力不太好,而且也还在学习,不过有什么问题的话我会尽量解释。 |
|
|
|
|
|
|
|
发表于 12-11-2007 09:06 PM
|
显示全部楼层
回复 #11 苦瓜汤 的帖子
说到AOP, 我也来提供一些资料。Rickard Oberg[1] 对于AOP是这样说的。
“the definitions are really spread out, and it's difficult to get an overview of how a large model with hundreds and thousands of aspects interact and relate to each other. If classes were too closed, aspects are too open.”[2]
而他也静悄悄的研发了一个新的programming方式(Composite Oriented Programming)叫qi4j[3], 结合了Dependency Injection, AOP和 DDD(Domain Driven Design)concept 把这问题解决。顺便一提,他将在明天oredev Sweden[4] 推介qi4j, 有兴趣的朋友,不妨看看。
对了,这贴有意思,再接再厉吧!谢谢分享!
[1]Rickard Oberg 是XDoclet, Webwork, Jboss EJB 的主要开发者。
http://www.oredev.org/toppmeny/c ... db498000125951.html
[2]http://lists.ops4j.org/pipermail/qi4j-dev/2007-June/000622.html
[3]qi4j - www.qi4j.org
[4]www.oredev.org
[ 本帖最后由 黑木头 于 12-11-2007 09:17 PM 编辑 ] |
|
|
|
|
|
|
|

楼主 |
发表于 13-11-2007 09:06 AM
|
显示全部楼层
回复 #12 黑木头 的帖子
谢谢分享。Richard的那句话觉得满有意思的,一个简单程序内有上万个Join Point,如何定义Pointcut绝对是一个重要的技巧,不然的话Aspect就会出差错了。 |
|
|
|
|
|
|
|
发表于 13-11-2007 07:01 PM
|
显示全部楼层
看了那个图,我的了解大概是这样子。。
原本的OOP,每个class 有自己的 methods and properties, 自己管理and 负责
而这个 AOP, 这个class 的 methods 却是从别个 class 凑起来的。。。 |
|
|
|
|
|
|
|

楼主 |
发表于 13-11-2007 08:29 PM
|
显示全部楼层
回复 #14 tensaix2j 的帖子
在明确一点是针对其他Class的Method或Properties而执行的。
再举个AOP目前最重要的用途 -- Transaction Management
AOP可以完全取代一个程序中的Services或DAO(Data Access Object)的Transaction管理,这意味着在主要程序中编程员可以完全不必考虑Transaction的问题,他们可以在之后用AOP将所有的TX Logic都集中在Aspect,然后对所需要的Method进行拦截。 |
|
|
|
|
|
|
|
发表于 13-11-2007 11:29 PM
|
显示全部楼层
回复 #15 苦瓜汤 的帖子
认同!Spring 在这点做到超方便,比如:Spring 的 TransactionProxyFactoryBean 就是最好的declarative transaction demarcation例子。 如果你是用hibernate 的话, 你不需要重复又重复以下的codes
Session session = factory.openSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
//do something
tx.commit(); //synchronize to db
}
catch (RuntimeException e) {
tx.rollback();
throw e;
}
finally {
session.close();
}
超累,在Spring, 你只需做
<bean id="blaService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="target" ref="blaServiceTarget" />
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
这样 blaServiceTarget class 的methods such as
void saveBlaBla bla){}
void updateBla(Bla bla) {}
void deleteBla(Bla bla){}
这些methods全都在Transaction管理里面, 很方便对吗? 我觉得唯一令我讨厌的是Spring 的xml file, 虽然spring已经有很完整的annotation, 但我还没试, 不懂有那些明显的好处。
以上只是一个例子,希望不会confuse新手。
[ 本帖最后由 黑木头 于 13-11-2007 11:39 PM 编辑 ] |
|
|
|
|
|
|
|
发表于 14-11-2007 02:30 AM
|
显示全部楼层
回复 #15 苦瓜汤 的帖子
如果是 Web Application, 也可以用 JSP Filter
TransactionFilter.java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
UserTransaction utx = getUserTransaction();
try {
utx.begin();
chain.doFilter(request, response); //jsp, java class that perform database operation
utx.commit();
} catch (Exception e) {
utx.rollback();
e.printStackTrace();
}
}
web.xml
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>Test1.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>TransactionFilter</filter-name>
<url-pattern>Test2.jsp</url-pattern>
</filter-mapping>
应该比AOP容易吧。 |
|
|
|
|
|
|
|

楼主 |
发表于 14-11-2007 01:48 PM
|
显示全部楼层
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="increasePrice*" propagation="REQUIRED"/>
<tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
<tx:method name="getSomethingElse*" propagation="REQUIRED" isolation="READ_UNCOMMITED"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
</aop:config>
Spring+AspectJ的TX Management不难啊...
[ 本帖最后由 苦瓜汤 于 14-11-2007 01:55 PM 编辑 ] |
|
|
|
|
|
|
|
发表于 14-11-2007 03:26 PM
|
显示全部楼层
Java framework 都用那么多 xml
[ 本帖最后由 tensaix2j 于 14-11-2007 04:40 PM 编辑 ] |
|
|
|
|
|
|
|
发表于 14-11-2007 10:03 PM
|
显示全部楼层
|
|
|
|
|
|
| |
本周最热论坛帖子
|