Spring AOP的概念与实现过程详解
作者:绿仔牛奶_ 发布时间:2023-02-17 02:54:12
Aop
什么是Aop?
AOP就是面向切面编程,通过预编译方式以及运行期间的 * 技术来实现程序的统一维护功能。
什么是切面,我理解的切面就是两个方法之间,两个对象之间,两个模块之间就是一个切面。假设在两个模块之间需要共同执行一系列操作,并且最后将这一系列操作注入到两个模块之间的指定位置。此时这一系列操作就是切面,注入这些操作的位置称之为切点。
举例:公司员工上班
A员工上班需要在前台进行打卡,同样的B员工…其他员工都需要在前台打卡,那么如果为每一位员工提供单独的打卡通道就有些过于浪费资源。像这样
于是,在前台这个位置设置接口,声明公共的打卡方法,所有员工共同通过该接口进行打卡,那么在打卡时的一系列校验或者记录的整个操作就可以被称之为切面,前台这个空间位置就被称之为切点,如下图所示
aop主要作用就是进行日志记录、事务/异常处理等功能以便更好地维护开发,主要目的就是将这些行为从项目的业务逻辑代码中分离出来并且降低各个业务逻辑模块之间的耦合度。保证在执行aop操作的同时不会影响到项目的业务逻辑代码
几个概念
Aspect
:切面 Aspect中会包含一些pointCut切入点以及一些Advice
Advice
:通知 切面需要完成的工作,通过before、after、around来区别是在连接点之前或者之后 或者环绕
Target
:目标 目标对象,该对象会被织入advice
PointCut
:切点 即切面通知执行的地点 advice将会在这里发生
JointPoint
:连接点 所有方法的执行点
PointCut用来修饰JointPoint,PointCut是advice执行的点,而JointPoint表示所有方法的执行点,通过PointCut可以确定哪些JointPoint是可以被织入的点
我们通常不希望advice会在所有的JointPoint点执行,PointCut的作用就是可以进行校验来更精准的匹配执行点。简单概括就是Jointpoint可以执行但未必执行,只有PointCut匹配到了JointPoint才可以在该点执行
Aspect切面可以理解为PointCut+Advice
使用AOP织入导入包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.7</version>
<scope>runtime</scope>
</dependency>
实现aop方式一
使用spring内置的API接口
准备:UserService、UserServiceImpl简单实现CRUD
在spring核心配置文件applicationContext.xml中配置aop:
<?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/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userServiceImpl" class="com.mount.service.UserServiceImpl"/>
<bean id="log" class="com.mount.log.log"/>
<bean id="afterLog" class="com.mount.log.AfterLog"/>
<!-- 配置AOP -->
<aop:config>
<!-- 切入点 expression表达式 表示从哪里开始执行 -->
<aop:pointcut id="pointcut" expression="execution(* com.mount.service.UserServiceImpl.*(..))"/>
<!-- 执行环绕增强 -->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
</beans>
编写执行前后日志
// 执行前 实现spring内置接口MethodBeforeAdvice
public class log implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"执行了"+method.getName()+"方法");
}
}
// 执行后 实现AfterReturningAdvice
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println(target.getClass().getName()+"调用了"+method.getName()+"方法"+"返回的结果为"+returnValue);
}
}
测试:
@Test
public void test01(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = context.getBean("userServiceImpl", UserService.class);
bean.insert();
}
实现aop方式二
使用自定义类,只需要在applicationContext.xml中重新配置aop,并且自己diy一个类即可,在配置时可以选择任一方法为前置日志或后置日志即可
<!-- 配置AOP 方式二 -->
<bean id="diy" class="com.mount.diyLog.diyLogImpl"/>
<aop:config>
<!-- 自定义切面 ref引入的类 -->
<aop:aspect ref="diy">
<aop:pointcut id="pointcut" expression="execution(* com.mount.service.UserServiceImpl.*(..))"/>
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
diylog类
public class diyLog {
public void before(){
System.out.println("===before方法执行===");
}
public void after(){
System.out.println("===after方法执行===");
}
}
注解实现aop
首先需要在applicationContext.xml文件中打开SpringAOP对注解的支持
<!-- SpringAop开启注解支持 -->
<aop:aspectj-autoproxy/>
<!-- 映射自定义注解实现log类 -->
<bean id="annoLog" class="com.mount.annoLog.annoLogImpl"/>
annoLog
// Aspect标注该类是一个切面
@Aspect
public class annoLogImpl {
// 前置增强
@Before("execution(* com.mount.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---方法执行前---");
}
// 后置增强
@After("execution(* com.mount.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---方法执行后---");
}
// 环绕增强
@Around("execution(* com.yuqu.dao.UserMapperImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前");
Object proceed = pjp.proceed();
System.out.println("环绕后");
System.out.println("执行信息:"+pjp.getSignature());
return proceed;
}
}
最终打印:
环绕前
---方法执行前---
删除成功!
---方法执行后---
方法签名Integer com.mount.service.UserService.delete()
方法执行返回=1
环绕后
注意around环绕增强,如果我们执行的sql中是有返回值的话,那么必须显式的将pjp.proceed();返回回去,否则在调用处将会无法获取到结果集,报空指针异常
可以发现,around环绕增强首先执行,在执行到joinPoint.proceed()
时,会执行对应方法,执行对应方法的时候才会执行前置或后置的其他增强操作
来源:https://blog.csdn.net/yuqu1028/article/details/129017916


猜你喜欢
- 一、@RequestMapping注解的功能从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方
- break和continue的说明break 循环结构,一旦执行,就结束(或跳出)当前循环结构,此关键字的后面,不能
- CSV是一种通用的、相对简单的文件格式,最广泛的应用是在程序之间转移表格数据,而这些程序本身是在不兼容的格式上进行操作的。那么,C#如何读取
- 本文实例讲述了C#获取每个年,月,周的起始日期和结束日期的方法。分享给大家供大家参考,具体如下:我们在写程序的时候往往要计算出年,月,周的开
- 本文实例讲述了java使用归并删除法删除二叉树中节点的方法。分享给大家供大家参考。具体分析如下:实现的思想很简单:first:找到要删除的节
- 一、总体说明 XML和JSON 是最为常用的数据交换格式本例子演示如何将java对象,转成XML输出。二、流程1.在上文的例子中,创建一个包
- 一、背景TC 集群具有高可用架构,应用到集群是这样一个间接的关系:应用 -》事务分组 -》TC 集群,应用启动后所指定的事务分组不能变,可通
- 本文实例讲述了java在网页上面抓取邮件地址的方法。分享给大家供大家参考。具体实现方法如下:import java.io.BufferedR
- 详解Android使用@hide的API的方法今天早上想修改MediaPlaybackService.Java(/packages/apps
- 提示出现unresolved external symbol _main搜了下找了下原因如下在创建MFC项目时
- 1 StringString:字符串常量,字符串长度不可变。2 StringBufferStringBuffer:字符串变量(Synchro
- 本文实例为大家分享了Java单例模式利用HashMap实现缓存数据的具体代码,供大家参考,具体内容如下一、单例模式是什么?单例模式是一种对象
- 安装JDK/安装JRE以及配置java环境变量对于java初学者来说是一件比较头疼的事情,这边分享一个简单的批处理命令,助大家一步完成JDK
- 本文实例讲述了Spring实战之清除缓存操作。分享给大家供大家参考,具体如下:一 配置文件<?xml version="1.
- 先来看两段代码: Thread t = new Thread(() => { AddIt AddDelegate = new
- 本文实例总结了Android TextView字体颜色设置方法。分享给大家供大家参考,具体如下:对于setTextView(int a)这里
- 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程:首先温馨提示, 这一小节高度耦合第三章的
- 前言项目里有个功能,在应用内切换语言,之前上线了大半年,一直都是正常运行,但是最近这次发版以后,在国外的同事反馈(这里面还包括CEO...
- 一、前言在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试。这些都是通过对同一时刻请求数量
- 效果图:为了使图片浏览器左右无限循环滑动 我们要自定义gallery的adapter如果要想自定义adapter首先要了解这几个方法@Ove