신기하게도 그냥 우리가 쓰는대로 JpaRepository를 상속 받은 interface java 파일만 존재하면, Spring Boot의 경우에는 알아서 JpaRepository가 빈으로 등록되어 서비스에 사용되는 걸 알 수 있다. (@Repository 어노테이션을 붙이지 않아도 된다!)

이러한 것들이 어떻게 가능한 것일까? 그에 대해서 한번 정리해보았다.

Spring Boot의 JPA 자동설정

스프링 auto configuration의 작동은 의존성 추가로부터 시작한다.

spring-boot-starter-data-jpa와 h2 데이터베이스를 의존성으로 추가하면,

자동 설정은 이 라이브러리를 인식하여 JPA와 H2 설정을 스프링 부트 기본 설정 값으로 자동 설정 해준다.

SpringAutoConfiguration을 뜯어보면 다음과 같은 Configuration이 존재한다.

JpaRepositoriesAutoConfiguriation.java

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's JPA Repositories.
 * <p>
 * Activates when there is a bean of type {@link javax.sql.DataSource} configured in the
 * context, the Spring Data JPA
 * {@link org.springframework.data.jpa.repository.JpaRepository} type is on the classpath,
 * and there is no other, existing
 * {@link org.springframework.data.jpa.repository.JpaRepository} configured.
 * <p>
 * Once in effect, the auto-configuration is the equivalent of enabling JPA repositories
 * using the {@link EnableJpaRepositories @EnableJpaRepositories} annotation.
 * <p>
 * This configuration class will activate <em>after</em> the Hibernate auto-configuration.
 *
 * @author Phillip Webb
 * @author Josh Long
 * @author Scott Frederick
 * @since 1.0.0
 * @see EnableJpaRepositories
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
		matchIfMissing = true)
@Import(JpaRepositoriesRegistrar.class)
@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
public class JpaRepositoriesAutoConfiguration {

이를 통해서 SpringAutoConfiguration은 @EnableJpaRepositories로 Configuration 하는 것과 동일하다는 것을 알 수 있고, 사용자가 Configuration 한 부분이 따로 없을 경우, Spring boot에서 직접 JpaRepository에 대한 설정을 하는 것을 할 수 있다.

@EnableJpaRepositories를 살펴보면 위의 AutoConfiguration 처럼 동일하게

JapRepositoriesRegistrar 클래스를 Import해오는 것을 확인할 수 있다.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)
public @interface EnableJpaRepositories {

JpaRepositoriesRegistrar는 ImportBeanDefinitionRegistrar를 구현함으로써 Repository를 Bean으로 등록하고 있는 로직을 담는다. 계속해서 파헤쳐보니

RepositoryBeanDefinitionRegistrarSupport → RepositoryConfigurationDelegate를 생성하여 이것이 repository를 bean으로 등록할 수 있도록 해준다.

// RepositoryBeanDefinitionRegistrarSupport.java

@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry,
      BeanNameGenerator generator) {

Assert.notNull(metadata, "AnnotationMetadata must not be null!");
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
   Assert.notNull(resourceLoader, "ResourceLoader must not be null!");

   // Guard against calls for sub-classes
   if(metadata.getAnnotationAttributes(getAnnotation().getName())== null) {
return;
}

AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata,
         getAnnotation(), resourceLoader, environment, registry, generator);

   RepositoryConfigurationExtension extension = getExtension();
   RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);

   RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
         environment);

   delegate.registerRepositoriesIn(registry, extension);
}