Spring AOP理解与研发使用

Spring AOP理解与研发使用SpringAOP理解与研发使用:基本理论(基本术语总结+具体开发注意事项+切点正则和指示器规则)+AOP开发应用与分析

大家好,又见面了,我是你们的朋友全栈君。

一、前言

Spring AOP是一种基于方法的AOP,只能用在方法上,在业务上我们一般使用Spring AOP去约定编程一套业务逻辑织入到相关的业务处理中,并抽取通用逻辑默认加入到相关业务中(前置处理后置业务处理或异常处理等),同时在应用上在想对一些现成业务进行干预处理时都会使用(比方说可以考虑对特定方法进行mock操作等处理)。Spring AOP和我们平时开发中使用的约定编程基本类似,本质都是通过约定对相应的方法通过动态代理技术织入约定流程中。本次一些基本的理念还是直接通过自己看过的一些书籍和博客进行总结了,实际应用上基本是结合自己的开发经验进行了一些基本的应用分享,如果有理解错误的地方请留言指正,谢谢!

二、Spring AOP基本理论知识

(一)基本术语总结

术语 基本概念
连接点(join point) 具体被拦截的对象,往往指的就是特定的方法,也可以指某个包路径下的所有类下的所有方法(进行mock干预经常这样干)或直接就是某个类下面的所有方法
切点(point cut) 切面作用的方法不只是单一的,可以通过正则和指示器的规则去定义,适配连接点
通知(advice)

按照约定的流程方法,根据约定织入流程中。

分类:前置通知、后置通知、环绕通知、事后反悔通知、异常通知

目标对象(target) 即被代理对象,也就是上面连接点(join point)中的指定的类
引入(introduction) 增加现有Bean的功能,引入新的类和方法
织入(weaving) 通过动态代理,为原对象生成代理对象,然后与切点定义匹配的连接点拦截,按约定将各类通知织入约定流程中
切面(aspect) Spring AOP通过切面信息来增加Bean的功能或将对应方法织入流程,在切面中定义切点、各类 通知和引入内容

(二)具体开发注意事项

有相关疑问的可以留言直接问,以下内容,只是我平时觉得重点的应用注意事项总结:

  1. ProceedingJoinPoint,继承自连接点JoinPoint,实际作用上就是我们指定的具体某个类或某些类,本质上连接点就是什么类的什么方法
  2. 具体切面采用@Aspect注解标注,切面中定义我们想要约定实现的基本内容,以及相关的通知@Before、@After、@AfterReturning、@AfterThrowing、@Around,各通知中需要统一指明切点,一般我们都是直接定义切点@Pointcut,然后直接在各通知中进行引入即可。
  3. @Around是我们业务干预中的重点流程改造,一般主要干预处理的内容都在该通知中实现,处理上务必重点分析处理逻辑,代码上做兜底是必须操作。
  4. @DeclareParents业务处理中针对老逻辑进行干预并增加功能时,可以考虑采用引入方式处理,即通过该注解指明两个主要参数value和defaultImpl,value指明要增强的目标对象,defaultImpl指引入增强功能的类,具体我们后续案例介绍下。
  5. 非环绕通知中对于参数的处理直接使用JoinPoint(当然也可以不用),例如前置打印参数等内容,环绕通知则使用ProceedingJoinPoint,在使用上需要注意。
  6. 如果多个切面作用的类和方法是一样的,则切面的执行顺序是无序的,如果切面之间需要有先后顺序的的话,需要使用@Order注解或实现Ordered接口实现,数字越小优先级越高,实际处理上如果既有前置通知又有后置通知,则可以看到前置从小到大执行,后置从大到小执行,这说明其内部是一个典型的责任链模式(其应用具体见:)。

(三)切点正则和指示器规则

平时开发时对于切点的定义较多,如果已经很明确处理的切点的话则不需要进行复杂正则配置,但是如果是多类多方法时则正则是必须的。一般我们常用的AspectJ的指示器主要如下表:

AspectJ指示器 描述
arg() 限制连接点匹配参数为制定类型的方法
@args() 限制而连接点匹配参数为指定注解标注的只是方法
execution 用于匹配连接点的执行方法
this() 限制连接点匹配AOP代理的Bean,引用为指定类型的类
target 限制连接点匹配被代理对象为指定的类型
@target 限制连接点匹配特定的执行对象,这些对象要符合指定的注解类型
@within() 限制连接点匹配指定的类型
within() 限制连接点匹配指定的包
@annotation 限定匹配带有指定注解的连接点

基本使用举例如下

联合使用切点指示器

/*匹配任意public访问修饰符修饰的方法*/
@Pointcut("execution(public * (..))")
private void anyPublicOperation() {} 

/*匹配包名以com.zyf.javabasic.trading开头的任意类的所有方法*/
@Pointcut("within(com.zyf.javabasic.trading..)") 
private void inTrading() {} 

/*匹配以com.zyf.javabasic.trading开头的任意类中任意以public访问修饰符修饰的方法*/
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} 

共享切点定义

/*匹配定义在web层且目标对象在com.zyf.javabasic.web包及其子包中的所有类的任意方法*/
@Pointcut("within(com.zyf.javabasic.web..)")
public void inWebLayer() {}

/*匹配定义在service层且目标对象在com.zyf.javabasic.service包及其子包中所有类中的任意方法*/
@Pointcut("within(com.zyf.javabasic.service..)")
public void inServiceLayer() {}

/*匹配定义在数据访问层且目标对象在com.zyf.javabasic.dao包及其子包中所有类中的任意方法*/
@Pointcut("within(com.zyf.javabasic.dao..)")
public void inDataAccessLayer() {}

/*匹配数据库访问层中目标对象在com.zyf.javabasic.dao..(..)及其子包中的任意方法*/
@Pointcut("execution( com.zyf.javabasic.dao..(..))")
public void dataAccessOperation() {}

切点表达式解读与使用示例

/*匹配任意公共方法*/
execution(public * *(..)) 

/*匹配任意方法名以set字符开头的方法*/ 
execution(* set*(..)) 
    
/*匹配com.xyz.service.AccountService类下任意方法*/
execution(* com.xyz.service.AccountService.*(..)) 
    
/*匹配com.zyf.service包及其子包下任意类的所有方法*/     
execution(* com.zyf.service.*.*(..))

/*匹配com.zyf.service包下任意接口的所有方法*/
within(com.zyf.service.*) 

/*匹配com.zyf.service包及其子包下任意类的所有方法*/
within(com.zyf.service..*) 

/*匹配实现AccountService接口的代理的任意方法*/
this(com.zyf.service.AccountService) 

/*匹配实现AccountService接口的目标对象的任意方法*/ 
target(com.zyf.service.AccountService)

/*匹配只带一个参数的方法,且该参数为可序列化参数*/
args(java.io.Serializable) 
 
/*匹配具有Transactional的目标对象任意方法*/
@target(org.springframework.transaction.annotation.Transactional)

/*匹配目标对象声名有Transactional的方法*/  
 @within(org.springframework.transaction.annotation.Transactional)

/*匹配带有个参数的方法,且运行时参数类型绑定有Classified注解*/
@args(com.zyf.security.Classified)
 
/*匹配Spring容器中id或name属性值为tradeService的bean实例的方法*/
bean(tradeService)
 
/*匹配Spring容器中id或name属性值以Service结尾的bean实例的方法*/
bean(*Service)

三、AOP开发应用与分析

(一)方法自动打印出入参举例

(二)mock赋能干预举例

(三)场景业务处理举例

(四)异步业务处理举例

(五)@DeclareParents使用举例

参考文献、书籍和链接

1.杨开振,深入浅出Spring Boot 2.X,中国工信出版社集团/人民邮电出版社,2015.10.

2.Spring AOP详解及其用法(一)_heshengfu1211的博客-CSDN博客_auditable注解

3.Spring AOP 面向切面编程_劳资是学渣丶的博客-CSDN博客

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/158125.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • nbsp啥意思_UG拆分体

    nbsp啥意思_UG拆分体转载:http://blog.csdn.net/olei_oleitao/article/details/7919307 一、DM36X的BOOT过程介绍DM36x的BOOT过程和DM6446、DM6467完全是一样的,因为都是ARM926EJS架构,里边都有一个RBL,这RBL在芯片出厂的时候都烧写在ROM里,芯片上电复位后RBL在运行,然后读取BOOTMODE引脚的电平状态,决定

  • Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

    Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?今天打开centos7,由于没设置开机自启,当我启动docker时,发现与docker有关的命令都会报错,百度过,说什么重装docker啥的,我都试过,但是还是解决不了问题,下面我贴出问题集:1.例如我查看版本信息2.重启容器3.systemctlstatusdocker查看docker状态也发现错误:最后解决问题方法是:进入/etc/docker,没有daemon.json文件就自己新建一个:cd/etc/docker编辑daemon.json文件:加入这段代码:

  • matlab下的AIC和SIC准则,求助:AIC准则和SC准则不一致情况下如何判断滞后期?

    matlab下的AIC和SIC准则,求助:AIC准则和SC准则不一致情况下如何判断滞后期?Includedobservations:3994LagLogLLRFPEAICSCHQ025192.43NA1.14e-08-12.61414-12.61099-12.613021…

  • 一分钟速算_最快的计算机1秒能计算

    一分钟速算_最快的计算机1秒能计算WIN7以上的系统可以直接运行,XP的系统要按装.NET2.0程序下载  http://files.cnblogs.com/xe2011/%E4%B8%80%E7%A7%92%E9%92%9F%E5%BF%83%E7%AE%97.rar更新历史转载于:https://www.cnblogs.com/xe2011/p/3782337.html…

    2022年10月27日
  • WPF 入门教程WrapPanel介绍「建议收藏」

    WPF 入门教程WrapPanel介绍「建议收藏」WrapPanel将定位每个子控件的旁边,另外,水平方向(默认值)或垂直,直到没有更多的空间,在那里将换到下一行,然后继续。当您想要一个垂直或水平列表控件在没有更多空间时自动换行时使用它。当WrapPanel使用Horizo​​ntal方向时,子控件将被赋予相同的高度,基于最高的项目。当WrapPanel为垂直方向时,子控件将被赋予相同的宽度,基于最宽的项目。在第一个示例中,我们将检查具有默认(水平)方向的WrapPanel:<Windowx:Class=”WpfTu

  • linux下的文件io编程实现文件的拷贝[通俗易懂]

    linux下的文件io编程实现文件的拷贝[通俗易懂]/*copy_file.c*/#include#include#include#include#include#include#define BUFFER_SIZE 1024 /*每次读写缓存大小,影响运行效率*/#defineSRC_FILE_NAME “src_file” /*源文件名*/#defineDEST_FILE_NAME “dest_

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号