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 来封装一些自定义功能。

目录
相关文章
|
5天前
|
XML Java 开发者
Spring Boot中的AOP实现
Spring AOP(面向切面编程)允许开发者在不修改原有业务逻辑的情况下增强功能,基于代理模式拦截和增强方法调用。Spring Boot通过集成Spring AOP和AspectJ简化了AOP的使用,只需添加依赖并定义切面类。关键概念包括切面、通知和切点。切面类使用`@Aspect`和`@Component`注解标注,通知定义切面行为,切点定义应用位置。Spring Boot自动检测并创建代理对象,支持JDK动态代理和CGLIB代理。通过源码分析可深入了解其实现细节,优化应用功能。
|
14天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
57 8
|
2月前
|
XML Java 数据安全/隐私保护
Spring Aop该如何使用
本文介绍了AOP(面向切面编程)的基本概念和术语,并通过具体业务场景演示了如何在Spring框架中使用Spring AOP。文章详细解释了切面、连接点、通知、切点等关键术语,并提供了完整的示例代码,帮助读者轻松理解和应用Spring AOP。
Spring Aop该如何使用
|
2月前
|
监控 安全 Java
什么是AOP?如何与Spring Boot一起使用?
什么是AOP?如何与Spring Boot一起使用?
96 5
|
2月前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
91 8
|
2月前
|
Java 开发者 Spring
Spring AOP 底层原理技术分享
Spring AOP(面向切面编程)是Spring框架中一个强大的功能,它允许开发者在不修改业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理等。本文将深入探讨Spring AOP的底层原理,包括其核心概念、实现方式以及如何与Spring框架协同工作。
|
2月前
|
XML 监控 安全
深入调查研究Spring AOP
【11月更文挑战第15天】
53 5
|
2月前
|
Java 开发者 Spring
Spring AOP深度解析:探秘动态代理与增强逻辑
Spring框架中的AOP(Aspect-Oriented Programming,面向切面编程)功能为开发者提供了一种强大的工具,用以将横切关注点(如日志、事务管理等)与业务逻辑分离。本文将深入探讨Spring AOP的底层原理,包括动态代理机制和增强逻辑的实现。
59 4
|
3月前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
62 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
2月前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
51 1