初涉面向切面编程

    在阅读《J2EE项目实训——Spring框架技术》一书的时候,对AOP的相关介绍吸引,特此对作者表示感谢。虽然对于书中的观点和实现方案未必完全赞同,总感觉有对于技术优点的吹嘘有“自夸”之嫌,但是书中所说问题确实具有普遍性,给出的意见也是有很强借鉴性的,故学而时习之、以待实践之检验。

    AOP:Aspect Oriented Programming,Aspect直译为“方面”,在此译为“切面”。它可以解决传统的OOP方法不能很好地解决CrossCut(横切)方面的问题,比如在应用系统中所经常需要解决的,如事务、安全、日志、缓存和并发访问中的锁定等,都属于应用系统中的“横切”方面的问题。

    AOP的基本要求:尽可能地避免重复(代码、模块等),这也是OOP设计的基本要求,然而OOP在解决代码重复方面有一定的局限性[1]。

    下面看一个例子,它会展现一些OOP代码的问题:

  1. public class UserDAODBBean implements DAOInterface
  2. {
  3.     private Connection con=null;
  4.     public UserDAODBBean()
  5.     {
  6.         //....在此获得数据库连接Connection对象
  7.     }
  8.     public boolean deleteOneUserInfo(String insertSQL)
  9.     {
  10.         try
  11.         {
  12.             con.setAutoCommit(false);
  13.             //......
  14.             con.commit();
  15.         }
  16.         catch (SQLException e)
  17.         {
  18.             con.rollback();
  19.         }
  20.         //......
  21.     }
  22.     public boolean updateOneUserInfo(String updateSQL)
  23.     {
  24.         try
  25.         {
  26.             con.setAutoCommit(false);
  27.             //......
  28.             con.commit();
  29.         }
  30.         catch (SQLException e)
  31.         {
  32.             con.rollback();
  33.         }
  34.         //......
  35.     }
  36. }

    你很容易就会发现:con.setAutoCommit()......con.commit()......con.rollback()这样的调用不断重复出现在应用系统的各个模块中,也就是“横切于应用系统的多处”[2]。

    我们可以采用AOP技术解决此类型的代码重复问题:将目标功能代码也进行模块化[3],并通过某种描述方式来设定对此功能代码的调用点[4]。当应用系统中的某个业务功能类执行到调用点时,由平台系统[5]中的某个特定的加载器程序[6]来加载前面的目标功能代码[3]。经过这样的处理后,就不会在各个业务功能类中重复出现对这些Advice(通知)调用的代码。

 

下面对于AOP解决的问题,以及传统OO方法的局限,作一些简单的介绍:

    应用AOP的目的:尽量分离“技术问题实现”和“业务问题实现”

    关于这一点,是容易理解的:银行系统中“存钱、取钱”是金融业务问题;安全、事务、日志、缓存、并发等技术手段,也是必须实现的。常规的面向对象编程方法是将两者混合在一起的[7]。

    OOP擅长解决有纵向关系的问题,但不擅长解决有横向关系的问题

    在OOP中引入封装、继承、多态建立多层对象结构,辅助消息机制模拟应用系统的真实实体工作状态。但如果利用OO来解决跨越多个系统模块的共同功能行为的问题[8]时,不仅复杂,而且还会产生大量重复代码。

    为什么OOP不擅长解决有横向关系的问题

    1、基于OOP技术所产生的各种编程语言中的类结构是静态的;

    2、利用GoF设计模式完善和优化系统中的类设计;

    3、设计模式没有深入到类的内部结构中。

使用AOP进行系统开发

    它利用一种被称为“横切”的技术,剖解开封装的对象类内部并深入到类的内部结构的组成成员。在AOP中提供AOP的Advice(通知)、Interceptor(拦截器)、Introduction(引入)、Metadata(元数据)和Pointcut(切入点)等方面的技术和组件,能够拦截某方法的执行过程,从而可以为应用系统的开发者提供了“在任何对象的方法调用前/后加入自定义行为”的能力,也就允许开发者动态修改静态的类结构[9],满足用户新增需求的系统[10]。

    遵循AOP的思想原则,业务功能人员只考虑核心关注点的功能代码实现,而不应该考虑各个横切关注点的功能代码[11];

    可以在如下的应用场景中应用AOP技术

    显然,这种设计的灵活性会带来运行效率方面的损失(我不想证明这点了),使用时需要谨慎。

    Authentication(权限)、Caching(缓存)、Context Passing(内容传递)、Error Handling(错误处理)、Lazy Loading(延迟加载)、Debugging(程序调试)、Logging、Tracing、Profiling and Monitoring:日志记录、跟踪、系统优化和监控、Performance Optimization(性能调优)、Persistence:持久化、Resource Pooling(资源池)、Synchronization(同步)、Transactions(事务)。

    AOP的思想在J2EE中的应用:Web过滤器组件

    1,在项目中添加一个过滤器组件以实现对Web系统进行保护。包名称为:com.px1987.webbs.filter,类名称如代码所示,实现javax.servlet.Filter接口。

    2,下面摘录一些核心的代码,用以说明问题:

  1. public class CheckUserLoginFilter implement Filter
  2. {
  3.     public void doFilter()
  4.     {
  5.         //...
  6.         String actionString = request.getParameter("action");
  7.         //...
  8.         targetQueryParam = actionString.equals("showUserLogout") ||
  9.                            actionString.equals("showUpdateUserInfo") ||
  10.                            actionString.equals("doShowOnLineUserInfo");
  11.         //.....
  12.         if (targetQueryParam)    //识别用户是否对敏感资源进行访问
  13.         {
  14.             //如果没有登录过系统,则跳转到错误页面,提示非法访问。
  15.         }
  16.     }
  17. }

3,在web.xml中部署该过滤器组件类CheckUserLoginFilter

    在<filter-mapping>中设置需要监控的URL地址;在<filter-name>中给出类名;在<filter-class>中给出类的包路径:com.px1987.webbs.filter.CheckUserLoginFilter。

    是不是觉得很方便?但是......这么好用的机制是如何实现的呢?别急,我们慢慢看来。

    分别用AOP与代理模式来实现日志记录

    按照上面《应用场景》一节的介绍,日志记录是AOP的用武之地。我们先从简单的日志记录实现看起,然后为了达到“技术问题实现”和“业务问题实现”的分离,采用GoF设计模式中的代理模式实现[12],最后引出AOP(很可惜,书上没有给出Spring AOP实现的代码)。

Spring AOP的实现和开发要点

    对于这方面,书上介绍得就更粗略了。我索性也摘录简略点:

    1,Spring AOP,是通过Spring框架中的控制反转(IoC)模式、Java语言中的动态代理和反射技术等来实现的。

    2,Spring AOP也可以通过基于CGLIB来实现对某个类的代理。

    3,Spring框架AOP中的代理组件ProxyFactoryBean类、自动代理及实现类、创建静态切入点的实用类RegexpMethodPointcut等,它们的使用,不是我们这里讨论的重点——必须实际动手才能了解。由于现在对反射机制还不是了解得很深入,对于AOP也就冷静得对待吧!

 

[1] 继承能够解决“纵向方面”的代码重复,聚合关系能够解决组成某个问题的“元素代码”重复。特点:减少“重复”时,两个程序类之间必须要有一定的紧密关系。

    然而,在应用系统中还存在另一种形式的代码重复——在各个功能类中“对目标功能代码进行调用的代码”。它无法通过OOP实现,因为“对目标功能代码进行调用的代码”与应用系统中的各个功能类之间既不存在继承关系,也不存在聚合关系。

[2] 尽管JDBC API对事务管理的实现代码进行了模块化,但仍然要求开发者在应用系统DAO组建的各个方法中对JDBC的事务管理代码进行调用。对于这种“重复”,我们怎么办?我们应该如何消除它们?是继续采用面向对象的编程技术吗?

[3] 此功能代码在AOP技术中称为Advice——通知。

[4] 在AOP技术中称为PointCut:连接点或者切入点。

[5] 在AOP技术中称为容器,不要下意识地理解“平台系统”就是操作系统。Java也提供平台。

[6] 在AOP技术中称为advisor——通知者,或者通知的装载器。

[7] 一旦解决技术问题的代码发生变化或扩展,开发者就不得不修改各个相关的业务问题的功能实现代码。试想一下:在上面的代码例子中,con.setAutoCommit()函数名称改变了,你该怎么办?不得不到每个调用的代码地方修改吧!如果改变了函数调用顺序呢?每个地方都改......不仅效率低,而且容易出错!

根据“开放-封闭原则”,由于技术功能类的实现而导致业务类代码的“修改”(只能增加、不要修改),这绝对是违反原则的!我们的目的之一,还是要能实现代码的重用,一旦某个横切关注点的代码实现发生变化,不必修改很多相关的业务功能类,而只需要修改共享的横切关注点的代码类即可。

[8] 也就是前面提到的“技术问题”(相对于业务问题而言),重复代码的例子如上:在login的流程中,掺杂了数据库操作——也就是典型的“技术”。

[9] 也就是弥补上一节第一点提到的OOP的不足。

[10] 从某种程度上,这种介绍让我想到了windows提供的钩子函数。

[11] 我需要一个示例:一部分人考虑业务、一部分人考虑“横切点”,最后关联到一起形成应用系统——这个过程如何实现的?有示例代码吗?

[12] 在实现的时候,分别采用静态代理技术和动态代理技术。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值