Spring实战 | Spring AOP核心秘笈之葵花宝典

2024-01-24 17:20

本文主要是介绍Spring实战 | Spring AOP核心秘笈之葵花宝典,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Spring实战系列文章:

Spring实战 | Spring IOC不能说的秘密?

国庆中秋特辑系列文章:

国庆中秋特辑(八)Spring Boot项目如何使用JPA

国庆中秋特辑(七)Java软件工程师常见20道编程面试题

国庆中秋特辑(六)大学生常见30道宝藏编程面试题

国庆中秋特辑(五)MySQL如何性能调优?下篇

国庆中秋特辑(四)MySQL如何性能调优?上篇

国庆中秋特辑(三)使用生成对抗网络(GAN)生成具有节日氛围的画作,深度学习框架 TensorFlow 和 Keras 来实现

国庆中秋特辑(二)浪漫祝福方式 使用生成对抗网络(GAN)生成具有节日氛围的画作

国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词

目录

  • 一、Spring AOP 简介
  • 二、Spring AOP 原理
  • 三、Spring AOP 案例分析
  • 四、Spring AOP 提供了两种动态代理方式
    • 4.1 下面是一个简单的 Spring AOP JDK 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:
    • 4.2 下面是一个简单的 Spring AOP CGLIB 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:

Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要模块。
在这里插入图片描述

一、Spring AOP 简介

Spring AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 框架的一个重要模块,用于提供声明式的事务管理、日志记录、性能监控等功能。Spring AOP 底层依赖于 AspectJ 实现,可以与 Spring 框架无缝集成,提供一种更加简单、直观的方式来处理企业应用中的常见问题。

二、Spring AOP 原理

  1. 代理机制
    Spring AOP 采用代理机制实现,可以分为 JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是通过实现目标类的接口,生成目标类的代理对象;CGLIB 动态代理是通过继承目标类,生成目标类的子类作为代理对象。
  2. 通知(Advice)
    通知是 Spring AOP 中实现切面功能的核心,可以分为五种类型:Before、After、AfterReturning、AfterThrowing 和 Around。通知的作用是在目标方法执行前、后或者抛出异常时执行特定的逻辑,实现对目标方法的增强。
  3. 切入点(Pointcut)
    切入点是 Spring AOP 中定义的一个表达式,用于指定哪些方法需要被增强。切点表达式可以使用 AspectJ 语言来编写,非常灵活。通过定义切入点,可以精确地控制哪些方法需要被增强。
  4. 切面(Aspect)
    切面是 Spring AOP 中的一种组件,包含切点和通知。切面可以将通用的逻辑(如日志、事务管理等)封装在一起,便于管理和维护。在 Spring AOP 中,可以通过 XML 配置文件或者 Java 代码来定义切面。
  5. 自动代理
    Spring AOP 框架支持自动代理,可以在运行时自动为指定类生成代理对象。自动代理的核心是 Spring AOP 容器,负责管理代理对象、切面和通知。通过自动代理,可以简化开发者的操作,提高开发效率。

三、Spring AOP 案例分析

以下通过一个简单的案例来演示 Spring AOP 的使用。

  1. 配置文件
    首先,创建一个配置文件 applicationContext.xml,用于定义目标类和切面类。
<beans xmlns="http://www.springframework.org/schema/beans"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  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/aop  http://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 定义目标类 -->  <bean id="target" class="com.example.TargetClass"></bean><!-- 定义切面类 -->  <bean id="aspect" class="com.example.AspectClass"></bean><!-- 开启自动代理 -->  <aop:config proxy-target-class="true">  <!-- 指定切入点表达式 -->  <aop:aspect ref="aspect">  <aop:before pointcut="execution(* com.example.TargetClass.*(..))" method="com.example.AspectClass.beforeAdvice"></aop:before>  <aop:after pointcut="execution(* com.example.TargetClass.*(..))" method="com.example.AspectClass.afterAdvice"></aop:after>  </aop:aspect>  </aop:config>  
</beans>  
  1. 目标类(TargetClass)
    目标类是一个简单的计算类,包含两个方法:doAdd 和 doSubtract。
package com.example;
public class TargetClass {  public int doAdd(int a, int b) {  System.out.println("TargetClass doAdd method called");  return a + b;  }public int doSubtract(int a, int b) {  System.out.println("TargetClass doSubtract method called");  return a - b;  }  
}
  1. 切面类(AspectClass)
    切面类包含两个通知方法:beforeAdvice 和 afterAdvice,分别用于在目标方法执行前和执行后执行特定逻辑。
package com.example;
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.After;
public class AspectClass {  @Before("execution(* com.example.TargetClass.*(..))")  public void beforeAdvice(JoinPoint joinPoint) {  System.out.println("Before advice: " + joinPoint.getSignature().getName());  }@After("execution(* com.example.TargetClass.*(..))")  public void afterAdvice(JoinPoint joinPoint) {  System.out.println("After advice: " + joinPoint.getSignature().getName());  }  
}
  1. 测试类(TestClass)
    测试类用于测试 Spring AOP 的效果。
package com.example;
import org.springframework.context.ApplicationContext;  
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestClass {  public static void main(String[] args) {  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  TargetClass target = (TargetClass) context.getBean("target");int result1 = target.doAdd(2, 3);  int result2 = target.doSubtract(5, 2);System.out.println("Result 1: " + result1);  System.out.println("Result 2: " + result2);  }  
}

运行测试类,输出结果如下:

TargetClass doAdd method called  
Before advice: doAdd  
After advice: doAdd  
Result 1: 5  
TargetClass doSubtract method called  
Before advice: doSubtract  
After advice: doSubtract  
Result 2: 3  

从输出结果可以看出,在目标方法执行前和执行后分别执行了 beforeAdvice 和 afterAdvice 方法,说明 Spring AOP 已经成功实现了对目标方法的增强。

四、Spring AOP 提供了两种动态代理方式

JDK 动态代理和 CGLIB 动态代理。JDK 动态代理是基于接口实现的,而 CGLIB 动态代理是基于类实现的。这两种代理方式在性能上有一定的差别,JDK 动态代理更适合用于接口较多的场景,而 CGLIB 动态代理则更适合用于类较多的场景。

4.1 下面是一个简单的 Spring AOP JDK 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:

  1. 首先,创建一个切面类(Aspect),包含一个通知(Advice):
package com.example.aspect;
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.aspectj.lang.annotation.Pointcut;  
import org.springframework.stereotype.Component;
@Aspect  
@Component  
public class LoggingAspect {@Pointcut("execution(* com.example.service.*.*(..))")  public void serviceMethods() {  }@Before("serviceMethods()")  public void logBefore(JoinPoint joinPoint) {  System.out.println("Before method: " + joinPoint.getSignature().getName());  }@AfterReturning(pointcut = "serviceMethods()", returning = "result")  public void logAfterReturning(JoinPoint joinPoint, Object result) {  System.out.println("After returning method: " + joinPoint.getSignature().getName());  System.out.println("Result: " + result);  }  
}
  1. 接下来,创建一个目标类(Target Class),包含一个需要增强的方法:
package com.example.service;
import org.springframework.stereotype.Service;
@Service  
public class TargetService {public String sayHello(String name) {  System.out.println("Hello, " + name);  return "Hello, " + name;  }  
}
  1. 然后,创建一个 Spring 配置类,启用 AOP 支持,并扫描包含切面和目标类的包:
package com.example;
import org.springframework.context.annotation.ComponentScan;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration  
@EnableAspectJAutoProxy  
@ComponentScan(basePackages = {"com.example.aspect", "com.example.service"})  
public class AppConfig {  
}
  1. 最后,创建一个测试类,使用 Spring AOP 提供的 API 调用目标类的方法:
package com.example;
import org.springframework.context.ApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
import org.springframework.stereotype.Component;
@Component  
public class Test {public static void main(String[] args) {  ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  TargetService targetService = context.getBean(TargetService.class);  String result = targetService.sayHello("World");  System.out.println("Result: " + result);  }  
}

运行测试类,你将看到目标方法被切面增强的日志输出。这个示例展示了如何使用 Spring AOP JDK 动态代理实现简单的日志切面,以记录目标方法执行的前后状态。这有助于实现代码的重用和提高可维护性。

4.2 下面是一个简单的 Spring AOP CGLIB 动态代理示例,演示了如何使用 Spring AOP 实现日志切面:

  1. 首先,创建一个切面类(Aspect),包含一个通知(Advice):
package com.example.aspect;
import org.aspectj.lang.JoinPoint;  
import org.aspectj.lang.annotation.AfterReturning;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Pointcut;  
import org.springframework.stereotype.Component;
@Aspect  
@Component  
public class LoggingAspect {@Pointcut("execution(* com.example.service.*.*(..))")  public void serviceMethods() {  }@Before("serviceMethods()")  public void logBefore(JoinPoint joinPoint) {  System.out.println("Before method: " + joinPoint.getSignature().getName());  }@AfterReturning(pointcut = "serviceMethods()", returning = "result")  public void logAfterReturning(JoinPoint joinPoint, Object result) {  System.out.println("After returning method: " + joinPoint.getSignature().getName());  System.out.println("Result: " + result);  }  
}
  1. 接下来,创建一个目标类(Target Class),包含一个需要增强的方法:
package com.example.service;
import org.springframework.stereotype.Service;
@Service  
public class TargetService {public String sayHello(String name) {  System.out.println("Hello, " + name);  return "Hello, " + name;  }  
}
  1. 然后,创建一个 Spring 配置类,启用 AOP 支持,并扫描包含切面和目标类的包:
package com.example;
import org.springframework.context.annotation.ComponentScan;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration  
@EnableAspectJAutoProxy  
@ComponentScan(basePackages = {"com.example.aspect", "com.example.service"})  
public class AppConfig {  
}
  1. 最后,创建一个测试类,使用 Spring AOP 提供的 API 调用目标类的方法:
package com.example;
import org.springframework.context.ApplicationContext;  
import org.springframework.context.annotation.AnnotationConfigApplicationContext;  
import org.springframework.stereotype.Component;
@Component  
public class Test {public static void main(String[] args) {  ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);  TargetService targetService = context.getBean(TargetService.class);  String result = targetService.sayHello("World");  System.out.println("Result: " + result);  }  
}

运行测试类,你将看到目标方法被切面增强的日志输出。这有助于实现代码的重用和提高可维护性。
需要注意的是,CGLIB 动态代理需要 TargetService 类实现 equals() 和 hashCode() 方法,否则会报错。这是因为 CGLIB 需要生成目标类的代理类,而如果 TargetService 类没有实现 equals() 和 hashCode() 方法,那么生成的代理类将无法正确处理目标类的对象。

这篇关于Spring实战 | Spring AOP核心秘笈之葵花宝典的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/640403

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2