小工具知识网

当前位置: 首页 > 行业知识 > [详解]Spring AOP

行业知识

[详解]Spring AOP

2026-11-04 02:07:43 来源:互联网转载

 

  •  个人主页:Dikz12
  • 个人专栏:Spring学习之路
  • 格言:吾愚多不敏,而愿加学
  • 欢迎大家点赞✍评论⭐收藏

    目录

    什么是AOP? 

    Spring AOP 快速入门

    Spring AOP核心概念

    切点(Pointcut)

    连接点(Join Point) 

    通知(Advice)

    切⾯(Aspect)  

     通知类型

    切点表达式

     execution表达式

    @annotation  

    Spring AOP 的实现方式

    Spring AOP 原理 

    代理模式 

    动态代理 

    CGLIB动态代理实现

     总结


    什么是AOP? 

    Aspect Oriented Programming(⾯向切⾯编程)

    什么是⾯向切⾯编程呢? 切⾯就是指某⼀类特定问题, 所以AOP也可以理解为⾯向特定⽅法编程. 什么是⾯向特定⽅法编程呢? 就是⼀类特定问题. 登录校验拦截器, 就 是对"登录校验"这类问题的统⼀处理. 所以, 拦截器也是AOP的⼀种应⽤. 简单来说: AOP是⼀种思想, 是对某⼀类事情的集中处理.Spring对AOP进行了实现,并提供了一些API,就是Spring AOP.  比如:统一数据格式 和 统一异常处理,也是AOP的一种实现.

    Spring AOP 快速入门

    实现记录Controller方法每个方法执行花费的时间.

    import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.springframework.stereotype.Component;@Slf4j@Aspect@Componentpublic class TimeAspect {    /**     * 查看每个接口所需的时间     */    @Around("execution(* com.example.demo.controller.*.*(..))")    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {        log.info("方法执行前");        long start = System.currentTimeMillis();        Object result = joinPoint.proceed();        long end = System.currentTimeMillis();        log.info("方法执行后");        log.info(joinPoint+ "消耗时间" +(end-start)+ "ms");        return result;    }}

    运行效果: 

    对程序代码进⾏简单的讲解:
    1.  @Aspect: 标识这是⼀个切⾯类
    2.  @Around: 环绕通知, 在⽬标⽅法的前后都会被执⾏. 后⾯的表达式表⽰对哪些⽅法进⾏增强.
    3. ProceedingJoinPoint.proceed() 让原始⽅法执⾏

     整个代码划分为三部分:

    通过上⾯的程序, 我们也可以感受到AOP⾯向切⾯编程的⼀些 优势 :
    • 代码⽆侵⼊: 不修改原始的业务⽅法, 就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变
    • 减少了重复代码
    • 提⾼开发效率
    • 维护⽅便

      Spring AOP核心概念

      切点(Pointcut)

      切点(Pointcut), 也称之为"切⼊点" Pointcut 的作⽤就是提供⼀组规则 (使⽤ AspectJ pointcut expression language 来描述), 告诉程序对 哪些⽅法来进⾏功能增强.

      连接点(Join Point) 

      满⾜切点表达式规则的⽅法 , 就是连接点. 也就是可以被AOP控制的⽅法 以⼊⻔程序举例, 所有 com.example.demo.controller 路径下的⽅法, 都是连接点。
      package com.example.demo.controller;@RequestMapping("/book")@RestControllerpublic class BookController {   @RequestMapping("/addBook")   public Result addBook(BookInfo bookInfo) {    //...代码省略   }    @RequestMapping("/queryBookById")   public BookInfo queryBookById(Integer bookId){   //...代码省略  }}
      上述BookController 中的⽅法都是连接点。(也就是目标方法) 连接点是满⾜切点表达式的元素. 切点可以看做是保存了众多连接点的⼀个集合. ⽐如: 切点表达式: 学校全体教师 连接点就是: 张三,李四等各个⽼师

      通知(Advice)

      通知就是具体要做的⼯作, 指哪些重复的逻辑,也就是共性功能(最终体现为⼀个⽅法) ⽐如上述程序中记录业务⽅法的耗时时间, 就是通知。

      切⾯(Aspect)  

      切⾯(Aspect) = 切点(Pointcut) + 通知(Advice) 通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法, 在什么时候执⾏什么样的操作. 切⾯既包含了通知逻辑的定义, 也包括了连接点的定义。 切⾯所在的类, 我们⼀般称为切⾯类(被@Aspect注解标识的类)

       通知类型

      Spring中AOP的通知类型有以下⼏种: 

      •  @Around:环绕通知,此注解标注的通知⽅法在⽬标⽅法前,后都被执⾏.
      •  @Before:前置通知,此注解标注的通知⽅法在⽬标⽅法前被执⾏.
      • @After:后置通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,⽆论是否有异常都会执⾏.
      • @AfterReturning:返回后通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,有异常不会执⾏.
      • @AfterThrowing:异常后通知,此注解标注的通知⽅法发⽣异常后执⾏.

        执行顺序: 

        程序发⽣异常的情况下: 

        切点表达式

        上⾯的代码中, 我们⼀直在使⽤切点表达式来描述切点. 下⾯我们来介绍⼀下切点表达式的语法. 切点表达式常⻅有两种表达⽅式
        1. execution(RR):根据⽅法的签名来匹配
        2. @annotation(RR) :根据注解匹配

         execution表达式

        execution(   )
        切点表达式⽀持通配符表达: 1. * :匹配任意字符,只匹配⼀个元素(返回类型, 包, 类名, ⽅法或者⽅法参数)      a. 包名使⽤ * 表⽰任意包(⼀层包使⽤⼀个*)      b. 类名使⽤ * 表⽰任意类      c. 返回值使⽤ * 表⽰任意返回值类型      d. ⽅法名使⽤ * 表⽰任意⽅法      e. 参数使⽤ * 表⽰⼀个任意类型的参数 2. .. :匹配多个连续的任意符号, 可以通配任意层级的包, 或任意类型, 任意个数的参数      a. 使⽤ .. 配置包名,标识此包以及此包下的所有⼦包      b. 可以使⽤ .. 配置参数,任意个任意类型的参数

        @annotation  

        实现步骤:
        1. 编写⾃定义注解
        2. 使⽤ @annotation 表达式来描述切点
        3. 在连接点的⽅法上添加⾃定义注解

        1.⾃定义注解 @MyAspect 

        import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MyAspect {}

         2.使⽤ @annotation 切点表达式定义切点

        @RestController@Slf4j@Aspectpublic class MyAspectDemo {    @Before("@annotation(com.example.demo.aspect.MyAspect)")    public void doBefore() {        log.info("MyAspect -> Before ...");    }   @After("@annotation(com.example.demo.aspect.MyAspect)")    public void doAfter() {       log.info("MyAspect -> After ...");   }}

        3.添加⾃定义注解  

        public class TestController {    @MyAspect    @RequestMapping("/t2")    public String t2(){        return "t2";    }}

        Spring AOP 的实现方式

        1. 基于注解 @Aspect (最常用)
        2.  基于⾃定义注解 (参考⾃定义注解 @annotation 部分的内容)
        3.  基于Spring API (通过xml配置的⽅式, ⾃从SpringBoot ⼴泛使⽤之后, 这种⽅法⼏乎看不到了, 课 下⾃⼰了解下即可)
        4. 基于代理来实现(更加久远的⼀种实现⽅式, 写法笨重, 不建议使⽤)

        Spring AOP 原理 

        Spring AOP的原理, 也就是Spring 是如何实 现AOP的. Spring AOP 是基于动态代理实现的.(也就是代理模式中的动态代理)

        代理模式 

        在某些情况下, ⼀个对象不适合或者不能直接引⽤另⼀个对象, ⽽代理对象可以在客⼾端和⽬标对象之 间起到中介的作⽤. 使⽤代理前:

        使用代理后: 

         

         生活中的代理:

        • 艺⼈经纪⼈: ⼴告商找艺⼈拍⼴告, 需要经过经纪⼈,由经纪⼈来和艺⼈进⾏沟通.
        • 房屋中介: 房屋进⾏租赁时, 卖⽅会把房屋授权给中介, 由中介来代理看房, 房屋咨询等服务.
        • 经销商: ⼚商不直接对外销售产品, 由经销商负责代理销售. 静态代理: 由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译, 在程序运⾏前代理类的 .class ⽂件就已经存在了。 (在程序运行前,代理对象就应经对目标对象进行了步骤的预执行代码)代码写***了.

          动态代理 

          动态代理: 在程序运⾏时, 运⽤反射机制动态创建⽽成。(不需要针对每个目标对象都单独创建一个代理对象)

           Java也对动态代理进⾏了实现, 并给我们提供了⼀些API, 常⻅的实现⽅式有两种:

          1. JDK动态代理
          2. CGLIB动态代理

          CGLIB动态代理实现

          实现步骤: 

          1. 定义⼀个类(被代理类)
          2. ⾃定义 MethodInterceptor 并重写 intercept ⽅法, intercept ⽤于增强⽬标⽅ 法,和 JDK 动态代理中的 invoke ⽅法类似
          3. 通过 Enhancer 类的 create()创建代理类

          ⾃定义 MethodInterceptor 

          import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CGLIBInterceptor implements MethodInterceptor { //⽬标对象, 即被代理对象 private Object target; public CGLIBInterceptor(Object target){     this.target = target; } @Override public Object intercept(Object o, Method method, Object[] objects,      MethodProxy methodProxy) throws Throwable {   // 代理增强内容    System.out.println("我是中介, 开始代理");   //通过反射调⽤被代理类的⽅法   Object retVal = methodProxy.invoke(target, objects);    //代理增强内容   System.out.println("我是中介, 代理结束");   return retVal; }}

           创建代理类, 并使⽤

          public class DynamicMain { public static void main(String[] args) {    HouseSubject target= new RealHouseSubject();    HouseSubject proxy= (HouseSubject)     Enhancer.create(target.getClass(),new CGLIBInterceptor(target));    proxy.rentHouse(); }}

          Spring AOP 源码解析就不在进行详细解析,这东西了解就行了。

          Spring对于AOP的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator 去完成 ⽣成代理对象的逻辑在 ⽗类 AbstractAutoProxyCreator 中。

           总结

          1. AOP是⼀种思想, 是对某⼀类事情的集中处理. Spring框架实现了AOP, 称之为SpringAOP。
          2.  Spring AOP常⻅实现⽅式有两种: 1. 基于注解@Aspect来实现 2. 基于⾃定义注解来实现, 还有⼀些 更原始的⽅式,⽐如基于代理, 基于xml配置的⽅式, 但⽬标⽐较少⻅
          3. Spring AOP 是基于动态代理实现的, 有两种⽅式: 1. 基本JDK动态代理实现 2. 基于CGLIB动态代理 。JDK动态代理只能代理接口,CGLIB既可以代理接口,也可以代理类. 如果是类的话一定是CGLIB代理的.  

上一篇:怎么组件自己的云服务器?

下一篇:dell服务器创建虚拟磁盘