美文网首页
Spring AOP从使用到原理

Spring AOP从使用到原理

作者: JerrysCode | 来源:发表于2023-01-01 21:38 被阅读0次

    如果把Spring比作一座大厦的话,那么IoC和AOP无疑是大厦的两座基石。无论是日常工作还是应付面试,IoC和AOP都是绕不开的话题。上一篇文章我们介绍了IoC,今天我们通过一个例子详细介绍AOP的使用和原理

    AOP

    如果让你开发一个购物网站,你会考虑哪些功能呢?
    我们不仅要完成商品浏览,订单管理,物流管理等业务功能,而且要支持鉴权,日志,审计等非业务功能。非业务功能横跨多个业务领域,我们称之为横切业务,或者Aspect业务。业务功能和非业务功能属于不同的关注点,我们需要把他们分离开来。
    AOP全称是面向“切面”编程,是对面向对象编程的增强,可以在不修改业务代码的前提下添加非业务功能。

    image.png

    基本概念

    Spring aop的概念理解比较拗口,建议大家不要翻译成中文,直接理解英文。
    我们通过一个例子理清这些基本概念。下面这句话描述了一个切面业务。

    image.png
    • aspect: What,期望spring执行什么样的横切逻辑
    • advice:When, 什么时候执行aspect。可以通过注解指定执行时机
      • @Before:方法执行前
      • @After:方法执行后
      • @AfterReturning:方法返回后
      • @AfterThrowing:抛出异常后
    • pointcut:Where,在哪里执行横切逻辑
    • join point: Who, 谁触发横切,spring里一般是方法调用触发

    Spring AOP使用例子

    1. 我们先定义一个Class ProductService,这个Class有两个方法,addProduct负责添加商品,removeProduct负责移除商品。
    @Service
    public class ProductService {
        public void addProduct()
        {
            System.out.println("ProductService add product");
        }
        public void removeProduct()
        {
            System.out.println("ProductService remove product");
        }
    }
    

    现在来了新需求,我们要在添加商品时记录AuditLog,便于事后审计。这是一个典型的横切功能,我们就要借助AOP来实现

    1. 开启AOP
      定义一个Configuration类,然后打上@EnableAspectJAutoProxy标注
    @Configuration
    @EnableAspectJAutoProxy
    public class AopConfig {
    }
    
    1. 定义一个注解ToAudit,只有打了@ToAudit注解的方法才执行横切逻辑
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ToAudit {
        String name();
    }
    
    1. 定义Aspect类
    @Aspect      //@Aspect表示这是一个切面类  
    @Component
    public class LogAspect {
        //定义PointCut: 所有标注了@ToAudit的方法   
        //定义advice:在pointcut执行横切逻辑   
        @Before("@annotation(ToAudit)")
        public void after(JoinPoint joinPoint){
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            System.out.println("---------Write audit log for " + method.getName() + "-------------");
        }
    }
    
    1. 修改ProductService,给方法打上@ToAudit注解
    @Service
    public class ProductService {
        @ToAudit(name = "Add Product")
        public void addProduct()
        {
            System.out.println("ProductService add product");
        }
        
        public void removeProduct()
        {
            System.out.println("ProductService remove product");
        }
    }
    
    1. 启动程序,查看允许结果
      main方法中通过Spring context获取Bean,执行addProduct方法。
    public class AopTest {
        public static void main(String[] args) {
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AopConfig.class);
            ProductService productService = ctx.getBean(ProductService.class);
    
            productService.addProduct();
            
            ctx.close();
        }
    }
    

    执行结果如下,我们看到addProduct执行时记录了auditlog, 符合预期。

    ---------Write audit log for addProduct-------------
    ProductService add product
    

    更多高级特性

    1. aspect优先级
      如果有多个aspect拦截同一个方法,Spring不保证Aspect的执行顺序。
      可以使用@Order为aspect指定执行顺序,@Order value越小表示优先级越高
    @Aspect
    @Order(1) 
    @Component
    public class LogAspect {
        //...
    }
    
    1. 可以通过AOP修改方法参数
      可以在aspect代码中,修改原始的请求参数
    @Around("annotationPoinCut()")
        public void around(ProceedingJoinPoint joinPoint) throws Throwable {
            //获取被拦截的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
    
            //改变原始的请求参数,然后交给Aspect Chain上的下一个Aspect处理   
            Object[] args = joinPoint.getArgs();
            Object[] newArgs = {"New args in aspect"};
            joinPoint.proceed(newArgs);
        }
    

    AOP实现原理

    Spring AOP功能非常强大,Spring很多特性都是基于AOP实现的(如@Transaction事务管理)。
    我们不仅仅要知其然,而且要知其所以然,AOP的实现基于动态代理,当我们开启@EnableAspectJAutoProxy,如果某个Bean有对应的@Aspect逻辑要执行,Spring就会为这个Bean生成一个proxy Bean,在Proxy Bean中对原始Bean增加切面逻辑。

    image.png

    我们在Idea中debug也可以发现,ctx.getBean(ProductService.class)得到的并不是原始的ProductService实例,而是一个经过CGLib增强后的Proxy实例。


    product-service.JPG

    没有银弹

    AOP虽好,但是滥用也不好。过多使用AOP会让我们业务散落在各个地方,让维护工作变得非常困难。

    总结

    AOP是是Spring的重要基石,我在文章中通过一个例子介绍了AOP的基本概念和使用。我也介绍了AOP的实现技术动态代理。

    相关文章

      网友评论

          本文标题:Spring AOP从使用到原理

          本文链接:https://www.haomeiwen.com/subject/ssthcdtx.html