我看过得最易懂的一段AOP的解释

面向切面编程(AOP)是对面向对象编程(OOP)的一种补充, 它允许程序员在运行时将代码片段(切面)动态地切入到类的指定方法中, 从而改变其原有行为, 实现代码复用并降低耦合度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向切面编程(AOP是Aspect Oriented Program的首字母缩写)

我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。

但是人们也发现,在分散代码的同时,也增加了代码的重复性。什么意思呢?比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法, 我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。

也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?这种在运行时,动态地将代码切入到类的指定方法、 指定位置上的编程思想就是面向切面的编程。

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。

这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。

AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

<think>好的,我需要帮助用户解决AOP失效的问题。首先,我得回顾一下用户提供的引用内容,看看有哪些可能的原因和解决方案。根据引用[1]、[3],AOP失效的常见原因包括重复扫描导致代理对象未被正确注入,以及内部方法调用绕过代理。引用[2]和[4]提到了通过接口继承AopProxy或使用SpringUtil获取代理对象的方法。 用户的问题是关于AOP失效的原因及解决方案,我需要把这些信息整理清楚。首先,要列举可能的原因,比如重复扫描、内部调用、配置错误等。然后,针对每个原因给出对应的解决方法,比如调整扫描范围、使用自注入或ApplicationContext获取代理实例。 需要确保回答结构清晰,分点说明,每个原因和方案对应起来。同时,要使用用户提到的引用中的例子,但可能不需要全部引用,只要相关部分即可。另外,按照用户的要求,行内数学公式用$...$,但这里可能用不上,但要注意其他格式要求,比如代码块用正确语法,单独成段的公式用$$...$$,但这个问题可能不涉及数学公式,所以可能不需要处理这部分。 还要检查是否有其他常见原因未被提及,比如动态代理方式(CGLIB vs JDK)、作用域问题等。根据引用[3],提到AOP的原理是基于代理,如果直接调用目标对象的方法,代理不会生效,所以内部调用是一个重要点。此外,引用[1]提到的重复扫描导致Spring容器和SpringMVC容器都创建了Bean,可能未被正确代理。 在解决方案中,除了调整扫描配置,还可以建议使用@Autowired注入自身代理,或者通过AopContext.currentProxy()获取当前代理,但需要注意暴露代理的配置。引用[4]中的例子是使用SpringUtil.getBean来获取代理实例,这也是一种可行方法。 总结下来,回答应该分为几个部分:原因分点,解决方案对应每个原因,可能还需要注意事项或佳实践。确保语言简洁,结构清晰,符合用户要求的系统级指令和用户可见层的格式。</think>### AOP失效常见原因及解决方案 #### 一、失效原因分析 1. **重复扫描导致代理对象未被注入** - **场景**:Spring容器和SpringMVC容器同时扫描相同包路径时,可能导致目标类被两个容器分别实例化。若AOP配置在Spring容器中,但SpringMVC容器实例化的对象未被代理,导致注入时使用未代理的实例[^1]。 - **示例**:`execution`表达式匹配的类被SpringMVC重新扫描并托管,生成未被代理的原始对象。 2. **内部方法调用绕过代理** - **场景**:同类中非代理方法直接调用被AOP增强的方法时,由于调用发生在目标对象内部,未经过代理类拦截,导致增强逻辑失效[^3]。 - **示例**: ```java public void methodA() { methodB(); // 直接调用,未触发代理 } @Transactional public void methodB() { ... } ``` 3. **动态代理模式限制** - **场景**:若目标类未实现接口且未配置使用CGLIB代理(默认JDK动态代理需接口),可能导致代理生成失败[^3]。 4. **Bean作用域或生命周期问题** - **场景**:原型作用域(Prototype)Bean或手动`new`创建的对象未被Spring管理,无法生成代理。 --- #### 二、解决方案 1. **解决重复扫描问题** - **调整包扫描范围**:明确区分Spring容器和SpringMVC容器的扫描路径,例如: ```xml <!-- Spring容器扫描业务层 --> <context:component-scan base-package="com.example.service"/> <!-- SpringMVC容器扫描控制器 --> <context:component-scan base-package="com.example.controller"/> ``` - **使用`exclude-filter`**:在配置中排除Controller层的扫描。 2. **解决内部调用问题** - **方案1:通过代理对象调用** 使用`ApplicationContext`或工具类获取代理实例: ```java @Service public class UserService { @Autowired private ApplicationContext context; public void methodA() { context.getBean(UserService.class).methodB(); // 通过代理调用 } } ``` - **方案2:自注入代理对象** 通过`@Autowired`注入自身代理(需配合`@Lazy`避免循环依赖): ```java @Service public class UserService { @Autowired @Lazy private UserService selfProxy; public void methodA() { selfProxy.methodB(); // 通过代理调用 } } ``` 3. **强制使用CGLIB代理** 在Spring配置中启用CGLIB代理(支持类代理而非接口): ```xml <aop:aspectj-autoproxy proxy-target-class="true"/> ``` 4. **使用AopContext获取当前代理** - **步骤**: 1. 暴露代理对象(需配置`@EnableAspectJAutoProxy(exposeProxy = true)`)。 2. 在代码中调用: ```java ((UserService) AopContext.currentProxy()).methodB(); ``` --- #### 三、验证与调试建议 1. **检查Bean类型**:通过`applicationContext.getBean("beanName").getClass()`确认是否为代理类(如`EnhancerBySpringCGLIB`)。 2. **日志分析**:开启Spring调试日志,观察代理生成过程。 3. **单元测试**:编写切面逻辑的单元测试,验证增强是否生效。 --- ### 相关问题 1. 如何判断Spring AOP代理是否生效? 2. CGLIB和JDK动态代理在AOP中的区别是什么? 3. 如何通过注解配置强制使用CGLIB代理? 4. 在Spring Boot中如何避免重复扫描问题?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值