Spring Aop 详解(上)

简介: 面向切面编程(AOP)是面向对象编程(OOP)的补充,它提供了另一种关于程序结构的思考方式。OOP中模块化的关键单元是类,而在AOP中,模块化单元是切面。切面支持跨多个类型和对象的切点(如事务管理)的模块化。Spring AOP 是 Spring 框架的关键组件之一。Spring IOC 容器不依赖于AOP组件,如果不要我们项目中不需要 AOP 功能那么就可以不加载这个模块。AOP 补充了 Spring IOC,以提供一个非常强大的中间件解决方案。

Spring Aop


Spring Aop 核心概念


  1. Aspect: 切面是包含了多个切点,事务管理器(Transaction management)就是一个很好的例子,通常在 spring aop 中通过 @Aspect 来申明


  1. Join point: 连接点,表示具体的方式执行的一个点,异常处理或者方法执行。


  1. Advice: 通知,在特定的连接点才去操作,比如在通知之前,通知之后,通知过程中增加自己的逻辑。


  1. Pointcut: 切点,表示我们想再那个具体的方法,那个具体或者匹配一类 "select" 开头的方法。这里支持 EL 表达式进行匹配。


  1. Introduction: 引入,表示 AOP 可以在代理过程中,将一个已有的类引入新的接口。


  1. Target object: 当前对象,其实也是原始对象,如果发生 AOP 代理的时候,此时返回的始终是 AOP 代理对象。


  1. AOP proxy: AOP 创建的代理对象。


  1. Weaving: 切面植入时机,可以是运行时,运行前,运行后。


Spring AOP 包括以下几个类型的通知(Advice):


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


后置通知:在连接点正常完成之后要运行的通知(例如,如果方法返回而没有引发异常)。


异常通知:如果方法因引发异常而退出,则运行通知。


建议(最终)之后:无论连接点退出的方式如何(正常或特殊收益),均应执行通知。

环绕通知:围绕连接点的通知,例如方法调用。这是最有力的通知。环绕通知可以在方法调用之前和之后执行自定义行为。它还负责选择是返回连接点还是通过返回其自身的返回值或引发异常来结束方法的执行


环绕通知是最通用的通知。由于 Spring AOP 与 AspectJ 一样,提供了各种通知类型,建议使用功能最弱的建议类型,以实现所需的行为。例如,如果您只需要使用方法的返回值更新缓存,则最好使用后置通知而不是环绕通知,尽管环绕通知可以完成相同的事情。使用最具体的通知类型可以提供更简单的编程模型,并减少出错的可能性。例如,您不需要调用用于around通知的proceed() 方法JoinPoint,因此不会导致主业务失败。


所有建议参数都是具体的类型,因此您可以使用适当类型(例如,方法执行的返回值的类型)而不是Object数组的建议参数。


切入点匹配的连接点的概念是 AOP 的关键,它与仅提供拦截功能的旧技术有所不同。切入点使建议的目标独立于面向对象的层次结构。例如,可以将提供声明性事务管理的环绕建议应用于跨越多个对象(例如服务层中的所有业务操作)的一组方法。


Spring Aop 实现概述


Spring AOP默认将标准JDK动态代理用于AOP代理。这使得可以代理任何接口(或一组接口)。


Spring AOP也可以使用CGLIB代理。这对于代理类而不是接口是必需的。默认情况下,如果业务对象未实现接口,则使用CGLIB。由于最好是对接口而不是对类进行编程,因此业务类通常实现一个或多个业务接口。在某些情况下(可能极少数),当您需要建议未在接口上声明的方法或需要将代理对象作为具体类型传递给方法时,可以强制使用 CGLIB。


总结: Spring AOP 默认是采用 JDK 动态代理,通常情况下,如果一个类没有实现接口那么就是用 GCLIB 代理


Spring Aop 常用注解


1. 启动 @AspectJ支持


要在Spring配置中使用@AspectJ切面,您需要启用Spring支持以基于@AspectJ方面配置Spring AOP,并基于这些切面是否触发通知对Bean进行自动代理。通过自动代理,我们的意思是,如果Spring确定一个或多个切面通知使用bean,它将自动为该bean生成一个代理以拦截方法调用并确保按需运行建议。


可以使用XML或Java样式的配置来启用@AspectJ支持。无论哪种情况,您都需要确保AspectJ的aspectjweaver.jar库位于应用程序的类路径(版本1.8或更高版本)上。

代码例子:


@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}


2. 声明一个切面


启用@AspectJ支持后,@Aspect Spring会自动检测到在应用程序上下文中使用@AspectJ方面(具有注释)的类定义的任何bean,并用于配置Spring AOP。下面是一个声明切面的例子


代码例子:


@Aspect
public class LogAspect {
}


3. 声明一个切入点


切入点确定了拦截的连接点,从而使我们能够控制运行建议的时间。Spring AOP仅支持Spring Bean的方法执行连接点,因此您可以将切入点视为与Spring Bean上的方法执行相匹配。切入点声明由两部分组成:一个包含名称和任何参数的签名,以及一个切入点表达式,该切入点表达式精确地确定我们感兴趣的方法执行。在AOP的@AspectJ批注样式中,常规方法定义提供了切入点签名。 并通过使用@Pointcut 注解定义切入点表达式(用作切入点签名的方法必须具有void返回类型)。


一个例子:


@Pointcut("execution(* cn.edu.xxx.service.*.*(..))")
public void serviceOperation() {
}


Spring AOP支持以下在切入点表达式中使用的AspectJ切入点指示符:


  • execution:用于匹配方法执行的连接点。这是使用Spring AOP时要使用的主要切入点指示符。


  • within:将匹配限制为某些类型内的连接点(使用Spring AOP时,在匹配类型内声明的方法的执行)。


  • this:限制匹配到连接点(使用Spring AOP时方法的执行)的匹配,其中bean引用(Spring AOP代理)是给定类型的实例。


  • target:在目标对象(代理的应用程序对象)是给定类型的实例的情况下,将匹配限制为连接点(使用Spring AOP时方法的执行)。


  • args:将匹配限制为连接点(使用Spring AOP时方法的执行),其中参数是给定类型的实例。


  • @target:在执行对象的类具有给定类型的注释的情况下,将匹配限制为连接点(使用Spring AOP时方法的执行)。


  • @args:限制匹配的连接点(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。


  • @within:将匹配限制为具有给定注释的类型内的连接点(使用Spring AOP时,使用给定注释的类型中声明的方法的执行)。


  • @annotation:将匹配点限制为连接点的主题(在Spring AOP中运行的方法)具有给定注释的连接点。


4. 定义通知


议与切入点表达式关联,并且在切入点匹配的方法执行之前,之后或周围运行。切入点表达式可以是对命名切入点的简单引用,也可以是就地声明的切入点表达式。


在同一个定义的通知方法@Aspect的类,需要在同一连接点运行基于分配优先级上按以下顺序他们的通知类型,从最高到最低的优先级:@Around@Before@After@AfterReturning@AfterThrowing。但是请注意,由于Spring的实现风格,在同一方面中的any或advice方法之后AspectJAfterAdvice@Afteradvice方法将被有效地调用。@AfterReturning@AfterThrowing


当在同一类中@After定义的两个相同类型的建议(例如,两个建议方法)@Aspect都需要在同一连接点上运行时,其顺序是不确定的(因为无法通过以下方式检索源代码声明顺序) Javac编译类的反射)。请考虑将此类建议方法折叠为每个@Aspect类中每个连接点的一个建议方法,或将这些建议重构为单独的@Aspect类,您可以通过Ordered或 在方面级别进行排序@Order


定义通知的例子:


@Before("cn.edu.cqvie.aspect.LogAspect.serviceOperation()")
public void doServiceCheck() {
    System.out.println("doServiceCheck .....");
}
@After("cn.edu.cqvie.aspect.LogAspect.serviceOperation()")
public void doReleaseLock() {
    System.out.println("doReleaseLock .....");
}
@AfterReturning(
        pointcut="cn.edu.cqvie.aspect.LogAspect.serviceOperation()",
        returning="retVal")
public void doServiceCheck(Object retVal) {
    System.out.println("doServiceCheck ....." + retVal);
}


5. 引入


简介(在AspectJ中称为类型间声明)使方面可以声明建议对象实现给定的接口,并代表那些对象提供该接口的实现。 您可以使用 @DeclareParents 进行引入。此批注用于声明匹配类型具有新的父类(因此具有名称)。


Spring Aop 核心原理


前面我们分析了 Spring AOP 的使用方法和基础概念,下面我们来继续分析一下 Spring AOP 的原理。


AnnotationAwareAspectJAutoProxyCreator  AOP 后置处理器


Spring 再初始化 bean 的过程中,完成初始化过后,就执行 AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization 来执行代理,所以,代理的时机是在 Spring 初始化完成之后,并且在将 Bean 完整对象放入 Spring Bean 容器之前其目的就是将 Bean 的原始对象 BeanWapper 转换为一个代理对象。执行的过程如下:


  1. 先判断当前的 bean 是不是要进行AOP,比如当前的Bean的类型是 Pointcut, Advice, Advisor 等那些就不需要 AOP。


  1. 如果匹配到 Advisors 不为 null, 那么进行代理并且返回代理对象。


  1. 判断代理实现如果实现如果没有设置为 GCLIB 并且实现了接口,那么就采用 JDK 代理,否则使用 GCLIB。


  1. 执行代理的代码。


ProxyFactory 代理工厂


ProxyFactory 是一个代理工厂类,我们在使用 AOP 代理之前首先需要通过代理工厂获取一个具体的 AOP 代理对象 AopProxy 的实例,Spring 的源码如下:


public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    // config 就是 ProxyFactory 对象
    // optimize为 true,或 proxyTargetClass 为 true,或用户没有给 ProxyFactory 对象添加 interface
    if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("TargetSource cannot determine target class: " +
                    "Either an interface or a target is required for proxy creation.");
        }
        // targetClass是接口,直接使用Jdk动态代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        // 使用Cglib
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        // 使用Jdk动态代理
        return new JdkDynamicAopProxy(config);
    }
}


在这里我们需要关注两个类 JdkDynamicAopProxy, ObjenesisCglibAopProxy


相关文章
|
1月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
23天前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
46 5
|
27天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
53 8
|
27天前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
27天前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
41 5
|
27天前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
39 4
|
2月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
50 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
1月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
37 1
|
3月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
1月前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
40 0