[SPRING] Spring Batch (2)

[SPRING] Spring Batch (2)

Last modified on 2025-04-18 , by hjjae2

애플리케이션 실행 순서 #

SpringApplication 
    ---> JobLauncherApplicationRunner (ApplicationRunner)
    ---> 

JobLauncherApplicationRunner #

SpringApplication 에 의해 run() 메서드가 호출되고, execute() 메서드를 실행한다.

    protected void execute(Job job, JobParameters jobParameters)
            throws JobExecutionAlreadyRunningException, 
            JobRestartException, 
            JobInstanceAlreadyCompleteException,
            JobParametersInvalidException, 
            JobParametersNotFoundException {
        JobParameters parameters = getNextJobParameters(job, jobParameters); // (1)
        JobExecution execution = this.jobLauncher.run(job, parameters); // (2)
        if (this.publisher != null) {
            this.publisher.publishEvent(new JobExecutionEvent(execution)); // (3)
        }
    }

(1) jobParameter 를 가져온다.
(2) jobLauncher.run() 을 통해 job 을 실행한다. jobLauncher.run()JobExecution 을 반환한다.
(3) JobExecutionEvent 발행한다.


SimpleJobLauncher (JobLauncher) #

    // Run the provided job with the given JobParameters. 
    // The JobParameters will be used to determine if this is an execution of an existing job instance, or if a new one should be created.
    // JobParameter 는 새 잡 인스턴스를 생성할 지, 기존 잡 인스턴스를 실행할 지 판단하기 위해 사용된다.
    public JobExecution run(final Job job, final JobParameters jobParameters)
			throws JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException,
			JobParametersInvalidException {
        ...
		
        // LastJobExecution 을 가져온다.
        // LastJobExecution 은 JobInstance 기준으로 마지막에 생성된 JobExecution 을 의미한다.
        JobExecution lastExecution = jobRepository.getLastJobExecution(job.getName(), jobParameters);

		// 이전 jobExecution 이 있다면 재시작 가능한 상태인지 확인합니다.
		if (lastExecution != null) {
			if (!job.isRestartable()) {
				throw new JobRestartException("JobInstance already exists and is not restartable");
			}
            
			for (StepExecution execution : lastExecution.getStepExecutions()) {
				BatchStatus status = execution.getStatus();
				if (status.isRunning() || status == BatchStatus.STOPPING) {
					throw new JobExecutionAlreadyRunningException(...);
				} else if (status == BatchStatus.UNKNOWN) {
					throw new JobRestartException(...);
				}
			}
		}

		// JobParameter 유효성 검증합니다.
		job.getJobParametersValidator().validate(jobParameters);

		// 기존 JobInstance, JobExecution 이 있는 경우 : (LastJobExecution의) ExecutionContext 사용합니다.
		// 기존 JobInstance, JobExecution 이 없는 경우 : 새 JobInstnace, ExecutionContext 를 생성합니다.
		// [참고] JobExecution 은 항상 새롭게 생성된다. (JobInstnace, ExecutionContext 내용이 달라지는 것이다.)
		jobExecution = jobRepository.createJobExecution(job.getName(), jobParameters);

		try {
			taskExecutor.execute(new Runnable() {
				@Override
				public void run() {
					try {
						...

						job.execute(jobExecution);

						...
					}
					catch (Throwable t) {
						...
					}
				}
			});
		}
		catch (TaskRejectedException e) {
			// 에러가 발생한 경우, JobExecution 의 상태를 업데이트 합니다.
			// 에러가 발생하지 않은 경우, 위 job.execute() 호출 내에서 업데이트 됩니다.
			jobExecution.upgradeStatus(BatchStatus.FAILED);
			if (jobExecution.getExitStatus().equals(ExitStatus.UNKNOWN)) {
				jobExecution.setExitStatus(ExitStatus.FAILED.addExitDescription(e));
			}
			jobRepository.update(jobExecution);
		}

		return jobExecution;
	}

(1) 이미 존재하고 있는 JobInstance, JobExecution 내용을 확인하고 검증합니다. (JobParameter 유호성 검증도 포함합니다.)
(2) JobExecution 을 생성합니다. (ExecutionContext 는 새 것이거나, 기존 내용이 됩니다.)
(3) Job 을 실행합니다. (AbstractJob -> SimpleJob)


Job (AbstractJob, SimpleJob) #