spring的AOP

原文地址:https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486938&idx=1&sn=c99ef0233f39a5ffc1b98c81e02dfcd4&chksm=cea24211f9d5cb07fa901183ba4d96187820713a72387788408040822ffb2ed575d28e953ce7&token=1736772241&lang=zh_CN#rd

什么是AOP

AOP(Aspect Oriented Programming 面向切面编程),AOP是一种设计思想,它本质是为了解耦。AOP是OOP(Object Oriented Programming 面
向对象编程)的一种延续。为什么这么说呢?

先看一个OOP的例子:
例如:现有三个类,HorsePigDog,这三个类中都有 eat()run() 两个方法。

通过 OOP 思想中的继承,我们可以提取出一个 Animal 的父类,然后将 eat()run() 方法放入父类中,HorsePigDog通过继承Animal类即可自动获得 eat() 和 run() 方法。这样将会少些很多重复的代码。

OOP 编程思想可以解决大部分的代码重复问题。但是有一些问题是处理不了的。比如在父类 Animal 中的多个方法的相同位置出现了重复的代码,OOP 就解决不了

/**
 * 动物父类
 */
public class Animal {

    /** 身高 */
    private String height;

    /** 体重 */
    private double weight;

    public void eat() {
        // 性能监控代码
        long start = System.currentTimeMillis();

        // 业务逻辑代码
        System.out.println("I can eat...");

        // 性能监控代码
        System.out.println("执行时长:" + (System.currentTimeMillis() - start)/1000f + "s");
    }

    public void run() {
        // 性能监控代码
        long start = System.currentTimeMillis();

        // 业务逻辑代码
        System.out.println("I can run...");

        // 性能监控代码
        System.out.println("执行时长:" + (System.currentTimeMillis() - start)/1000f + "s");
    }
}

这部分重复的代码,一般统称为 横切逻辑代码
在这里插入图片描述
横切逻辑代码存在的问题:

  • 代码重复问题
  • 横切逻辑代码和业务代码混杂在一起,代码臃肿,不便维护

AOP 就是用来解决这些问题的

AOP 另辟蹊径,提出横向抽取机制,将横切逻辑代码和业务逻辑代码分离,不再是侵入式的
在这里插入图片描述

AOP 解决了什么问题

通过上面的分析可以发现,AOP 主要用来解决:在不改变原有业务逻辑的情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复。

比如想在多个业务都有个需求,在每个业务的最初阶段添加日志,如果在每个业务代码里都写日志记录的代码,这样代码重复太严重,也就是代码冗余,使用SpringAOP将重复的日志记录的代码提取为日志切面

spring里的aop

基于注解的aop

  1. 引入依赖
    使用spring6会报错类文件具有错误的版本 61.0, 应为 52.0
    解决办法:
    (1)使用spring5
    (2)升级jdk1.8到17
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.27</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.27</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.27</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.9.3</version>
    <scope>test</scope>
</dependency>
  1. bean.xml
<?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:context="http://www.springframework.org/schema/context"
       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/context 
       https://www.springframework.org/schema/context/spring-context.xsd 
       http://www.springframework.org/schema/aop 
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <context:component-scan base-package="com.example.aopdemo"></context:component-scan>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
  1. 新建测试接口及其实现类,这里以登录业务为例
public interface UserService {

    void login();
}
@Component
public class UserServiceImpl implements UserService {
    @Override
    public void login() {
        System.out.println("登录");
    }
}
  1. 新建aop包,创建切面类LogAspect
  • @Aspect标记为切面

注意切面表达式的写法,这里的切面表达式表示将此段 横切逻辑代码 应用在com.example.aopdemo.service.impl包下的UserServiceImpl类里的修饰符为public、返回值类型为void没有参数的名为login的方法

@Component
@Aspect
public class LogAspect {
    @Before(value = "execution(public void com.example.aopdemo.service.impl.UserServiceImpl.login())")
    public void beforeLogin() {
        System.out.println("登陆前");
    }
}

以上代码还可以写成如下

@Component
@Aspect
public class LogAspect {

    @Pointcut("execution(public void com.example.aopdemo.service.impl.UserServiceImpl.login())")
    public void serviceLayerMethods() { }
    

    @Before("serviceLayerMethods()")
    public void beforeLogin() {
        System.out.println("登陆前");
    }
}

下面是一些切面表达式写法示例

1
2
3
4
5
6

  1. 测试
public class AOPTest {
    @Test
    public void testAOP() {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean.xml");

        UserService service = context.getBean(UserService.class);
        service.login();
    }
}

每个注解都可以获取到方法的名称,参数等信息,只需要在方法参数加入JointPoint jointPoint,以上面的例子为例

@Before(value = "execution(public void com.example.aopdemo.service.impl.UserServiceImpl.login())")
public void beforeLogin(JoinPoint joinPoint) {
    String methodName = joinPoint.getSignature().getName();
    Object[] args = joinPoint.getArgs();
    System.out.println("方法名: " + methodName + "  参数 " + Arrays.toString(args));
    System.out.println("登陆前");
}

除了@before外,还有@After,@AfterReturning,@AfterThrowing,@Around

@AfterReturning可以得到返回值

@AfterReturning(value = "", returning = "result")
public void xxx(Object result) {
    System.out.println("返回结果: " + result);
}

@AfterThrowing:目标方法出现异常,此通知执行

@AfterThrowing(value = "", throwing = "exception")
public void xxx(Throwable exception) {
    System.out.println("异常信息: " + exception);
}

@Around

基于xml的AOP

与基于注解的aop差不多,就是把原来切面类里的注解@Aspect和@Before换成了bean.xml里面的配置(切面类还在,切面类里的方法不能删),原本bean.xml里的<aop:aspectj-autoproxy></aop:aspectj-autoproxy>删掉,删不删其实都ok

有注解肯定用注解,正常谁用这个啊😅

<aop:config>
    <aop:aspect ref="logAspect">  <!--指定切面类,因为切面类类名为LogAspect,ioc默认取类名首字母小写-->
        <aop:pointcut id="pointcut"
                      expression="execution(public void com.example.aopdemo.service.impl.UserServiceImpl.login())"/>
        <aop:before method="beforeLogin" pointcut-ref="pointcut"></aop:before>   <!--指定-->
    </aop:aspect>
</aop:config>

相关术语说明

  1. 连接点:我们要在哪个方法周围添加横切逻辑,哪个方法就是连接点,此处的连接点是UserService中的login()方法
  2. 切入点:横切逻辑代码就是切入点,这里的切入点是LogAspect类中的beforeLogin()方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值