问题描述
@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
}
}
解决方法
问题
当您注入带有AOP内容的Spring Bean(即@Transcational
)时,Spring Framework不会注入真正的具体类,而是注入一个代理对象。代理类包含字段timezone
,并且不会委派给您的真实类实例。
Spring Framework中的代理如何工作:
存在代理是因为您的run()
方法看起来像这样:
public void run() {
transactionManager.begin();
realInstance.run();
transactionManager.commit();
}
因此,当您像这样设置时区sendUpdatesRunnable.timezone = timezone;
时,它实际上是在执行proxy.timezone = timezone;
,并且您的sendUpdatesRunnable
对象仍然具有timezone
为空。
解决方案
您需要使用getters / setters。
当您使用setter方法时,代理类将具有如下的实现:
public void setTimezone(String timezone) {
realInstance.setTimezone(timezone);
}
因此,当您调用set方法时,它将正确传播到具体实现所在的真实实例。
话虽如此,您不应该在Spring Bean实现中完全使用实例级可变变量。唯一的实例变量应该是在bean初始化时注入的依赖项。