标准答案
- 外部调用代理对象时,代理先执行切面再调用目标方法。
- 目标对象内部直接调用另一个方法绕过代理,不能触发基于代理的注解切面。
- 可以调整服务边界、把方法拆到另一个 Bean、通过代理调用或使用更底层的编织方式,但要评估可维护性。
- 排查时确认注解、代理类型、Bean 是否由容器管理以及实际调用对象。
题目解析
Spring AOP 的注解语义来自代理拦截。外部通过容器拿到的代理调用时才会经过事务、缓存或权限切面,同一目标对象内部的 this.method() 会绕过代理。
手动 new、错误的 Bean 扫描、final 方法/类和代理类型都可能让切面不生效。排查要打印实际对象类型,确认调用路径和注解所在方法是否可被代理。
自注入代理可以作为特定场景的过渡,但会增加依赖复杂度。更清晰的方案通常是拆分服务边界或使用明确的编织机制,并测试事务、缓存和异常结果。
常见误区
- 误区:认为同类内部调用一定开启事务。改正:确认调用是否经过 Spring 代理,必要时拆分 Bean。
- 误区:手动 new Service 却期待注解生效。改正:让容器创建和注入 Bean,或显式配置代理/编织。
- 误区:为绕过自调用直接注入自身并形成复杂依赖。改正:优先调整服务职责和调用边界,减少隐藏代理依赖。