• Spring AOP 的底层实现(spring官网描述)


    1.AOP全称(Aspect Oriented Programming)

    面向切面编程的简称,而Spring AOP 只是aop其中一种实现的方式,这里我们着重看一下spring aop.

    1.1 AOP 使用场景

    1、AOP框架种类
    AspectJ
    JBoss AOP
    Spring AOP
    2、使用 AOP 场景
    性能监控:在方法调用前后记录调用事件,方法执行太长或超时报警。
    缓存代理:缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。
    软件破解:使用AOP修改软件的验证类的判断逻辑。
    记录日志:在方法执行前后记录系统日志。
    工作流系统:工作流系统需要将业务代码和流畅引擎代码混合在一起执行,那么可以使用 AOP 将其分离,并动态挂载业务。
    权限验证:方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕获。

    2.官网介绍: 

    在spring官网-Spring Framework-Core中, Aspect Oriented Programming with Spring这一节对spring aop做了详细描述如下: 

    2.1 spring中的面向切面(aspect)编程

    面向切面编程(AOP)通过提供另一种思考程序结构的方法,补充了面向对象编程(OOP)。面向对象的模块化的关键单位是类,而面向方面的模块化单位是方面。方面支持跨多个类型和对象的关注点(如事务管理)的模块化。(这种关注点在AOP文献中通常称为“横切”关注点。)

    Spring的关键组件之一是AOP框架。虽然Spring IoC容器不依赖于AOP(这意味着如果您不想使用AOP,则不需要使用),但AOP对Spring IoC进行了补充,从而提供了一个功能非常强大的中间件解决方案。

    AOP在Spring框架中被用于:

    1.提供声明式企业服务。最重要的服务是声明式事务管理。

    2.让用户实现自定义方面,用AOP来补充他们对OOP的使用。

    2.2 AOP概念

    切面(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在运行时执行编织。

    2.2.1 Spring AOP包括以下类型的通知:

    前置通知(Before advice) :在连接点之前运行的通知,但它不能够阻止执行流进行到连接点(除非它引发异常)。

    后置通知(After returning advice):在连接点正常完成后运行的通知(例如,如果方法返回而没有抛出异常)。

    抛出异常通知(After throwing advice): 如果方法通过抛出异常退出,则运行的建议

    最终通知(After (finally) advice): 不管连接点退出的方式是什么(正常或异常返回),都要运行的通知

    环绕通知(Around advice): 围绕连接点(如方法调用)的通知。这是最有力的建议。Around通知可以在方法调用之前和之后执行自定义行为。它还负责选择是继续到连接点,还是通过返回自己的返回值或抛出异常来缩短建议的方法执行。

    2.3 Spring AOP 的功能和目标

    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应用中的大多数问题提供了一个优秀的解决方案

    2.4  AOP代理(AOP Proxies)

    Spring AOP默认为AOP代理使用标准JDK动态代理。这允许代理任何接口(或接口集)

    Spring AOP也可以使用CGLIB代理。这对于代理类而不是代理接口是必要的。默认情况下,如果业务对象没有实现接口,则使用CGLIB。由于针对接口而不是类编程是一种好的实践,业务类通常实现一个或多个业务接口。强制使用CGLIB是可能的,在这些情况下(希望很少发生),您需要通知一个没有在接口上声明的方法,或者需要将代理对象作为具体类型传递给方法

    2.5 @AspectJ支持(@AspectJ support)

    @AspectJ是一种将方面声明为使用注释注释的常规Java类的风格。@AspectJ样式是由AspectJ项目作为AspectJ 5版本的一部分引入的。Spring解释与AspectJ 5相同的注释,使用AspectJ提供的用于切入点解析和匹配的库。AOP运行时仍然是纯Spring AOP,并且不依赖于AspectJ编译器或编织器。

    2.5.1 使用Java配置启用@AspectJ支持

    使用Java @Configuration启用@AspectJ支持:

    1. @Configuration
    2. @EnableAspectJAutoProxy
    3. public class AppConfig {
    4. }

    2.5.2 声明一个切面(Declaring an Aspect)

    启用了@AspectJ支持后,在您的应用程序上下文中定义的任何带有@AspectJ方面(具有@Aspect注释)类的bean都将被Spring自动检测并用于配置Spring AOP

    1. package org.xyz;
    2. import org.aspectj.lang.annotation.Aspect;
    3. @Aspect
    4. public class NotVeryUsefulAspect {
    5. }

    2.5.3 声明一个切入点(Declaring a Pointcut)

    下面的示例定义了一个名为anyOldTransfer的切入点,它与任何名为transfer的方法的执行相匹配:

    1. @Pointcut("execution(* transfer(..))") // the pointcut expression
    2. private void anyOldTransfer() {} // the pointcut signature

    2.5.4 声明通知(Declaring Advice)

     前置通知(Before Advice): 你可以使用@Before注释在方面的advice之前声明:

    1. import org.aspectj.lang.annotation.Aspect;
    2. import org.aspectj.lang.annotation.Before;
    3. @Aspect
    4. public class BeforeExample {
    5. @Before("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    6. public void doAccessCheck() {
    7. // ...
    8. }
    9. }

    后置通知(After Returning Advice): 

    1. import org.aspectj.lang.annotation.Aspect;
    2. import org.aspectj.lang.annotation.AfterReturning;
    3. @Aspect
    4. public class AfterReturningExample {
    5. @AfterReturning("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    6. public void doAccessCheck() {
    7. // ...
    8. }
    9. }

    抛出异常后通知(After Throwing Advice):

    1. import org.aspectj.lang.annotation.Aspect;
    2. import org.aspectj.lang.annotation.AfterThrowing;
    3. @Aspect
    4. public class AfterThrowingExample {
    5. @AfterThrowing("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    6. public void doRecoveryActions() {
    7. // ...
    8. }
    9. }

    最终通知(After (Finally) Advice )

    1. import org.aspectj.lang.annotation.Aspect;
    2. import org.aspectj.lang.annotation.After;
    3. @Aspect
    4. public class AfterFinallyExample {
    5. @After("com.xyz.myapp.CommonPointcuts.dataAccessOperation()")
    6. public void doReleaseLock() {
    7. // ...
    8. }
    9. }

    2.6 通知参数(Advice Parameters)

    访问当前连接点(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. 打印被建议的方法的有用描述

    2.7 多个切点的执行顺序

    1. @Aspect
    2. public class ConcurrentOperationExecutor implements Ordered {
    3. private static final int DEFAULT_MAX_RETRIES = 2;
    4. private int maxRetries = DEFAULT_MAX_RETRIES;
    5. private int order = 1;
    6. public void setMaxRetries(int maxRetries) {
    7. this.maxRetries = maxRetries;
    8. }
    9. public int getOrder() {
    10. return this.order;
    11. }
    12. public void setOrder(int order) {
    13. this.order = order;
    14. }
    15. @Around("com.xyz.myapp.CommonPointcuts.businessService()")
    16. public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
    17. int numAttempts = 0;
    18. PessimisticLockingFailureException lockFailureException;
    19. do {
    20. numAttempts++;
    21. try {
    22. return pjp.proceed();
    23. }
    24. catch(PessimisticLockingFailureException ex) {
    25. lockFailureException = ex;
    26. }
    27. } while(numAttempts <= this.maxRetries);
    28. throw lockFailureException;
    29. }
    30. }

    上面的例子中,定义了order=3,重新创建一个切面,定义order=6,执行的结果是:

    1. @Around:进入方法---环绕通知--order=3
    2. @Before:开始添加--order=3
    3. @Around:进入方法---环绕通知--order=6
    4. @Before:开始添加--order=6
    5. ============执行业务方法findUser,查找的用户是:张三=============
    6. @Around:退出方法---环绕通知--order=6
    7. @After:最终通知--order=6
    8. @AfterReturning:后置通知--order=6
    9. ---张三---
    10. @Around:退出方法---环绕通知--order=3
    11. @After:最终通知--order=3
    12. @AfterReturning:后置通知--order=3
    13. ---张三---
    14. @Around:进入方法---环绕通知--order=3
    15. @Before:开始添加--order=3
    16. @Around:进入方法---环绕通知--order=6
    17. @Before:开始添加--order=6
    18. ============执行业务方法addUser=============
    19. @After:最终通知--order=6
    20. @AfterThrowing:例外通知--order=6
    21. null
    22. @After:最终通知--order=3
    23. @AfterThrowing:例外通知--order=3
    24. null

     

    3.Spring AOP 代理机制( Proxying Mechanisms)

    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创建动态代理。
     

  • 相关阅读:
    Linux 的CPU分析
    一文看懂推荐系统:召回02:Swing 模型,和itemCF很相似,区别在于计算相似度的方法不一样
    MongoDB数据库的安装和使用
    低代码平台全解析:衍生历程、优势呈现与未来趋势一览无余
    springboot+vue“漫画之家”在线漫画周边销售购物交流系统#毕业设计
    如何保障汽车嵌入式软件的质量与安全?您需要了解ASPICE标准
    Nacos配置管理
    大模型的开源不同于传统的开源软件
    Something is wrong with your installed virtualenv version: 15.1.0
    OpenGL ES EGL eglGetDisplay
  • 原文地址:https://blog.csdn.net/footbridge/article/details/127732021