@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
相关产品推荐
相关产品推荐

