什么是面向切面编程AOP

来源:互联网 发布:917发卡平台源码 编辑:程序博客网 时间:2024/05/24 05:43
作者:知乎用户
链接:https://www.zhihu.com/question/24863332/answer/48376158
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最近在学这方面的内容,读到的这段话我感觉说的很清楚了:这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。
但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。
也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。
这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。
AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

重新组织一下。

首先面向切面编程是什么。
就指是把逻辑代码和处理琐碎事务的代码分离开,以便能够分离复杂度。
让人在同一时间只用思考代码逻辑,或者琐碎事务。
代码逻辑比如是插入一条数据,那么琐碎事务就包括获取连接和关闭连接,事务开始,事务提交。
切面就是指在大堆繁琐事务中的逻辑代码。

然后举个例子:
先假设你有一段逻辑代码要写~ 在这段代码之前要写log;代码完成之后要写log。结局就是一大堆的log代码就淹没了逻辑代码。
aop的想法就是将非逻辑部分的代码抽离出来,只考虑逻辑代码就行了,我把框框画好,这里写前面的log,这里写逻辑,这里写后面的log。
事实上用着嘛~ 我没用过。逻辑代码好像和非逻辑代码是分开在不同文件的。个人觉得跳文件也很烦躁。
感觉应该是系统大到某种程度才会需要这么严格的复杂度控制吧。

--------------------------无责任吐槽分割线--------------------------
(本故事纯属虚构,如有雷同纯属巧合)
虽然我没用过,但是突然想到我曾经在某个系统里见过这货。这个系统是这样的,使用了aop,将数据库的事务管理啊什么的都搞定了封印了。 但是,这个系统的log语句没有被封印,逻辑代码都还被logger.log("xxxxx")这样的语句给包围着。 如果说一个函数的话,可能就是有一半在做log,一个类有一半在做log。就算框架给框框画好了要用aop,也有人有办法不用,或者用不好。
其实不用aop的框架的话,也可以把前面和后面的琐碎事务自己抽象一下也能分开,这虽然不用框架但是也有aop的意思,就把琐碎事务都压缩到一个前置函数和一个后置函数里面。

刚写了一篇好书一起读(115):重学Spring之面向切面,下面是原文,欢迎批评指教:


昨天说了IoC,今天来说AOP。


IoC和AOP这两个缩写总是一起出现。在形式上,两者同为三个字母的缩写,而且第二个字母都是O,有对仗美;在性质上,两者同为Spring的核心技术和特色,是最常被提起的概念。


但与面向切面编程AOP真正对应的,是OOP,即面向对象编程。

未说面向切面,先说面向过程。


面向对象侧重静态,名词,状态,组织,数据,载体是空间;

面向过程侧重动态,动词,行为,调用,算法,载体是时间;


这两者,运行于不同维度,本不互相冲突,理应携手合作,相互配合。


所以,web项目中的controller,service,dao等各层组件,有行为无状态,有方法无属性,即使有属性,也只是对下一层组件的持有;

所以,web项目中的entity,dto等各种实体,有状态无行为,有属性无方法,即使有方法,也只是getter/setter等,围着状态打转;


反倒是我们刚学「面向对象」时说的「既有眼睛又会叫」的小狗那种状态行为俱全的对象,基本见不到了。


程序需要状态,但对象不需要状态。

如果对象有了状态,就会引发烦人的多线程问题,在集群环境下更是麻烦。

程序的状态,统一由数据库,缓存,任务队列这些外部容器来容纳,在处理时,仅仅在对象的方法中以局部变量的面目偶尔出现,被封在线程内部,朝生夕灭,任由回收。


基于Java语言的web开发,本质是用面向对象的组织,面向过程的逻辑,来解决问题。应用实践中灵活具体,不拘泥,不教条。


但仍会遇到一种麻烦,即假如一个流程分三个步骤,分别是X,A,Y,另一个流程的三个步骤是X,B,Y。

写在程序里,两个方法体分别是XAY和XBY,显然,这出现了重复,违反了DRY原则。

你可以把X和Y分别抽成一个方法,但至少还是要写一条语句来调用方法,xAy,xBy,重复依然存在。


如果控制反转来处理这问题,将采用模板方法的模式,在抽象父类方法体中声明x?y,其中?部分为抽象方法,由具体子类实现。

但这就出现了继承,而且调用者只能调用父类声明的方法,耦合性太强,不灵活。

所以,我们常看到,只有那些本来就是调用者调用父类声明的方法的情况,比如表现层,或者本来就不用太灵活,比如只提供增删改查的持久层,才总出现抽象父类的身影。


具体Controller is-a 抽象Controller,具体Dao is-a 抽象Dao,这大家都能接受。

但除了在抽象Controller、抽象Dao中固定的步骤之外,我们就不需要点别的吗?

比如在某些Controller方法运行之前做点什么,在某些Dao方法运行之前之后做点什么?

而且最好能基于配置,基于约定,而不是都死乎乎硬编码到代码里。


这些需求,基本的编程手段就解决不了了。

于是乎,面向切面横空出世。

《Spring3.x企业应用开发实战》(下称《3.x》)第6章写道:

AOP是OOP的有益补充。

Spring实现的AOP是代理模式,给调用者使用的实际是已经过加工的对象,你编程时方法体里只写了A,但调用者拿到的对象的方法体却是xAy。


x和y总还是需要你来写的,这就是增强。

x和y具体在什么时候被调用总还是需要你来规定的,虽然是基于约定的声明这种简单的规定,这就是切点。

《EXPERT ONE ON ONE J2EE DEVELOPMENT WITHOUT EJB》第8章、《Spring实战》第4章:

增强(advice,另译为通知,但《3.x》作者不赞成):在特定连接点执行的动作。

切点(pointcut):一组连接点的总称,用于指定某个增强应该在何时被调用。

连接点(join point):在应用执行过程中能够插入切面的一个点。(我注:就是抽象的「切点」声明所指代的那些具体的点。)

切面(aspect):通知(即增强)和切点的结合。

其他概念不赘,如果有兴趣可以自行去翻书,我每次看到这些东西都很头大。


用人话说就是,增强是「干啥」,切入点是「啥时候干」。

生活中例子如端碗-吃饭-放筷子,端碗-吃面-放筷子,你只要定义好端碗和放筷子,并声明在吃点啥之前之后调用它们,业务方法只要实现吃饭、吃面就行了,以后想加个吃饺子也很方便。

生产中例子如事务、安全、日志(*),用声明的方式一次性配好,之后漫漫长夜专注于写业务代码就行了,不再为这些事而烦。

《Spring实战》第4章:

散布于应用中多处的功能(日志、安全、事务管理等)被称为横切关注点。

把横切关注点与业务逻辑分离是AOP要解决的问题。

*:但《Spring3.x企业应用开发实战》第6章说:

很多人认为很难用AOP编写实用的程序日志。笔者对此观点非常认同。(我注:我也认同)


总之,面向切面的目标与面向对象的目标没有不同。

一是减少重复,二是专注业务。

相比之下,面向对象是细腻的,用继承和组合的方式,绵绵编织成一套类和对象体系。

而面向切面是豪放的,大手一挥:凡某包某类某开头的方法,一并如斯处理!


《Javascript DOM编程艺术》说,dom是绣花针,innerHTML是砍柴斧。

我看面向对象和面向切面,也可做如是观。


没有依赖注入,面向切面就失去立足之本。

没有面向切面,依赖注入之后也只好在各个方法里下死力气写重复代码,或者搞出来一个超级复杂的抽象基类。

同时有了这两者,才真正能履行拆分、解耦、模块化、约定优于配置的思想,才真正能实现合并重复代码、专注业务逻辑的愿望。


不过,这面向切面不是Spring的专利,Java Web开发中最基本的Filter,就是一层一层的切面,突破了之后才能触及Servlet这内核。

但Filter过于暴力粗放,只能运行在Servlet之外而不能在之内,能上不能下,稍微细一点的批处理它就不行了,而Spring的AOP可以。

(Struts2的Intercepter也算,关于这就不多说了,如感兴趣可看《Struts2技术内幕》第8章Intercepter部分)

从理论上说,Filter和Spring AOP前者是责任链模式(Struts2 Intercepter也是),后者是代理模式,性质不同,但从「层层包裹核心」的共同特点看,是一致的。


所以无论是宽是窄,只要你遇到了「好多方法里都有重复代码好臭哇呀」的情况(关于代码的坏气味可以参考《重构》),而又无法应用策略、装饰器、模板方法等模式,就考虑AOP吧!

毕竟虽然Spring的书籍里讲到AOP就连篇累牍、名词繁多、配法多样、望而生畏,但具体写起来还是非常简单的。

(不过,如果能用「绣花针」OOP的设计模式实现,还是不建议轻易动用AOP这「劈柴刀」,不得已才用之。关于设计模式,推荐《Java与模式》一书)




我举个例子吧,不大擅长讲理论的东西你的程序写好了现在发现要针对所有业务操作添加一个日志,或者在前面加一道权限控制,怎么办呢?传统的做法是,改造每个业务方法这样势必把代码弄得一团糟而且以后再扩展还是更乱aop的思想是引导你从另一个切面来看待和插入这些工作日志,不管加在哪,它其实都是属于日志系统这个角度的权限控制也一样aop允许你以一种统一的方式在运行时期在想要的地方插入这些逻辑不知解释清楚没。。。手机上码字好慢。。。
0 0
原创粉丝点击