一文搞懂AOP 通俗易懂


前言

提示:这里可以添加本文要记录的大概内容:

Spring有两大核心,AOP和IOC,本文着重讲解AOP。


一、AOP是什么?

AOP是Aspect Oriented Programming,即面向切面编程。
什么是面向切面呢?
要理解AOP的概念,我们先用OOP举例,OOP大家都知道,面向对象编程。比如一个业务组件BookService,它有几个业务方法:

  • createBook:添加新的Book;
  • updateBook:修改Book;
  • deleteBook:删除Book。

对每个业务方法,例如,createBook(),除了业务逻辑,还需要安全检查、日志记录和事务处理,它的代码像这样:

public class BookService {
    
    
    public void createBook(Book book) {
    
    
        securityCheck();
        Transaction tx = startTransaction();
        try {
    
    
            // 核心业务逻辑
            tx.commit();
        } catch (RuntimeException e) {
    
    
            tx.rollback();
            throw e;
        }
        log("created book: " + book);
    }
}

然后你就会发现增删改的方法都需要做这些判断,很繁琐,解决办法有两种:
一种可行的方式是使用Proxy模式,将某个功能,例如,权限检查,放入Proxy中,但是这种方式的缺点是比较麻烦,必须先抽取接口,然后,针对每个方法实现Proxy。
所以第二种方法AOP应运而生了。
AOP技术看上去比较神秘,但实际上,它本质就是一个动态代理,让我们把一些常用功能如权限检查、日志、事务等,从每个业务方法中剥离出来。

二、使用步骤

1.引入Aspect依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>${
    
    spring.version}</version>
</dependency>

2.编写代码

这里我先模拟一个简单的场景,用最简洁的代码教会大家。
假设我们进行了一条数据的更新(update),需要记录日志,如果报错需要抛出异常,这时候我们可以用到前置通知(其实是在插入成功后,后置通知)和异常通知

2.1 使用注解+AOP方式实现切面

首先我们定义一个注解类AdminOnly

@Retention(RetentionPolicy.RUNTIME)  //运行期
@Target(ElementType.METHOD) //作用在方法上
public @interface AdminOnly {
    
    
}

然后就可以编写切面类了,

@Aspect
@Component
public class CheckUserAspect {
    
    

    //注解方式,方法其实是个空壳
    //此时注解就跟此方法绑定了,后续使用注解的方法都会被带上切面所标识的方法
    @Pointcut("@annotation(com.tao.aop.annotation.AdminOnly)")
    private void checkAdmin(){
    
    

    }


    @Before("checkAdmin()") // 前置通知,checkAdmin()跟拦截器进行绑定
    private void Before(){
    
    
        System.out.println("前置通知...日志记录");
    }

    @AfterThrowing( value = "checkAdmin()",throwing = "e")  //异常通知 checkAdmin()跟拦截器进行绑定
    private void afterThrowing(Throwable e) {
    
    
        System.out.println("报错后通知...");
        System.out.println(e.getMessage());
    }
}

UserService类

@Service
public class UserService {
    
    

    @AdminOnly  //注:要想被切面的方法需要加上这个注解
    public void update() {
    
    
        System.out.println("进行了update操作...");
    }
}

此时使用测试类,调用UserService中的update方法,你会发现结果是:

前置通知…权限校验
进行了update操作…

已经加上了AOP操作。接下来讲解通过execution来划定范围

2.2 使用execution+AOP方式实现切面

切面类 CheckUserAspect ,看得出,我把service包下的所有类,所有方法都拦截了

@Aspect
@Component
public class CheckUserAspect {
    
    

 //表达式方式
    // execution 代表的意思
    /*
    * 第一个 * 号代表返回值为任意类型
    *第二位代表 包名
    *第三位 .* 代表任意类
    *第四位 .*(..) 表示任何方法名的任何参数
    *
    * */
    @Pointcut("execution(* com.tao.aop.service.*.*(..))")
    private void checkAdmin(){
    
    
        //此方法是空壳
    }


    @Before("checkAdmin()") // 前置通知,checkAdmin()跟拦截器进行绑定
    private void Before(){
    
    
        System.out.println("前置通知...日志记录");
    }

    @AfterThrowing( value = "checkAdmin()",throwing = "e")  //异常通知 checkAdmin()跟拦截器进行绑定
    private void afterThrowing(Throwable e) {
    
    
        System.out.println("报错后通知...");
        System.out.println(e.getMessage());
    }
}

UserService1类

@Service
public class UserService1 {
    
    


    public void update() throws Exception {
    
    

        System.out.println("进行了update操作...");
        throw new  Exception("自造异常");
    }
}

前置通知以及我自己模拟的异常通知都触发了,结果如下:

前置通知…权限校验
进行了update操作…
报错后通知…
自造异常

3. 写在最后

还有一些拦截通知类型,我就不一一举例,感兴趣的同学可以关注公众号,探讨及提问。

@Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了;

@After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行;

@AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码;

@AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码;

@Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能。

总结

总的来说,AOP已经是开发必备或者说面试必问,老生常谈的问题了,如果想要详细源码或者不懂的地方关注公众号,回复AOP实例源码 即可获取全部源码

如果您有Java方面的问题(不局限于此文章的问题),欢迎公众号提出您的问题,我将在第一时间为您解答~

在这里插入图片描述

有惊喜哟~
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_42429369/article/details/125146530
今日推荐