SpringAOP形象化解读
注:此文章是为了防止找不到作者博客而搬运的,想观看感更强可去此文底部找到原文地址
Spring的AOP
Spring通过动态代理模式的实现后,我们可以定义AOP其实就是用于通过规则设置来拦截方法,加入可以统一处理的代码。
关于代理的选择
在spring中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。
AOP相关术语
Joinpoint(连接点):
所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
---就是根据规则,可以指定拦截的方法,我们将每一个被拦截的方法称为连接点。
Pointcut(切入点):
–所谓的切入点,就是拦截方法设置的规则
所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。一连串方法。
Advice(通知/增强):
–就是可以设置在方法之前拦截或者方法执行之后拦截或者方法出异常后拦截,或者方法之前和之后都拦截。我们将这些拦截场景称为通知
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
Aspect(切面):
–所谓的切面就是我们的拦截处理类。
是切入点和通知的结合。
Weaving(织入):
-把切面加入到对象,并创建出代理对象的过程。(该过程由Spring来完成)
配置AOP
第一步:创建一个Java项目
需求:编写一个切面类,在执行insert,update方法,分别在方法执行前、方法之后、异常出现后、最终方法执行前后调用编写统一处理的代码,
创建一个Java项目,加入Spring框架的基础支持包和aop依赖包
第二步:编写业务层类和接口
--UserServiceImpl
package cn.zj.spring.service.impl;
import cn.zj.spring.pojo.User;
import cn.zj.spring.service.UserService;
public class UserServiceImpl {
public void insert(User user) {
System.out.println("执行dao的层 insert方法");
//System.out.println(1/0);
}
public void updateByPrimaryKey(User user) {
System.out.println("执行dao的层 update方法");
}
}
第三步:编写模拟事务管理器类
package cn.zj.spring.utils;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
/*
- 此类模拟事务管理器(专门处理事务的类)
- 提供,开启,提交,回滚,关闭事务相关操作的方法
*/
public class TransactionManagerHandler {
public void begin(JoinPoint jp) {
System.out.println("连接点:"+jp);
System.out.println("args :"+Arrays.toString(jp.getArgs()));
System.out.println("开启事务");
}
public void commit() {
System.out.println("提交事务");
}
public void rollback(Throwable ex) {
System.out.println("ex :"+ex.getMessage());
System.out.println("回滚事务");
}
public void close() {
System.out.println("释放资源");
}
}
第四步:编写applicationContext.xml
<!-- 配置模拟的事务管理器 -->
<bean id="MyTxManager" class="cn.zj.spring.utils.TransactionManagerHandler"/>
<!-- 被代理对象:真实对象 -->
<bean id="userService" class="cn.zj.spring.service.impl.UserServiceImpl"/>
<!--
开始AOP配置
-->
<!-- 开始AOP配置 -->
<aop:config>
<!--
<aop:pointcut expression="" id=""/> 切入点标签
id :唯一标示
expression : 切入点的表达式
AOP的切入点有专门切入点语法
-->
<!-- where ? -->
<aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt"/>
<!--
配置切面
切面 = 切入点 +通知
<aop:aspect ref="">
ref :引入 要增强的功能类,当前就是模拟的事务管理器
-->
<aop:aspect ref="MyTxManager">
<!--
<aop:before method="" pointcut-ref=""/>
配置前置通知/增强
method : 模拟事务管理器对应的方法名
pointcut-ref : 切入点引用
-->
<aop:before method="begin" pointcut-ref="pt"/>
<!-- 后置通知 -->
<aop:after-returning method="commit" pointcut-ref="pt"/>
<!-- 异常通知
<aop:after-throwing method="rollback" throwing="" pointcut-ref="pt"/>
throwing : 抛出的异常,异常可以抛给增强的方法中
异常类型 Throwable
值必须和对应方法中异常的参数名称相同
-->
<aop:after-throwing method="rollback" throwing="ex" pointcut-ref="pt"/>
<!-- 最终通知,相当于finaly -->
<aop:after method="close" pointcut-ref="pt"/>
</aop:aspect>
<!-- Weaving : 织入 运行过程中spring完成 -->
</aop:config>
第五步:测试代码
package cn.zj.spring.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import cn.zj.spring.pojo.User;
import cn.zj.spring.service.UserService;
import cn.zj.spring.service.impl.UserServiceImpl;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserServiceTest {
@Autowired
private UserServiceImpl userService;
@Test
public void testInsert() {
User user = new User(null, "钢铁侠", "gtx@qq.com");
System.out.println(userService.getClass().getName());
//有接口 : com.sun.proxy.$Proxy13
//没有接口 :cn.zj.spring.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$ea3c2726
userService.insert(user);
}
@Test
public void testUpdateByPrimaryKey() {
User user = new User(2, "蜘蛛侠", "zhizhuxia@qq.com");
userService.updateByPrimaryKey(user);
}
@Test
public void testName() throws Exception {
userService.toString();
}
}
测试结果
AOP常用标签
aop:config
作用:
用于声明开始aop的配置
aop:aspect
作用:
用于配置切面。
属性:
id:给切面提供一个唯一标识。
ref:引用配置好的通知类bean的id。
aop:pointcut
作用:
用于配置切入点表达式
属性:
expression:用于定义切入点表达式。
id:用于给切入点表达式提供一个唯一标识。
aop:before
作用:
用于配置前置通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
aop:after-returning
作用:
用于配置后置通知,如果出了异常就一定不会调用切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
aop:after-throwing
作用:
用于配置异常通知,只有出了异常才会调用切面对应的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
aop:after
作用:
用于配置最终通知,不管出不出异常,调用的切面的方法
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
aop:around
作用:
用于配置环绕通知
属性:
method:指定通知中方法的名称。
pointct:定义切入点表达式
pointcut-ref:指定切入点表达式的引用
切入点表达式说明
匹配方法的执行(常用)
execution(表达式)
表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))
写法说明:
全匹配方式:
public void cn.zj.service.impl.CustomerServiceImpl.saveCustomer()
访问修饰符可以省略
void com.zj.service.impl.CustomerServiceImpl.saveCustomer()
返回值可以使用*号,表示任意返回值
- com.zj.service.impl.CustomerServiceImpl.saveCustomer()
包名可以使用号,表示任意包,但是有几级包,需要写几个 - ....CustomerServiceImpl.saveCustomer()
使用..来表示当前包,及其子包 - com..CustomerServiceImpl.saveCustomer()
类名可以使用*号,表示任意类 - com...saveCustomer()
方法名可以使用号,表示任意方法 - com...()
参数列表可以使用*,表示参数可以是任意数据类型,但是必须有参数 - com...(*)
参数列表可以使用..表示有无参数均可,有参数可以是任意类型 - com...(..)
全通配方式: - ...*(..)
环绕通知
TransactionManagerHandler
package cn.zj.spring.utils;
import org.aspectj.lang.ProceedingJoinPoint;
/*
- 此类模拟事务管理器(专门处理事务的类)
- 提供,开启,提交,回滚,关闭事务相关操作的方法
*/
public class TransactionManagerHandler {
public void allInOne(ProceedingJoinPoint jp) {
try {
System.out.println("开启事务----------------");
//执行真实对象的方法
jp.proceed();
System.out.println("提交事务-----------------");
} catch (Throwable e) {
System.out.println("回滚事务-------------------");
}finally {
System.out.println("释放资源------------------");
}
}
}
applicationContext.xml
<!-- 配置模拟的事务管理器 -->
<!-- what? -->
<bean id="MyTxManager" class="cn.zj.spring.utils.TransactionManagerHandler"/>
<!-- 被代理对象:真实对象 -->
<bean id="userService" class="cn.zj.spring.service.impl.UserServiceImpl"/>
<!--
开始AOP配置
配置AOP遵循 WWW原则
w :what ?干什么?
w :where ? 地点?
w :when ? 时机?
-->
<!-- 开始AOP配置 -->
<aop:config>
<!--
<aop:pointcut expression="" id=""/>
id :唯一标示
expression : 切入点的表达式
AOP的切入点有专门切入点语法
-->
<!-- where ? -->
<aop:pointcut expression="execution(* cn.zj.spring.service..*.*(..))" id="pt"/>
<!--
配置切面
切面 = 切入点 +通知
<aop:aspect ref="">
ref :引入 要增强的功能,当前就是模拟的事务管理器
-->
<aop:aspect ref="MyTxManager">
<!-- 环绕通知 -->
<!-- <aop:around method="allInOne" pointcut="execution(* cn.zj.spring.service..*.*(..))"/> -->
<aop:around method="allInOne" pointcut-ref="pt"/>
</aop:aspect>
<!-- Weaving : 织入 运行过程中spring完成 -->
</aop:config>
————————————————
版权声明:本文为CSDN博主「搞钱自律」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43472934/article/details/108055833
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理