• Spring batch jobScope, stepScope
    DEV 2023. 12. 2. 21:21

    scope

    Spring batch의 jobScope, stepScope를 알아가기 위한 흐름

    jobScope, stepScope → bean scope → application context → IoC, DI

    IoC부터 시작해 보자.

    IoC (Inversion of Control)

    • 제어의 역전
      - 객체 생명주기, 메서드 호출 외부에서 관리
    • 일반적인 의존성에 대한 제어 → 객체 내에서 제어
    public class Mundo {
        private FrostfireGauntlet frostfireGauntlet;
        private BerserkerShoes berserkerShoes;
        private DoranShield doranShield;
    	
        public Mundo(){
            this.frostfireGauntlet = new FrostfireGauntlet();
    	this.berserkerShoes = new BerserkerShoes();
    	this.doranShield = new DoranShield();
        }
    }
    • 의존성을 외부의 누군가(spring, IoC container) 제어 → 객체 내 의존성 제어권이 없다.
    public class Mundo {
    	private Gauntlet gauntlet;
    	private Shoes shoes;
    	private Shield shield;
    	
    	public Mundo(Gauntlet gauntlet, Shoes shoes, Shield shield){
    		this.gauntlet = gauntlet;
    		this.shoes = shoes;
    		this.shield = shield;
    	}
    }
    
    pubilic interface Gauntlet{
    }
    
    pubilc class FrostfireGauntlet implements Gauntlet{
    }
    • 제어권이 내부에서 외부로 역전 → 내가 작성한 코드를 외부의 누군가 실행
      → 목적 = 역할과 관심을 분리
      → 필요한 객체를 외부에서 생성하여 필요한 객체에 주입해 주겠다.
      → 할리우드 원칙 (Don’t call us, We’ll call you)
      → 내가 알아서 필요한 객체 생성해서 주입해 줄 테니, 넌 가만히 있어..
      → IoC principle

    IoC container

    Difference between DIP and IoC

    그렇다면 DIP와 IoC의 차이는 무엇일까?

    DIP (Dependency Inversion Principle)

    • 의존 역전 원칙
    • 상위 레벨 모듈이 하위 레벨 모듈의 구현에 의존하지 않고
      → 저수준 모듈이 고수준 모듈의 인터페이스에 의존
      → 의존 방향이 역전
    public class Mundo {
        private FrostfireGauntlet frostfireGauntlet;
        private BerserkerShoes berserkerShoes;
        private DoranShield doranShield;
    	
        public Mundo(){
            this.frostfireGauntlet = new FrostfireGauntlet();
    	this.berserkerShoes = new BerserkerShoes();
    	this.doranShield = new DoranShield();
        }
    }
    public class Mundo {
    	private Gauntlet gauntlet;
    	private Shoes shoes;
    	private Shield shield;
    	
    	public Mundo(Gauntlet gauntlet, Shoes shoes, Shield shield){
    		this.gauntlet = gauntlet;
    		this.shoes = shoes;
    		this.shield = shield;
    	}
    }
    
    pubilic interface Gauntlet{
    }
    
    pubilc class FrostfireGauntlet implements Gauntlet{
    }
    • 근데 이건 위에서 봤던 IoC에 대한 예제 아니었나? 왜 DIP가 적용되어있는 거지?

    IoC와 DIP가 뭔가 헷갈리는데?

    한 단계씩 가보면

     

    1. init

    public class Mundo {
    	private FrostfireGauntlet frostfireGauntlet;
    	private BerserkerShoes berserkerShoes;
    	private DoranShield doranShield;
    	
    	public Mundo(){
    		this.frostfireGauntlet = new FrostfireGauntlet();
    		this.berserkerShoes = new BerserkerShoes();
    		this.doranShield = new DoranShield();
    	}
    }

    2. IoC 적용 → 의존 외부 주입 → 제어권이 내부에서 외부로

    public class Mundo {
    	private FrostfireGauntlet frostfireGauntlet;
    	private BerserkerShoes berserkerShoes;
    	private DoranShield doranShield;
    	
    	public Mundo(FrostfireGauntlet frostfireGauntlet, 
        						BerserkerShoes berserkerShoes, DoranShield doranShield){
    		this.frostfireGauntlet = frostfireGauntlet;
    		this.berserkerShoes = berserkerShoes;
    		this.doranShield = doranShield;
    	}
    }

    3. DIP 적용 → 고수준 모듈이 저수준 모듈에 의존하지 않도록, 고수준 입장에서 만든 인터페이스에 저수준 모듈이 의존 → 의존 방향 역전

    public class Mundo {
    	private Gauntlet gauntlet;
    	private Shoes shoes;
    	private Shield shield;
    	
    	public Mundo(Gauntlet gauntlet, Shoes shoes, Shield shield){
    		this.gauntlet = gauntlet;
    		this.shoes = shoes;
    		this.shield = shield;
    	}
    }
    
    pubilic interface Gauntlet{
    }
    
    pubilc class FrostfireGauntlet implements Gauntlet{
    }
    • 우리는 알게 모르게 IoC와 DIP를 함께 사용하고 있고, 스프링 또한 함께 사용하길 강력히 권장하고 있기도 하다.

    IoC, DIP의 공통된 목적

    • 클래스 간 결합을 느슨히 하기 위함
      → 하나의 클래스에서 변경 발생 시 다른 클래스의 미치는 영향을 최소화
      → application을 지속가능하고 확장성 있게 만들 수 있다.

    Relationship between IoC and DI

    spring에서는 IoC와 DI는 항상 같이 이야기되던데...

    DI (Dependency Injection)

    In software engineering, dependency injection is a design pattern in which an object or function receives other objects or functions that it depends on. A form of inversion of control, dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs.
    - wikipedia
    • 위키피디아에 따르면 dependency injection(DI)는 디자인 패턴이고, inversion of control(IoC)의 한 종류
    • 관심의 분리, 느슨한 연결을 목표로 한다.

    Principle, Pattern

    • principleIoC (Inversion of Control)
    • pattern → Factory, Template Method, Strategy, Dependency Injection(DI)
    • Ioc는 제어의 역전이라는 원칙, 아이디어이고, DI는 이를 해결하는 패턴 중 하나
      - 위 코드를 예로 들면 IoC는 ‘Mundo class가 자신의 아이템을 스스로 선택할 수 없게 한다,
        제어권을 외부로 넘긴다.’라는 추상적인 개념. 누가 어떻게 아이템을 주입해 줄지는 중요하지 않다.
      - DI는 Mundo class에 아이템을 위부에서 주입해주는 구체적인 행위

    DI in Spring

    • 그럼 스프링에서의 DI를 간단히 살펴보면..
    • 스프링 프레임워크에서 IoC를 위해 객체(bean)를 등록, 생성, 조회, 반환, 관리를 위한 컨테이너
      → IoC 컨테이너 (spring container)

    IoC container

    • 가져온 그림에서 약간 잘못된 부분이 있는데 A객체가 IoC컨테이너에 생성된 객체(bean)를 주입받기 위해서는 A객체도 bean으로 등록되어있어야 한다.
      → A객체도 IoC 컨테이너 안에 있어야 한다

    Bean

    Bean factory

    • 스프링 IoC를 담당하는 핵심 컨테이너, interface
    • bean 등록, 생성, 조회, 반환 관리
    • bean factory를 바로 사용하지 않고 이를 확장한 application context를 사용

    application context

    • bean factory를 확장한 IoC 컨테이너
    • 스프링의 각종 부가 기능지원
    • spring bean으로 등록되면 spring이 자동으로 생성, 주입해 주기 때문에 직접 사용할 일은 별로 없다.
      - 필드 주입, setter 주입, 생성자 주입

    bean faactory

    Bean scope

    • 객체(bean)가 영향을 미치는 범위 ⇒ scope
    • application context(IoC container)가 context가 시작하기 전에 모든 빈을 생성
    • singleton(default), prototype, request…
      - singleton 객체를 하나만 생성 → 각 thread가 공유 → non-thread safe → bean은 상태를 가져서는 안 됨
      - prototype으로 호출 시마다 객체 생성도 가능하지만 리소스 이슈
    @Component
    @Scope("singletone")
    public class Mundo {
        
        @PostConstruct
        public void load() {
            loadItems();
        }
        
        @PreDestroy
        public void release() {
            releaseItems();
        }
    }

    Bean scope in Spring batch

    • batch job 시작 → jobInstance 생성 → jobInstance = jobName + jobParameter
      - 동일한 jobParmeter로 동일한 job을 두 번 이상 실행 할 수 x
    • 기본적인 spring bean의 생성 시점 → application의 시작 → default = singleton scope
      - jobScpe, stepScope → job, step의 실행 시점에 Spring bean으로 생성
      - request scope: request 받을 때 bean을 생성하고 request가 반환되면 bean을 반환하는 것처럼
      - job, step의 시작, 종료 → bean을 생성, 반환 ⇒ jobScope, stepScope
    • jobScope, stepScope을 통해 bean 생성을 지연시켜서 얻는 이점
      - 아래 예제에서 jobScope가 없다면 application이 실행되는 시점에만 jobPrameter를 주입 가능
      (정확히는 jobParameter를 사용하려면 jobScope, stepScore를 무조건 사용해야 하지만.. 아무튼 개념설명을 위해)
      → jobScpe를 통해 controller, service 같은 비즈니스 로직 레벨에서 jobParmeter를 할당 가능
      → application 실행 후에도 동적으로 reader, processer, writer를 동적으로 만들 수 있다.
      - 병렬처리에 안전 → singleton scope로 생성한다면 → non-thread safe → job, step의 시작, 종료 → bean을 각각 생성, 반환
    @Bean
    @JobScope
    public Step analysisWinRateStep(@Value("#{jobParameters[requestDate]}") final String requestDate) {
           log.info("requestDate: {}", requestDate);
           return stepBuilderFactory.get("analysisWinRateStep")
                   .<LolWinRate, AnalysisWinRate>chunk(100)
                   .reader(lolWinRateReader())
                   .processor(analysisWinRateProcessor())
                   .writer(winRateWriter())
                   .build();
    }

    Fin.

    • principle → IoC (Inversion of Control), DIP (Dependency Inversion Principle)
      - IoC, DIP는 원칙
    • pattern  DI (Dependency Injection)
      - DI는 IoC를 따르기 위한 디자인 패턴
    • framework → Spring DI / IoC
      - DI를 자동으로 해주면서 프로그램의 제어권을 가짐

    Reference

    728x90

    'DEV' 카테고리의 다른 글

    LangChain에 대하여  (1) 2023.12.03
    JIT compiler & GraalVM in java  (2) 2023.12.03
    Elasticsearch에서의 relation  (1) 2023.12.02
    Spring batch integration test (feat.Elasticsearch)  (0) 2023.12.02
    K8S, DNS 간헐적 5~15초 지연  (0) 2023.12.02
go.