Spring05- AOP

Sprng AOP 其实就是代理,它可以在运行时动态地将代码切入到类的指定方法、指定位置上。即可以使用 AOP 在方法执行前或执行之后(批量)执行一些额外的操作。AOP 采用第三方库实现动态代理,可以以接口或父类的形式实现代理。

环境说明

  • JDK 17
  • Spring 6.0.6
  • Spring-aspects 6.0.6

环境准备

添加 Spring 和 spring-aspects 依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>6.0.6</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.6</version>
</dependency>

使用配置实现AOP

创建 spring 配置文件,引入 aop 命名空间并为其指定 xsd 规范文件(用于编写标签时给出提示,防止配置出错):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

然后需要明确哪个类的哪个方法需要被切入,在方法执行之前还是之后切入以及配置 spring 进行切入。

首先指定需要切入的方法为 Student 的 study() 方法,然后将 Student 类(目标 Bean)交由容器管理:

public class Student {
    public void study(){
        System.out.println(str + "室友还在打游戏,我狠狠的学Java,太爽了");
    }
}
<bean id="student" class="com.test.entity.Student"/>

创建一个新的类,并将要执行的操作封装为一个方法,同时这个类也要注册为 Bean:

public class StudentAOP {
    public void beforeStudy(){
        System.out.println("我和富二代考进了同一所大学,我们都有光明的未来!!!");
    }

    public void afterStudy() {
        System.out.println("为什么毕业了他们都继承家产,我还倒给他们打工,我努力的意义在哪里...");
    }
}
<bean id="studentAOP" class="com.test.entity.StudentAOP"/>

之后配置切入的相关信息:

<aop:config>
    <aop:pointcut id="test" expression="execution(* com.test.entity.Student.study(String))"/>
    <aop:aspect ref="studentAOP">
        <aop:before method="beforeStudy" pointcut-ref="test"/>
        <aop:after method="afterStudy" pointcut-ref="test"/>
    </aop:aspect>

</aop:config>

上述配置文件首先配置了一个切点,后续切入操作通过切点 id 进行切入,并且切点通过 expression 表达式来指定需要切入的方法(参考)这里使用 execution),格式:修饰符 包名.类名.方法名称(方法参数)

  • 修饰符:public、protected、private、包括返回值类型、static等等(使用*代表任意修饰符)
  • 包名:如com.test(* 代表全部,比如com.*代表com包下的全部包)
  • 类名:使用*也可以代表包下的所有类
  • 方法名称:可以使用*代表全部方法
  • 方法参数:填写对应的参数即可,比如(String, String),也可以使用*来代表任意一个参数,使用…代表所有参数。

接着指定将要将要切入的 Bean ,并通过 <aop:before >等标签添加切入方式。

如果需要在切入方法中获取目标方法的参数,可以为切入方法添加一个JoinPoint参数,通过该参数可以获取目标方法的参数数组:

public void study(String str){
    System.out.println(str + "室友还在打游戏,我狠狠的学Java,太爽了");
}
public void afterStudy(JoinPoint point) {
    System.out.println(point.getArgs()[0] + "为什么毕业了他们都继承家产,我还倒给他们打工,我努力的意义在哪里...");
}

注意需要同步修改目标方法的切点配置:

<aop:pointcut id="test" expression="execution(* com.test.entity.Student.study(String))"/>

环绕方法可以自定义目标方法执行前后需要执行的操作,相当于直接代理了目标方法:

public void around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("环绕方法开始前");
    joinPoint.proceed();
    System.out.println("参数0:" + joinPoint.getArgs()[0]);
    System.out.println("环绕方法开始后");
};
<aop:config>
    <aop:pointcut id="test" expression="execution(* com.test.entity.Student.study(String))"/>
    <aop:aspect ref="studentAOP">
        <aop:before method="beforeStudy" pointcut-ref="test"/>
        <aop:after method="afterStudy" pointcut-ref="test"/>
        <aop:around method="around" pointcut-ref="test"/>
    </aop:aspect>

</aop:config>

注意如果目标方法有返回值,那么环绕方法也需要有对应返回值。

AOP 中的术语:

  • 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理,也就是我们上面编写的方法实现。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出,实际上就是我们在方法执行前或是执行后需要做的内容。
  • 切点(PointCut): 可以插入增强处理的连接点,可以是方法执行之前也可以方法执行之后,还可以是抛出异常之类的。
  • 切面(Aspect): 切面是通知和切点的结合,我们之前在xml中定义的就是切面,包括很多信息。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,我们之前都是在将我们的增强处理添加到目标对象,也就是织入(这名字挺有文艺范的)

使用接口实现AOP

原理类似于动态代理,通过一个类实现 Advice 接口,并配置 aop:advisor 标签,可以实现在方法开始执行之前或是执行之后会调用我们实现的接口:

public class AOP implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) {
        System.out.println("通过Advice实现AOP");
    }

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
        System.out.println("After");
    }
}
<bean id="student" class="com.test.entity.Student"/>
<bean id="aop" class="com.test.entity.AOP"/>

<aop:config>
    <aop:pointcut id="test" expression="execution(* com.test.entity.Student.study())"/>
    <aop:advisor advice-ref="aop" pointcut-ref="test"/>
</aop:config>

通过实现 MethodInterceptor 接口可以实现类似于环绕方法的效果:

public class AOP implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕之前");
        invocation.proceed();
        System.out.println("环绕之后");
        return null;
    }
}

使用注解实现AOP

首先编写配置类主类:

@EnableAspectJAutoProxy
@ComponentScan("com.test.entity")
@Configuration
public class MainConfig {
}

然后通过@Component 注解注册实体类:

@Component
public class Student {
    public void study(){
        System.out.println("室友还在打游戏,我狠狠的学Java,太爽了");
    }
}

接着在定义AOP增强操作的类上添加@Aspect注解和@Component将其注册为Bean,对于增强方法,直接在方法之上添加对应注解即可实现 AOP, 同时也可以同xml方式一样给方法添加 JoinPoint 参数来获取目标方法的参数信息:

@Aspect
@Component
public class StudentAOP {

    @Before("execution(* com.test.entity.Student.study())")
    public void beforeStudy(){
        System.out.println("我和富二代考进了同一所大学,我们都有光明的未来!!!");
    }

    @After("execution(* com.test.entity.Student.study())")
    public void afterStudy() {
        System.out.println("为什么毕业了他们都继承家产,我还倒给他们打工,我努力的意义在哪里...");
    }

    @Around("execution(* com.test.entity.Student.study())")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕方法开始前");
        joinPoint.proceed();
        System.out.println("环绕方法开始后");
    };
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值