You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@Scheduled修饰的抽象类字段无法被实现类实例访问问题

问题分析

Spring的CGLIB代理通过生成目标类的子类实现,代理类会继承父类的所有字段。当被@Scheduled修饰的onSchedule方法执行时,Spring是通过代理实例调用该方法的,此时你给schedule字段赋值,实际是存在代理类自身的schedule字段中,而非真实的KillBossServiceImpl实例的字段。而你的KillBossServiceImpl成员方法或onEnd方法如果是通过真实实例调用(比如内部方法直接调用,未走代理),自然拿不到代理实例里的schedule值;反之,onSchedule每次在代理实例上执行,所以能保留之前的赋值。

解决方案

方案1:通过AopContext获取代理实例(推荐)

首先在启动类或配置类上开启代理暴露:

@EnableAspectJAutoProxy(exposeProxy = true)

然后在需要调用getSchedule的地方,获取代理实例并调用:

// 在KillBossServiceImpl的成员方法或onEnd中
KillBossServiceImpl proxy = (KillBossServiceImpl) AopContext.currentProxy();
Schedule schedule = proxy.getSchedule();

这样就能拿到代理实例中被onSchedule赋值的schedule,因为onSchedule是在代理上执行的,值存储在代理的字段里。

方案2:依赖注入自身(简单直接)

在KillBossServiceImpl中注入自己,Spring会自动注入代理实例:

@Service
public class KillBossServiceImpl extends TimedImpl {
    @Autowired
    private KillBossServiceImpl self;

    public void someMemberMethod() {
        Schedule schedule = self.getSchedule();
        // 业务逻辑处理
    }

    public void onEnd() {
        Schedule schedule = self.getSchedule();
        // 活动结束逻辑处理
    }
}

调用self.getSchedule()本质是调用代理实例的方法,能直接拿到正确的schedule值。

方案3:重构代码,避免实例字段依赖(最佳实践)

把schedule从实例字段改为注入全局的定时活动管理器,彻底规避代理与真实实例的字段不一致问题:

@Component
public class ScheduleManager {
    private Map<String, Schedule> activitySchedules = new ConcurrentHashMap<>();

    public void setSchedule(String activityKey, Schedule schedule) {
        activitySchedules.put(activityKey, schedule);
    }

    public Schedule getSchedule(String activityKey) {
        return activitySchedules.get(activityKey);
    }
}

然后在抽象类和子类中注入管理器:

public abstract class TimedImpl {
    @Autowired
    protected ScheduleManager scheduleManager;
    protected abstract String getActivityKey();

    @Scheduled(cron = "...")
    public void onSchedule() {
        Schedule schedule = // 初始化schedule逻辑
        scheduleManager.setSchedule(getActivityKey(), schedule);
    }
}

@Service
public class KillBossServiceImpl extends TimedImpl {
    @Override
    protected String getActivityKey() {
        return "kill_boss";
    }

    public void onEnd() {
        Schedule schedule = scheduleManager.getSchedule(getActivityKey());
        // 活动结束逻辑处理
    }
}

这种方式所有组件共享同一个ScheduleManager,数据存储在全局容器中,还能适配多实例或分布式场景。

内容的提问来源于stack exchange,提问作者Criwran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 09:05:25