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中讨论其实现。  
 
  |