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

目录
相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
15天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
27 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
4月前
|
Java Spring
在Spring Boot中使用AOP实现日志切面
在Spring Boot中使用AOP实现日志切面
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
26天前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
44 2
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
114 9
|
25天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
41 0
|
2月前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
3月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
41 0
Spring高手之路22——AOP切面类的封装与解析