Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/371.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java @间谍级';多线程环境中的s私有字段_Java_Spring_Junit_Mockito - Fatal编程技术网

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方法时在间谍上但是不在真实实例上,您不会在真实实例上看到任何效果

我觉得这是一个非常错误的实现!它不是一个间谍,而是一个隐藏和替换功能(


很抱歉浪费您的时间。

请考虑自己发布一个答案,这样这个问题就不会显示为“未回答”