본문 바로가기
Spring JPA

[Spring Data JPA] JpaRepository를 상속받기만 하면 빈으로 등록된다?

by 에드박 2021. 8. 23.

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 내부로 들어가보겠습니다.

 

@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

댓글