StepExecution 내 ExecutionContext는 Step안에서만 공유됩니다. 즉 특정 Step에서만 접근이 가능합니다. 실패한 Step이 재시작된 경우도 이전까지 작업한 내용을 불러들일수 있습니다.
JobExeuction 내 ExecutionContext는 모든 Step안에서 공유됩니다.
예시 코드
아래와 같이 ExecutionContext를 StepContribution 또는 ChunkContext를 통해 접근하고, 값을 넣어줄 수 있습니다. 넣어준 값은 BATCH_JOB_EXECUTION_CONTEXT , BATCH_STEP_EXECUTION_CONTEXT 테이블에 각각 직렬화되어 저장됩니다.
jobExecutionContext.put("jobName", "developer"); //1. BATCH_JOB_EXECUTION_CONTEXT 에 저장 (모든 STEP에서 공유) stepExecutionContext.put("stepName", "software"); // 2. BATCH_STEP_EXECUTION_CONTEXT 에 저장 (특정 STEP에서 공유)
return RepeatStatus.FINISHED; } }
Spring Batch에서 제공하는 Step 구현체(5)
Step 인터페이스를 AbstractStep이라는 추상 클래스에서 구현하고,AbstractStep 추상클래스를 구현하는 구조입니다.
Batch에서 제공하는 Step의 구현체는 아래와 같은 5개의 구현체가 존재합니다.
Tasklet Step
Partition Step
Job Step
Flow Step
Decision Step
TaskletStep
RepeatTemplate을 사용해서 Tasklet 코드 block을 트랜잭션 경계 내(성공시 커밋,실패시 롤백)에서 반복해서 실행합니다. 언제까지 반복해서 실행할것인가에 대한 판단은 Tasklet 객체에서 반환하는 RepeatStatus값에 의해 결정됩니다. RepeatStatus.FINISHED 와 같이 특정 RepeatStatus를 반환할떄까지 계속해서 실행합니다.
TaskletStep은 아래와 같은 tasklet 인터페이스를 구현하는 tasklet 구현체를 멤버변수로 가지고 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/** * Strategy for processing in a step. */ publicinterfaceTasklet{
/** * @return an {@link RepeatStatus} indicating whether processing is * continuable. Returning {@code null} is interpreted as {@link RepeatStatus#FINISHED} * * @throws Exception thrown if error occurs during execution. */ @Nullable RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext)throws Exception;
}
TaskletStep의 코드를 확인해보면 Tasklet 호출횟수를 조정하기 위한 RepeatTemplate 와 실행되야할 작업 그 자체 (tasklet) 구현체를 멤버변수로 들고있는것을 확인할 수 있습니다.
jobLauncher.run(job,jobParameters); // 2. Job Launcher가 주어진 Job과 Job Parameter를 받아서 Job을 실행합니다. } }
Job Instance
Job이 실행될떄 생성되는 Job의 논리적 실행 단위 객체. 즉 작업 실행을 의미한다.
Job과 JobInstance는 왜 구분해야 할까?
Job의 설정과 구성은 동일하지만 Job이 실행되는 시점에 처리하는 내용은 다르기 때문에 Job의 실행을 구분해야 합니다.
JobInstance는 Job과 JobParameter의 조합으로 생성하며, 처음 시작하는 Job과 Job Parameter의 경우는 새로운 Job Instance를 생성합니다.
하나의 Job은 여러번 실행될 수 있으므로 Job과 JobInstance의 연관관계는 1:N의 관계입니다.
BATCH_JOB_INSTANCE 테이블에 매핑됩니다.
JobInstance의 경우 동일한 Job과 Job Parameter의 조합인 경우 예외(JobInstanceAlreadyCompleteException)를 던집니다.즉 동일한 Job을 동일한 Job Parameter로 돌리면 중복 JobInstance로 판정됩니다.
1 2 3 4 5 6
Caused by: org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException: A job instance already exists and is complete for parameters={name=user1}. If you want to run this job again, change the parameters. at org.springframework.batch.core.repository.support.SimpleJobRepository.createJobExecution(SimpleJobRepository.java:139) ~[spring-batch-core-4.3.9.jar:4.3.9] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
Job Parameter
말그대로 Job을 실행할 때 함께 포함되어 사용되는 parameter 가진 도메인 객체입니다
하나의 Job안에 존재하는 여러 JobInstance를 구분하기 위한 용도이다. 즉 JobInstance와 JobParameter는 1:1 관계입니다. (테이블 관계에서는 1:N)
BATCH_JOB_EXECUTION_PARAM 테이블과 매핑됩니다.
Spring Batch에서는 STRING, DATE, LONG, DOUBLE 타입을 지원을 하며, BATCH_JOB_EXECUTION_PARAM 테이블에서 타입별로 칼럼을 가지고 있습니다.
Job Parameter 생성방식
Application 생성 시점에 외부 환경변수로 주입되는 방법
빌드한 Jar파일에 다음과 같이 외부 매개변수로 KEY-VALUE 형태로 값을 주입해줄 수 있다. 이떄 문자형이 아닌 경우는 별도로 (타입명)을 지정해주어야 합니다. 혹은 program argument로 key=value형태로 넣어줄 수 있습니다.
@Override public Object invoke(Object proxy, Method method, Object[] args)throws Throwable { String ret = (String) method.invoke(target,args); // 타깃 클래스에게 위임 return ret.toUpperCase(); // 공통 부가 기능 수행 } }
이제 실제로 Dynamic Proxy 를 생성해주는 코드를 보면 Proxy의 정적 팩토리 메소드를 통해 생성할 수 있다.
1 2 3 4 5 6 7
Hello proxy(){ return (Hello) Proxy.newProxyInstance( getClass().getClassLoader(), // 동적으로 생성되는 프록시 클래스 로딩에 사용될 클래스 로더 new Class[]{Hello.class}, // 구현할 인터페이스 new UppercaseHandler(new HelloTarget()) // 부가기능 코드 ); }
Dynamic Proxy의 장점
인터페이스의 메소드가 늘어나도 , 클래스로 직접 구현한 프록시와 다르게 수정이 일어나지 않는다.
부가 기능 코드는 InvocationHandler 구현체에 들어있어서 , 타겟의 종류와 상관없이 적용가능하다. 꼭 특정타입의 타겟이 아니라 , 다른 종류의 타겟에도 적용이 가능하다.
Spring 은 지정된 클래스 이름을 가지고 , Reflection을 통해서 해당 클래스의 객체를 생성한다.
1
Date now = (Date) Class.forName("java.util.Date").newInstance();
반면 Dynamic Proxy 객체 생성 방식은 Proxy 클래스의 newProxyInstance 정적 팩토리 메소드에 의해 생성되며 , 클래스 자체도 런타임에 결정된다. Spring은 어떤 클래스 타입을 해당 Proxy객체가 가질지 , 컴파일 타임에는 알수가 없다.
Factory Bean 을 통해 Dynamic Proxy를 Spring Bean으로 등록한다.
부제 그대로 Spring은 Factory Bean을 통해 Dynamic Proxy 를 Spring Bean으로 등록한다.
Factory Bean이란 Spring을 대신해서 객체의 생성 로직을 담당하도록 만들어진 빈을 말하며 , Spring이 제공해주는 FactoryBean 인터페이스를 구현함으로서 만들 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
package org.springframework.beans.factory;
publicinterfaceFactoryBean<T> {
@Nullable T getObject()throws Exception;
@Nullable Class<?> getObjectType();
defaultbooleanisSingleton(){ returntrue; } }
FactoryBean 인터페이스는 3가지 method로 구성되어있는데 , getObject() method에 Spring Bean으로 등록할 객체 생성로직이 들어가고 , 해당 객체 어떤 클래스 타입인지는 getObjectType() method에 들어간다. isSingleton method는 getObject() method가 반환해주는 객체가 항상 동일한 객체인지 , 즉 싱글톤인지 여부를 명시한다.
아래와 같이 정적 팩토리 메소드를 통해서만 객체 생성을 할 수 있는 경우에 FactoryBean을 활용할 수 있다.
@Override public Message getObject()throws Exception { return Message.newMessage(text); }
@Override public Class<?> getObjectType() { return Message.class; }
@Override publicbooleanisSingleton(){ returnfalse; // Message 정적 팩토리 메소드는 매번 새로운 Message 객체를 // 반환함으로 false로 설정하지만 , 실제로 만들어진 Bean 객체는 싱글톤으로 Spring이 관리해줄 수 있다. } }
Factory Bean 설정
일반 Bean과 다르게 Factory Bean의 경우 bean class property는 FactoryBean 이지만 , 실제로 반환되는 타입은 getObjectType method에 명시된 타입이다. 즉 위 예제에서는 Message 타입이 반환된다.
1 2 3 4
<beanid="message"class="MessageFactoryBean"> <!-- FactoryBean 타입으로 빈 클래스 프로퍼티 설정 --> <constructor-argname="text"value="Factory Bean"></constructor-arg> </bean>
front controller pattern 은 주로 web 환경에서 client 요청이 들어오면 먼저 공통적인 로직을 처리하는 하나의 controller를 두고, 해당 controller가 적합한 controller 를 호출하는 패턴이다.
front controller의 정확한 정의는 다음과 같다.
Front Controller is defined as “a controller that handles all requests for a Web site”. It stands in front of a web-application and delegates requests to subsequent resources. It also provides an interface to common behavior such as security, internationalization and presenting particular views to certain users
front controller 가 결국 모든 controller이전에 수행됨으로 controller에서 발생할 수 있는 공통로직을 front controller에서 처리하고, 중복코드를 제거하고 유지보수성을 높여준다.
front controller를 제외한 나머지 controller는 servlet을 사용하지 않아도 된다.
Front controller pattern 은 MVC pattern과 함께 자주 쓰인다. 대표적으로 spring framework에서 사용하고 있으며,org.springframework.servlet.dispatcherServlet이 front controller이다.
UML diagram
front controller pattern은 front controller와 요청을 위임할 class (controller) 로 구성된다. 이떄 요청을 위임할 class는 공통 abstract class 또는 interface를 상속하고 있다.
spring은 입력데이터에 대한 validation과 예외처리를 지원해준다. org.springframework.validation.BindingResult 가 validation 기능을 지원해주는 주요 객체 중 하나이다. BindingResult는 입력 form의 필드값 중에 오류가 있으면 오류정보를 담아둔다.
/** * A converter converts a source object of type {@code S} to a target of type {@code T}. */ @FunctionalInterface publicinterfaceConverter<S, T> {
/** * Convert the source object of type {@code S} to target type {@code T}. * @param source the source object to convert, which must be an instance of {@code S} (never {@code null}) * @return the converted object, which must be an instance of {@code T} (potentially {@code null}) * @throws IllegalArgumentException if the source cannot be converted to the desired target type */ @Nullable T convert(S source);