类内部方法调用,自注入避免AOP失效

代码

java 复制代码
public abstract class AbstractSyncTaskHandler implements SyncTaskHandler, BeanSelfAware {

protected AbstractSyncTaskHandler self;
/**
 * 注入自身对象.
 *
 * @param proxyBean 代理bean
 */
@Override
public void setSelf(Object proxyBean) {
    self = (AbstractSyncTaskHandler) proxyBean;
}
@Override
public void batchExecute(List<SyncTaskBO> syncTaskBOs, SyncTaskJobParamConfig syncTaskJobParamConfig) throws InterruptedException {
    List<SyncTaskBO> syncTaskBOS = self.refreshSyncTask(syncTaskBOs, syncTaskJobParamConfig);
    // 任务执行时间较长且并发较高的情况, 可能会出现上下文中日志记录文件错误的情况
    XxlJobContext xxlJobContext = XxlJobContext.getXxlJobContext();
    CountDownLatch countDownLatch = new CountDownLatch(syncTaskBOS.size());
    List<SyncTaskContext> contexts = CollUtil.newArrayList();
    syncTaskBOS.forEach(syncTaskBO -> {
        customerThreadPool.getSyncTaskPool().execute(() -> {
            XxlJobContext.setXxlJobContext(xxlJobContext);
            SyncTaskContext context = new SyncTaskContext();
            contexts.add(context);
            context.setSyncTaskBO(syncTaskBO);
            // 处理前置逻辑
            beforeExecute(context);
            boolean result = self.execute(context, syncTaskJobParamConfig);
            // 处理后置逻辑
            if (result) {
                afterExecute(context);
            }
            XxlJobContext.setXxlJobContext(null);
            countDownLatch.countDown();
        });
    });
    countDownLatch.await();
    batchMessageRule(contexts, syncTaskJobParamConfig);
}
@XLock(prefix = "ORDER_CENTER_REFRESH_TASK", keys = {"#syncTaskBOs[0].orderType", "#syncTaskBOs[0].type", "#syncTaskBOs[0].syncSystem", "#syncTaskJobParamConfig.handlerSuffix", "#syncTaskJobParamConfig.orderSources"},
        waitTime = 30, leaseTime = 30, errorMessage = "任务执行中,请勿重复执行")
public List<SyncTaskBO> refreshSyncTask(List<SyncTaskBO> syncTaskBOs, SyncTaskJobParamConfig syncTaskJobParamConfig) {
@XLock(prefix = "ORDER_CENTER", keys = {"#context.syncTaskBO.orderType", "#context.syncTaskBO.referenceId"}, waitTime = 30, leaseTime = 30, errorMessage = "任务执行中,请勿重复执行")
@Override
public boolean execute(SyncTaskContext context, SyncTaskJobParamConfig syncTaskJobParamConfig) {

疑问

为什么不通过这种方式注入

复制代码
@Resource
protected AbstractSyncTaskHandler self;

为什么不通过 @Resource 注入自身

这是一个很好的问题。虽然表面上看起来使用 `@Resource` 注入自身更简洁,但实际上存在多个技术和设计层面的问题:

1. Spring容器循环依赖问题

1.1 自注入会导致循环依赖

```java

@Component

public class AbstractSyncTaskHandler {

@Resource

protected AbstractSyncTaskHandler self; // 自身依赖自身 - 循环依赖

// ...

}

```

1.2 Spring处理循环依赖的限制

  • Spring默认只能解决单例Bean之间的setter循环依赖

  • 构造器循环依赖会导致启动失败

  • 自注入属于极端的循环依赖情况

2. 代理对象与原始对象的区别

2.1 代理对象的特殊性

```java

// Spring创建的结构:

// 原始Bean: AbstractSyncTaskHandler (实际业务逻辑)

// 代理Bean: Proxy (包含AOP增强逻辑)

// 如果使用@Resource注入自身:

@Resource

protected AbstractSyncTaskHandler self;

// 注入的可能是原始对象,而不是代理对象

// 这样AOP注解(@XLock)仍然不会生效

```

2.2 自注入机制的优势

```java

// 通过setSelf方式:

@Override

public void setSelf(Object proxyBean) {

self = (AbstractSyncTaskHandler) proxyBean; // 确保获得的是代理对象

}

```

3. 抽象类的特殊性

3.1 抽象类无法直接实例化

```java

// AbstractSyncTaskHandler是抽象类

public abstract class AbstractSyncTaskHandler implements SyncTaskHandler, BeanSelfAware {

// ...

}

// @Resource无法直接注入抽象类实例

```

3.2 实际注入的是具体子类

```java

// 实际运行时,注入的是具体子类的代理对象

// 如: SalesCreateSyncTaskHandler的代理对象

```

4. Spring生命周期考虑

4.1 Bean创建时机问题

```java

// 使用@Resource方式可能在Bean完全初始化前就尝试注入

// 导致注入失败或注入不完整的Bean

// 而setSelf在Bean完全初始化后由容器调用

// 确保注入的是完整可用的代理对象

```

5. 设计模式对比

5.1 @Resource方式的问题

```java

@Component

public abstract class AbstractSyncTaskHandler {

@Resource

protected AbstractSyncTaskHandler self; // 问题:可能注入原始对象而非代理对象

public void methodA() {

// AOP注解可能不生效

}

public void methodB() {

self.methodA(); // 可能绕过AOP

}

}

```

5.2 setSelf方式的优势

```java

@Component

public abstract class AbstractSyncTaskHandler implements BeanSelfAware {

protected AbstractSyncTaskHandler self; // 由容器在适当时机注入代理对象

@Override

public void setSelf(Object proxyBean) {

// 确保注入的是Spring代理对象

self = (AbstractSyncTaskHandler) proxyBean;

}

@XLock(...)

public void methodA() {

// AOP注解生效

}

public void methodB() {

self.methodA(); // 通过代理调用,AOP生效

}

}

```

6. 实际验证

如果尝试使用 `@Resource` 注入自身,很可能会遇到以下问题:

  1. Spring启动时报循环依赖错误

  2. 注入的不是代理对象,导致AOP失效

  3. 抽象类无法直接注入的问题

总结

使用 setSelf(file://C:\Users\NLDS\workspace\cloud-order\order-portal\src\main\java\com\cloud\order\sync\handler\AbstractSyncTaskHandler.java#L465-L468) 方法而非 `@Resource` 注入自身的原因:

  1. **避免循环依赖**:防止Spring容器启动时出现循环依赖问题

  2. **确保代理对象**:保证获得的是包含AOP增强的代理对象而非原始对象

  3. **生命周期控制**:在Bean完全初始化后才进行注入

  4. **抽象类兼容性**:正确处理抽象类的注入问题

  5. **设计模式标准**:这是Spring框架推荐的处理同类调用AOP失效的标准解决方案

这是Spring生态系统中解决AOP代理调用问题的成熟方案,具有良好的可靠性和可维护性。

相关推荐
cpolar技术支持5 分钟前
Kafka Streams 窗口统计怎么验收:本地跑订单流聚合,用 cpolar 给同事看只读结果页
java·docker·kafka·cpolar·kafka streams
爱读源码的大都督13 分钟前
DeepSeek面试官问:生产RAG系统回答不准确,该如何定位和优化?这样回答,能让面试官当场给你Offer!
java·后端·python
Cx330❀15 分钟前
【Linux网络】深入TCP协议:从滑动窗口到拥塞控制与性能优化全景解析
linux·开发语言·网络·tcp/ip·ai·性能优化·ai编程
“AI国潮设计-小江”30 分钟前
【Python实战】SDXL精准控制“普宁英歌舞×星空蛋糕”IP落地,附核心Prompt与商用授权思路
开发语言·人工智能·python·prompt·aigc
k4m7v2pz35 分钟前
从 Python 搬到 Rust:pyglet MIDI DAW 变成 egui 鬼畜采样器的迁移复盘
开发语言·python·rust
C++ 老炮儿的技术栈37 分钟前
MFC CPtrArray的用法
开发语言·数据结构·c++·算法·mfc·c
杨运交39 分钟前
[069][公共模块]Spring Boot 全局异常处理与参数校验实战(下):校验异常精细化处理与 WebFlux 适配
java·spring boot·后端
佳児素花痴╮41 分钟前
C++基础速通
开发语言·c++
是立不是利1 小时前
深入理解JavaScript事件委托
开发语言·前端·javascript
啊阿狸不会拉杆1 小时前
《计算机网络-自顶向下方法》6.4 交换局域网 读书笔记
开发语言·计算机网络·php