七、Spring 面向切面编程(AOP)学习总结

文章目录

  • 一、初识面向切面编程(AOP)
    • 1.1 什么是 AOP
    • 1.2 AOP的应用场景
    • 1.3 Aop 在 Spring 中的作用
      • 1.3.1 Aop 的核心概念
    • 1.4 使用 Spring 实现 AOP
      • 1.4.1 方式一:使用 Spring API 接口实现 AOP 【主要是SpringAPI接口实现】
      • 1.4.2 方式二:自定义类来实现 AOP【主要是切面定义】
      • 1.4.3 方式三:使用注解实现 AOP




一、初识面向切面编程(AOP)


  •        在以往的企业开发过程中,一些已经写完的功能可以会在原本的基础上进行扩展,这个时候就需要去修改原有的代码,将新扩展的内容完善进去。但是这个动作其实是企业级开发的大忌,因为原本好用的代码很可能因为新增的内容导致出现问题。


           而解决这个问题会用到代理模式,将要新扩展的功能维护到代理角色中,再用代理角色去调用真实角色来实现功能的扩展,这样做不但将新扩展的功能实现类,原本的功能代码也无需变动,更加稳妥。而这也是 Spring AOP 的实现机制。

在这里插入图片描述



1.1 什么是 AOP

        AOP(Aspect Oriented Programming)意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 如下图所示:AOP 就是通过一些方式,在原有业务逻辑不变的情况下将一些扩展功能完成
    在这里插入图片描述




1.2 AOP的应用场景


  • 日志记录: 记录调用方法的入参和结果返参。

  • 用户的权限验证: 验证用户的权限放到AOP中,与主业务进行解耦。

  • 性能监控: 监控程序运行方法的耗时,找出项目的瓶颈。

  • 事务管理: 控制Spring事务,Mysql事务等。

  • AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。




1.3 Aop 在 Spring 中的作用


       AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。

       主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。

       简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。

1.3.1 Aop 的核心概念


  • 提供声明式事务;允许用户自定义切面

    • 横切关注点: 跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志,安全,缓存,事务等等…

    • 切面(ASPECT): 横切关注点被模块化的特殊对象。即,它是一个类。

    • 通知(Advice): 切面必须要完成的工作。拦截到连接点之后,对切入点增强的内容。即,它是类中的一个方法。

    • 目标(Target): 被代理的目标对象。

    • 代理(Proxy): 指生成的代理对象

    • 切入点(PointCut): 指需要对那些 JointPoint 进行拦截,即被拦截的连接点

    • 连接点(JointPoint): 指那些被拦截到的点,在 Spring 中,指可以被动态代理拦截目标类的方法

    • 植入点(Weaving): 指把增强代码应用到目标上,生成代理对象的过程

通俗理解

横切关注点:在业务代码种需要新增的业务,即为关注点
切面:将关注点维护成一个类,即新增的业务就是切面
通知:切面中具体的方法,也就是具体需要完成的业务就是通知。
目标:即需要在那段业务新增业务代码,即目标对象。
代理:代理目标对象的对象
切入点:需要通知在那个位置执行新的业务代码

注:目标和代理被 Spring 完成了,InvocationHandler、Proxy



  • Spring AOP中,通过 “通知(Advice)” 定义横切逻辑,Spring中支持5种类型的Advice:即AOP在不改变原有代码的情况下,去增加新的功能。
通知类型连接点
Before(前置通知)通知方法在目标方法调用之前执行,前置通知不会影响目标方法的执行,除非此处抛出异常
After(后置通知)通知方法在目标方法返回或者异常后调用(必然会执行)
After-Returning(最终通知)通知方法在目标方法返回后调用,如果连接点抛出异常,则不会执行
After-Throwing(抛出异常后通知)通知方法在目标方法抛出异常后调用
Around(环绕通知)环绕目标方法的通知,例如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它可以选择是否继续执行目标方法或直接返回自定义的返回值又或抛出异常将执行结束。

注:目标方法就是连接点



1.4 使用 Spring 实现 AOP


1.4.1 方式一:使用 Spring API 接口实现 AOP 【主要是SpringAPI接口实现】


  • 创建 Maven 项目并在 pom.xml 中引入如下依赖(使用 AOP 织入需要此依赖)

    <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency>
    
    • 在service包下,定义UserService业务接口和UserServiceImpl实现类

      • UserService 接口

        public interface UserService {void add();void query();void delete();void update();
        }
        
      • UserServiceImpl 实现类

        public class UserServiceImpl implements UserService {@Overridepublic void add() {System.out.println("新增一个用户");}@Overridepublic void query() {System.out.println("查询用户信息");}@Overridepublic void delete() {System.out.println("删除一个用户");}@Overridepublic void update() {System.out.println("修改用户信息");}
        }
        
    • 在 log 包下定义 一个前置增强和一个后置增强类

      • 前置通知增强类

        import org.springframework.aop.MethodBeforeAdvice;
        import java.lang.reflect.Method;//MethodBeforeAdvice : 前置通知
        public class BeforeLog implements MethodBeforeAdvice {/** method:要执行的目标对象的方法* args:参数* target:目标对象* */@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName() + "的" + method.getName() + "被执行了");}
        }
        
      • 后置通知增强类

        import org.springframework.aop.AfterReturningAdvice;
        import java.lang.reflect.Method;// AfterReturningAdvice : 后置通知
        public class AfterLog implements AfterReturningAdvice {/** returnValue: 返回值* method:要执行的目标对象的方法* args:参数* target:目标对象* */@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);}
        }
        
    • 最后去 spring 的文件中注册 , 并实现aop切入实现 , 注意导入约束,配置 applicationContext.xml 文件

      <?xml version="1.0" encoding="UTF-8"?>
      <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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!--注册bean--><bean id="afterLog" class="com.sys.log.AfterLog"/><bean id="beforeLog" class="com.sys.log.BeforeLog"/><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!--配置 Spring aop:需要导入aop约束--><!--实现 aop 的方式一:使用原生 Spring API接口--><aop:config><!--配置切入点:execution 表达式:execution(要执行的位置!)execution(* *) :第一个 * 代表返回值的类型任意,第二个 * 代表类,如果为 * 就代表所有类execution(* com.sys.XXX.*(..)):.*(..):表示任何方法名,括号表示参数,两个点表示任何参数类型--><aop:pointcut id="pointcut" expression="execution(* com.sys.service.impl.UserServiceImpl.*(..))"/><!--配置 Advice(通知)--><!--前置加后置约等于环绕通知--><aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/><aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/></aop:config></beans>
      
      • Spring AOP切入点@Pointcut – execution表达式
    • 创建 MyTest 测试类

      public class MyText {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");// 需要注意的是:AOP的代理是动态代理,也就是此处代理的并不是实现类,而是实现类实现的接口UserService userService = (UserService) context.getBean("userService");userService.add();userService.update();userService.query();userService.delete();}
      }
      
    • 执行结果 :因为接口定义的方法都是 Void 所以没有返回值
      在这里插入图片描述



1.4.2 方式二:自定义类来实现 AOP【主要是切面定义】


  • 不使用 Spring 提供的 API 接口,使用自定义的 diy 类,搭配 xml 的 aop 配置实现

    • 创建 diy 工具类

      public class DiyPointCut {public void before(){System.out.println("======方法执行前======");}public void after(){System.out.println("======方法执行后======");}
      }
      
    • 创建 Beans.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 将 Diy 工具类注册到 SPring 容器种 --><bean id="diyPointCut" class="com.sys.diy.DiyPointCut"/><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!-- 配置 aop --><aop:config><!--自定义切面,ref 要引用的类--><aop:aspect ref="diyPointCut"><!--配置切入点--><aop:pointcut id="pointcut" expression="execution(* com.sys.service.impl.UserServiceImpl.*(..))"/><!--配置通知before:前置通知after:后置通知--><aop:before method="before" pointcut-ref="pointcut"/><aop:after method="after" pointcut-ref="pointcut"/></aop:aspect></aop:config></beans>
      
    • 修改 MyTest,其他代码内容不变

      public class MyTest {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");// 需要注意的是:AOP的代理是动态代理,也就是此处代理的并不是实现类,而是实现类实现的接口UserService userService = (UserService) context.getBean("userService");userService.add();System.out.println("-------------------------------------------------");userService.update();System.out.println("-------------------------------------------------");userService.query();System.out.println("-------------------------------------------------");userService.delete();}
      }
      
    • 执行结果



1.4.3 方式三:使用注解实现 AOP


  • @Aspect : 被该注解标注的类就是一个切面

  • @Before: 标注切面类中的方法为前置通知

    • 参数:需要传入切入点,即 execution 表达式
  • @After: 标注切面类中的方法为后置通知

    • 参数:需要传入切入点,即 execution 表达式
  • @Around: 标注切面类中的方法为环绕通知

    • 参数:需要传入切入点,即 execution 表达式

    • 被标记为环绕通知的方法还需要搭配 proceed() 来完成通知,如果没有该方法,那么切入点对应的方法不执行,也可以理解为这个方法就是执行切入点中的方法用的

      • proceed() : 通过这个方法判断切入点中的方法在环绕通知中的那个位置执行


  • 代码示例:(前置通知和后置通知)

    • 新增 Diy 切面类

      @Aspect // 将该类标记为一个切面
      public class AnnotationPointCut {// 将该方法标记为前置通知@Before("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void before(){System.out.println("=====方法执行前=====");}// 将该方法标记为后置通知@After("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void after(){System.out.println("=====方法执行后=====");}
      }
      
    • 修改 Beans.xml 开启注解支持

      <?xml version="1.0" encoding="UTF-8"?>
      <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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><bean id="userService" class="com.sys.service.impl.UserServiceImpl"/><!--方式三:使用注解--><bean id="annotationPointCut" class="com.sys.diy.AnnotationPointCut"/><!--开启aop注解支持! JDK(默认是 proxy-target-class="false")  cglib(proxy-target-class="true")--><aop:aspectj-autoproxy/></beans>
      
    • 其他代码不变,运行测试类,执行结果


  • 环绕通知代码示例

    • 修改 Diy 切面类

      @Aspect // 将该类标记为一个切面
      public class AnnotationPointCut {@Around("execution(* com.sys.service.impl.UserServiceImpl.*(..))")public void around(ProceedingJoinPoint jp) throws Throwable{System.out.println("环绕前");Signature signature = jp.getSignature();// 获得签名System.out.println("signature:"+signature);jp.proceed(); //执行方法System.out.println("环绕后");}}
      
    • 其他代码不变,运行测试类,执行结果

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

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

相关文章

掌握主动权:职场中如何主动寻找并拓宽工作领域

引言 在现代职场的竞争环境中&#xff0c;主动性是一种重要的优势。对于企业而言&#xff0c;员工的主动性是其成功的关键因素之一。而对于个人职业发展来说&#xff0c;主动性能帮助我们把握更多的机会&#xff0c;提升我们的职业能力。因此&#xff0c;学会在职场中主动寻找…

mybatisJava对象、list和json转换

1. 参考mybatis-plus mybatis Java对象、list和json转换 网上好多不靠谱&#xff0c;参考mybatis-plus中TableField&#xff0c;mybatis中自定义实现 这样不需要对象中属性字符串接收&#xff0c;保存到表中&#xff0c;都是转义字符&#xff0c;使用时还要手动转换为对象或者…

车载总线系列——J1939三

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 没有人关注你。也无需有人关注你。你必须承认自己的价值&#xff0c;你不能站在他人的角度来反对自己。人…

Golang之路---04 并发编程——信道/通道

信道/通道 如果说 goroutine 是 Go语言程序的并发体的话&#xff0c;那么 channel&#xff08;信道&#xff09; 就是 它们之间的通信机制。channel&#xff0c;是一个可以让一个 goroutine 与另一个 goroutine 传输信息的通道&#xff0c;我把他叫做信道&#xff0c;也有人将…

PLC4X踩坑记录

plc4x引起的oom 使用Jprofiler查看dump文件 由上可以看出有大量的NioEventLoop对象没有释放 PlcConnection#close 设备断连重连后导致的oom&#xff0c;看源码close方法主要是channel通道关闭。 修改NettyChannelFactory源码 plc4x设计思想是一个设备一个连接&#xff0c;…

k8s ingress获取客户端客户端真实IP

背景 在Kubernetes中&#xff0c;获取客户端真实IP地址是一个常见需求。这是因为在负载均衡架构中&#xff0c;原始请求的源IP地址会被替换成负载均衡器的IP地址。 获取客户端真实IP的需求背景包括以下几点&#xff1a; 安全性&#xff1a;基于客户端IP进行访问控制和认证授…

工厂模式(C++)

定义 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦&#xff0c;手段:虚函数)到子类。 应用场景 在软件系统中&#xff0c;经常面临着创建对象的工作;由于需求的变化&#xff0c;需要创建的对象的具体类…

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本

Spring Boot、Spring Cloud、Spring Alibaba 版本对照关系及稳定兼容版本 引言 在 Java 生态系统中&#xff0c;Spring Boot、Spring Cloud 和 Spring Alibaba 是非常流行的框架&#xff0c;它们提供了丰富的功能和优雅的解决方案。然而&#xff0c;随着不断的发展和更新&…

【ARM Coresight 系列文章 2.3 - Coresight 寄存器】

文章目录 Coresight 寄存器介绍1.1 ITCTRL&#xff0c;integration mode control register1.2 CLAIM寄存器1.3 DEVAFF(Device Affinity Registers)1.4 LSR and LAR1.5 AUTHSTATUS(Authentication Status Register) Coresight 寄存器介绍 Coresight 对于每个 coresight 组件&am…

架构训练营学习笔记:5-3接口高可用

序 架构决定系统质量上限&#xff0c;代码决定系统质量下限&#xff0c;本节课串一下常见应对措施的框架&#xff0c;细节不太多&#xff0c;侧重对于技术本质有深入了解。 接口高可用整体框架 雪崩效应&#xff1a;请求量超过系统处理能力后导致系统性能螺旋快速下降 链式…

STM32CubeMx之FreeRTOS的中断优先级+配置

编译运行即可 例如我编写的是一个灯亮500ms 一个等200ms的亮灭 如果他们的优先级是同等的&#xff0c;那么任务都可以实现&#xff0c;时间片会自动切换 但是如果亮500ms的灯 任务优先级更高 还用HALdelay的话 就会让任务二饿死&#xff0c;从而就会只看到任务一的内容 解…

回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测

回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测 目录 回归预测 | MATLAB实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SO-CNN-BiGRU蛇群算法…

Cilium系列-13-启用XDP加速及Cilium性能调优总结

系列文章 Cilium 系列文章 前言 将 Kubernetes 的 CNI 从其他组件切换为 Cilium, 已经可以有效地提升网络的性能. 但是通过对 Cilium 不同模式的切换/功能的启用, 可以进一步提升 Cilium 的网络性能. 具体调优项包括不限于: 启用本地路由(Native Routing)完全替换 KubeProx…

什么是微服务

微服务的架构特征&#xff1a; 单一职责&#xff1a;微服务拆分粒度更小&#xff0c;每一个服务都对应唯一的业务能力&#xff0c;做到单一职责自治&#xff1a;团队独立、技术独立、数据独立&#xff0c;独立部署和交付面向服务&#xff1a;服务提供统一标准的接口&#xff0…

九、pig安装

1.上传pig包 2.解压文件 3.改名 4.赋权 5.配置环境变量 export PIG_HOME/usr/local/pig export PATH$PATH:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HIVE_HOME/bin:$HBASE_HOME/bin:$SQOOP_HOME/bin:$PIG_HOME/bin 6.测试

开发运营监控

DevOps 监控使管理员能够实时了解生产环境中的元素&#xff0c;并有助于确保应用程序平稳运行&#xff0c;同时提供最高的业务价值&#xff0c;对于采用 DevOps 文化和方法的公司来说&#xff0c;这一点至关重要。 什么是开发运营监控 DevOps 通过持续开发、集成、测试、监控…

使用JProfiler进入JVM分析

要评测JVM&#xff0c;必须将JProfiler的评测代理加载到JVM中。这可以通过两种不同的方式发生&#xff1a;在启动脚本中指定-agentpath VM参数&#xff0c;或者使用attach API将代理加载到已经运行的JVM中。 JProfiler支持这两种模式。添加VM参数是评测的首选方式&#xff0c;集…

Maven项目中Lifecycle和Plugins下的install的区别

在Maven中&#xff0c;如果你的web和service在不同的模块下&#xff0c;如果直接用用tomcat插件运行web层&#xff0c;那么运行时会报错 Failed to execute goal org.apache.maven.plugins:maven-install-plugin:2.5.2:install (default-cli) on project springboot: The pack…

常用SQL语句总结

SQL语句 文章目录 SQL语句1 SQL语句简介2 DQL&#xff08;数据查询语句&#xff09;3 DML&#xff08;数据操纵语句&#xff09;4 DDL&#xff08;数据定义语句&#xff09;5 DCL&#xff08;数据控制语句&#xff09;6 TCL&#xff08;事务控制语句&#xff09; 1 SQL语句简介…

nginx网站服务

nginx&#xff1a;是一个高性能&#xff0c;轻量级web软件 1、稳定性高&#xff08;没有Aapache稳定&#xff09; 2、资源消耗比较低&#xff0c;体现在处理http请求的并发能力很高&#xff0c;单台物理服务器可以处理到3万-5万个请求 稳定&#xff1a;一般在企业中为了保持…