Java @间谍级';多线程环境中的s私有字段
我有一个集成Java @间谍级';多线程环境中的s私有字段,java,spring,junit,mockito,Java,Spring,Junit,Mockito,我有一个集成@SpringBootTest,它也意味着另一个后台线程 从测试的线程(!)中,一些公共方法调用修改了@SpyBean-ed实例(@Scope在singleton中)的私有字段 问题是来自后台线程的调用看不到此更改 经过数小时的调试(例如尝试volatile),我探索了@SpyBean-ed实例,发现所提到的字段(\u state)在引用实例和包装实例(假设spiedInstance表示包装实例)以及其他私有字段之间确实不同 有什么想法吗 更新: 我简化了我的环境以帮助复制 @Se
@SpringBootTest
,它也意味着另一个后台线程
从测试的线程(!)中,一些公共方法调用修改了@SpyBean
-ed实例(@Scope
在singleton中)的私有字段
问题是来自后台线程的调用看不到此更改
经过数小时的调试(例如尝试volatile),我探索了@SpyBean
-ed实例,发现所提到的字段(\u state
)在引用实例和包装实例(假设spiedInstance
表示包装实例)以及其他私有字段之间确实不同
有什么想法吗
更新:
我简化了我的环境以帮助复制
@Service
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class TestServiceImpl {
private Logger logger = LoggerFactory.getLogger(TestServiceImpl.class);
private final TaskExecutor taskExecutor;
private volatile int value = 0;
private volatile boolean isEnabled = false;
@Autowired
public TestServiceImpl(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
taskExecutor.execute(() -> pingToLog());
}
private void pingToLog() {
while(true) {
logger.debug(String.valueOf(value));
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
// no op
}
}
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = BicisoApplication.class)
@DirtiesContext
public class TestServiceTest {
@SpyBean
protected TestServiceImpl testService;
@Test
public void doTest() {
try {
TimeUnit.MILLISECONDS.sleep(3000);
} catch (InterruptedException e) {
// no op
}
testService.setValue(1);
try {
TimeUnit.MILLISECONDS.sleep(3000);
} catch (InterruptedException e) {
// no op
}
}
}
预期:在日志中的一些“TestServiceImpl-0”行之后
应该出现一些“TestServiceImpl-1”行
当前的调查结果:如果我将execute的调用从构造函数中移出,放入public方法并通过代理调用,它将
按预期工作
但是上面的testserviceinpl
是一种简化。在实代码中,类在构造函数中订阅将由其他类触发的事件
线程。此子分类引用了原始实例,而不是spy包装,因此问题就出现了
更新2:
似乎@SpyBean
创建了一个新实例,而不是使用已经存在的实例
在上下文中。我仍然认为这是一个bug:@SpyBean
应该包装现有实例而不是重复实例,并复制其可能已经在使用的字段
更新3:
突然,结果是情况已经被记录下来了。
Mockito的文档在“监视真实对象”部分中包含以下内容:
Mockito不委托调用传递的实际实例,而是实际创建其副本。因此,如果您保留真实实例并与之交互,不要期望间谍知道这些交互及其对真实实例状态的影响。推论是,当在间谍上调用未隐藏的方法,但在真实实例上调用而在真实实例上调用时,您将看不到对真实实例的任何影响
我觉得这是一个非常错误的实现!
它不是间谍,而是隐藏和替换功能(
很抱歉浪费您的时间。突然,结果是情况已经被记录在案,至少您反向设计了这个问题,直到您学会不相信单词的明显含义,而是寻找文档。Mockito的文档包含以下内容在“监视真实对象”部分下: Mockito不委托调用传递的真实实例,而是实际创建它的副本。因此,如果您保留真实实例并与之交互,不要期望间谍知道这些交互以及它们对真实实例状态的影响。推论是,当调用unsubbed方法时在间谍上但是不在真实实例上,您不会在真实实例上看到任何效果 我觉得这是一个非常错误的实现!它不是一个间谍,而是一个隐藏和替换功能(
很抱歉浪费您的时间。请考虑自己发布一个答案,这样这个问题就不会显示为“未回答”