신기하게도 그냥 우리가 쓰는대로 JpaRepository를 상속 받은 interface java 파일만 존재하면, Spring Boot의 경우에는 알아서 JpaRepository가 빈으로 등록되어 서비스에 사용되는 걸 알 수 있다. (@Repository 어노테이션을 붙이지 않아도 된다!)
이러한 것들이 어떻게 가능한 것일까? 그에 대해서 한번 정리해보았다.
스프링 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);
}