为啥 Runnable 中的实例变量在执行时消失了?
Posted
技术标签:
【中文标题】为啥 Runnable 中的实例变量在执行时消失了?【英文标题】:How come the instance variable inside a Runnable is gone when it's executed?为什么 Runnable 中的实例变量在执行时消失了? 【发布时间】:2021-01-03 09:40:17 【问题描述】:@Autowired
ThreadPoolTaskScheduler taskScheduler;
@Autowired
SendUpdatesRunnable sendUpdatesRunnable;
private void createJob(String timezone)
ZonedDateTime zoned = LocalDate.now().atTime(10, 11).atZone(ZoneId.of(timezone));
sendUpdatesRunnable.timezone = timezone;
taskScheduler.schedule(
sendUpdatesRunnable,
Date.from(zoned.toInstant())
);
和
@Component
public class SendUpdatesRunnable implements Runnable
@Autowired
ProductRepository productRepository;
String timezone;
@Override
@Transactional
public void run()
List<Product> newProds = productRepository.findProductByCreateDateTimeIsAfter(LocalDateTime.now().minusHours(24));
List<Product> updatedProds = productRepository.findProductByUpdateDateTimeIsAfter(LocalDateTime.now().minusHours(24));
//System print out timezone variable = null
【问题讨论】:
你能解释一下你在问什么吗?你能指望什么?会发生什么,何时以及如何发生? 【参考方案1】:问题
当您注入带有 AOP 内容的 Spring Bean(即@Transcational
)时,Spring Framework 不会注入真正的具体类,而是注入代理对象。代理类包含字段timezone
,它不会被委托给您的真实类实例。
Spring Framework 中的代理如何工作:
source
存在代理是因为您的 run()
方法看起来像这样:
public void run()
transactionManager.begin();
realInstance.run();
transactionManager.commit();
因此,当您像 sendUpdatesRunnable.timezone = timezone;
这样设置时区时,实际上是在这样做 proxy.timezone = timezone;
并且您的 sendUpdatesRunnable
对象仍然将 timezone
设为 null。
解决方案
你需要使用getter/setter。
当你使用 setter 方法时,代理类会有这样的实现:
public void setTimezone(String timezone)
realInstance.setTimezone(timezone);
因此,当您调用 set 方法时,它将正确传播到具体实现所在的真实实例。
话虽如此,您根本不应该在 Spring Bean 实现中使用实例级别的可变变量。您应该拥有的唯一实例变量是在 bean 初始化时注入的依赖项。
【讨论】:
我的意思是非依赖实例变量。我会更新以澄清。以上是关于为啥 Runnable 中的实例变量在执行时消失了?的主要内容,如果未能解决你的问题,请参考以下文章
继承Thread类和实现Runnable接口创建的线程对于实例变量和类变量的共享
继承Thread类和实现Runnable接口创建的线程对于实例变量和类变量的共享