spring aop的基本应用


前言

Spring AOP,即Aspect-Oriented Programming的缩写,意为面向切面编程,是Spring框架中的一个重要内容。它是一种编程范式,用于在不修改源代码的情况下向现有应用程序添加新功能。


一、基本概念

  • 切面(Aspect):切面是AOP的核心概念,它定义了在何时、何地以及以何种方式执行通知(Advice)。切面由通知和切入点(Pointcut)共同组成,用于描述在哪些连接点(JoinPoint)上执行哪些增强(Advice)操作。
  • 连接点(JoinPoint):连接点是程序执行过程中的一个点,如方法的执行或异常的抛出等。在Spring AOP中,连接点通常指的是方法的执行点。
  • 切入点(Pointcut):切入点用于定义通知将要应用的具体连接点。它是一组连接点的集合,通过表达式(如正则表达式或AspectJ表达式)来指定哪些连接点应该被增强。
  • 通知(Advice):通知定义了切面要完成的工作以及何时执行这些工作。它是增强功能的实际代码,可以在连接点之前、之后或抛出异常时执行。Spring AOP支持多种类型的通知,包括前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。
  • 目标对象(Target):目标对象是被增强的对象,它包含主业务逻辑。在AOP中,目标对象不会直接执行其方法,而是通过代理对象来执行,代理对象会在目标方法执行前后插入增强代码。
  • 代理对象(Proxy):代理对象是Spring AOP通过动态代理技术为目标对象创建的一个对象。代理对象会在目标方法执行前后插入通知代码,从而实现增强功能。根据目标对象是否实现接口,Spring AOP会使用JDK动态代理或CGLIB代理来生成代理对象。
  • 织入(Weaving):织入是将切面应用到目标对象以创建代理对象的过程。在Spring AOP中,织入通常发生在运行时,通过动态代理技术实现。
  • 在这里插入图片描述

二、Pointcut切点表达式

1. 通配符

通配符说明
*匹配任何数量字符。在类型模式中,它通常用于匹配任何类型或类名的一部分。例如,*Service 可以匹配任何以 Service 结尾的类名。
. .匹配任何数量字符的重复。在类型模式中,它用于匹配任何数量的子包或类名中的任意部分。例如,com.example…* 可以匹配 com.example 包及其所有子包下的任何类。在方法参数模式中,它用于匹配任何数量的参数
+匹配指定类型的子类型。这个通配符仅能作为后缀放在类型模式后边。例如,java.lang.Number+ 匹配 java.lang 包下的任何 Number 的子类型,如 Integer、Double 等。

示例:

  • java.lang.String:精确匹配 String 类型。
  • java.*.String:匹配 java 包下的任何一级子包中的 String 类型,但不包括更深层次的子包,如 java.lang.String 匹配,但 java.lang.ss.String 不匹配。
  • java…*:匹配 java 包及其所有子包下的任何类型,如 java.lang.String、java.util.List 等。
  • java.lang.*ing:匹配 java.lang 包下所有以 ing 结尾的类型。

2、execution 表达式

execution 是最常用的切点指示器,用于匹配方法执行的连接点。其语法如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
表达式说明
modifiers-pattern?:方法的修饰符(如 public、protected)模式,可选。
ret-type-pattern:返回类型模式。
declaring-type-pattern?:声明类型模式(即类名),可选,表示方法所在的类。
name-pattern(param-pattern):方法名(参数模式)。
throws-pattern?:抛出的异常类型模式,可选。

示例:

// 匹配 com.example.service 包下所有类的所有方法执行。
@Pointcut("execution(* com.example.service.*.*(..))")  
public void serviceLayerExecution() {}

3、within 表达式

within 指示器用于匹配指定类型内所有方法的执行

// 匹配 com.example.service 包及其子包下所有类的所有方法执行。
@Pointcut("within(com.example.service..*)")  
public void withinServicePackage() {}

4、this 和 target 表达式

this 和 target 指示器分别用于匹配当前 AOP 代理对象的引用和代理对象的目标对象的引用。

@Pointcut("this(com.example.MyType)")  
public void thisIsMyType() {}  
  
@Pointcut("target(com.example.MyType)")  
public void targetIsMyType() {}

5、args 表达式

args 指示器用于匹配运行时方法参数为指定类型的连接点。

// 匹配所有方法,其参数列表中包含至少一个 java.io.Serializable 类型的参数。
@Pointcut("args(java.io.Serializable)")  
public void methodWithSerializableParam() {}

6、@annotation 表达式

@annotation 指示器用于匹配那些方法上标注了特定注解的连接点。

// 匹配所有标注了 @Service 注解的方法
@Pointcut("@annotation(org.springframework.stereotype.Service)")  
public void serviceMethods() {}

7、组合表达式

切点表达式还可以使用逻辑运算符(如 &&、||、!)进行组合,以构建更复杂的切点表达式。

// 匹配 com.example.service 包下所有被 @Transactional 注解标注的方法。
@Pointcut("execution(* com.example.service.*.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")  
public void transactionalServiceMethods() {}

三、实现方式

Spring AOP提供了多种实现方式,包括基于代理的AOP、@AspectJ注解驱动的切面、纯POJO切面以及注入式AspectJ切面。其中,基于代理的AOP和@AspectJ注解驱动的切面是较为常用的方式。

1、基于代理的AOP(不推荐)

引入pom

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

定义UserService接口

public interface UserService {
    public String query();
}

实现类UserServiceImpl

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public String query() {
        return "查询用户信息!";
    }

}

定义切面

import org.springframework.aop.MethodBeforeAdvice;  
import java.lang.reflect.Method;  
  
public class MyBeforeAdvice implements MethodBeforeAdvice {  
  
    @Override  
    public void before(Method method, Object[] args, Object target) throws Throwable {  
        // 在目标方法执行之前执行的逻辑  
        System.out.println("Before method: " + method.getName());  
    }  
}

xml配置

<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans.xsd">  
  
    <!-- 定义目标 bean -->  
    <bean id="UserService" class="com.example.UserServiceImpl"/>  
  
    <!-- 定义 advice bean -->  
    <bean id="myBeforeAdvice" class="com.example.MyBeforeAdvice"/>  
  
    <!-- 配置代理 -->  
    <bean id="myServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
        <property name="target" ref="UserService"/>  
        <property name="interceptorNames">  
            <list>  
                <value>myBeforeAdvice</value>  
            </list>  
        </property>  
    </bean>  
</beans>

测试类

@Component
public class AspectTest implements ApplicationRunner {

    @Autowired
    private UserService userService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("AspectTest = " + userService.query());
    }
}

结果
在这里插入图片描述

2、@AspectJ注解驱动的切面

引入pom

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

定义UserService接口

public interface UserService {
    public String query();
}

实现类UserServiceImpl

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public String query() {
        return "查询用户信息!";
    }

}

定义切面


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    // 定义一个切入点表达式,匹配service包下所有类的所有方法
    @Pointcut("execution(* fly.aspect.UserService.*(..))")
    public void serviceCut() {
    }

    // 在切点处,方法执行前执行此通知
    @Before("serviceCut()")
    public void before(JoinPoint joinPoint) {
        System.out.println("Method is being before.");
    }

    // 在切点处,方法执行后执行此通知
    @After("serviceCut()")
    public void after(JoinPoint joinPoint) {
        System.out.println("Method is being after.");
    }

    // 环绕通知,环绕增强,相当于MethodInterceptor
    @Around("serviceCut()")
    public Object Around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Method is being Around before.");
        Object o = joinPoint.proceed();
        System.out.println("Method is being Around after result : " + o);
        return o;
    }

    // 后置返回通知
    @AfterReturning(returning = "result", pointcut = "serviceCut()")
    public void doAfterReturning(Object result) throws Throwable {
        System.out.println("Method is being Return : " + result);
    }

    // 后置异常通知
    @AfterThrowing("serviceCut()")
    public void throwing(JoinPoint jp) {
        System.out.println("Method is being throwing : ");
    }

}

测试类

@Component
public class AspectTest implements ApplicationRunner {

    @Autowired
    private UserService userService;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("AspectTest = " + userService.query());
    }
}

结果
在这里插入图片描述


总结

Spring AOP(Aspect-Oriented Programming,面向切面编程)在Spring框架中扮演着至关重要的角色。它的主要意义在于提供了一种强大的机制来模块化横切关注点(cross-cutting concerns),这些关注点通常与业务逻辑没有直接关联,但又是多个业务模块所共有的,比如日志记录、事务管理、安全控制、性能监控等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值