JPA Listener 에서 Repository Injection


JPA를 사용하면서 Listener를 생성할 때 당연하듯 Listener 클래스@Component를 통해 등록하고 내부에 repository를 injection하여 사용하였다.

테스트 결과 repository에 NPE가 발생하였고, 원인을 찾아보았다.

이를 이해하기 위해서는 Bean이 생성되는 과정을 알아야한다.


Bean이 생성되는 과정

1.Application 실행
2.EntityManagerFactory 등록
3.EntityManger 생성
4.SimpleJpaRepository 생성
5.JpaRepository 생성
6.Listener 생성

  1. JpaRepository를 Bean으로 등록해주는 EntityManagerFactroy(이하 EMF)는
    Application 실행시 Bean으로 등록된다.

  2. EMF는 EntityManger를 생성해주는 클래스이다.
    • EntityManager는 SharedEntityManagerCreator의 createSharedEntityManager 메서드에 의해 생성이되는데 EMF가 필수이다.
1
2
3
public static EntityManager createSharedEntityManager(EntityManagerFactory emf) {
return createSharedEntityManager(emf, null, true);
}

  1. JpaRepository의 부모 클래스인 SimpleJpaRepository는 생성될 때 EntityManager가 필요하다.

Listener를 Component로 명시하여 Scan을 하게 되면, Listener를 사용하기 위해 존재하여야 하는 객체들이 먼저 등록이 되기 전에 사용하려는 꼴이다.

할아버지가 아버지를 낳지 않았는데, 내가 태어나려고 하는 상황과 유사하다.


Listener에서 Repository Injection 하는 방법

1. ApplicationContext

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class BeanUtils Implements ApplicationContextAware {
private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
BeanUtils.applicationContext = applicationContext;
}

public static <T> T getBean(Class<T> clazz){
return applicationContext.getBean(clazz);
}
1
2
3
4
5
6
7
public class ExampleListener {
@PreUpdate
public void preUpdate(Object o){
ExampleRepository er = BeanUtils.getBean(ExampleRepository.class);
/* ... */
}
}

2. @Lazy 어노테이션

1
2
3
4
5
6
7
8
9
10
public class PersonEntityListener {
@Lazy
@Autowired
private ExampleRepository exampleRepository;

@PreUpdate
public void preUpdate(Example example) {
/* ... */
}
}

JPA Listener 에서 Repository Injection

http://inwoo.github.io/12/01/jpainjection/

Author

Inwoo Jeong

Posted on

2021-12-01

Updated on

2021-12-01

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.

댓글