>source

UPD : 표시된 답변으로 문제가 해결되었습니다. 작은 수정 사항에 대한 설명을 참조하십시오.

우선 도살 된 코드를 용서 해주세요. 이 문제는 나를 미치게 만들고 문제의 원인을 파악하기 위해 많은 것을 시도했지만 실패했습니다.

FMonitor 서비스가있는 Spring Boot 웹 앱이 있습니다. 앱이 실행되는 동안 작동하며 특정 폴더에 ".done"확장자를 가진 파일이 나타나면 알려줍니다. 앱을 실행하면 완벽하게 작동합니다 (적어도 그런 것 같습니다).

나는 시험으로 연습하기로 결정하고 간단한 세 가지를 던졌습니다. 한 번에 하나씩 실행할 때 성공으로 작동합니다. 그러나 전체 테스트 클래스를 실행할 때 처음 하나만 통과하고 다른 두 개는 실패합니다.

내 테스트는 System.out을 확인하고 원하는 출력과 비교합니다. 그리고 여기 저에게 가장 이상한 것이 있습니다. 그게 무슨 일이야.

첫 번째 테스트를 통과하면 FMonitor의 출력이 assertEquals에서와 똑같습니다. test1을 찾았습니다. test2를 찾았습니다. test3 발견

두 번째 테스트가 실패합니다. 출력은 어떤 이유로 이상하게 두 배가됩니다. test1을 찾았습니다. test1을 찾았습니다. test3 발견 test3 발견

그리고 세 번째는 실패합니다. 출력은 이제 3 배입니다. 찾은 테스트 찾은 테스트 찾은 테스트

내 생각 엔 내가 쓰레드로 완전히 잘못된 일을했기 때문에 fm.monitor ()는 어떻게 든 모든 이벤트와 그와 비슷한 것을 잡는다. 나는 매우 혼란 스럽습니다. 여기에서 스레딩을 구현하는 방법에 대해 많은 것을 시도했지만 잘하지 못했지만 여전히 동일하게 작동합니다. 또한 monitor ()에 대한 @Async 주석이 뭔가를 엉망으로 만들 수 있다고 생각했지만 제거해도 아무것도 변경되지 않았습니다. 도와주세요.

BunchOfTests

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.*;
import java.util.concurrent.Executor;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FMonitor.class)
public class BunchOfTests {
    private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
    private Executor executor;
    @Autowired
    FMonitor fm;
    @Test
    public void test1() throws InterruptedException, IOException {
        Runnable task = () -> {
            System.setOut(new PrintStream(outContent));
            fm.monitor();
            System.setOut(System.out);
        };
        executor = (runnable) -> new Thread(runnable).start();
        executor.executeTask(task);
        Thread.sleep(3000);
        File file1 = new File("C:\\dir\\test1.done");
        File file2 = new File("C:\\dir\\test2.done");
        File file3 = new File("C:\\dir\\test3.done");
        file1.createNewFile();
        file2.createNewFile();
        file3.createNewFile();
        Thread.sleep(3000);
        file1.delete();
        file2.delete();
        file3.delete();
        Assert.assertEquals("Found test1\r\n" + "Found test2\r\n" + "Found test3\r\n", outContent);
    }
    @Test
    public void test2() throws InterruptedException, IOException {
        Runnable task = () -> {
            System.setOut(new PrintStream(outContent));
            fm.monitor();
            System.setOut(System.out);
        };
        executor = (runnable) -> new Thread(runnable).start();
        executor.executeTask(task);
        Thread.sleep(3000);
        File file1 = new File("C:\\dir\\test1.done");
        File file2 = new File("C:\\dir\\test2.txt");
        File file3 = new File("C:\\dir\\test3.done");
        file1.createNewFile();
        file2.createNewFile();
        file3.createNewFile();
        Thread.sleep(3000);
        file1.delete();
        file2.delete();
        file3.delete();
        Assert.assertEquals("Found test1\r\n" + "Found test3\r\n", outContent);
    }
    @Test
    public void test3() throws InterruptedException, IOException {
        Runnable task = () -> {
            System.setOut(new PrintStream(outContent));
            fm.monitor();
            System.setOut(System.out);
        };
        executor = (runnable) -> new Thread(runnable).start();
        executor.executeTask(task);
        Thread.sleep(3000);
        File file = new File("C:\\dir\\test.done");
        file.createNewFile();
        Thread.sleep(3000);
        file.delete();
        Assert.assertEquals("Found test\r\n", outContent);
    }
}

FMonitor

import org.springframework.stereotype.Service;
import org.srpingframework.scheduling.annotation.Async;
import java.io.IOException;
import java.nio.file.*;
@Service
public class FMonitor {
    @Async("fMonitor")
    public void monitor() {
        Path path = Paths.get("C:\\dir");
        try {
            WatchService watchService = FileSystems.getDefault.newWatchService();
            path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
            WatchKey key;
            while ((key = watchService.take()) != null) {
                for (WatchEvent<?> event: key.pollEvents()) {
                    String filename = event.context().toString();
                    if (filename.endsWith(".done")) {
                        processFile(filename.substring(0, filename.lastIndexOf('.')));
                    }
                }
                key.reset();
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
    private void processFile(String filename) {
        System.out.println("Found " + filename);
    }
}

AConfiguration

import org.srpingframework.context.annotation.Bean;
import org.srpingframework.context.annotation.Configuration;
import org.srpingframework.scheduling.annotation.EnableAsync;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@Configuration
@EnableAsync
public class AConfiguration {
    @Bean(name = "fMonitor")
    public Executor asyncExecutor() { return Executors.newSingleThreadExecutor(); }
}


  • 답변 # 1

    우선, 당신은 깨끗하게 중지해야합니다 WatchService 완료되면. 이를 수행하고 주석을 추가하는 메소드를 구현하십시오. @PreDestroy .

    @Service
    public class FMonitor {
        private final WatchService watchService = FileSystems.getDefault.newWatchService();
    
        @Async("fMonitor")
        public void monitor() {
            Path path = Paths.get("C:\\dir");
            try {
                path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE);
                WatchKey key;
                while ((key = watchService.take()) != null) {
                    for (WatchEvent<?> event: key.pollEvents()) {
                        String filename = event.context().toString();
                        if (filename.endsWith(".done")) {
                            processFile(filename.substring(0, filename.lastIndexOf('.')));
                        }
                    }
                    key.reset();
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
        private void processFile(String filename) {
            System.out.println("Found " + filename);
        }
        @PreDestroy
        public void shutdown() {
          try {
            watchService.close();
          } catch (IOException ex) {}
        }
    }
    
    

    다음은 사용하지 마십시오 @SpringBootTest 그것은 당신의 테스트에 복잡성과 느린 것 외에는 아무것도 추가하지 않습니다. 대신 직접 인스턴스화하고 적절한 실행기를 사용하여 monitor 방법.

    public class BunchOfTests {
        @Rule
        public OutputCaptureRule output = new OutputCaptureRule();
        
        private ExecutorService executor = Executors.newSingleThreadExecutor();
        private final FMonitor fm = new FMonitor();
        @After
        public void cleanUp() throws Exception {
          fm.shutdown();
          executor.shutdown();
          while (!executor.awaitTermination(100, TimeUnit.MICROSECONDS));
        }
        @Test
        public void test1() throws InterruptedException, IOException {
            executor.submit(() -> fm.monitor());
            Thread.sleep(3000);
            File file1 = new File("C:\\dir\\test1.done");
            File file2 = new File("C:\\dir\\test2.done");
            File file3 = new File("C:\\dir\\test3.done");
            file1.createNewFile();
            file2.createNewFile();
            file3.createNewFile();
            Thread.sleep(3000);
            file1.delete();
            file2.delete();
            file3.delete();
            Assert.assertEquals("Found test1\r\n" + "Found test2\r\n" + "Found test3\r\n", output.toString());
        }
        @Test
        public void test2() throws InterruptedException, IOException {
            executor.submit(() -> fm.monitor());
            Thread.sleep(3000);
            File file1 = new File("C:\\dir\\test1.done");
            File file2 = new File("C:\\dir\\test2.txt");
            File file3 = new File("C:\\dir\\test3.done");
            file1.createNewFile();
            file2.createNewFile();
            file3.createNewFile();
            Thread.sleep(3000);
            file1.delete();
            file2.delete();
            file3.delete();
            Assert.assertEquals("Found test1\r\n" + "Found test3\r\n", output.toString());
        }
        @Test
        public void test3() throws InterruptedException, IOException {
            executor.submit(() -> fm.monitor());
            Thread.sleep(3000);
            File file = new File("C:\\dir\\test.done");
            file.createNewFile();
            Thread.sleep(3000);
            file.delete();
            Assert.assertEquals("Found test\r\n", output.toString());
        }
    }
    
    

    이와 같은 것은 당신이 원하는 것을 어느 정도 할 것입니다.

관련 자료

  • 이전 html - 맞춤 요소가 축소되는 이유는 무엇입니까?
  • 다음 html - Select2 부트 스트랩 요소가 예상대로 크기가 조정되지 않음