Java

[SpringFramework] PostConstruct & PreDestroy, CDI Jakart Contexts & Dependency Injection

ride-dev 2023. 12. 18. 18:24

 PostConstruct & PreDestroy, CDI Jakart Contexts & Dependency Injection

1. PostConstruct 및 PreDestroy

PostConstruct

초기화를 수행하기 위한 의존성 주입이 완료된 후 실행해야 하는 메서드에서 사용합니다,

클래스를 사용하기 전에 호출되어야 합니다.

(다른 Bean이 이 Bean을 사용할 수 있게 되기 전에 이 메서드가 호출됩니다)

이 때 애플리케이션이 실행되고 Bean이 로드됩니다.

PreDestroy

컨테이너에서 인스턴스를 삭제하는 과정 중에 있음을 알려주는 콜백 알림으로,

보유하고 있던 리소스를 해제하는 데 일반적으로 사용됩니다.

 

애플리케이션이 종료되기 전에, 컨텍스트에서 Bean이 삭제되기 전에 특정 작업을 하고자 할 때 사용합니다.

특정 작업은 보통 cleanup을 합니다.

(DB 등에 연결되어 있다면 cleanup으로 종료할 수 있습니다)

 

예시입니다.

@Component
class SomeClass {
    // 의존성을 연결하는 대로 초기화하는 것을 가정
    private SomeDependency someDependency;
    public SomeClass(SomeDependency someDependency) {
        super();
        this.someDependency = someDependency;
        System.out.println("SomeClass All dependencies are ready!");
    }

    /**
     * 초기화 과정의 일부,
     * 초기화를 수행하기 위해 의존성 주입이 완료된 후 실행해야 하는 메서드에서 사용,
     * 클래스를 사용하기 전에 호출되어야 함
     * (다른 Bean이 이 Bean을 사용할 수 있게 되기 전에 이 메서드가 호출)
     * dovmv
     */
    @PostConstruct
    public void initialize() {
        System.out.println("@PostConstruct Initialize");
        someDependency.getReady();
    }

    @PreDestroy
    public void cleanup() {
        System.out.println("@PreDestroy Cleanup");
    }
}

@Component
class SomeDependency{

    public void getReady() {
        System.out.println("[getReady] Some logic using SomeDependency");
    }
}

@Configuration
@ComponentScan
public class PrePostAnnotationsContextLauncherApplication {

    public static void main(String[] args) {
        try (
                var context =
                        new AnnotationConfigApplicationContext
                                (PrePostAnnotationsContextLauncherApplication.class);
        ) {
                Arrays.stream(context.getBeanDefinitionNames())
                        .forEach(System.out::println);
        }
    }
}

위 코드의 출력문은 아래와 같습니다.

SomeClass All dependencies are ready!
@PostConstruct Initialize
[getReady] Some logic using SomeDependency
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
prePostAnnotationsContextLauncherApplication
someClass
someDependency
@PreDestroy Cleanup

 

의존성을 연결하는 대로 초기화 논리를 실행하려는 경우

(예컨대 데이터베이스 등에서 데이터를 가져오려는 경우)

@PostConstruct를 사용할 수 있으며,

컨테이너에서 Bean이 삭제되기 전에,

(애플리케이션 컨텍스트에서 삭제되기 전에)

cleanup을 수행하려는 경우에는

@PreDestroy를 사용할 수 있습니다.

2. CDI Jakart Contexts & Dependency Injection

CDI는 어노테이션 그룹을 정의한 규격(스펙)이며 의존성 주입을 수행하는 데 사용할 수 있습니다.

CDI는 Jakarta EE에 속한 규격(specifiction)이며 Spring Framework에서 지원합니다.

Spring Framework V1은 2004년에 도입되었고,

CDI 규격은 2009년의 Java EE 6 플랫폼에 도입되었습니다.

지금은 Jakarta Contexts and Dependendy Injection이라고 부릅니다.

CDI는 규격이고, 인터페이스입니다. 따라서 구현이 없습니다.

(Spring Framework에서 CDI를 구현합니다)

CDI에는 중요한 API 어노테이션 몇 가지가 정의되어 있습니다.

Inject, Named, Qualifier, Scope, Singleton 이 예시에 해당합니다.

Inject는 Autowired와 비슷하고,

Named는 Component와 비슷합니다.

Qualifier, Scope, Singleton은 Spring에 있는 어노테이션과 비슷합니다.

 

의존성을 추가하고 코드를 작성하며 확인해보겠습니다.

pom.xml

<dependency>
	<groupId>jakarta.inject</groupId>
	<artifactId>jakarta.inject-api</artifactId>
	<version>2.0.1</version>
</dependency>

gradle

implementation 'jakarta.inject:jakarta.inject-api:2.0.1'

 

데이터 서비스를 사용하는 비즈니스 서비스가 있다고 가정을 해보겠습니다.

import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Arrays;

//@Component
@Named
class BusinessService {
    private DataService dataService;

    //@Autowired
    @Inject
    public void setDataService(DataService dataService) {
        System.out.println("Setter Injection");
        this.dataService = dataService;
    }

    public DataService getDataService() {
        return dataService;
    }

}
//@Component
@Named
class DataService {

}
@Configuration
@ComponentScan
public class CdiContextLauncherApplication {

    public static void main(String[] args) {
        try (
                var context =
                        new AnnotationConfigApplicationContext
                                (CdiContextLauncherApplication.class);
        ) {
                Arrays.stream(context.getBeanDefinitionNames())
                        .forEach(System.out::println);

            System.out.println(context.getBean(BusinessService.class)
                    .getDataService());
        }
    }
}

위 코드의 출력문은 아래와 같습니다.

Setter Injection
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
cdiContextLauncherApplication
businessService
dataService
com.ride.learnspringframework2.example.g1.DataService@20f5239f

두 어노테이션간 출력문의 차이는 없으며,

통상적으로 Spring 프로젝트에서는 CDI 스펙보다는 Componet, Autowired를 사용합니다.

728x90