一篇文章详细解读Spring的AOP原理过程(Spring面向切面详解)

article/2025/9/18 10:20:41

目录

概述

使用登录例子说明 AOP

AOP(底层原理,了解)

有两种情况动态代理

第一种 有接口情况,使用 JDK动态代理

第二种 没有接口情况,使用 CGLIB 动态代理 

 回顾JDK静态代理

①明星唱歌,经纪人替他签合同以及收钱的例子

②我们找房屋中介租房子 

回顾JDK动态代理

1、使用 Proxy 类里面的方法创建代理对象

 调用 newProxyInstance 方法

2.编写 JDK 动态代理代码

测试

AOP(术语)

连接点

切入点

通知(增强)

通知类型

切面

AOP操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作

基于 AspectJ 实现 AOP 操作

引入相关依赖

 切入点表达式

AOP 操作(AspectJ 注解)

1、创建类,在类里面定义方法

2、创建增强类(编写增强逻辑)

3.进行通知的配置

(1)在 spring 配置文件中,开启注解扫描

(2)使用注解创建 UserDao 和 UserProxy 对象

(3)在增强类上面添加注解 @Aspect

(4)在 spring 配置文件中开启生成代理对象

4、配置不同类型的通知

5、相同的切入点抽取

测试

AOP 操作(AspectJ 配置文件)

创建被增强类和增强类

在xml配置文件中创建两个类的对象

在 xml 配置文件中配置aop

 写在最后


概述

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

通俗描述:不通过修改源代码方式,在主干功能里面添加新功能。

使用登录例子说明 AOP

 也许这个例子还是比较空洞,我们接着往下看,后面有代码了又实际需求了就会明白了。

AOP(底层原理,了解)

底层原理了解即可,因为Spring底层为我们封装好了,这里之所以介绍是因为我们得了解这是怎么回事。

有两种情况动态代理

第一种 有接口情况,使用 JDK动态代理

第二种 没有接口情况,使用 CGLIB 动态代理 

 回顾JDK静态代理

①明星唱歌,经纪人替他签合同以及收钱的例子

有一个接口Show(表演)里面有个抽象方法sing()

有一个Star类(明星)实现Show接口中的sing去唱歌,他只需要唱歌其他不用管。

明星去唱歌就行了,至于谈酬劳以及签合同都是经纪人的事情了,因此我们这里需要一个经纪人类AgentMan,商家有事情都找他就行了,因为经纪人就是明星的代理人,他可以全权负责。下面我们用代码来实现。

Show接口
public interface Show {void sing();
}
RealStar类
public class RealStar implements Show {@Overridepublic void sing() {System.out.println("我是歌手只负责唱歌就可以了");}
}
经纪人类
public class AgentMan implements Show {private Show show;public AgentMan(Show show) {this.show = show;}@Overridepublic void sing() {System.out.println("我是经纪人我负责签合同");show.sing();System.out.println("唱完了,我该去收钱了");}
}

 测试

 

②我们找房屋中介租房子 

//租房接口
public interface Rent {void rent();
}
//租房者类
public class You implements Rent {@Overridepublic void rent() {System.out.println("你真实的需要租房子而且还需要你自己去花钱哦");}
}
//中介类
public class LianJia implements Rent {private Rent rent;public LianJia(Rent rent){this.rent = rent;}@Overridepublic void rent() {System.out.println("我是中介,我在帮你找房子,马上就要找到了");rent.rent();System.out.println("我是中介,你房子也租到了,你现在该干嘛干嘛去吧");}
}

测试

 通过上面两个例子你是否对代理有了深入了解呢?相信你一定能感受到了代理类能做我们想做的事情而核心的事情需要我们被代理类自己去实现。

回顾JDK动态代理

从上面的两个例子不知道你发现问题了没有。当我们需要代理明星的时候就需要创建一个AgentMan来代理明星,当我们需要代理客户租房的时候,需要创建一个LianJian来代理客户。也就是说静态代理需要对应实际的被代理类(明星、客户)创建对应的代理类。如果我们在开发中有100个类需要被代理,难道我们还需要创建100个代理类吗?当然不需要,我们仅仅需要动态代理即可。

1、使用 Proxy 类里面的方法创建代理对象

这个是官方API的介绍

动态代理类 (以下简称为代理类 )是一个实现在类创建时在运行时指定的接口列表的类,具有如下所述的行为。 代理接口是由代理类实现的接口。 代理实例是代理类的一个实例。 每个代理实例都有一个关联的调用处理程序对象,它实现了接口InvocationHandler 。 通过其代理接口之一的代理实例上的方法调用将被分派到实例调用处理程序的invoke方法,传递代理实例, java.lang.reflect.Method被调用方法的java.lang.reflect.Method对象以及包含参数的类型Object Object的数组。 调用处理程序适当地处理编码方法调用,并且返回的结果将作为方法在代理实例上调用的结果返回。代理类具有以下属性:代理类是公共的,最终的,而不是抽象的,如果所有代理接口都是公共的。
如果任何代理接口是非公开的,代理类是非公开的,最终的,而不是抽象的 。
代理类的不合格名称未指定。 然而,以字符串"$Proxy"开头的类名空间应该保留给代理类。
一个代理类扩展了java.lang.reflect.Proxy 。
代理类完全按照相同的顺序实现其创建时指定的接口。
如果一个代理类实现一个非公共接口,那么它将被定义在与该接口相同的包中。 否则,代理类的包也是未指定的。 请注意,程序包密封不会阻止在运行时在特定程序包中成功定义代理类,并且类也不会由同一类加载器定义,并且与特定签名者具有相同的包。
由于代理类实现了在其创建时指定的所有接口, getInterfaces在其类对象上调用getInterfaces将返回一个包含相同列表接口的数组(按其创建时指定的顺序),在其类对象上调用getMethods将返回一个数组的方法对象,其中包括这些接口中的所有方法,并调用getMethod将在代理接口中找到可以预期的方法。
Proxy.isProxyClass方法将返回true,如果它通过代理类 - 由Proxy.getProxyClass返回的类或由Proxy.newProxyInstance返回的对象的类 - 否则为false。
所述java.security.ProtectionDomain代理类的是相同由引导类装载程序装载系统类,如java.lang.Object ,因为是由受信任的系统代码生成代理类的代码。 此保护域通常将被授予java.security.AllPermission 。
每个代理类有一个公共构造一个参数,该接口的实现InvocationHandler ,设置调用处理程序的代理实例。 而不必使用反射API来访问公共构造函数,也可以通过调用Proxy.newProxyInstance方法来创建代理实例,该方法将调用Proxy.getProxyClass的操作与调用处理程序一起调用构造函数。

 调用 newProxyInstance 方法

 

方法有三个参数:
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
第三参数,实现这个接口 InvocationHandler ,创建代理对象,写增强的部分

2.编写 JDK 动态代理代码

public class ProxyAgent {public static Object getInstance(Object obj){return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),new MyInvocationHandler(obj));}}
class MyInvocationHandler implements InvocationHandler{private Object obj;public MyInvocationHandler(Object obj){this.obj=obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是动态代理类,我在真实方法处理之前被执行");Object invoke = method.invoke(obj, args);System.out.println("我是动态代理类,我在真实方法处理之后被执行");return invoke;}
}

测试

    @Testpublic void test3(){Show realStar = new RealStar();Show show = (Show) ProxyAgent.getInstance(realStar);show.sing();System.out.println("===========================");Rent you = new You();Rent rent = (Rent) ProxyAgent.getInstance(you);rent.rent();}

执行结果

这样,是不是对动态代理有了很好的理解呢,用一个类就可以实现动态的代理。

好了现在我们进入本篇文章最重要的地方了。

以上都是在回顾再介绍,没必要掌握,因为我们也讲过了Spring对aop有了封装,我们只需要对Spring封装的aop会操作即可,当然了能掌握其原理和底层也是极好的。

AOP(术语)

连接点

即为在类中可以被增强的方法。

切入点

即为类中实际被增强的方法。

通知(增强)

被增强的逻辑部分。

通知类型

前置通知

后置通知

环绕通知

异常通知

最终通知

切面

所谓切面是一个过程,就是把通知加到切入点上的过程。

AOP操作

Spring 框架一般都是基于 AspectJ 实现 AOP 操作

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使
用,进行 AOP 操作 。

基于 AspectJ 实现 AOP 操作

(1)基于 xml 配置文件实现
(2)基于注解方式实现 (先讲基于注解)

引入相关依赖

 <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.16</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.16</version></dependency>

 切入点表达式

(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
举例 1:对 com.csdn.dao.BookDao 类里面的 add 进行增强
execution(* com.csdn.dao.BookDao.add(..))
举例 2:对 com.csdn.dao.BookDao 类里面的所有的方法进行增强
execution(* com.csdn.dao.BookDao.* (..))
举例 3:对 com.csdn.dao 包里面所有类,类里面所有方法进行增强
execution(* com.csdn.dao.*.* (..))

AOP 操作(AspectJ 注解)

1、创建类,在类里面定义方法

package com.csdn.dao;public class UserDao {public int add(int a, int b) {System.out.println("我被调用了,参数为"+a+"和"+b);return a + b;}
}

2、创建增强类(编写增强逻辑)

public class UserProxy {public void before(){System.out.println("我是在方法调用之前执行");}
}

3.进行通知的配置

1)在 spring 配置文件中,开启注解扫描

 <context:component-scan base-package="com.csdn"></context:component-scan>

这个根据你自己的报名写就可以。

(2)使用注解创建 UserDao UserProxy 对象

 

(3)在增强类上面添加注解 @Aspect

 

 

(4)在 spring 配置文件中开启生成代理对象

<!-- 开启 Aspect 生成代理对象--> 
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

4、配置不同类型的通知

在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
@Component
@Aspect
public class UserProxy {
//    前置通知在方法调用之前调用@Before(value="execution(* com.csdn..dao.*.*(..))")public void before(){System.out.println("我是前置通知");}//    返回通知或者后置通知在方法调用后执行@AfterReturning(value="execution(* com.csdn..dao.*.*(..))")public void afterReturn(){System.out.println("我是后置通知或成为返回通知");}
//最终通知,不管程序是否出现异常都会被执行@After(value="execution(* com.csdn..dao.*.*(..))")public void after(){System.out.println("我是最终通知,类似于try-catch-finally中的finally");}
//出现异常的时候调用@AfterThrowing(value="execution(* com.csdn..dao.*.*(..))")public void throwing(){System.out.println("我是异常通知");}
//环绕通知在方法执行前后都会执行@Around(value="execution(* com.csdn..dao.*.*(..))")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("我是环绕通知,我在方法前执行");Object obj joinPoint.proceed();System.out.println("我是环绕通知,我在方法后执行");return obj;}}

5、相同的切入点抽取

我们看到上面方法中的execution是一样的,我们可以考虑将其提取再使用,将增强类修改为如下

   @Pointcut(value="execution(* com.csdn.dao.*.*(..))")public void pointCut(){}//    前置通知在方法调用之前调用@Before(value="pointCut()")public void before(){System.out.println("我是前置通知");}//    返回通知或者后置通知在方法调用后执行@AfterReturning(value="pointCut()")public void afterReturn(){System.out.println("我是后置通知或成为返回通知");}
//最终通知,不管程序是否出现异常都会被执行@After(value="pointCut()")public void after(){System.out.println("我是最终通知,类似于try-catch-finally中的finally");}
//出现异常的时候调用@AfterThrowing(value="pointCut()")public void throwing(){System.out.println("我是异常通知");}
//环绕通知在方法执行前后都会执行@Around(value="pointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("我是环绕通知,我在方法前执行");Object proceed = joinPoint.proceed();System.out.println("我是环绕通知,我在方法后执行");return proceed;}}

测试

    @Testpublic void test4(){ApplicationContext ac = new ClassPathXmlApplicationContext("app.xml");UserDao userDao = ac.getBean("userDao", UserDao.class);System.out.println(userDao.add(1,2));}

结果

如果你想获取更多信息可以使用环绕通知的参数ProceedingJoinPoint  或者在其他方法上加入JoinPoint

我对环绕通知和最终通知修改如下:

 

 

 再测试查看打印,会看到一些关于你调用的方法名以及方法的一些其他信息,可以选择打印,不用纠结

这是再次执行测试侯打印的结果

我是环绕通知,我在方法前执行
[1, 2]
class com.csdn.dao.UserDao
Integer com.csdn.dao.UserDao.add(int,int)
com.csdn.dao.UserDao
1
add
public java.lang.Integer com.csdn.dao.UserDao.add(int,int)
UserDao.add(..)
我是前置通知
我被调用了,参数为1和2
我是后置通知或成为返回通知
[1, 2]
add
我是最终通知,类似于try-catch-finally中的finally
我是环绕通知,我在方法后执行
3
Disconnected from the target VM, address: '127.0.0.1:57941', transport: 'socket'Process finished with exit code 0

AOP 操作(AspectJ 配置文件)

创建被增强类和增强类

package com.csdn.service;/*** 被增强类*/
public class UserService {public void add(){System.out.println("userService's add().....");}
}
//增强类
public class UserServiceProxy {public void before(){System.out.println("我在之前被执行");}
}

在xml配置文件中创建两个类的对象

 <bean id="userService" class="com.csdn.service.UserService"></bean><bean id="userServiceProxy" class="com.csdn.service.UserServiceProxy"></bean>

在 xml 配置文件中配置aop

   <aop:config>
<!--        写切入点--><aop:pointcut id="point" expression="execution(* com.csdn.service.*.*(..))"/>
<!--        写被增强的那个类,就是类似用注结时候写的@Aspect配置切面--><aop:aspect ref="userServiceProxy"><aop:before method="before" pointcut-ref="point"></aop:before></aop:aspect></aop:config>

测试

    @Testpublic void test5(){ApplicationContext ac = new ClassPathXmlApplicationContext("app.xml");UserService userService = ac.getBean("userService", UserService.class);userService.add();}

 写在最后

如果我们的类被多个类增强怎么办呢?

如果是注解的方式可以使用Order注解写在增强类上,数值越小越先被执行

如下

如果是配置形式的话,也是通过order属性来标记

 

 


http://chatgpt.dhexx.cn/article/fOC3WqcJ.shtml

相关文章

IOC/DI、AOP相关原理

文章目录 IOC/DI为什么IOC就降低了耦合性 AOP原理使用AOP的名词们概念性名词JoinPoint/TargetIntroductionProxyWeaving 在代码中有对应注解的名词AspectPointcut&#xff08;在哪儿切&#xff09;Advice&#xff08;什么时候切&#xff09; 示例代码 IOC/DI IOC全称是Inversio…

Spring——AOP原理及流程详解

AOP原理及流程详解 一、AOP结构介绍Pointcut通知原理连接点拦截器 二、Bean介入点EnableAspectJAutoProxyAspectJAutoProxyRegistrarAnnotationAwareAspectJAutoProxyCreatorAbstractAutoProxyCreator实例前执行初始化后执行循环依赖会调用 总结 三、处理切面获取所有切面其下通…

Spring中的AOP原理

目录 0 为什么需要AOP 1 基本概念 2 AOP原理 2.1 JDK动态代理 2.2 CGLIB 动态代理 0 为什么需要AOP 现在有一个情景&#xff1a; 我们要把大象放进冰箱&#xff0c;步骤为&#xff1a;打开冰箱->放入大象->关闭冰箱 如果再把大象拿出来&#xff0c;步骤为&#x…

Spring AOP的实现原理 ?

Spring AOP的实现原理 ? - 知乎 AOP的实现&#xff0c;最关键的有两步&#xff1a; 得到代理对象利用递归责任链执行前后置通知及目标方法 IOC容器初始化时&#xff0c;对于涉及AOP操作的目标类&#xff0c;其实Spring返回的是代理对象&#xff0c;而不是目标类的实例。至于…

AOP理解及底层原理

AOP 基础概念 一、概述 银行系统的简易取款流程如图&#xff1a; 将方框里的流程合为一个&#xff0c;另外系统还会有一个查询余额流程&#xff0c;如图&#xff1a; 这两个业务有一个共同的验证流程&#xff0c;如图&#xff1a; 为什么会有面向切面编程(AOP)&#xff…

Spring AOP原理分析一次看懂

什么是AOP AOP&#xff08;Aspect-OrientedProgramming&#xff0c;面向方面编程&#xff09;&#xff0c;可以说是OOP&#xff08;Object-Oriented Programing&#xff0c;面向对象编程&#xff09;的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构&…

AOP-底层原理

申明&#xff1a;学习笔记整理&#xff0c;内容非原创&#xff0c;仅供参考学习&#xff01;视频详细地址如下&#xff1a; 尚硅谷Spring框架视频教程&#xff08;spring5源码级讲解&#xff09;_哔哩哔哩_bilibili 目录 一、AOP概念 二、AOP底层原理 三、JDK动态代理代码…

Spring AOP原理详解及实例

Spring AOP原理详解及实例 1.Spring AOP简介2.AOP与OOP对比3.AOP使用场景4.AOP相关概念5.AOP实例5.1 基于xml配置方式5.2 基于注解配置方式5.3 AspectJ切点函数 6.可能出现的问题及解决方法6.1 java.lang.IllegalArgumentException: error at :: 0 cant find referenced pointc…

JAVA AOP概念和实现原理 详解

// AOP 概念 1. 什么是AOP&#xff1f; // 面向切面&#xff08;方面&#xff09;编程 //利用AOP可以对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率 // 不通过修改源代码方…

Spring IOC和AOP 原理彻底搞懂

本博中关于Spring的文章&#xff1a;Spring IOC和AOP原理&#xff0c;Spring事务原理探究&#xff0c;Spring配置文件属性详解&#xff0c;Spring中的代理模式 Spring提供了很多轻量级应用开发实践的工具集合&#xff0c;这些工具集以接口、抽象类、或工具类的形式存在于Sprin…

spring AOP 原理

一、spring注册 AnnotationAwareAspectJAutoProxyCreator 通过EnableAspectJAutoProxy可以看到先把AspectJAutoProxyRegistrar通过Import注册到spring。 AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口&#xff0c;所以就有了将某个bean引入spring 的能力…

Spring的AOP实现原理

本学习笔记将尽可能的将AOP的知识讲解的通俗易懂&#xff0c;先从一个典型的问题出发&#xff0c;引入AOP这个概念&#xff0c;介绍AOP的基本概念&#xff0c;再到Spring中的AOP的实现方案&#xff0c;最后进行一个简单的总结归纳。本学习笔记中不考虑cglib、也不会太关注Sprin…

spring的AOP和IOC的原理

目录 一、spring的ioc与aop原理 二、代理模式&#xff1a; 三、静态代理 四、动态代理 五、实际的操作 六、动态代理的实现&#xff1a; 七、什么是AOP 八、主流的AOP框架&#xff1a; 九、术语&#xff1a; 十、通知的五种类型&#xff1a; 十一、AOP的优点&#x…

spring aop原理

&#x1f345; Java学习路线&#xff1a;搬砖工逆袭Java架构师 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、CSDN哪吒公众号作者✌ 、Java架构师奋斗者&#x1f4aa; &#x1f345; 扫描主页左侧二维码&#xff0c;加入群聊&#xff0c;一起学习、一起进步…

AOP原理

AOP原理 什么是AopAOP的作用AOP的基本概念AOP使用场景AOP原理如何动态修改功能AOP的编程思想AOP面向切面编程操作AOP通知执行的顺序代码执行流程准备工作源码揭开面纱 什么是Aop AOP&#xff08;Aspect Orient Programming&#xff09;也就是面向切面编程&#xff0c;作为面向对…

无法显示页面,因为发生内部服务器错误。

用iis添加网站后&#xff0c;访问域名&#xff0c;显示“无法显示页面&#xff0c;因为发生内部服务器错误。” 之前我将这些文件放在一个Demo文件夹中&#xff0c;把Demo放在test站点下&#xff0c;通过域名访问Demo报错。 我将Demo下的文件直接放在test下&#xff0c;访问成…

遇到“服务器内部错误http500怎么办?

出现500错误的原因是很多的&#xff0c;一般来说都是程序错误导致的&#xff0c;如果程序出错&#xff0c;那么在浏览器内会返回给用户一个友好的错误提示&#xff0c;统一称之为服务器500错误。 解决的方法就是您必须在http中能够正确的获得错误信息&#xff0c;方法为&#x…

500 - 内部服务器错误--解决方案

一般网上的方法是这样的&#xff1a; 一、打开 Internet 信息服务(IIS)管理器。点击出错的站点&#xff0c;并双击右边的ASP图标&#xff0c;如下图所示&#xff1a; 二、展开右侧配置中的“调试属性”&#xff0c;把“将错误发送到浏览器”的值设为 "true"&#xf…

HTTP状态 500 - 内部服务器错误 类型 异常报告,初学servlet遇到的问题

写给自己看&#xff0c;初学记录一下&#xff0c;maven项目中tomcat,Servlet遇到的问题 HTTP状态 500 - 内部服务器错误 类型 异常报告&#xff0c;初学servlet遇到的问题 类似这种报错&#xff0c;在hello world级别的servlet中碰到。 报错分析 大概是说自定义java类在实例…

IIS 配置网站出现500内部服务器错误,显示具体错误信息

1、打开IIS 找到如下图的部分&#xff0c;双击点开 2、点开之后找到如下图部分&#xff0c;点击 3、选择如下图部分&#xff0c;然后点击确定。 4、这个时候页面会出现详细的错误&#xff0c;如果没有出现详细错误&#xff0c;配置如下图部分&#xff0c;不勾选。 5、这时会出现…