@PostConstruct 어노테이션이 사용되는 이유를 알아보기 위해 먼저 스프링 빈의 생명주기에서 부터 초기화 콜백이 무엇인지, 어떤 용도로 쓰이는지에 대해 살펴보겠습니다.
스프링 프레임워크는 IoC(Inversion of Control) 컨테이너를 통해 Bean 객체들을 관리하는데요.
인스턴스 생성부터 소멸까지의 생명주기 관리를 개발자가 아닌 컨테이너가 대신해주기 때문에 개발자는 로직에 집중할 수 있다는 장점이 있습니다. (즉, 객체 관리 주체가 프레임워크가 되는 것입니다.)
스프링 빈 생명주기(Bean LifeCycle)
스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존 관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 콜백 -> 스프링 종료
Spring Bean은 다음과 같은 생명 주기를 가지고 있는데요.
스프링 빈으로 등록되는 객체를 사용하기 위해서는 객체가 생성된 후 의존성 주입까지(Dependency Injection) 끝나야 해당 객체를 사용할 수 있게 됩니다.
(생성자 주입 방식의 경우 '객체 생성' -> '의존 관계 주입'의 과정이 한 번에 처리됩니다.)
그리고 의존 관계가 주입된 후에 값을 설정해주는 단계를 초기화 단계라고 하는데요. 스프링에서는 스프링 빈이 의존 관계 주입이 완료되면 콜백 메서드를 통해 초기화 시점을 알려주는 기능을 제공하며(초기화 콜백), 이것을 통해 개발자는 의존 관계 주입이 완료된 상태에서 필요한 기능을 수행할 수 있게 되는 것입니다.
(스프링 컨테이너의 소멸 시점에서도 콜백 메서드를 통해 필요한 기능을 수행할 수 있으며, 이것을 '소멸 콜백'이라고 합니다.)
스프링 빈의 생명주기 콜백을 관리하는 방법
- 인터페이스 (InitializingBean, DisposableBean) 상속
- 설정 정보에 초기화 메서드, 종료 메서드 지정
- @PostConstruct, @PreDestroy 어노테이션
스프링 빈에서는 위 3가지 방법을 통해 생명주기 콜백(초기화 콜백, 소멸 콜백)을 지원하는데요.
실무에서 가장 간편하게 사용되며 Spring에서도 권고하는 방법이 바로 @PostConstruct, @PreDestroy 어노테이션을 통한 방법입니다.
@PostConstruct
정리해보자면 @PostConstruct 어노테이션은 빈이 생성되고 의존 관계 주입이 완료된 후 실행되는 '초기화 콜백'을 적용할 수 있는 어노테이션인데요.
해당 어노테이션이 적용된 메서드는 모든 access level을 가질 수는 있지만 static 메서드는 될 수 없다는 특징이 있으며, 이 어노테이션이 적용된 메서드는 다른 리소스에서 호출되지 않아도 어플리케이션 서버 기동 시 자동으로 수행된다는 특징이 있습니다.
또한 @PostConstruct, @PreDestroy 어노테이션은 Spring에 종속된 기술이 아니라 javax.annotation 패키지에 포함되어 있는 자바 표준 기술 JSR-250인데요. 때문에 스프링이 아닌 다른 프레임워크에서도 동작한다는 특징이 있습니다.
/*
@PostConstruct, @PreDestroy annotation은 Java EE의 일부인데, Java 9와 Java 11에서는 Java EE가 사용되지 않기 때문에 아래 종속성을 추가해줘야 한다는 이슈가 있습니다.
*/
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
@PostConstruct 사용 예시
@RequiredArgsConstructor
@Component
public class DBInit {
private final MemberRepository memberRepository;
@PostConstruct
public void setDefaultAdmin() {
Member adminMember = new Member("admin", "password");
memberRepository.save(adminMember);
}
}
주의할 점으로는 @PostConstruct 어노테이션은 해당 빈 자체만 생성된다고 가정되어 호출된다는 것인데요.
즉, 해당 빈에 관련된 AOP 등을 포함한 전체 스프링 어플리케이션 컨텍스트(Application Context)가 초기화된 것을 의미하지 않으며, 트랜잭션을 처리하는 AOP 등은 스프링의 후 처리기(Post Processer)가 완전히 동작된 후 스프링 어플리케이션 컨텍스트의 초기화가 완료되어야 적용된다는 이슈가 있습니다.
< 참고 자료 >
https://ksr930.tistory.com/282
https://www.inflearn.com/questions/26902/postconstruct%EC%99%80-transactional-%EB%B6%84%EB%A6%AC
'Programming > Java' 카테고리의 다른 글
FTP로 살펴보는 SocketTimeout(soTimeout)과 ConnectionTimeout 차이점 (0) | 2023.02.01 |
---|---|
(java) Enum field에 Enum List를 사용하며 발생한 코드 리팩토링 (0) | 2023.01.13 |
Java Iterable Iterator 차이점 정리 (0) | 2022.12.02 |
로깅시 System.out.println() 사용을 지양해야 하는 이유 (0) | 2022.11.16 |
Java Enum 활용하기2 - ConverterFactory (1) | 2022.10.08 |