您的位置:首页 > 运维架构

浅谈AOP

2009-02-03 10:38 155 查看
AOP基本思想
英文全称是Aspect Oriented Programming,它使开发人员可以更好地将本不该彼此粘合在一起的功能分离开。www.manguo.net*n9M"p7j0r9z9q

认识AOP
开发人员在编写应用程序时,通常包含两种代码:一种是和业务系统有关的代码,一是和业务系统关系不大的代码,例如日志、权限、异常处理、事务处理等。以前编写代码时,这两种代码基本是写在一起的,这样在程序中,到处充满着相同或类似的代码,例如日志信息的输出,每个方法都要写日志的输出,不利于程序的维护。而AOP就是使这两种代码分离的思想。使用AOP,就不用在业务逻辑中实现与业务功能关系不大的代码,从而降低了两种代码的耦合性,达到易于维护和重用的目的。互联网资讯,新闻,图片,视频,综合性社区.-L,`8`'i2Z6/7G

从字面的意思来理解就是面向方面编程,但却容易让初学者一头雾水,很多资料也都对AOP进行过解释,但过多地从理论的角度进行讲解其实用性,这里不再多讲,仅用一个例子来说明:基本上每个方法都要用日志进行记录,那么如果按照面向对象的思路来说,就是每个对象都有记录日志这样一个行为。要在每个方法里添加日志的信息,必然会产生大量的重复代码,但可以将记录日志看作是一个横切面,所有对这些方法的调用都要经过这个横切面,然后在这个横切面进行记录日志的操作,这样就达到代码重用和易于维护的目的了,这就是AOP的思想。

www.manguo.net,C!n5c+`7b*_;Q/J
数码,偷拍,写真,曝料,视频,汽车,手机'w*Q;S$G/C9Y/R5E
AOP与OOP对比分析
(面向对象编程)对现代编程产生了深远的影响,经过多年的发展,目前已日趋成熟,它能很好地解决软件系统中角色划分的问题,借助于面向对象的分析、设计和实现,开发人员可以将现实领域的实体转换成软件系统中的对象,从而很自然地完成从现实到软件的转换。但OOP并不是一种完美的思想,在某些方面,OOP也有其不足之处。比如在日志、事务处理、权限管理等方面,当应用OOP将这些内容封装为对象的行为时,会产生大量的重复代码,虽然通过一些方法可以减少这种重复,但却不能彻底地解决该问题,于是AOP出现了。前面讲过AOP能够降低代码的耦合性,使得代码易于维护和重用。一个应用程序分为核心关注点和横切关注点。核心关注点和具体应用的功能相关,而横切关注点存在于整个系统的范围内。

在AOP里,每个关注点的实现并不知道是否有其他关注点关注它,这是AOP和OOP的主要区别。在AOP里,组合的流向是从横切关注点到主关注点,而OOP中组合的流向则是从主关注点到横切关注点。从这点可以看出AOP和OOP它们所关注的对象是不同的,所以AOP可以和OOP很好地共存,AOP是OOP的有益补充,而不是其对立面。

芒果网"?0J2e#W"e$p
www.manguo.net2^&r;N.t3s2b+o7r)}#q%l
AOP与Java的代理机制
AAOP是一种思想,它和具体的实现技术无关。任何一种符合AOP思想的技术实现,都可以看作是AOP的实现。2H$Z*`5m4c$n6e,I4H"C
JDK 1.3以后,Java提供了动态代理的机制。通过Java的动态代理机制,就可以很容易地实现AOP的思想。实际上Spring的AOP也是建立在Java的代理机制之上的。要理解Spring的AOP,先来了解Java的代理机制。下面主要通过一个输出日志的实例来了解Java的代理机制,从而引出AOP的几个关键点。
N2w-E

从一个输出日志的实例分析Java的代理机制数码,偷拍,写真,曝料,视频,汽车,手机2A+d!s$x/F#I7c5p
上面讲到,要了解Spring的AOP,先来了解Java的代理机制。本节主要通过一个输出日志的实例来分析Java的代理机制。首先介绍以前写日志的时候是怎么实现的,然后讲解使用Java的代理机制怎么实现日志的输出,接着讲解怎样通过Java的动态代理机制把这个日志输出改成通用的,最后引出AOP的几个关键点。
网 y$k0g$r./!A7G)A7^8W&k
通用的日志输出方法
在笔者使用Spring以前开发的程序中,不管是使用Java自动的日志工具,还是使用Log4j,或是自己编写的日志工具,都要在每一个业务逻辑方法里编写记录日志的代码。使用AOP就可以使业务逻辑和记录日志这两件事情分离开。这个输出日志实例的实现思路是:首先给出原来在程序中编写日志的方法,然后编写测试程序,查看输出结果,最后对这种方法进行总结,指出这种方法的缺点。具体编写步骤如下:
(1)打开Eclipse,在com.gc.action包中建立一个Java文件TimeBook.java,用来模拟实际业务中考勤审核的业务逻辑。
)(2)原来在程序中编写日志时,都要在每一个业务逻辑方法里编写记录日志的代码。TimeBook.java的示例代码如下:

代码:
//******* TimeBook.java**************
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
public class TimeBook {
private Logger logger = Logger.getLogger(this.getClass().getName());
//审核数据的相关程序
public void doAuditing(String name) {
logger.log(Level.INFO, name + " 开始审核数据....");
//审核数据的相关程序
……
logger.log(Level.INFO, name + " 审核数据结束....");
}
}

代码说明:
● 在业务逻辑中使用log4j作为日志输出的工具。
● doAuditing()方法用来处理实际业务中的考勤审核。
● 参数name,用来传入是谁执行了类TimeBook中的doAuditing()方法。
● 在审核代码的前后添加了用logger.log()方法实现日志输出的功能。'G6n3Q,J(Y+@
(3)编写测试程序,继续在以前的测试程序TestHelloWorld的基础上进行修改,TestHelloWorld.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
public class TestHelloWorld {
public static void main(String[] args) {
TimeBook timeBook = new TimeBook();
timeBook.doAuditing("张三");
}
}

代码说明:timeBook.doAuditing("张三")表示该程序的执行人是“张三”。
4k5]4}.^6n2i5Y6p.t芒果网(4)运行测试程序,查看通过TimeBook类输出的日志信息,如图




[ 转自芒果网 ]

通过TimeBook类输出日志信息
在上面的示例中,笔者把日志信息添加在了具体的业务逻辑中,假如程序中其他的代码都需要日志输出的功能,那么每个程序就都要添加和上面类似的代码。这样,在程序中,就会存在很多类似的日志输出代码,造成了很大的耦合,通过什么方法可以使业务逻辑和输出日志的代码分离呢?通过面向接口编程可以改进这个问题。

www.manguo.net+],`7i#`%G1a,V,G

通过面向接口编程实现日志输出
互联网资讯,新闻,图片,视频,综合性社区.4O7J.[4v(Y)A-o%@"d"z
通过前面的示例程序,读者可以了解到以前添加日志信息方法的缺点。下面主要通过面向接口编程来改进这个缺点。其实现思路是:首先把执行考勤审核的doAuditing()方法提取出来成为接口,然后通过一个实体类来实现这个方法,在这个方法里编写具体的考勤审核的业务逻辑,接着通过一个代理类来进行日志输出,最后编写测试程序,查看输出结果。具体步骤如下:
(1)在com.gc.impl包中,建立一个接口TimeBookInterface。TimeBookInterface.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TimeBookInterface.java**************
package com.gc.impl;
import org.apache.log4j.Level;
//通过面向接口编程实现日志输出
public interface TimeBookInterface
{
public void doAuditing(String name);
}


(2)在com.gc.action包中,使前面已经建立好的类TimeBook实现接口TimeBookInterface,在doAuditing()方法中编写具体的考勤审核代码。TimeBook.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TimeBook.java**************
package com.gc.action;
import com.gc.impl.TimeBookInterface;
public class TimeBook implements TimeBookInterface
{
public void doAuditing(String name) {
//审核数据的相关程序
……
}
}


(3)编写一个代理类,用来实现日志的输出,在该类中针对前面的接口TimeBookInterface编程,而不针对具体的类,从而实现具体业务逻辑与日志输出代码。TimeBookProxy.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TimeBookProxy.java**************
package com.gc.action;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import com.gc.impl.TimeBookInterface;
public class TimeBookProxy
{
private Logger logger = Logger.getLogger(this.getClass().getName());
private TimeBookInterface timeBookInterface;
//在该类中针对前面的接口TimeBookInterface编程,而不针对具体的类
public TimeBookProxy(TimeBookInterface timeBookInterface)
{
this.timeBookInterface = timeBookInterface;
}
//实际业务处理
public void doAuditing(String name)
{
logger.log(Level.INFO, name + " 开始审核数据....");
timeBookInterface.doAuditing(name);    //调用方法
logger.log(Level.INFO, name + " 审核数据结束....");
}
}


(4)修改测试程序TestHelloWorld,把类TimeBook当作参数传入代理类TimeBookProxy中,从而实现对具体负责考勤审核类TimeBook的调用。TestHelloWorld.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
import com.gc.action.TimeBookProxy;
public class TestHelloWorld {
public static void main(String[ ] args)
{
//这里针对接口进行编程
TimeBookProxy timeBookProxy  = new TimeBookProxy(new TimeBook());
timeBookProxy .doAuditing("张三");
}
}


(5)运行测试程序,可以得到通过TimeBookProxy类输出日志信息,如图所示。



[ 转自芒果网 ]

通过TimeBookProxy类输出日志信息
和前面一个日志输出做对比,可以看到,在这个示例中,具体负责考勤审核的业务逻辑代码和日志信息的代码分离开了,并且以后只要实现了接口TimeBookInterface的类,都可以通过代理类TimeBookProxy实现日志信息的输出,而不用再每个类里面都写日志信息输出的代码,从而实现了日志信息的代码重用。
www.manguo.net6Y.k;g4}3a

使用Java的代理机制进行日志输出
数码,偷拍,写真,曝料,视频,汽车,手机1R9Q8Q7X7q-F#l:T(i#B
前面的代码虽然有了一些改进,但是仍然有一定局限性,因为要使用代理类,就必须要实现固定的接口,有没有一种通用的机制,不管是不是实现这个接口,都可以实现日志信息的输出呢?
www.manguo.net e2]-W!H,K$Z1}'w H2~*p

Java提供的InvocationHandler接口可以实现这种功能,首先编写一个日志信息的代理类,这个代理类实现了接口InvocationHandler,然后和前面一个实例类似,编写一个接口,并实现这个接口,在实现类中编写具体的考勤审核代码,最后针对接口编写测试类,查看测试结果。具体步骤如下:

(1)编写一个日志信息的代理类LogProxy,这个代理类实现了接口InvocationHandler,可以对任何接口实现日志信息的输出。LogProxy.java的示例代码如下:

复制内容到剪贴板
代码:
//******* LogProxy.java**************
package com.gc.action;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
//代理类实现了接口InvocationHandler
public class LogProxy implements InvocationHandler
{
private Logger logger = Logger.getLogger(this.getClass().getName());
private Object delegate;
//绑定代理对象
public Object bind(Object delegate)
{
this.delegate = delegate;
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
delegate.getClass().
getInterfaces(), this);
}
//针对接口编程
public Object invoke(Object proxy, Method method, Object[ ] args) throws Throwable
{
Object result = null;
try {
//在方法调用前后进行日志输出
logger.log(Level.INFO, args[0] + " 开始审核数据....");
result = method.invoke(delegate, args); //调用绑定对象的方法
logger.log(Level.INFO, args[0] + " 审核数据结束....");
}
catch (Exception e)
{
logger.log(Level.INFO, e.toString());
}
return result;
}
}


(2)使用com.gc.impl包中的接口TimeBookInterface。TimeBookInterface.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TimeBookInterface.java**************
package com.gc.impl;
import org.apache.log4j.Level;
//针对接口编程
public interface TimeBookInterface
{
public void doAuditing(String name);
}


(3)使用com.gc.action包中的类TimeBook,doAuditing()方法中编写具体的考勤审核代码。TimeBook.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TimeBook.java**************
package com.gc.action;
import com.gc.impl.TimeBookInterface;
public class TimeBook implements TimeBookInterface
{
public void doAuditing(String name)
{
//审核数据的相关程序
……
}
}


(4)修改测试代码TestHelloWorld,使用日志代理类LogProxy 实现日志的输出。TestHelloWorld.java的示例代码如下:

复制内容到剪贴板
代码:
//******* TestHelloWorld.java**************
package com.gc.test;
import com.gc.action.TimeBook;
import com.gc.action.TimeBookProxy;
import com.gc.impl.TimeBookInterface;
import com.gc.action.LogProxy;
public class TestHelloWorld {
public static void main(String[ ] args)
{
//实现了对日志类的重用
LogProxy logProxy  = new LogProxy();
TimeBookInterface timeBookProxy = (TimeBookInterface)logProxy.bind(new TimeBook());
timeBookProxy.doAuditing("张三");
}
}


(5)运行测试程序,可以得到通过LogProxy类输出日志信息,如图所示。



[ 转自芒果网 ]

这种方式,对于其他的类也同样适用,这样就真正地实现了业务逻辑和输出日志信息代码的分离。

对这3种实现方式进行总结

']5n8/;P6B*C9_6p;z.n
第一种方式,需要在每个类里都增加对输出日志信息的代码;

第二种方式,虽然实现了业务逻辑与输出日志信息代码的分离,但还是必须依赖于固定的接口;
第三种方式,真正实现了对输出日志信息代码的重用,并且不依赖于固定的接口实现。
,s!|'J3N#r7P9n!J2a
从第三种方式中,也可以看出Java动态代理机制的强大,而Spring的AOP正是建立在Java动态代理的基础上的,通过上面的示例一步一步地对Java的动态代理机制有了一定的了解后,接下来就可以逐渐地进入AOP了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: