zl程序教程

您现在的位置是:首页 >  后端

当前栏目

依赖注入及AOP简述(十二)——依赖注入对象的行为增强(AOP) .

注入依赖对象AOP 增强 行为 十二 简述
2023-09-11 14:18:00 时间

四、依赖注入对象的行为增强(AOP)

前面讲到,依赖注入框架的最鲜明的特点就是能够提供受容器管理的依赖对象,并且可以对对象提供行为增强(AOP)功能,所以这一章我们来讨论有关AOP的话题。

 

1.     对依赖对象进行行为增强

所谓AOP,就是Aspect Oriented Programming(面向方面的编程),核心思想是把一个“方面”独立出来,从而实现组件间的松耦合。也许有些晦涩难懂,所以我们还是看个简单的例子。

在我们的银行依赖中,假设有个需求,即在每一笔取款业务的前后都要输出日志信息。因此我们需要这样修改我们的代码:

 

public class BankICBC implements Bank {

    private static Logger logger = Logger.getLogger(Bank.class.getName());

 

    @Override

    public Cash withDraw(DepositBook depositBook, BigDecimal amount) {

        logger.log(Level.INFO, "withdraws starting...");

        // ……

        logger.log(Level.INFO, "withdraws ended…");

    }

}

 
 

 

 

 

 

 

 


可以看到,我们为了实现这个需求需要在其中加入定义logger、输出日志等代码。而这些代码,就是我们所说的独立的“方面”。为什么这么说呢?因为日志的输出工作,是与开发者真正要做的取款业务完全没有关系的。再假设,如果我们需要在很多其它地方也要做同样的日志处理,而这样的处理又完全不依赖于那些地方的逻辑,则我们需要打开全部的代码页,不分情况的Ctrl+C和Ctrl+V,这无疑给开发和维护带来了不小的困扰。

因此AOP的出现,就可以将这个独立的日志处理的“方面”从实际的依赖对象里分离开来,而在依赖对象在运行的时候,这个“方面”又可以加到依赖对象身上得以运行,也就是我们所说的依赖对象的行为被增强了,因为它的行为不但实现了它本身的逻辑,而且也实现了被增强的其它“方面”的逻辑。而在AOP体系内,用以将其它“方面”的逻辑增强到某对象上的组件往往被称作Interceptor(拦截器)。

Spring、Seam和Guice都提供了相应的拦截器定义方式,由于Seam是基于原注解模式的定义方法,在开发者使用时稍有不便,因此我们这里以Spring为例简单介绍一下如何将刚才的“日志处理方面”应用到我们的程序当中。

首先我们将“日志处理方面”分离出来作为一个独立的类,这个类即是被独立出来的“方面”。


 

 

public class LogInterceptor {

    private static Logger logger = Logger.getLogger(LogInterceptor.class.getName());

   

    public Object log(ProceedingJoinPoint call) throws Throwable {

        logger.log(Level.INFO, "withdraws starting...");

        try {

            return call.proceed();

        } finally {

            logger.log(Level.INFO, "withdraws ended...");

        }

    }

}

 
 

 

 

 

 

 

 

 


Spring提供了很多种拦截器组装方式,这里我们采用XML配置的方式将这个“方面”增强到我们的银行依赖对象上:

 

<beans>

  <!-- …… -->

 

  <!-- 这里将“方面”类声明为Spring管理的依赖 -->

  <bean id="logger" class="tutorial.di.ch01.LogInterceptor"/>

 

  <!-- 这里将所声明的“方面”增强到需要的地方 -->

  <aop:config>

      <aop:aspect ref="logger">

          <aop:pointcut id="pointcuts.withdrawMethod"

              expression="execution(* tutorial.di.ch01.BankICBC.withDraw(..))" />

          <aop:around pointcut-ref="pointcuts.withdrawMethod " method="log"/>

      </aop:aspect>

  </aop:config>

</beans>

 

 
 

 

 

 

 

 

 

 

 

 


此后原先的BankICBC类就不需要再写任何的关于日志输出的代码,就可以将该功能导入进来了。反之如果想去掉这个需求,同样不需要变更BankICBC类,只将拦截器的配置删除即可,从而大大降低了程序逻辑与其它“方面”的耦合度。

注意所有AOP功能的底层实现都是靠Java的动态代理机制实现的,往往是基于JDK自身的代理类,或者是Javassist、CGLIB工具等,因此AOP的作用对象不能是私有方法、静态方法以及final方法。