Spring Data Jpa를 사용하면서 우리가 사용하는 Repository 인터페이스는 아래와 같습니다.
import org.springframework.data.jpa.repository.JpaRepository;
public interface AccountRepository extends JpaRepository<Account, Long> {
}
이 코드만 봐서는 어떻게 AccountRepository가 빈으로 등록되는지 알 수 없습니다. @Repository 애노테이션을 붙이지 않았지만 실제로 빈으로 등록되어있고 다른곳에서 주입받아서 사용할 수 있습니다.
추측할 수 있는 곳은 JpaRepository내부 구현입니다. 하지만 JpaRepository 내부를 타고 들어가도 빈으로 등록해주는 코드는 존재하지 않습니다.
@EnableJpaRepositories
마법이 일어나는 곳은 @EnableJpaRepositories 애노테이션 입니다.
@EnableJpaRepositories는 JPA Repository들을 활성화하기 위한 애노테이션입니다.
Spring에서는 @Configuration 클래스에서 @EnableJpaRepositories 애노테이션을 사용 해야합니다.
@Configuration
@EnableJpaRepositories
public JpaConfig() {
}
@EnableJpaRepositories는 기본적으로 해당 Config 클래스의 하위 패키지를 스캔합니다.
아래와 같이 base package를 지정할 수 있습니다.
@Configuration
@EnableJpaRepositories(basePackages = "com.charlie.test")
public JpaConfig {
}
SpringBoot 에서는 @EnableJpaRepositories가 자동설정이 돼서 생략해도 됩니다.
그러면 이제 JpaRepository를 상속받은 인터페이스가 어떻게 빈으로 등록되는지 @EnableJpaRepositories 내부로 들어가보겠습니다.
위의 코드를 보면 @Import(JpaRepositoriesRegistrar.class)가 보일겁니다.
이 JpaRepositoriesRegistrar 가 JpaRepository(상위 클래스 PagingAndSortingRepository등등)를 상속받은 모든 인터페이스를 빈으로 등록해줍니다.
이 JpaRepositoriesRegistrar 클래스는 ImportBeanDefinitionRegistrar 인터페이스의 구현체라고 볼 수 있습니다.
JpaRepositoriesRegistrar, ImportBeanDefinitionRegistrar는 Spring에서 제공하는 클래스입니다.
ImportBeanDefinitionRegistrar 인터페이스는 BeanDefinition을 정의할 수 있는 특수한 형태의 인터페이스 입니다.
이것은 프로그래밍을 통해서 빈을 등록할 수 있도록 합니다.
ImportBeanDefinitionRegistrar 인터페이스를 이용해서 빈으로 등록하기
우선 Charlie 라는 클래스를 만들었습니다.
현재 Charlie 클래스는 빈으로 등록될 수 있는 코드가 전혀 없는 상태입니다.
Charlie 클래스의 빈을 만들기 위해 ImportBeanDefinitionRegistrar 인터페이스를 구현한 CharlieRegistrar 클래스를 작성합니다.
package me.charlie.springjpa;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class CharlieRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// BeanDefinition 생성
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(Charlie.class);
beanDefinition.getPropertyValues().add("name", "charlie Park");
// 만들어진 BeanDefinition을 등록 registerBeanDefinition(빈의 이름, BeanDefinition)
registry.registerBeanDefinition("charlie", beanDefinition);
}
}
작성한 CharlieRegistrar 클래스를 설정파일에 @Import를 해줍니다. 여기서는 @SpringBootApplication 애노테이션이 있는곳에 추가해주겠습니다.
package me.charlie.springjpa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@SpringBootApplication
@Import(CharlieRegistrar.class)
public class SpringJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJpaApplication.class, args);
}
}
이제 빈으로 등록하는 모든 설정이 끝났습니다.
아래와같이 Charlie 빈을 주입받아서 사용할 수 있습니다.
package me.charlie.springjpa;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class TestRunner implements ApplicationRunner {
@Autowired
private Charlie charlie;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("=======================");
System.out.println(charlie.getClass());
System.out.println(charlie.getName());
System.out.println("=======================");
}
}
JpaRepository의 빈을 만드는 과정은 JpaRepositoriesRegistrar 내부적으로 복잡한 로직을 가지고 있습니다.
좀 더 자세하게 알고싶다면 JpaRepositoriesRegistrar의 상위 클래스를 따라가보면 내부 구현을 알 수 있습니다.
참고자료
'Spring JPA' 카테고리의 다른 글
[Hibernate] setAutoCommit 최적화 (0) | 2023.01.03 |
---|---|
[Spring Data Common] Repository, @NoRepositoryBean (0) | 2021.08.23 |
[JPA] JPA 기초 (4) | 2021.08.23 |
댓글