一、我们在做系统设计的时候,一个非常重要的工作就是把一个大系统做分解, 按业务功能分解成一个个低耦合、高内聚的模块,就像这样:

  但是分解以后就会发现有些很有趣的东西, 这些东西是通用的,或者是跨越多个模块的:

  日志: 对特定的操作输出日志来记录

  安全:在执行操作之前进行操作检查

  性能:要统计每个方法的执行时间

  事务:方法开始之前要开始事务, 结束后要提交或者回滚事务

  等等....

  这些可以称为是非功能需求, 但他们是多个业务模块都需要的, 是跨越模块的, 把他们放到什么地方呢?

  最简单的办法就是把这些通用模块的接口写好, 让程序员在实现业务模块的时候去调用就可以了。



  这样做看起来没问题, 只是会产生类似这样的代码:



  这样的代码也实现了功能,但是看起来非常的不爽, 那就是日志,性能,事务 相关的代码几乎要把真正的业务代码给淹没了。

二、 设计模式:模板方法

  用设计模式在某些情况下可以部分解决上面的问题,例如著名的模板方法:

  在父类(BaseCommand)中已经把那些“乱七八糟“的非功能代码都写好了, 只是留了一个口子(抽象方法doBusiness())让子类去实现。

  子类变的清爽, 只需要关注业务逻辑就可以了。

  调用也很简单,例如:

BaseCommand  cmd = ...  获得PlaceOrderCommand的实例...
cmd.execute();

  但是这样方式的巨大缺陷就是父类会定义一切: 要执行哪些非功能代码, 以什么顺序执行等等

  子类只能无条件接受,完全没有反抗余地。

  如果有个子类, 根本不需要事务, 但是它也没有办法把事务代码去掉。

三、设计模式:装饰者

  如果利用装饰者模式, 针对上面的问题,可以带来更大的灵活性:



  现在让这个PlaceOrderCommand 能够打印日志,进行性能统计

Command cmd = new LoggerDecorator(
new PerformanceDecorator(
new PlaceOrderCommand()));
cmd.execute();

  如果PaymentCommand 只需要打印日志,装饰一次就可以了:

Command cmd = new LoggerDecorator(
new PaymentCommand());
cmd.execute();

  可以使用任意数量装饰器,还可以以任意次序执行(严格意义上来说是不行的), 是不是很灵活?

四、AOP

  如果仔细思考一下就会发现装饰者模式的不爽之处:

  (1) 一个处理日志/性能/事务 的类为什么要实现 业务接口(Command)呢?

  (2) 如果别的业务模块,没有实现Command接口,但是也想利用日志/性能/事务等功能,该怎么办呢?

  最好把日志/安全/事务这样的代码和业务代码完全隔离开来,因为他们的关注点和业务代码的关注点完全不同 ,他们之间应该是正交的,他们之间的关系

应该是这样的:

  如果把这个业务功能看成一层层面包的话, 这些日志/安全/事务 像不像一个个“切面”(Aspect) ?

  如果我们能让这些“切面“能和业务独立, 并且能够非常灵活的“织入”到业务方法中, 那就实现了面向切面编程(AOP)!

五、实现AOP

  现在我们来实现AOP吧, 首先我们得有一个所谓的“切面“类(Aspect), 这应该是一个普通的java 类 , 不用实现什么“乱七八糟”的接口。

  以一个事务类为例:

  我们想达到的目的只这样的: 对于com.coderising这个包中所有类的execute方法, 在方法调用之前,需要执行Transaction.beginTx()方法, 在调用之后, 需要执行Transaction.commitTx()方法。

  暂时停下脚步分析一下。

  “对于com.coderising这个包中所有类的execute方法” , 用一个时髦的词来描述就是切入点(PointCut) , 它可以是一个方法或一组方法(可以通过通配符来支持,你懂的)

  ”在方法调用之前/之后 , 需要执行xxx“ , 用另外一个时髦的词来描述就是通知(Advice)

  码农翻身认为,PointCut,Advice 这些词实在是不直观, 其实Spring的作者们也是这么想的 : These terms are not Spring-specific… unfortunately, AOP terminology is not particularly intuitive; however, it would be even more confusing if Spring used its own terminology.

  当然,想描述这些规则, xml依然是不二之选:

  注意:现在Transaction这个类和业务类在源代码层次上没有一点关系,完全隔离了。

  隔离是一件好事情, 但是马上给我们带来了大麻烦 。

  Java 是一门静态的强类型语言, 代码一旦写好, 编译成java class 以后 ,可以在运行时通过反射(Reflection)来查看类的信息, 但是想对类进行修改非常困难。

  而AOP要求的恰恰就是在不改变业务类的源代码(其实大部分情况下你也拿不到)的情况下, 修改业务类的方法, 进行功能的增强,就像上面给所有的业务类增加事务支持。

  为了突破这个限制,大家可以说是费尽心机, 现在基本是有这么几种技术:

  (1) 在编译的时候, 根据AOP的配置信息,悄悄的把日志,安全,事务等“切面”代码 和业务类编译到一起去。

  (2) 在运行期,业务类加载以后, 通过Java动态代理技术为业务类生产一个代理类, 把“切面”代码放到代理类中, Java 动态代理要求业务类需要实现接口才行。

  (3) 在运行期,业务类加载以后, 动态的使用字节码构建一个业务类的子类,将“切面”逻辑加入到子类当中去, CGLIB就是这么做的。

  Spring采用的就是(1) +(2) 的方式,限于篇幅,这里不再展开各种技术了, 不管使用哪一种方式, 在运行时,真正干活的“业务类”其实已经不是原来单纯的业务类了, 它们被AOP了 !

Spring本质-AOP的更多相关文章

  1. spring的aop的例子

    一个简单的Spring的AOP例子 2009-06-23 11:33:29|  分类: Spring |  标签: |举报 |字号大中小 订阅     package aop; /** * 目标对象的 ...

  2. Java基础-SSM之Spring的AOP编程

    Java基础-SSM之Spring的AOP编程 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   Spring的本质说白了就是动态代理,接下来我们会体验AOP的用法.它是对OOP的 ...

  3. spring(13)------全面深入解析spring的AOP

    一,AOP的基本思想 AOP(Aspect Oriented Programming)翻译成中文的大意是面向切面编程,主要目的解决让不该牵扯在一起的代码分离开来. (1)认识AOP 应用程序中通常包含 ...

  4. 一起学Spring之AOP

    概述 在软件开发中,我们重点关注的是业务逻辑代码,但在实际开发中,需要写的代码却不仅仅是业务逻辑,还需要处理记录日志,异常处理,事务控制等一些与业务无关的事情.而且这些代码也是服务端必须的,类似这样的 ...

  5. Spring基于AOP的事务管理

                                  Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...

  6. Spring实现AOP的4种方式

    了解AOP的相关术语:1.通知(Advice):通知定义了切面是什么以及何时使用.描述了切面要完成的工作和何时需要执行这个工作.2.连接点(Joinpoint):程序能够应用通知的一个“时机”,这些“ ...

  7. spring的AOP

    最近公司项目中需要添加一个日志记录功能,就是可以清楚的看到谁在什么时间做了什么事情,因为项目已经运行很长时间,这个最初没有开来进来,所以就用spring的面向切面编程来实现这个功能.在做的时候对spr ...

  8. Spring(五)AOP简述

    一.AOP简述 AOP全称是:aspect-oriented programming,它是面向切面编号的思想核心, AOP和OOP既面向对象的编程语言,不相冲突,它们是两个相辅相成的设计模式型 AOP ...

  9. Spring中AOP原理,源码学习笔记

    一.AOP(面向切面编程):通过预编译和运行期动态代理的方式在不改变代码的情况下给程序动态的添加一些功能.利用AOP可以对应用程序的各个部分进行隔离,在Spring中AOP主要用来分离业务逻辑和系统级 ...

随机推荐

  1. UVA-10273 Cyborg Genes (DP)

    题目大意:给两个字符串a.b,找出一个最短的字符串c,使得这两个字符串都是c的子序列.只需找出p的最小长度和最小长度时的个数. 题目分析:与LCS问题类似.最小长度的状态转移方程,dp(i,j)=mi ...

  2. SQL语句增加列、修改列类型、修改列、删除列

    1.增加列: alter table tableName add columnName varchar(30) 2.修改列类型: alter table tableName alter column ...

  3. EBS OAF开发中的错误/异常处理(ErrorHandling) (转)

    原文地址 EBS OAF开发中的错误/异常处理(ErrorHandling) EBS OAF开发中的错误/异常处理(ErrorHandling) (版权声明,本人原创或者翻译的文章如需转载,如转载用于 ...

  4. SSH 远程端口转发

    既然"本地端口转发"是指绑定本地端口的转发,那么"远程端口转发"(remote forwarding)当然是指绑定远程端口的转发. 还是接着看上面那个例子,ho ...

  5. HDU 1936 区间贪心

    /* *区间贪心.前几天刚做了POJ 1328 ...思路完全相同... *最多有100个表情,100行文字.遍历寻找每个表情的所在区间.时间复杂度大约在10^5 ~ 10^6 可以接受. *然后对每 ...

  6. 【转】EDNS

    转自:https://www.cnblogs.com/cobbliu/p/3188632.html 随着业务的复杂化和多样化,RFC1035中定义的DNS消息格式和它支持的消息内容已经不足以满足一些D ...

  7. 网络协议栈学习(一)socket通信实例

    网络协议栈学习(一)socket通信实例 该实例摘自<linux网络编程>(宋敬彬,孙海滨等著). 例子分为服务器端和客户端,客户端连接服务器后从标准输入读取输入的字符串,发送给服务器:服 ...

  8. 免费申请一年版Eset NOD32 Antivirus激活码(无限制)- 已失效

    You will receive a 1-year license of the antivirus software Eset NOD32 (version 6), including all up ...

  9. 你应该关注的几个Eclipse超酷插件

    来自非营利性Eclipse基金会的Eclipse IDE以其插件生态系统著称.Eclipse市场拥有海量插件可供下载,你可以通过插件定制自己的Eclipse. 最近我在Eclipse Marketpl ...

  10. easyui 定义的右键菜单 在 浏览器中 失效.

    使用 火狐 打开 easy ui 开发的网页, 鼠标右键 没有出现 自定义的 easyUI右键菜单, 出现的是鼠标本身的右键菜单. 如果使用ie 则正常. 原因在于我 的火狐使用了解除右键限制 这个脚 ...