JAVA浅谈)
由来
Spring框架及衍生产品广泛地应用于商业级工程项目,大家聊起Spring时往往离不开IOC(Inversion Of Control)与AOP(Aspect-oriented Programming), Spring的普及以及其对@AspectJ表达式的支持在一定程度上掩盖了纯粹AspectJ框架的光芒,甚至一定程度上模糊了开发人员对面向切面编程的认知,本文以Spring AOP为例,尝试以尽可能简洁的言语介绍AOP这一门技术及其在一个商业级项目的用途以及指出Spring AOP 与其他编译级AOP框架的区别。
注:本文仅包含作者本文理解,各位大佬有任何不同见解欢迎留言指导
AOP简单概念
AOP(aspect oriented programming)是对OOP(object oriented programming)编程思想的补充。在面向对象的世界中,编程的关注点在类(Class)上,或者说 ——类文件是关键的模块单元。这种编程方式很大程度地加强了代码的复用性(或许任何一门编程语言都是以减少重复工作作为目标演变的)。而AOP编程思想的关键模块单元为切面,关于切面的描述有很多,从Spring Reference Documentation 中
Aspects enable the modularization of concerns (such as transaction
management) that cut across multiple types and objects. (Such concerns
are often termed “crosscutting” concerns in AOP literature.
认为AOP编程关注点由Class转移到了对横切面的处理上,从而将横跨多个类的处理逻辑集中在一个地方方便统一处理 。
以事务为例,传统的OOP编程对事务对处理是这样子:
public void transfer(Receiver receiver){
//开启事务
doOpenTransaction();
//执行交易
doTransftering(receiver);
//关闭事务
doCloseTransaction();
}
public void receive(Client client){
//开启事务
doOpenTransaction();
//执行交易
doReceiving(client);
//关闭事务
doCloseTransaction();
}
上述案例虽然看上去很正常,但是会造成代码中无可避免地大量重复地出现doOpenTransaction(), doCloseTransaction() 等方法。试想,如果这样的开关事务代码出现多次,以后想在开关事务之间再增加额外通用逻辑(比如安全检查等),对代码的编写者(我们)会是一件很糟糕的事情——我们必须一个个挑出有这种类型代码的类并且一一修改。
在这样的背景下,AOP相关编程的出现无疑带来了巨大的便利性,开发人员(我们)将关注点由类转移到了横切/切片上,而通过对切片的定义,可以轻松地将贯穿系统所有模块的代码集中在同一处,以Spring AOP为例,上述代码可以改成如下:
public void transferTo(Receiver receiver){
//执行交易
doTransftering(receiver);
}
public void receiveFrom(Client client){
//执行交易
doReceiving(client);
}
同时会有一个定义的切片来统一管理方法的执行,以Spring AOP基于@AspectJ表达式的切片为例:
@Aspect
public class MyAspectBlogDemo {
@Pointcut("execution(public **(..))")
private void anyPublicOperation() {}
@Around("anyPublicOperation()")
public Object doTransaction(ProceedingJoinPoint pjp) throws Throwable {
//开启事务
doOpenTransaction();
//执行交易
Object rst = pjp.proceed();
//关闭事务
doCloseTransaction();
return rst;
}
}
在上述涉及的方法只有两个transferTo(…) &receiveFrom(…)的场景下似乎代码量增加了,一个不了解切片式编程的开发可能会对这样的代码感到困惑。但是假使你要对100个方法进行事务管理呢?重复复制粘贴100次与一个切片管理对比,一目了然…
核心切片概念
以Spring AOP为例, AOP的实现无疑给JAVA带来了一系列新概念,下文会以Spring AOP为例,摘录官网描述的AOP概念并且在此基础上进行一定的补充:
Aspect: A modularization of a concern that cuts across multiple
classes.
切面(有些文档翻译为方面)为横切多个类的关注点集合,在JAVA语言场景下,个人将其理解为一个“能够管理不同类中的多个方法的特殊类”
Join point: A point during the execution of a program, such as the
execution of a method or the handling of an exception. In Spring AOP,
a join point always represents a method execution.
连接点,一个连接点对应一个方法执行
Advice: Action taken by an aspect at a particular join point.
Different types of advice include “around”, “before” and “after”
advice. (Advice types are discussed later.) Many AOP frameworks,
including Spring, model an advice as an interceptor and maintain a
chain of interceptors around the join point.
通知(奇怪的名字),一个连接点到达时执行的动作(或者说代码),通知在@AspectJ中以常规JAVA方法的形式定义(与注解配合使用),并且能够获取连接点处的程序栈信(比如连接点对应的方法的参数)
Pointcut: A predicate that matches join points. Advice is associated
with a pointcut expression and runs at any join point matched by the
pointcut (for example, the execution of a method with a certain name).
The concept of join points as matched by pointcut expressions is
central to AOP, and Spring uses the AspectJ pointcut expression
language by default.
切点,匹配连接点的断言,个人将其理解为“一个能够定义连接点的表达式”
Introduction: Declaring additional methods or fields on behalf of a
type. Spring AOP lets you introduce new interfaces (and a
corresponding implementation) to any advised object. For example, you
could use an introduction to make a bean implement an IsModified
interface, to simplify caching. (An introduction is known as an
inter-type declaration in the AspectJ community.)
类型间声明,允许被通知的类(类中的方法与定义切点时候的表达式匹配)实现额外的接口,需要注意的一点是在Spring AOP中,目标对象(target object)与 代理对象(proxy) 是不同的概念,类型间声明影响的是代理类而不是目标类。——简单的来讲,一旦被切片管理,Spring容器中注册的单例对象由常规类变成了代理类,并且这些代理类通过类型间声明实现了特定的接口
Target object: An object being advised by one or more aspects. Also
referred to as the “advised object”. Since Spring AOP is implemented
by using runtime proxies, this object is always a proxied object.
目标对象,躲在代理对象之后,方法的调用通过代理对象来访问目标对象
AOP proxy: An object created by the AOP framework in order to
implement the aspect contracts (advise method executions and so on).
In the Spring Framework, an AOP proxy is a JDK dynamic proxy or a
CGLIB proxy.
代理对象,根据目标类是否实现接口来判断采用动态代理或者CGLIB的技术实现(你也可以在配置中强行指定CGLIB,用于切片管理非接口定义的方法 ——类独有的方法)
Weaving: linking aspects with other application types or objects to
create an advised object. This can be done at compile time (using the
AspectJ compiler, for example), load time, or at runtime. Spring AOP,
like other pure Java AOP frameworks, performs weaving at runtime.
编织,理解切片中的定义信息,通过这些信息将切片与目标类或对象进行连接,从而创建被通知对象(proxy),值得一提的是,不同于纯粹AspectJ 框架在编译时进行编织,Spring AOP的编织是在运行时通过创建代理类实现
总结
本文探讨了AOP的便利性与Spring AOP的核心概念,在后续文章中对这些知识点进行进一步对描述