面向切面编程的简称,而Spring AOP 只是aop其中一种实现的方式,这里我们着重看一下spring aop.
1、AOP框架种类
AspectJ
JBoss AOP
Spring AOP
2、使用 AOP 场景
性能监控:在方法调用前后记录调用事件,方法执行太长或超时报警。
缓存代理:缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。
软件破解:使用AOP修改软件的验证类的判断逻辑。
记录日志:在方法执行前后记录系统日志。
工作流系统:工作流系统需要将业务代码和流畅引擎代码混合在一起执行,那么可以使用 AOP 将其分离,并动态挂载业务。
权限验证:方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕获。
在spring官网-Spring Framework-Core中, Aspect Oriented Programming with Spring这一节对spring aop做了详细描述如下:

面向切面编程(AOP)通过提供另一种思考程序结构的方法,补充了面向对象编程(OOP)。面向对象的模块化的关键单位是类,而面向方面的模块化单位是方面。方面支持跨多个类型和对象的关注点(如事务管理)的模块化。(这种关注点在AOP文献中通常称为“横切”关注点。)
Spring的关键组件之一是AOP框架。虽然Spring IoC容器不依赖于AOP(这意味着如果您不想使用AOP,则不需要使用),但AOP对Spring IoC进行了补充,从而提供了一个功能非常强大的中间件解决方案。
AOP在Spring框架中被用于:
1.提供声明式企业服务。最重要的服务是声明式事务管理。
2.让用户实现自定义方面,用AOP来补充他们对OOP的使用。
切面(Aspect): 跨越多个类的关注点的模块化。事务管理是企业Java应用程序中横切关注点的一个很好的例子。在Spring AOP中,方面是通过使用常规类(基于模式的方法)或使用@Aspect注释(@AspectJ风格)注释的常规类实现的。
连接点(Join point): 程序执行期间的一个点,如方法的执行或异常的处理。在Spring AOP中,连接点总是表示一个方法执行。
通知(Advice):方面在特定连接点上采取的动作。不同类型的建议包括“大约”、“之前”和“之后”的建议。(通知类型将在后面讨论。)许多AOP框架(包括Spring)将通知建模为拦截器,并维护连接点周围的拦截器链
切点(Pointcut):匹配连接点的谓词。通知与切入点表达式相关联,并在与切入点匹配的任何连接点上运行(例如,具有特定名称的方法的执行)。由切入点表达式匹配的连接点的概念是AOP的核心,Spring默认使用AspectJ切入点表达式语言。
引入(Introduction): 代表类型声明额外的方法或字段。Spring AOP允许您向任何被建议的对象引入新的接口(和相应的实现)。例如,您可以使用介绍使bean实现IsModified接口,以简化缓存。(在AspectJ社区中,引入称为类型间声明。)
目标对象(Target object):由一个或多个方面通知的对象。也称为“建议对象”。因为Spring AOP是通过使用运行时代理实现的,所以这个对象总是一个代理对象。
AOP代理(AOP proxy):AOP框架为实现方面契约(通知方法执行等)而创建的对象。在Spring框架中,AOP代理是JDK动态代理或CGLIB代理
织入(Weaving) :将方面与其他应用程序类型或对象链接,以创建被建议的对象。这可以在编译时(例如,使用AspectJ编译器)、加载时或运行时完成。与其他纯Java AOP框架一样,Spring AOP在运行时执行编织。
前置通知(Before advice) :在连接点之前运行的通知,但它不能够阻止执行流进行到连接点(除非它引发异常)。
后置通知(After returning advice):在连接点正常完成后运行的通知(例如,如果方法返回而没有抛出异常)。
抛出异常通知(After throwing advice): 如果方法通过抛出异常退出,则运行的建议
最终通知(After (finally) advice): 不管连接点退出的方式是什么(正常或异常返回),都要运行的通知
环绕通知(Around advice): 围绕连接点(如方法调用)的通知。这是最有力的建议。Around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点,还是通过返回自己的返回值或抛出异常来缩短建议的方法执行。
Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类装入器层次结构,因此适合在servlet容器或应用程序服务器中使用。
Spring AOP目前只支持方法执行连接点(建议在Spring bean上执行方法)。没有实现字段拦截,尽管可以在不破坏核心Spring AOP api的情况下添加对字段拦截的支持。如果您需要通知字段访问和更新连接点,可以考虑使用AspectJ这样的语言。
Spring AOP的AOP方法不同于大多数其他AOP框架。其目的不是提供最完整的AOP实现(尽管Spring AOP相当有能力)。相反,其目的是提供AOP实现和Spring IoC之间的紧密集成,以帮助解决企业应用程序中的常见问题。
Spring框架的AOP功能通常与Spring IoC容器一起使用。方面是通过使用普通的bean定义语法来配置的(尽管这允许强大的“自动代理”功能)。这是与其他AOP实现的一个关键区别。使用Spring AOP,您无法轻松或有效地做一些事情,例如通知非常细粒度的对象(通常是域对象)。在这种情况下,AspectJ是最佳选择。然而,我们的经验是Spring AOP为企业Java应用中的大多数问题提供了一个优秀的解决方案
Spring AOP默认为AOP代理使用标准JDK动态代理。这允许代理任何接口(或接口集)
Spring AOP也可以使用CGLIB代理。这对于代理类而不是代理接口是必要的。默认情况下,如果业务对象没有实现接口,则使用CGLIB。由于针对接口而不是类编程是一种好的实践,业务类通常实现一个或多个业务接口。强制使用CGLIB是可能的,在这些情况下(希望很少发生),您需要通知一个没有在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法
@AspectJ是一种将方面声明为使用注释注释的常规Java类的风格。@AspectJ样式是由AspectJ项目作为AspectJ 5版本的一部分引入的。Spring解释与AspectJ 5相同的注释,使用AspectJ提供的用于切入点解析和匹配的库。AOP运行时仍然是纯Spring AOP,并且不依赖于AspectJ编译器或编织器。
使用Java @Configuration启用@AspectJ支持:
- @Configuration
- @EnableAspectJAutoProxy
- public class AppConfig {
-
- }
启用了@AspectJ支持后,在您的应用程序上下文中定义的任何带有@AspectJ方面(具有@Aspect注释)类的bean都将被Spring自动检测并用于配置Spring AOP
- package org.xyz;
- import org.aspectj.lang.annotation.Aspect;
-
- @Aspect
- public class NotVeryUsefulAspect {
-
- }
下面的示例定义了一个名为anyOldTransfer的切入点,它与任何名为transfer的方法的执行相匹配:
- @Pointcut("execution(* transfer(..))") // the pointcut expression
- private void anyOldTransfer() {} // the pointcut signature
前置通知(Before Advice): 你可以使用@Before注释在方面的advice之前声明:
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
-
- @Aspect
- public class BeforeExample {
-
- @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
- public void doAccessCheck() {
- // ...
- }
- }
后置通知(After Returning Advice):
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.AfterReturning;
-
- @Aspect
- public class AfterReturningExample {
-
- @AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
- public void doAccessCheck() {
- // ...
- }
- }
抛出异常后通知(After Throwing Advice):
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.AfterThrowing;
-
- @Aspect
- public class AfterThrowingExample {
-
- @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
- public void doRecoveryActions() {
- // ...
- }
- }
最终通知(After (Finally) Advice )
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.After;
-
- @Aspect
- public class AfterFinallyExample {
-
- @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
- public void doReleaseLock() {
- // ...
- }
- }
访问当前连接点(Access to the Current JoinPoint)
JoinPoint接口提供了许多有用的方法:
getArgs(): Returns the method arguments.返回方法参数
getThis(): Returns the proxy object.返回代理对象
getTarget(): Returns the target object.返回目标对象
getSignature(): Returns a description of the method that is being advised.返回被建议的方法的描述
toString(): Prints a useful description of the method being advised. 打印被建议的方法的有用描述
- @Aspect
- public class ConcurrentOperationExecutor implements Ordered {
-
- private static final int DEFAULT_MAX_RETRIES = 2;
-
- private int maxRetries = DEFAULT_MAX_RETRIES;
- private int order = 1;
-
- public void setMaxRetries(int maxRetries) {
- this.maxRetries = maxRetries;
- }
-
- public int getOrder() {
- return this.order;
- }
-
- public void setOrder(int order) {
- this.order = order;
- }
-
- @Around("com.xyz.myapp.CommonPointcuts.businessService()")
- public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
- int numAttempts = 0;
- PessimisticLockingFailureException lockFailureException;
- do {
- numAttempts++;
- try {
- return pjp.proceed();
- }
- catch(PessimisticLockingFailureException ex) {
- lockFailureException = ex;
- }
- } while(numAttempts <= this.maxRetries);
- throw lockFailureException;
- }
- }
上面的例子中,定义了order=3,重新创建一个切面,定义order=6,执行的结果是:
- @Around:进入方法---环绕通知--order=3
- @Before:开始添加--order=3
- @Around:进入方法---环绕通知--order=6
- @Before:开始添加--order=6
- ============执行业务方法findUser,查找的用户是:张三=============
- @Around:退出方法---环绕通知--order=6
- @After:最终通知--order=6
- @AfterReturning:后置通知--order=6
- ---张三---
- @Around:退出方法---环绕通知--order=3
- @After:最终通知--order=3
- @AfterReturning:后置通知--order=3
- ---张三---
-
-
- @Around:进入方法---环绕通知--order=3
- @Before:开始添加--order=3
- @Around:进入方法---环绕通知--order=6
- @Before:开始添加--order=6
- ============执行业务方法addUser=============
- @After:最终通知--order=6
- @AfterThrowing:例外通知--order=6
- null
- @After:最终通知--order=3
- @AfterThrowing:例外通知--order=3
- null

Spring AOP使用JDK动态代理或CGLIB为给定的目标对象创建代理。JDK动态代理内置在JDK中,而CGLIB是一个通用的开源类定义库(重新打包到spring-core中)。
如果要代理的目标对象实现了至少一个接口,则使用JDK动态代理。目标类型实现的所有接口都是代理的。如果目标对象没有实现任何接口,则创建CGLIB代理
1、JDK 动态代理
JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。
Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
2、CGLib动态代理
CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。