天天看点

AOP及其在Spring中的应用

 AOP及其在Spring中的应用

AOP简介

  OOP思想对现代编程产生了深远的影响,但在某些方面,OOP也有其不足之处。比如在logging(日志)、transaction(事务)等方面,应用OOP将这些内容封装为对象的行为则会产生大量的代码重复,虽然通过一些设计模式可以减少这种重复,但我们还有更好的解决办法,那就是AOP(Aspect Oriented Programming)。AOP是最近兴起的一种编程思想,它是OOP思想的补充,而不是其对立面。

  AOP,从字面的理解来看就是面向切面的编程,用一个比较通俗的例子来说,比如在访问多个对象前需要进行权限检查,那么如果按照面向对象的思路来说,权限检查势必会成为这多个对象的行为。如果每个对象都需要去实现这些行为,势必会造成大量重复代码的产生,写程序也会变得枯燥无味。但我们可以将权限检查看作是一个切面,所有对这些对象的访问都要经过这个切面。要了解AOP,就必须先了解几个基本的且非常重要的概念。

  Aspect(切面):对象操作过程中的截面。如权限检查、日志、事务处理等。

  Join Point(连接点):程序运行中的某个阶段点。如某个方法调用,异常抛出等。

  Advice(处理逻辑):某个连接点所采用的处理逻辑。

  PointCut(切点):一系列连接点的集合,它指明Advice在什么时候被触发。

  示例

  还是用例子来说明一切,比如现在有一个DomainObjDAO接口以及其实现类DomainObjDAOImpl

  DomainObjDAO.java:

  public interface DomainObjDAO {

  public void save();

  }

  DomainObjDAOImpl:

  public class DomainObjDAOImpl implements DomainObjDAO {

  private Logger logger = Logger.getLogger(this.getClass().getName());

  public void save() {

  System.out.println("saving domain object......");

  }

  现在需要在save方法中添加对该业务对象的锁,比如在save前后加锁和解锁。拿到这个需求,在不影响外部调用逻辑以及不对现有代码改动的前提下,Proxy模式(GOF)是个不错的选择,新增一个Proxy类同样实现DomainObjDAO接口,在其实现方法中代理DomainObjDAOImpl类的save方法,并在save的前后调用lock以及unlock方法。这种方法使得我们不必改动外部调用逻辑以及现有代码,但是如果有多个DomainObjImpl的情况下,该方法的弊端便暴露无遗,我们必须实现与DomainObjImpl个数相同的Proxy类来实现该功能,这对我们来说将是非常恐怖且不可接受的。

  这个例子再次印证我们开始所描述的,针对这类问题,OOP显得有些力不从心,而AOP却能很好的解决它,JDK1.3后所提供的动态代理的特性为我们利用AOP的思想解决这个问题提供了很好的思路,下面我们来看它如何实现。

  动态代理实现AOP

  public class LockHandler implements InvocationHandler {

  private Logger logger = Logger.getLogger(this.getClass().getName());

  private Object originalObject;

  public Object bind(Object obj) {

  logger.info("coming here...");

  this.originalObject = obj;

  return Proxy.newProxyInstance(

  obj.getClass().getClassLoader(),

  obj.getClass().getInterfaces(),this);}

  public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {

  Object result=null;

  if(arg1.getName().startsWith("save")){

  lock();

  result=arg1.invoke(this.originalObject,arg2);

  unlock();

  }

  return result;

  }

  private void lock(){

  logger.info("lock object...");

  }

  private void unlock(){

  logger.info("unlock object...");

  }

  }

  上述代码中并没有出现与具体应用层相关的接口以及类的引用,所以对所有的类都适用。这便解决了用普通Proxy类实现的弊端。但是动态代理要求所代理的类必须是某个接口的实现(这点可以通过obj.getClass().getInterfaces()看出),不过这也符合面向对象的设计思想,如果所代理的类没有实现任何接口,可以通过GCLIB来实现,这里就不再详述。

最后我们写下一个TestCase来测试动态代理的实现,如下:

  public class DyproxyTestCase extends TestCase {

  private LockHandler handler=null;

  private DomainObjDAOImpl daoImpl=null;

  protected void setUp() throws Exception {

  // TODO Auto-generated method stub

  super.setUp();

  handler=new LockHandler();

  daoImpl=new DomainObjDAOImpl();

  }

  protected void tearDown() throws Exception {

  super.tearDown();

  }

  public void testSave(){

  ((DomainObjDAO)handler.bind(daoImpl)).save();

  }

  }

  运行结果如下:

  2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler bind

  信息: coming here...

  2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler lock

  信息: lock object...

  saving domain object......

  2004-12-1 23:01:10 test.aop.dynamicproxy.LockHandler unlock

  信息: unlock object...

  至此,我们用动态代理实现了AOP,Spring的AOP实现正是采用了动态代理,我将在下一个Blog中讨论其实现。

  Spring中AOP的实现就是通过动态代理来实现的。动态代理的实现在上篇blog中已经涉及。Spring中目前最为实用的AOP应用,非用其实现的事务管理机制莫属。也正是这一点,使得Spring AOP大方异彩。

  那么我们继续围绕上节的例子来探讨一下Spring中AOP机制的应用与开发。

  首先,看看AOP中几个基本的概念对应在AOP中的实现:

  ? 切点(PointCut)

  一系列连接点的集合,它指明处理方式(Advice)将在何时被触发。

  对于我们开发而言,“何时触发”的条件大多是面向Bean的方法进行制定。像Spring的配置化事务管理时针对方法名称可进行PointCut设置,从而指定对所有以声明字符开头的方法进行基于AOP的事务管理。那么同样,对于我们自己实现的AOP组件而言,我们也可以以方法名作为触发判定条件。

  我们可以通过在XML配置文件中配置以下节点,为我们的组件设定触发条件。  < p>

  class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

   .*save.*

   .*do.*

  上面我们针对MyInterceptor设定了一个基于方法名的触发条件,也就是说,当目标类的指定方法运行时,MyInterceptor即被触发。

  ? 处理方式(Advice)

  也就是说要实现一个Interceptor,以供在连接点时触发。Spring中采用了AOP联盟(AOP Alliance)的通用AOP接口(接口定义位aopalliance.jar)。这里我们采用aopalliance.jar中定义的MethodInterceptor作为我们的Advice实现接口。那么我们上节例子中的处理方式应实现为如下Interceptor:

  package test.aop.spring;

  import java.util.logging.Logger;

  import org.aopalliance.intercept.MethodInterceptor;

  import org.aopalliance.intercept.MethodInvocation;

  

  public class LockInterceptor implements MethodInterceptor {

  private Logger logger = Logger.getLogger(this.getClass().getName());

  public Object invoke(MethodInvocation invocation) throws Throwable {

  // TODO Auto-generated method stub

  lock();

  Object ret= invocation.proceed();

  unlock();

  return ret;

  }

  private void lock(){

  logger.info("lock domain object...");

  }

  private void unlock(){

  logger.info("unlock domain object...");

  }

  }

  实现后,对应的Interceptor实现类在配置文件中的体现如下:

最后,我们还需要定义一个Spring AOP ProxyFactory用于加载执行AOP组件,并且需要将Advice通过IOC的方式注入到接口以及实现类。

  对应的配置文件应如下配置从而实现这些内容:

   test.aop.spring.DomainObjDAO

   myPointcutAdvisor 

  万事大吉!写一个TestCase来看一下运行结果:

  package test.aop.spring;

  import junit.framework.TestCase;

  import org.springframework.context.ApplicationContext;

  import org.springframework.context.support.FileSystemXmlApplicationContext;

  

  public class SpringTestCase extends TestCase {

  DomainObjDAO test=null;

  protected void setUp() throws Exception {

  super.setUp();

  ApplicationContext ctx=new FileSystemXmlApplicationContext("test/Bean.xml");

  test = (DomainObjDAO) ctx.getBean("myAOPProxy");

  }

  protected void tearDown() throws Exception {

  super.tearDown();

  }

  public void testSave(){

  test.save();

  }

  }

  运行结果如下:

  信息: Creating shared instance of singleton bean 'MyInterceptor'

  2004-12-7 23:50:11 test.aop.spring.LockInterceptor lock

  信息: lock domain object...

  saving domain object......

  2004-12-7 23:50:11 test.aop.spring.LockInterceptor unlock

  信息: unlock domain object...

  OK!至此,我们已经探讨了AOP动态代理的实现以及其在Spring中的应用,接下来的Blog中我将会关注Spring中事务以及持久化的实现。

继续阅读