EntityListeners
로 등록한 클래스에서 Repository
를 주입받아 사용하고자 하는데 주입이 정상적으로 되지 않았습니다.
@EntityListeners(value = UserHistoryBackupListener.class)
위처럼 UserHistoryBackupListener
를 EntityListener
로 등록했고 Listener
클래스는 아래처럼 구현했습니다.
@Slf4j
@Component
@RequiredArgsConstructor
public class UserHistoryBackupListener {
private final UserHistoryRepository userHistoryRepository;
@PostPersist
@PostUpdate
public void preUpdate(Object obj) {
log.info("UserHistoryBackupListener preUpdate 호출");
User user = (User) obj;
UserHistory userHistory = UserHistory.builder()
.userId(user.getId())
.name(user.getName())
.email(user.getEmail())
.gender(user.getGender())
.build();
userHistoryRepository.save(userHistory);
}
}
특별한거 없이 UserHistoryRepository
를 주입받아 사용하고 있습니다.
EntityListener
클래스가 아니라면 문제가 없어야 하지만 앞서 말했듯 EntityListeners
로 등록된 클래스의 경우 Spring IOC
의 관리 대상이 아니므로 의존성을 땡겨올 저장소에 빈이 없을 수 있습니다.
IDE에서 에러메시지도 보여주네요.
찾아본 결과 EntityListeners
에 등록되는 클래스 역시 @Component
로 빈으로 등록되는데 여기서 EntityListeners
에 등록되는 클래스가 주입되는 빈보다 먼저 등록될 수도 있기 때문에 발생하는 에러라고 합니다.
그렇다면 해결방법 중 하나는 빈의 생성시점과 무관하게 모든 빈이 생성된 후 EntityListners
에 등록된 클래스에서 사용하고자 하는 Bean
을 직접 조회해서 가져온 후 사용하면 되는 것 입니다.
그럼 직접 Bean
을 조회하고 받아올 수 있는 유틸 클래스를 구현합니다.
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class BeanUtils implements ApplicationContextAware {
private static ApplicationContext ac;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanUtils.ac = applicationContext;
}
public static <T> T getBean(Class<T> clazz) {
return ac.getBean(clazz);
}
}
ApplicationContextAware
인터페이스를 구현하여 ApplicationContext
를 받아옵니다.
ApplicationCOntextAware
인터페이스의 구현 메서드는 setApplicationContext
하나입니다. ApplicationContext
를 받아오는 역할만을 수행하는 것 입니다.
ApplicationContext
를 받아왔다면 이를 통해 클래스 타입으로 빈을 조회합니다.
제네릭을 사용하여 여러 곳에서 사용 가능한 유틸메서드로 만들었습니다.
@Slf4j
@Component
@RequiredArgsConstructor
public class UserHistoryBackupListener {
@PostPersist
@PostUpdate
public void preUpdate(Object obj) {
log.info("UserHistoryBackupListener preUpdate 호출");
UserHistoryRepository userHistoryRepository = BeanUtils.getBean(UserHistoryRepository.class);
User user = (User) obj;
UserHistory userHistory = UserHistory.builder()
.userId(user.getId())
.name(user.getName())
.email(user.getEmail())
.gender(user.getGender())
.build();
userHistoryRepository.save(userHistory);
}
}
기존에 생성자 주입을 통해 Repository
를 주입받는 부분을 지우고 BeanUtils
의 getBean
메서드를 이용해서 UserHistoryRepository
를 직접 조회해서 Bean을 받아오는 방법으로 문제가 해결되었습니다.
감사합니다 😁