论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
ToB企服应用市场:ToB评测及商务社交产业平台
»
论坛
›
软件与程序人生
›
DevOps与敏捷开发
›
从XML配置角度理解Spring AOP
从XML配置角度理解Spring AOP
张国伟
金牌会员
|
2024-5-19 23:41:22
|
显示全部楼层
|
阅读模式
楼主
主题
815
|
帖子
815
|
积分
2445
本文分享自华为云社区《
Spring高手之路18——从XML配置角度理解Spring AOP
》,作者: 砖业洋__。
1. Spring AOP与动态代理
1.1 Spring AOP和动态代理的关系
Spring AOP使用动态代理作为其主要机制来实现面向切面的编程。这种机制允许Spring在运行时动态地创建代理对象,这些代理对象包装了目标对象(即业务组件),以便在调用目标对象的方法前后插入额外的行为(如安全查抄、事件管理、日志记录等)。
JDK动态代理:当目标对象实现了一个或多个接口时,Spring AOP默认使用JDK的动态代理。JDK动态代理通过反射机制,为接口创建一个代理对象,这个代理对象会拦截对目标接口方法的所有调用。
CGLIB代理:如果目标对象没有实现任何接口,Spring AOP会退回到使用CGLIB库生成目标类的子类。CGLIB(Code Generation Library)是一个强大的高性能代码生成库,它在运行时扩展了Java类,并在子类中覆盖了方法来实现方法拦截。
无论使用哪种代理方式,目的都是在不改变原有业务逻辑代码的基础上,通过切面定义的通知在方法执行的不同阶段插入附加行为。
1.2 AOP基本术语
切面(Aspect):切面是面向切面编程的核心,它是将横跨多个类的关注点(如日志记录、事件管理等)模块化的构造。一个切面可以包罗多种范例的通知(Advice)和一个或多个切点(Pointcut),用于定义在何处以及何时执行这些通知。
毗连点(Join Point):毗连点代表程序执行过程中的某个特定位置,Spring AOP限定这些位置为方法的调用。简而言之,毗连点就是能够插入切面通知的点。
通知(Advice):通知定义了切面在毗连点上要执行的动作。根据通知范例的不同,这些动作可以在方法调用之前、之后、返回结果后或抛出异常时执行。通知范例包括:
前置通知(Before advice):在方法执行之前执行。
后置通知(After advice):在方法执行后执行,无论其结果怎样。
返回后通知(After-returning advice):在方法成功执行之后执行。
异常后通知(After-throwing advice):在方法抛出异常后执行。
环绕通知(Around advice):在方法执行之前和之后执行,提供对方法调用的全面控制。
切点(Pointcut):切点是一个表达式,切点表达式允许通过方法名称、访问修饰符等条件来匹配毗连点,决定了通知应该在哪些方法执行时触发。
目标对象(Target Object):被一个或多个切面所通知的对象。也被称为被代理对象。
AOP代理(AOP Proxy):AOP框架创建的对象,用于实现切面契约(由通知和切点定义)。在Spring AOP中,AOP代理可以是JDK动态代理或CGLIB代理。
引入(Introduction):引入允许向现有的类添加新的方法或属性。这是通过定义一个或多个附加接口(Introduction interfaces)实现的,AOP框架会为目标对象创建一个代理,该代理实现这些接口。
如果照旧觉得抽象,我们再举一个影戏制作的例子来类比
切面(Aspect)
想象一下,有人正在拍摄一部影戏,而影戏中的殊效(比如爆炸和特别光效)就像是应用程序中需要处理的横切关注点(比如日志记录或事件管理)。这些殊效会在影戏的很多不同场景中出现,而不仅仅范围于某一个特定场景。在AOP中,这些“殊效”就是切面,它们可以被应用到程序的多个部分,而不需要改变实际的场景(或代码)。
毗连点(Join Point)
继承使用影戏的比喻,每个场景中的特定时刻,比如一个爆炸发生的瞬间,可以看作是一个毗连点。在编程中,这通常对应于方法的调用。
通知(Advice)
通知就像是导演对殊效团队的具体指令,比如“在这个场景开始之前参加一个爆炸结果”或“场景结束后显示烟雾渐散的结果”。这些指令告诉殊效团队在影戏的哪个具体时刻应该添加特定的结果。在AOP中,这些“指令”就是通知,指定了切面(殊效)应该在毗连点(特定的代码执行时刻)之前、之后或周围执行。
切点(Pointcut)
如果说通知是导演对殊效团队的指令,那么切点就是指令中包罗的具体条件,比如“所有夜晚的外景戏”。切点定义了哪些毗连点(比如哪些具体的方法调用)应该吸收通知(殊效指令)。
目标对象(Target Object)
目标对象就是那些需要添加殊效的场景。在我们的编程比喻中,它们是那些被切面逻辑影响的对象(比如需要日志记录的类)。
AOP代理(AOP Proxy)
AOP代理就像是殊效团队提供的一个虚拟的、可控制殊效的场景副本。这个副本在观众看来与原场景无异,但实际上它能在导演需要的时刻自动添加殊效。在编程中,代理是一个被AOP框架自动创建的对象,它包装了目标对象,确保了通知(殊效指令)在精确的时间被执行。
引入(Introduction)
引入就好比是在影戏中参加一个全新的脚色或者场景,这在原本的脚本中并不存在。在AOP中,引入允许我们向现有的类添加新的方法或属性,这就像是在不改变原始脚本的情况下扩展影戏的内容。
2. 通过XML配置实现Spring AOP
Spring提供了丰富的AOP支持,可以通过XML配置来定义切面、通知(advice)和切点(pointcuts)。这样可以在不修改源代码的情况下增长额外的行为(如日志、事件管理等)
实现步骤:
添加Spring依赖:在项目的pom.xml中添加Spring框架和AOP相关的依赖。
定义业务接口和实现类:创建业务逻辑接口及其实现,比如一个简单的服务类。
定义切面类:创建一个切面类,用于定义前置、后置、环绕等通知。
配置XML:在applicationContext.xml中配置切面和业务bean,以及AOP相关的标签。
2.1 添加Spring依赖
在pom.xml文件中,添加以下依赖
<dependencies>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><dependency>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><groupId>org.springframework</groupId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><artifactId>spring-context</artifactId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><version>5.3.10</version>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config></dependency>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><dependency>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><groupId>org.springframework</groupId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><artifactId>spring-aop</artifactId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><version>5.3.10</version>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config></dependency>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><dependency>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><groupId>org.aspectj</groupId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><artifactId>aspectjweaver</artifactId>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><version>1.9.6</version>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config></dependency>
</dependencies>
复制代码
2.2 定义业务接口和实现类
起首,我们定义一个业务逻辑接口MyService和它的实现MyServiceImpl。
MyService.java:
package com.example.demo.aop;
public interface MyService {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>String performAction(String input) throws Exception;
}
复制代码
MyServiceImpl.java:
package com.example.demo.aop;
public class MyServiceImpl implements MyService {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>@Override
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public String performAction(String action) throws Exception {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("Performing action in MyService: " + action);
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>if ("throw".equals(action)) {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>throw new Exception("Exception from MyService");
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>return "Action performed: " + action;
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
}
复制代码
2.3 定义切面类
接下来,我们定义一个切面类MyAspect,这个类将包罗一个前置通知(advice),它在MyService的performAction方法执行之前执行。
MyAspect.java:
package com.example.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>// 前置通知
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public void beforeAdvice() {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("Before advice is running!");
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>// 后置通知
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public void afterAdvice() {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("After advice is running!");
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>// 返回后通知
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public void afterReturningAdvice(Object retVal) {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("After returning advice is running! Return value: " + retVal);
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>// 异常后通知
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public void afterThrowingAdvice(Throwable ex) {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("After throwing advice is running! Exception: " + ex.getMessage());
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>// 环绕通知
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("Around advice: Before method execution");
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>Object result = null;
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>try {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>result = joinPoint.proceed();
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>} finally {
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("Around advice: After method execution");
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>return result;
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}
}
复制代码
2.4 配置XML
末了,我们需要在Spring的配置文件applicationContext.xml中配置上述bean以及AOP的相关内容。
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> xmlns:aop="http://www.springframework.org/schema/aop"
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> xsi:schemaLocation="http://www.springframework.org/schema/beans
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> http://www.springframework.org/schema/beans/spring-beans.xsd
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> http://www.springframework.org/schema/aop
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config> http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><bean id="myService" class="com.example.demo.aop.MyServiceImpl"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><bean id="myAspect" class="com.example.demo.aop.MyAspect"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:aspect id="myAspectRef" ref="myAspect">
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:pointcut id="serviceOperation" expression="execution(* com.example.demo.aop.MyService.performAction(..))"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:before method="beforeAdvice" pointcut-ref="serviceOperation"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:after method="afterAdvice" pointcut-ref="serviceOperation"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:after-returning method="afterReturningAdvice" pointcut-ref="serviceOperation" returning="retVal"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:after-throwing method="afterThrowingAdvice" pointcut-ref="serviceOperation" throwing="ex"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:around method="aroundAdvice" pointcut-ref="serviceOperation"/>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config></aop:aspect>
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config></aop:config>
</beans>
复制代码
myService:这是业务逻辑的bean,指向MyServiceImpl类的实例。
myAspect:这是切面的bean,指向MyAspect类的实例。
:这是AOP配置的根元素,所有的AOP配置,包括切面定义、切点和通知方法等,都需要在此元素内部定义。
切面(Aspect):通过元素定义,它包罗了一系列通知(advice)和一个或多个切点(pointcut)。这个元素将切面类(包罗通知逻辑的类)与具体的操作(怎样、何时对目标对象进行加强)关联起来。
切点(Pointcut):通过元素定义,切点通过表达式来指定,当需要精确控制哪些方法执行时会触发通知时,就需要定义切点。切点表达式可以非常精确地指定方法,例如通过方法名称、参数范例、注解等。expression定义了切点的表达式,指明了切点的匹配规则。这里的表达式execution(* com.example.demo.aop.MyService.performAction(..))意味着切点匹配MyService接口中performAction方法的执行,切点用于指定在哪些毗连点(Join Point,例如方法调用)上应用通知。
关于解析表达式execution(* com.example.demo.aop.MyService.performAction(..))
execution:是最常用的切点函数,用于匹配方法执行的毗连点。
*:表示方法的返回范例是任意的。
com.example.demo.aop.MyService.performAction:指定了全路径的接口名和方法名。
(…):表示方法参数是任意的,无论方法有多少个参数都匹配。
毗连点(Join Point):毗连点是指在程序执行过程中的某一点,比如方法的调用。 毗连点是通过切点(Pointcut)的表达式来辨认和匹配的,execution(* com.example.demo.aop.MyService.performAction(..))表达式定义了一个切点,它指定了一个明确的毗连点集合——即MyService接口的performAction方法的所有调用。这个例子中,MyService接口的performAction方法的调用就是潜伏的毗连点。每次performAction方法被调用时,就达到了一个毗连点。这个毗连点就是这里通知应用的时机。
通知(Advice):这是AOP通过在特定时机执行的操作来加强方法的执行。method属性指明当切点匹配时应该执行的切面的方法名,pointcut-ref引用了上面定义的切点。比如这里的beforeAdvice是在目标方法performAction执行之前被调用的方法。这意味着每当MyService.performAction(..)方法被调用时,beforeAdvice方法将起首被执行。
总结为一句话:Spring AOP通过在切面中定义规则(切点)来指定何时(毗连点)以及怎样(通知)加强特定方法,实现代码的模块化和关注点分离,无需修改原有业务逻辑。
通过这种方式,Spring AOP 允许定义在特定方法执行前、执行后、环绕执行等时机插入自定义逻辑,而无需修改原有业务逻辑代码。这是实现关注点分离的一种强大机制,特别是对于跨越应用程序多个部分的横切关注点(如日志、事件管理等)。
注意,如果设置为
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
复制代码
设置proxy-target-会使Spring AOP优先使用CGLIB代理,即使目标对象实现了接口。默认情况下,不需要设置proxy-target-class属性,或者将其设置为false,则是使用JDK动态代理。
主程序:
DemoApplication.java:
package com.example.demo;import com.example.demo.aop.MyService;import org.springframework.context.support.ClassPathXmlApplicationContext;public class DemoApplication {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>public static void main(String[] args) {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>MyService myService = (MyService) context.getBean("myService");<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>try {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println(myService.performAction("normal"));<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>} catch (Exception e) {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>e.printStackTrace();<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("=======================");<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>try {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println(myService.performAction("throw"));<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>} catch (Exception e) {<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>System.out.println("Exception caught in main: " + e.getMessage());<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config><aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>context.close();<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>}}
复制代码
运行结果:
通过结合动态代理技术和这些AOP概念,Spring AOP能够以非侵入式的方式为应用程序提供横切关注点的支持,这样开发者就可以将这些关注点模块化,并保持业务逻辑组件的聚焦和简洁。
如果对动态代理感爱好可以再调试看看,这里是JDK动态代理是由于public class MyServiceImpl implements MyService 实现了接口,调试如下:
简单说一下这里能看到的关键类和接口
ProxyFactory: 这是Spring AOP用来创建代理对象的工厂类。它可以根据目标对象是否实现接口来决定使用JDK动态代理照旧CGLIB代理。
AopProxy: 这个接口定义了获代替理对象的方法。它有两个主要实现:JdkDynamicAopProxy(用于JDK动态代理)和CglibAopProxy(用于CGLIB代理)。
JdkDynamicAopProxy: 实现了AopProxy接口,使用JDK动态代理技术创建代理。它实现了InvocationHandler接口,拦截对代理对象的所有方法调用。
CglibAopProxy: 同样实现了AopProxy接口,但使用CGLIB库来创建代理对象。对于没有实现接口的类,Spring会选择这种方式来创建代理。
如果大家想深入相识Spring AOP的源码,可以直接查察JdkDynamicAopProxy和CglibAopProxy这两个类的实现。这里不是本篇重点,简单提一下:
比如在JdkDynamicAopProxy中看到动态代理的实现:
JdkDynamicAopProxy类实现了InvocationHandler接口,这是JDK动态代理的核心。在其invoke方法中,会有逻辑判定是否需要对调用进行拦截,并在调用前后应用相应的通知。
创建代理的过程主要是在ProxyFactory通过调用createAopProxy()方法时完成的,这个方法会根据配置返回JdkDynamicAopProxy或CglibAopProxy的实例。
代理的使用:客户端代码通过ProxyFactory获代替理对象,并通过这个代理对象调用目标方法。代理对象在内部使用JdkDynamicAopProxy或CglibAopProxy来拦截这些调用,并根据AOP配置执行通知。通过ProxyFactory获代替理对象的过程,通常在Spring的配置和使用中是隐式完成的,特别是在使用Spring容器管理AOP时。这一过程不需要开发者直接调用ProxyFactory类。当Spring配置中定义了一个bean,并对其应用了切面,Spring容器会自动处理代理的创建和应用通知的过程。这是通过Spring的后处理器和AOP定名空间的支持实现的,开发者通常只需声明式地配置切面和通知即可。
如果想看到CGLIB代理,这里有2种方法
第1种方法是去掉MyServiceImpl实现的MyService接口,然后把主程序和expression表达式对应的地方改成MyServiceImpl。
第2种方法就是Spring配置文件中显式设置标签的proxy-target-属性来实现这一点。如下:
<aop:config proxy-target-class="true">
<aop:config proxy-target-class="true">
</aop:config>
</aop:config>
复制代码
调试如下:
欢迎一键三连~
有问题请留言,大家一起探讨学习
点击关注,第一时间相识华为云新鲜技术~
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
张国伟
金牌会员
这个人很懒什么都没写!
楼主热帖
XAF新手入门 - 类型子系统(Types Info ...
MyBatis 查询数据库
JAVA 装箱拆箱--到底指什么呢? ...
HarmonyOS(鸿蒙)开发一文入门 ...
ThinkPHP5 远程命令执行漏洞
浅入浅出 1.7和1.8的 HashMap
Prometheus配置Basic Auth进行安全防护 ...
红日安全内网渗透靶场-VulnStack-1 ...
本手、妙手、俗手?我用AI写2022高考全 ...
哈工大软件构造Lab3(2022)
标签云
挺好的
服务器
快速回复
返回顶部
返回列表