深入理解Spring AOP的工作流程

news2024/9/22 15:50:55

文章目录

    • 引言
    • 什么是AOP?
    • Spring AOP的工作原理
      • 1. JDK动态代理
      • 2. CGLIB代理
    • Spring AOP的注解方式
      • @Aspect注解
      • @EnableAspectJAutoProxy注解
    • Spring AOP的工作流程
    • 拓展应用
      • 1. 自定义注解
      • 2. 异常处理
      • 3. 切面优先级
    • 结论

在这里插入图片描述

🎉深入理解Spring AOP的工作流程


  • ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹
  • ✨博客主页:IT·陈寒的博客
  • 🎈该系列文章专栏:架构设计
  • 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习
  • 🍹文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️

引言

在现代的软件开发中,面向切面编程(AOP)是一种重要的编程范式,用于解耦业务逻辑和横切关注点(cross-cutting concerns)。Spring框架提供了强大而灵活的AOP支持,通过代理机制实现横切关注点的注入。本文将深入探讨Spring AOP的工作流程,帮助读者更好地理解其原理和应用。

什么是AOP?

AOP是一种编程思想,通过在程序中间插入横切关注点,将系统划分为核心业务逻辑和横切关注点两部分。横切关注点包括日志记录、事务管理、安全控制等与核心业务逻辑无关但又必须在程序中执行的功能。AOP通过将这些横切关注点与核心业务逻辑分离,提高了代码的模块化和可维护性。

Spring AOP通过代理机制实现横切关注点的注入,其中代理对象负责执行横切逻辑。在Spring AOP中,常见的横切关注点包括日志记录、性能监控、事务管理等。

Spring AOP的工作原理

Spring AOP基于代理模式,主要通过两种方式实现:

  1. JDK动态代理: 基于接口的代理机制,使用java.lang.reflect.Proxy类生成代理对象。

  2. CGLIB代理: 基于类的代理机制,使用CGLIB库生成代理对象。

1. JDK动态代理

JDK动态代理要求目标类实现一个或多个接口,代理对象实现这些接口并委托给目标对象。以下是一个简单的例子:

public interface UserService {
    void addUser(String username, String password);
}

public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String username, String password) {
        // 实际业务逻辑
        System.out.println("User added: " + username);
    }
}

public class LogAspect implements InvocationHandler {
    private Object target;

    public LogAspect(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Log: Method " + method.getName() + " is invoked");
        Object result = method.invoke(target, args);
        return result;
    }
}

// 使用代理
public class Main {
    public static void main(String[] args) {
        UserService target = new UserServiceImpl();
        InvocationHandler handler = new LogAspect(target);
        UserService proxy = (UserService) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                handler
        );
        proxy.addUser("John", "123456");
    }
}

2. CGLIB代理

CGLIB代理不要求目标类实现接口,代理对象继承目标对象。以下是一个简单的例子:

public class UserService {
    public void addUser(String username, String password) {
        // 实际业务逻辑
        System.out.println("User added: " + username);
    }
}

public class LogAspect implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("Log: Method " + method.getName() + " is invoked");
        Object result = proxy.invokeSuper(obj, args);
        return result;
    }
}

// 使用代理
public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new LogAspect());

        UserService proxy = (UserService) enhancer.create();
        proxy.addUser("John", "123456");
    }
}

Spring AOP的注解方式

除了基于XML的配置方式,Spring AOP还支持使用注解的方式配置切面。

@Aspect注解

在使用注解方式配置AOP时,首先需要使用@Aspect注解声明一个切面类。该类包含了多个切点和通知,用于定义横切逻辑。

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void pointcut() {}

    @Before("pointcut()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Log: Before " + joinPoint.getSignature().

getName());
    }

    @After("pointcut()")
    public void afterAdvice(JoinPoint joinPoint) {
        System.out.println("Log: After " + joinPoint.getSignature().getName());
    }
}

上述例子中,@Pointcut注解定义了一个切点,表示匹配com.example.service包下所有类的所有方法。@Before@After注解分别表示前置通知和后置通知。

@EnableAspectJAutoProxy注解

在Spring Boot中,还需要在配置类上使用@EnableAspectJAutoProxy注解开启自动代理功能。该注解告诉Spring Boot启用AspectJ自动代理。

@SpringBootApplication
@EnableAspectJAutoProxy
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

Spring AOP的工作流程

Spring AOP的工作流程可以概括为以下几个步骤:

  1. 定义切面: 创建一个Java类,并在类上使用@Aspect注解声明为切面类。在切面类中定义切点和通知。

  2. 配置通知: 使用@Before@After等注解配置通知,定义横切逻辑。

  3. 激活切面: 在配置类上使用@EnableAspectJAutoProxy注解激活切面。

  4. 容器初始化: Spring容器启动时,会扫描并解析所有标有@Aspect注解的类。

  5. 生成代理对象: 对于被代理的目标对象,Spring会根据切面定义生成代理对象。代理对象包含了横切逻辑。

  6. 执行横切逻辑: 在目标方法执行前、后或异常时,执行横切逻辑。

拓展应用

Spring AOP的应用远不止上述简单例子所示,还可以结合更复杂的切面和通知,实现更丰富的横切逻辑。以下是一些拓展应用的示例:

1. 自定义注解

可以使用自定义注解来标记切点,让代码更具可读性。例如,定义一个@Log注解,标记需要记录日志的方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
    String value() default "";
}

然后,在切面类中使用@Around注解拦截被@Log注解标记的方法。

@Aspect
@Component
public class LogAspect {

    @Around("@annotation(com.example.annotation.Log)")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("Log: Method " + joinPoint.getSignature().getName() + " is invoked");
        Object result = joinPoint.proceed();
        return result;
    }
}

2. 异常处理

通过@AfterThrowing注解可以实现异常处理逻辑,记录异常信息或进行其他处理。

@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
    System.out.println("Exception: " + ex.getMessage());
}

3. 切面优先级

通过@Order注解可以指定切面的优先级,数字越小,优先级越高。

@Aspect
@Component
@Order(1)
public class LogAspect {
    // ...
}

在这里插入图片描述

结论

Spring AOP是Spring框架中一个重要的组件,通过代理机制实现横切关注点的注入。本文深入介绍了Spring AOP的工作原理,包括基于JDK动态代理和CGLIB代理的实现方式,以及使用注解配置AOP的方法。通过理解Spring AOP的工作流程,我们能更好地应用和拓展AOP,提高代码的模块化和可维护性。希望本文能够帮助读者更深入地理解和应用Spring AOP。


🧸结尾 ❤️ 感谢您的支持和鼓励! 😊🙏
📜您可能感兴趣的内容:

  • 【Java面试技巧】Java面试八股文 - 掌握面试必备知识(目录篇)
  • 【Java学习路线】2023年完整版Java学习路线图
  • 【AIGC人工智能】Chat GPT是什么,初学者怎么使用Chat GPT,需要注意些什么
  • 【Java实战项目】SpringBoot+SSM实战:打造高效便捷的企业级Java外卖订购系统
  • 【数据结构学习】从零起步:学习数据结构的完整路径

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1244906.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue - - - - - vue-qr插件生成二维码

vue-qr插件生成二维码 1. 安装插件2. 组件使用示例图&#xff1a;扫码结果 1. 安装插件 【vue-qr 官网地址】 npm install vue-qr --save // or yarn add vue-qr --save2. 组件使用 <template><vue-qr :logo-src"logoSrc":size"237":margin&qu…

Cache学习(2):Cache结构 命中与缺失 多级Cache结构 直接映射缓存

1 Cache名词解释 命中&#xff08;hit&#xff09;&#xff1a; CPU要访问的数据在Cache中有缓存缺失&#xff08;miss&#xff09;&#xff1a; CPU要访问的数据在Cache中没有缓存Cache Size&#xff1a;Cache的大小&#xff0c;代表Cache可以缓存最大数据的大小Cache Line&a…

Java设计模式系列:单例设计模式

Java设计模式系列&#xff1a;单例设计模式 介绍 所谓类的单例设计模式&#xff0c;就是采取一定的方法保证在整个的软件系统中&#xff0c;对某个类只能存在一个对象实例&#xff0c;并且该类只提供一个取得其对象实例的方法&#xff08;静态方法&#xff09; 比如 Hiberna…

西门子(Siemens)仿真PLC启动报错处理

目录 一、背景&#xff1a; 二、卸载软件 三、安装软件 三、启动软件 四、下载PORTAL项目 五、测试 一、背景&#xff1a; 在启动S7-PLCSIM Advanced V3.0仿真PLC时报错&#xff0c;报错信息为&#xff1a;>>Siemens PLCSIM Virtual Switch<<is misconfigu…

iframe内部子页面与外部主页面通讯

文章目录 一、问题二、解决2.1、子页面2.2、主页面 三、知识点3.1、[浏览器兼容性](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/postMessage#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7)3.2、详解3.2.1、发送方3.2.2、接收方 一、问题 如上所示&a…

Vue3中如何响应式解构 props

目录 1&#xff0c;前言2&#xff0c;解决2.1&#xff0c;利用插件&#xff0c;实现编译时转换2.2&#xff0c;toRef 和 toRefs 1&#xff0c;前言 Vue3 中为了保持响应性&#xff0c;始终需要以 props.x 的方式访问这些 prop。这意味着不能够解构 defineProps 的返回值&#…

【云原生 Prometheus篇】Prometheus架构详解与核心组件的应用实例(Exporters、Grafana...)

Prometheus Part1 一、常用的监控系统1.1 简介1.2 Prometheus和zabbix的区别 二、Prometheus2.1 简介2.2 Prometheus的主要组件1&#xff09;Prometheus server2&#xff09;Exporters3&#xff09;Alertmanager4&#xff09;Pushgateway5&#xff09;Grafana 2.3 Prometheus的…

【性能优化】CPU利用率飙高与内存飙高问题

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

解决:ImportError: cannot import name ‘Adam‘ from ‘keras.optimizers‘

解决&#xff1a;ImportError: cannot import name ‘Adam‘ from ‘keras.optimizers‘ 背景 在使用之前的代码时&#xff0c;报错&#xff1a; from keras.optimizers import Adam ImportError: cannot import name ‘Adam’ 报错问题 from keras.optimizers import Adam I…

【赠书第7期】从零基础到精通Flutter开发

文章目录 前言 1 安装Flutter和Dart 2 了解Flutter的基础概念 2.1 Widget 2.2 MaterialApp和Scaffold 2.3 Hot Reload 3 编写你的第一个Flutter应用 3.1 创建一个Flutter项目 3.2 修改默认页面 3.3 添加交互 4 深入学习Flutter高级特性 4.1 路由和导航 4.2 状态管…

「 系统设计 」 为什么要做架构分层?

「 系统设计 」 为什么要做架构分层&#xff1f; 参考&鸣谢 3.设计模式之分层思维&#xff1a;为什么要做代码分层架构&#xff1f; 从零开始学架构&#xff08;八&#xff09;分层架构和设计模式 架构模式之分层架构总结 文章目录 「 系统设计 」 为什么要做架构分层&…

【libGDX】使用Mesh绘制立方体

1 前言 本文主要介绍使用 Mesh 绘制立方体&#xff0c;读者如果对 Mesh 不太熟悉&#xff0c;请回顾以下内容&#xff1a; 使用Mesh绘制三角形使用Mesh绘制矩形使用Mesh绘制圆形 在绘制立方体的过程中&#xff0c;主要用到了 MVP &#xff08;Model View Projection&#xff0…

<JavaEE> 什么是进程(Process)?进程管理,进程调度,内存管理,进程间通信和多进程编程

目录 一、进程&#xff08;Process&#xff09;的概念 二、进程管理 三、进程调度 四、内存管理 五、进程间通信 六、多进程编程 一、进程&#xff08;Process&#xff09;的概念 进程&#xff08;process&#xff09;也称为任务&#xff08;task&#xff09;&#xff0c…

sam和mobilesam的c#调用dll

这个主要注意&#xff1a; 我原本从一个地方把这个工程拷贝到另一个地方&#xff0c;然后我看了解决方案下的依赖项是有感叹号的&#xff0c;且这个时候代码出现很多下划的波浪红线。 然后我的做法如下&#xff1a; 然后我发现一直添加不了opencvsharp的dll文件&#xff0c;报…

Adiponectin 脂联素 ; T-cadherin +exosome

T-cadherin Adiponectin exosome T-cadherin Adiponectin exosome 代谢综合征中 外泌体、脂肪组织 和 脂联素 的器官间通讯-2019.pdf

基于IDEA+HTML+SpringBoot前后端分离电子商城

基于springboot的电子商城 项目介绍&#x1f481;&#x1f3fb; •B2C 商家对客户 •C2B2C 客户对商家对客户 1.1.1 B2C 平台运营方即商品的卖家 小米商城 •商品 •用户 1.1.2 C2B2C 平台运营方不卖商品&#xff08;也可以卖&#xff09; 卖家是平台的用户 买家也是平台用户 •…

HarmonyOS(五)—— 认识页面和自定义组件生命周期

前言 在前面我们通过如何创建自定义组件一文知道了如何如何自定义组件以及自定义组件的相关注意事项&#xff0c;接下来我们认识一下页面和自定义组件生命周期。 自定义组件和页面的关系 在开始之前&#xff0c;我们先明确自定义组件和页面的关系 自定义组件&#xff1a;Co…

基于C#实现线段树

一、线段树 线段树又称"区间树”&#xff0c;在每个节点上保存一个区间&#xff0c;当然区间的划分采用折半的思想&#xff0c;叶子节点只保存一个值&#xff0c;也叫单元节点&#xff0c;所以最终的构造就是一个平衡的二叉树&#xff0c;拥有 CURD 的 O(lgN)的时间。 从…

解决:javax.websocket.server.ServerContainer not available 报错问题

原因&#xff1a; 用于扫描带有 ServerEndpoint 的注解成为 websocket&#xff0c;该方法是 服务器端点出口&#xff0c;当进行 SpringBoot 单元测试时&#xff0c;并没有启动服务器&#xff0c;所以当加载到这个bean时会报错。 解决方法&#xff1a; 加上这个注解内容 Spr…

不做机器视觉工程师,转行,转岗的建议与想法

正所谓外行看热闹&#xff0c;内行看门道。提前咨询前辈们&#xff0c;多问问&#xff0c;多看看。要做就做&#xff0c;一定要提前做好防范。 无论你是要转行或者是转岗&#xff0c;看你有没有本钱和试错成本 有些人&#xff0c;家庭好&#xff0c;可以一直去试错和从头再来。…