작성
·
2.5K
0
답변 3
1
네
@JobScope 를 선언하게 되면 Step 은 빈이 아닌 프록시로 생성이 됩니다.
이 말은 jobOperator.stop(jobExecution.getId()) 의 실행되는 과정 중에 어떤 시점에서 Step 의 프록시가 아닌 실제 Step 객체를 참조하게 된다면 JobContext 에서 Step 의 객체를 찾게 되는데 해당 Step 을 저장할 JobContext 객체가 생성하기 전에 호출이 발생해서 일어나는 예외입니다.
@JobScope 를 선언하면 Step 마다 새로운 객체가 생성되기 때문에 Step 을 저장하는 JobContext 도 런타임때 생성이 됩니다.
이 부분은 사실 복잡도가 있는 부분입니다.
그리고 @JobScope 는 실시간적으로 웹에서 Job 이나 Step 를 제어하는 곳에서는 객체의 생성시점을 정확하게 동기화하기가 어렵기 때문에 스프링 배치 설정에서만 사용하길 권장합니다.
디버그 하면
그래서 이런 오류가 발생하고 있습니다.
@JobScope 를 선언하지 않으면 정상작동 합니다.
@JobScope 와 @StepScope 는 강의에서 자세히 설명하고 있으니 참고 바라며 적절한 상황에서 잘 사용해야 하는 기술입니다.
0
BatchConfig.java
@Configuration
@RequiredArgsConstructor
public class BatchConfig {
private final JobRegistry jobRegistry;
@Bean
public BeanPostProcessor JobRegistryBeanPostProcessor() {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
}
MyJobConfig.java
@Configuration
@RequiredArgsConstructor
public class MyJobConfig {
private final JobBuilderFactory jobBuilderFactory;
private final StepBuilderFactory stepBuilderFactory;
@Bean
public Job myJob() {
return jobBuilderFactory.get("myJob")
.start(myStep1())
.next(myStep2())
.build();
}
@Bean
@JobScope
public Step myStep1() {
return stepBuilderFactory.get("myStep1")
.tasklet((contribution, context) -> {
System.out.println("myStep1 start");
Thread.sleep(10000);
System.out.println("myStep1 end");
return RepeatStatus.FINISHED;
})
.build();
}
@Bean
@JobScope
public Step myStep2() {
return stepBuilderFactory.get("myStep2")
.tasklet((contribution, context) -> {
System.out.println("myStep2 start");
Thread.sleep(10000);
System.out.println("myStep2 end");
return RepeatStatus.FINISHED;
})
.build();
}
}
BatchController.java
@RestController
@RequiredArgsConstructor
@RequestMapping("/batch")
public class BatchController {
private final JobOperator jobOperator;
private final JobRegistry jobRegistry;
private final JobExplorer jobExplorer;
@GetMapping("/start")
public String jobStart(String data) throws NoSuchJobException, JobInstanceAlreadyExistsException, JobParametersInvalidException {
for(String jobName : jobRegistry.getJobNames()) {
jobOperator.start(jobName, "data="+data);
}
return "SUCCESS";
}
@GetMapping("/restart")
public String jobRestart(String data) throws JobInstanceAlreadyCompleteException, NoSuchJobExecutionException, NoSuchJobException, JobRestartException, JobParametersInvalidException {
for(String jobName : jobRegistry.getJobNames()) {
Long executionId = jobExplorer.getLastJobExecution(jobExplorer.getLastJobInstance(jobName)).getId();
jobOperator.restart(executionId);
}
return "SUCCESS";
}
@GetMapping("/stop")
public String jobStop() throws NoSuchJobExecutionException, JobExecutionNotRunningException {
for(String jobName : jobRegistry.getJobNames()) {
for(JobExecution jobExecution : jobExplorer.findRunningJobExecutions(jobName)) {
jobOperator.stop(jobExecution.getId());
}
}
return "SUCCESS";
}
}
위 코드에서 http://localhost:8080/batch/start?data=data로 job실행하고, 끝나기전에 http://localhost:8080/batch/stop 호출하면 "java.lang.IllegalStateException: No context holder available for job scope" 에러 발생합니다.
0