Spring深入浅出AOP(上)

简介: 面向切面编程(AOP)对编程结构提供了另一种思考方式,给面向对象编程(OOP)进行了补充。OOP 模块化的关键单元是类(Class),而 AOP 的模块化关键单元是切面(Aspect),切面实现的模块可以横切多个类型和对象,例如事务管理。AOP 框架是 Spring 的关键组件之一,Spring IoC 容器不依赖于 AOP,AOP 给 Spring IoC 提供了一个强大的中间件解决方案。


一、概述



面向切面编程(AOP)对编程结构提供了另一种思考方式,给面向对象编程(OOP)进行了补充。OOP 模块化的关键单元是类(Class),而 AOP 的模块化关键单元是切面(Aspect),切面实现的模块可以横切多个类型和对象,例如事务管理。AOP 框架是 Spring 的关键组件之一,Spring IoC 容器不依赖于 AOP,AOP 给 Spring IoC 提供了一个强大的中间件解决方案。

微信图片111.png


1. 基本概念


下面是一些 Spring AOP 相关的术语,我们会针对这些概念去讲怎么实现 Spring AOP 相关编程。


1.1 相关术语

  • 切面(Aspect):切面横切多个类的模块,可以使用常规类基于 XML 实现,也可以使用@Aspect注解来实现。
  • 连接点(Join point):程序执行时的一个特定节点,例如一个方法的执行或者一个异常的处理。
  • 通知(Advice):切面在特定连接点执行动作,例如前置通知、后置通知、环绕通知等。
  • 切点(Pointcut):用于匹配连接点的描述,Pointcut 表达式来匹配哪些连接点需要被执行,而执行的场景和业务则通过通知来定义。
  • 目标对象(Target object):被一个或多个切面切入的对象,因为 Spring AOP 通过运行时代理实现,所以通常是代理对象。
  • 织入(Weaving):将切面与其他对象连接起来,并创建目标对象的过程,这个过程可以发生在类的编译期、加载期或运行期,Spring AOP 采用动态代理实现,在运行时期实现织入。


1.2 通知类型

  • 前置通知(Before advice):在连接点前面执行的通知,它不能终止连接点后续的执行流程,除非抛出一个异常。
  • 后置通知(After returning):在连接点正常返回时执行的通知,并且没有抛出异常。
  • 环绕通知(Around advice):围绕在连接点执行的通知,环绕通知是功能最强大的通知,它可以在方法调用的前面或后面添加自定义操作,还可以在方法执行返回时或抛出异常时添加操作。
  • 最终通知(After (finally) advice):在连接点退出时执行的通知,无论是正常退出还是异常退出。
  • 异常通知(After throwing advice):在方法抛出异常时执行的通知。

环绕通知的功能最强大,它也能实现其他通知的功能,但我们在一般情况下,使用前置或后置等特定通知就能完成的功能需求,就不要使用环绕通知,这样可以避免很多错误。


2. 特性与能力


Spring AOP 使用纯 Java 实现,它不是在编译期或类加载期织入,因此它适用于 Servlet 容器或者其他应用服务。目前只支持对方法进行拦截(Spring Bean 里的方法),不支持字段级的拦截功能。

Spring AOP 不同于其他的 AOP 框架,比如 AspectJ,因为它的目的不是提供完完全全的 AOP 实现(尽管 Spring AOP 已经很强大了),而是更好地结合 Spring IoC,来解决企业项目的大多数问题。


二、AOP 实现



Spring AOP 的实现需要定义三个部分,包括切面(Aspect)、切点(Pointcut)和通知(Advice),所以不管使用什么方式来实现 AOP 功能,都不能缺少其中一个部分。


1. 基于XML方式实现


在 Spring 中基于 XML 实现 AOP 功能时,主要使用标签来配置,它下面包含三个标签,包含切点()、Advisor()和切面()的配置,而且这三个标签的配置是有序的。


1.1 定义切面(Aspect)

基于 XML 方式实现定义一个切面,需要在 Spring 容器中定义一个普通的 Java Bean 对象,这个对象的字段和方法描述了所有的状态和行为,要实现一个完整的 AOP 功能,还需要在切面标签里面定义切点和通知。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        ...
    </aop:aspect>
</aop:config>
<bean id="aBean" class="...">
    ...
</bean>

在 Spring 的 XML 配置中,属性里面带有ref的都是需要配置引用,而不是直接配置值。


1.2 定义切点(Pointcut)


切点可以控制通知需要执行的范围,Spring 只支持对注册为 Bean 类的方法作为切入点,在标签内使用标签来定义切点。

<aop:config>
    <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/>
</aop:config>

也可以在标签内使用标签定义切点。

<aop:config>
    <aop:aspect id="myAspect" ref="aBean">
        <aop:pointcut id="businessService" expression="execution(* com.xyz.myapp.service.*.*(..))"/>
    </aop:aspect>
</aop:config>

定义切点表达式的方法我们在第五节会详细讲到。


1.3 定义通知(Advice)


Spring AOP 五种通知类型,分别是前置通知、后置通知、环绕通知、最终通知和异常通知,在可以分别定义。

<aop:aspect id="beforeExample" ref="aBean">
    <aop:pointcut id="myPointcut" expression="execution(* cn.codeartist.spring.aop.service.*.*(..))"/>
    <aop:before pointcut-ref="myPointcut" method="doBefore"/>
    <aop:after-returning pointcut-ref="myPointcut" method="doAfterReturning"/>
    <aop:around pointcut-ref="myPointcut" method="doAround"/>
    <aop:after pointcut-ref="myPointcut" method="doAfter"/>
    <aop:after-throwing pointcut-ref="myPointcut" method="doAfterThrowing"/>
</aop:aspect>

在配置通知的标签中,pointcut-ref属性配置已经定义好的切点。method属性定义需要执行的方法,这些方法在切面中定义的 Bean 中实现。


1.4 通过定义 Advisor 实现


在 Spring 中,Advisor 是只包含一个通知和切点的切面,切点的定义和之前一样,通知的定义通过注册 Bean 来实现,注入的类根据实现不同的Advice接口来注册不同的通知类型。

实现接口来定义通知

public class AopBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) {
        System.out.println("AopBeforeAdvice.before");
    }
}

配置 Advisor 实现 AOP

<aop:config>
    <aop:pointcut id="p" expression="execution(* cn.codeartist.spring.aop.service.*.*(..))"/>
    <aop:advisor advice-ref="aopBeforeAdvice" pointcut-ref="p"/>
</aop:config>
<bean id="aopBeforeAdvice" class="cn.codeartist.spring.aop.advice.AopBeforeAdvice"/>


2. 基于注解方式实现


Spring 基于注解实现 AOP 只需要将在普通 Java 类上添加注解就可以定义切面,而不需要在 XML 文件中进行配置,在前面我们讲 Spring IoC 的时候就说到过,Bean 的管理和注册也可以使用基于 XML 或基于注解的方式来实现,其原理是一样的,在使用注解实现之前,我们要启用 AspectJ 注解支持。

在 XML 配置中启用

在 Spring 的 XML 配置文件中添加标签来启用。

<aop:aspectj-autoproxy/>

在 Java 配置中启用

在 Spring 的配置类上添加@EnableAspectJAutoProxy注解来启用。

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}


2.1 定义切面(Aspect)


基于注解定义一个切面,只需要将一个在 Spring 容器中注册过的类上加上@Aspect注解即可。定义了切面的类和普通的类一样,也有字段和方法,同时,它也要包含切点和通知的定义。

在 Spring 容器注册对象

<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
    <!-- configure properties of the aspect here -->
</bean>

添加注解来实现切面

@Aspect
public class NotVeryUsefulAspect {
}


2.2 定义切点(Pointcut)


在配置为切面的类中,在方法上使用@Pointcut注解可以定义切点。

@Aspect
public class NotVeryUsefulAspect {
    @Pointcut("execution(* transfer(..))") // the pointcut expression
    private void anyOldTransfer() {} // the pointcut signature
}


2.3 定义通知(Advice)


Spring AOP 五种通知类型可以分别通过在方法上添加@Before@AfterReturning@Around@After@AfterThrowing注解来实现。

@Aspect
public class NotVeryUsefulAspect {
    @Pointcut("execution(* transfer(..))") // the pointcut expression
    private void anyOldTransfer() {} // the pointcut signature
    @Before("anyOldTransfer()")
    public void doAccessCheck() {
        // 每个通知的注解都需要指定切点,可以是已经定义的切点方法
    }
    @Before("execution(* com.xyz.myapp.dao.*.*(..)")
    public void doAccessCheck() {
        // 每个通知的注解都需要指定切点,可以直接编写切点表达式
    }
    @AfterReturning("anyOldTransfer()")
    public void doAccessCheck() {
        // ...
    }
    @AfterReturning(pointcut="anyOldTransfer()", returning="retVal")
    public void doAccessCheck(Object retVal) {
        // 通过returning属性来获取方法的返回值
    }
    @Around("anyOldTransfer()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        // start stopwatch
        Object retVal = pjp.proceed();
        // stop stopwatch
        return retVal;
    }
    @After("anyOldTransfer()")
    public void doReleaseLock() {
        // ...
    }
    @AfterThrowing("anyOldTransfer()")
    public void doRecoveryActions() {
        // ...
    }
    @AfterThrowing(pointcut="anyOldTransfer()", throwing="ex")
    public void doRecoveryActions(DataAccessException ex) {
        // 通过throwing属性来获取抛出的异常
    }
}

每个通知的注解都需要指定切点,可以是已经定义的切点方法,也可以直接编写切点表达式。@AfterReturning注解中的returning属性可以获取方法的实际返回值,@AfterThrowing注解中的throwing属性可以获取方法抛出的异常,它们都是通过和方法的参数名称来进行绑定的。


2.4 通过定义 Advisor 实现


和基于 XML 配置定义的方法一样,Advisor 是只包含一个通知和切点的切面,我们可以使用 Spring 提供的 Advisor 接口实现类,也可以自定义实现该接口。

@Bean
public AspectJExpressionPointcutAdvisor aspectjExpressionPointcutAdvisor() {
    AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
    advisor.setExpression("execution(* cn.codeartist.spring.aop.service.*.*(..))");
    advisor.setAdvice(new AopBeforeAdvice());
    return advisor;
}

例如上面我们使用AspectJExpressionPointcutAdvisor类来实现 AOP,这种方法可以利用 AOP 来封装一些自定义功能。

目录
相关文章
|
1月前
|
XML 安全 Java
使用 Spring 的 @Aspect 和 @Pointcut 注解简化面向方面的编程 (AOP)
面向方面编程(AOP)通过分离横切关注点,如日志、安全和事务,提升代码模块化与可维护性。Spring 提供了对 AOP 的强大支持,核心注解 `@Aspect` 和 `@Pointcut` 使得定义切面与切入点变得简洁直观。`@Aspect` 标记切面类,集中处理通用逻辑;`@Pointcut` 则通过表达式定义通知的应用位置,提高代码可读性与复用性。二者结合,使开发者能清晰划分业务逻辑与辅助功能,简化维护并提升系统灵活性。Spring AOP 借助代理机制实现运行时织入,与 Spring 容器无缝集成,支持依赖注入与声明式配置,是构建清晰、高内聚应用的理想选择。
339 0
|
12天前
|
XML Java 数据格式
《深入理解Spring》:AOP面向切面编程深度解析
Spring AOP通过代理模式实现面向切面编程,将日志、事务等横切关注点与业务逻辑分离。支持注解、XML和编程式配置,提供五种通知类型及丰富切点表达式,助力构建高内聚、低耦合的可维护系统。
|
5月前
|
监控 安全 Java
Spring AOP实现原理
本内容主要介绍了Spring AOP的核心概念、实现机制及代理生成流程。涵盖切面(Aspect)、连接点(Join Point)、通知(Advice)、切点(Pointcut)等关键概念,解析了JDK动态代理与CGLIB代理的原理及对比,并深入探讨了通知执行链路和责任链模式的应用。同时,详细分析了AspectJ注解驱动的AOP解析过程,包括切面识别、切点表达式匹配及通知适配为Advice的机制,帮助理解Spring AOP的工作原理与实现细节。
|
2月前
|
人工智能 监控 安全
Spring AOP切面编程颠覆传统!3大核心注解+5种通知类型,让业务代码纯净如初
本文介绍了AOP(面向切面编程)的基本概念、优势及其在Spring Boot中的使用。AOP作为OOP的补充,通过将横切关注点(如日志、安全、事务等)与业务逻辑分离,实现代码解耦,提升模块化程度、可维护性和灵活性。文章详细讲解了Spring AOP的核心概念,包括切面、切点、通知等,并提供了在Spring Boot中实现AOP的具体步骤和代码示例。此外,还列举了AOP在日志记录、性能监控、事务管理和安全控制等场景中的实际应用。通过本文,开发者可以快速掌握AOP编程思想及其实践技巧。
|
2月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。
|
2月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
9月前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
479 6
|
8月前
|
XML Java 测试技术
Spring AOP—通知类型 和 切入点表达式 万字详解(通俗易懂)
Spring 第五节 AOP——切入点表达式 万字详解!
431 25
|
8月前
|
XML 安全 Java
Spring AOP—深入动态代理 万字详解(通俗易懂)
Spring 第四节 AOP——动态代理 万字详解!
352 24
|
7月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
855 0

热门文章

最新文章