概述
当我们使用 Spring Data JPA
的时候,典型的用法是这样的 :
- 将
spring-data-jpa
包,数据库驱动包等添加为项目依赖; - 配置文件定义相应的数据源;
- 为应用添加注解
@EnableJpaRepositories
; - 定义业务领域实体类,比如通过
@Entity
注解; - 定义自己业务相关的的
JPA repository
接口,这些接口都继承自JpaRepository
或者JpaSpecificationExecutor
; - 将上面自定义
JPA repository
接口注入到服务层并使用它们进行相应的增删改查;
在这个流程中,我们并没有对自定义JPA repository
接口提供任何实现,但神奇的是在服务层我们居然可以成功地注入该接口类型的bean
并使用。那么,问题来了,这个bean
到底是个什么东西呢,又是为什么能够成功注入呢 ?这篇文章我们基于Springboot
应用模式和Spring
的源代码试图回答一下这些问题 。
工作原理分析
1. 自动配置 JpaRepositoriesAutoConfiguration
因为我们已经将spring-data-jpa jar
包,数据库驱动jar
包都已经添加为了系统依赖,也就是说它们会出现在程序运行时的classpath
上,并且,我们已经配置了数据源。现在在应用程序启动过程中,springboot
的自动配置机制关于JPA
自动配置的部分就开始工作了,具体的配置自动配置类是JpaRepositoriesAutoConfiguration
。
该自动配置有一点很重要,它导入了 JpaRepositoriesAutoConfigureRegistrar
:
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
而JpaRepositoriesAutoConfigureRegistrar
进而使用了注解@EnableJpaRepositories
。
@EnableJpaRepositories
private static class EnableJpaRepositoriesConfiguration {
}
注解@EnableJpaRepositories
指出了创建repository
的FactoryBean
是什么:
// 注解@EnableJpaRepositories部分源代码
/**
* Returns the FactoryBean class to be used for each repository instance. Defaults to
* JpaRepositoryFactoryBean.
*
* @return
*/
Class<?> repositoryFactoryBeanClass() default JpaRepositoryFactoryBean.class;
这里指出缺省使用JpaRepositoryFactoryBean
为创建用户自定义JpaRepository
的FactoryBean
。
JpaRepositoryFactoryBean
位于包org.springframework.data.jpa.repository.support
。
2. 用户自定义JpaRepository
作为bean
注册到容器
因为JpaRepositoriesAutoConfiguration
导入了JpaRepositoriesAutoConfigureRegistrar
,而这是一个ImportBeanDefinitionRegistrar
,设计目的就是用于Springboot
自动配置机制中发现用户自定义JpaRrepository
。
在应用程序启动中ImportBeanDefinitionRegistrar
被处理阶段,这个JpaRepositoriesAutoConfigureRegistrar
会被执行:
// 调用栈
//SpringApplication#run
//SpringApplication#refreshContext
//AbstractApplicationContext#refresh
//AbstractApplicationContext#invokeBeanFactoryPostProcessors
//PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors
//ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
//ConfigurationClassPostProcessor#processConfigBeanDefinitions
//ConfigurationClassBeanDefinitionReader#loadBeanDefinitions
//ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
//ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars
/**
* 参数registrars会包含一项是JpaRepositoriesAutoConfigureRegistrar
**/
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry));
}
而JpaRepositoriesAutoConfigureRegistrar
又将任务交给了一个RepositoryConfigurationDelegate
来完成:
// 调用栈
// JpaRepositoriesAutoConfigureRegistrar#registerBeanDefinitions
// AbstractRepositoryConfigurationSourceSupport#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
new RepositoryConfigurationDelegate(getConfigurationSource(registry),
this.resourceLoader, this.environment).registerRepositoriesIn(registry,
getRepositoryConfigurationExtension());
}
// 调用栈
// 接上
// AbstractRepositoryConfigurationSourceSupport#registerBeanDefinitions
//RepositoryConfigurationDelegate#registerRepositoriesIn
/**
* Registers the found repositories in the given BeanDefinitionRegistry.
*
* @param registry
* @param extension
* @return BeanComponentDefinitions for all repository bean definitions found.
*/
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
if (LOG.isInfoEnabled()) {
// 如果你的日志打开的话,控制台输出上应该能看到这个信息输出
LOG.info("Bootstrapping Spring Data repositories in {} mode.", configurationSource.getBootstrapMode().name());
}
extension.registerBeansForRoot(registry, configurationSource);
// 为接下来将要通过包扫描发现的用户自定义JpaRepository构造一个RepositoryBeanDefinitionBuilder,注意
// 这个 builder 已经知道了对应将要到来的用户自定义JpaRepository bean注册时,都使用JpaRepositoryFactoryBean
// 作为FactoryBean,这个信息是由@EnableJpaRepositories定义的,通过configurationSource参数传进来
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension,
configurationSource, resourceLoader, environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
StopWatch watch = new StopWatch();
if (LOG.isDebugEnabled()) {
LOG.debug("Scanning for repositories in packages {}.",
configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
}
watch.start();
// extension.getRepositoryConfigurations() 会扫描相应的包并找到用户自定义JpaRepository接口
Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);
Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap<>(configurations.size());
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : configurations) {
configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);
// 对于每个扫描找到的用户自定义JpaRepository,构建一个BeanDefinitionBuilder,
// 就是在这个步骤中将该BeanDefinition同JpaRepositoryFactoryBean建立关系
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
// 这里根据所发现的用户自定义JpaRepository接口的名字构造一个bean名称
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
String beanName = configurationSource.generateBeanName(beanDefinition);
if (LOG.isTraceEnabled()) {
LOG.trace(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName, configuration.getRepositoryInterface(),
configuration.getRepositoryFactoryBeanClassName());
}
// 设置当前BeanDefinition的属性factoryBeanObjectType为用户自定义JpaRepository接口的全限定名
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
// 到目前为止针对所发现的一个用户自定义JpaRepository接口,已经构造了一个这样的bean定义:
// 1. bean 名称根据用户自定义JpaRepository接口的短名称生成,比如:studentRepository
// 2. bean 的类型为用户自定义JpaRepository接口
// 3. bean 实际使用时的实现类将由JpaRepositoryFactoryBean工厂生成 (beanClass)
// 现在把这个bean注册到容器
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
potentiallyLazifyRepositories(configurationsByRepositoryName, registry, configurationSource.getBootstrapMode());
watch.stop();
if (LOG.isInfoEnabled()) {
// 如果你的日志打开的话,控制台输出上应该能看到对上面过程的一个总结
LOG.info("Finished Spring Data repository scanning in {}ms. Found {} repository interfaces.", //
watch.getLastTaskTimeMillis(), configurations.size());
}
return definitions;
}
从上面的分析来看,不管开发人员自定义了多少个JpaRepository
,最终相应实际注入的bean
都会来自由JpaRepositoryFactoryBean
工厂来创建,而实际上,每个开发人员自定义的JpaRepository
又是针对不同的领域实体的。这一区别又是怎么体现的呢?
我们继续分析。
3. 使用用户自定义JpaRepository
bean
假如说服务层注入了一个用户自定义的JpaRepository
,比如通过这种方式:
@Autowired
StudentRepository repo;
那么在应用启动过程中,这个bean
会被实例化并被注入。上面我们已经分析过,针对每个用户自定义的JpaRepository
(在这个例子中,也就是StudentRepository repo
)其实现类都来自JpaRepositoryFactoryBean
工厂,现在我们看看这个bean
的实例化过程。
首先每个用户自定义JpaRepository
bean
在首次被注入时,都会构造一个JpaRepositoryFactoryBean
对象:
// 这里的参数就是用户自定义的JpaRepository接口
public JpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
}
因为JpaRepositoryFactoryBean
同时也实现了InitializingBean
接口,所以在bean
被创建后,它的方法afterPropertiesSet
会被调用:
public void afterPropertiesSet() {
// factory是用于创建用户自定义JpaRepository bean的工厂
this.factory = createRepositoryFactory();
// ......
// 注意下面使用了 Lazy/Supplier 模式,代码看起来有些绕,
// 实际上要做的事情是:
// 1. 如果是 lazy 初始化模式,先不创建相应的 repository 实例,让它保存在一个 Lazy 对象中;
// 2. 如果不是 lazy 初始化模式,直接创建相应的 repository 实例,也保存在 Lazy 对象中。
// 这里 this.factory.getRepository 是真正创建 repository 实例的调用,
// 因为这里的factory实际上是JpaRepository,我们下面会看它的getRepository方法实现
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
if (!lazyInit) {
// 如果不是lazy初始化模式,这里创建真正的的repository
this.repository.get();
}
}
3.1 createRepositoryFactory
该方法的第一件事情,就是调用createRepositoryFactory
,这个方法创建了一个JpaRepositoryFactory
对象,用于创建最终开发人员自定义JpaRepository
对应的bean
实例。
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
// 这里创建了一个 JpaRepositoryFactory 对象
JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
return jpaRepositoryFactory;
}
这里需要提醒的是,JpaRepositoryFactory
类定义了生成最终JpaRepository
的bean
使用实现类SimpleJpaRepository
:
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// SimpleJpaRepository 位于包 org.springframework.data.jpa.repository.support
return SimpleJpaRepository.class;
}
3.2 getRepository
JpaRepositoryFactory
继承自RepositoryFactorySupport
,其方法getRepository
也在该基类中实现 :
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing repository instance for {}…", repositoryInterface.getName());
}
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
Assert.notNull(fragments, "RepositoryFragments must not be null!");
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
RepositoryInformation information = getRepositoryInformation(metadata, composition);
validate(information, composition);
// 下面语句最终创建一个 SimpleJpaRepository 对象,并且该对象是针对用户自定义JpaRepository
// repositoryInterface 的,注意参数 information 根据 repositoryInterface 生成,其中
// 隐含了要操作哪个领域实体,或者说是操作哪个表。
// 为什么会使用 SimpleJpaRepository ? 因为当前对象是一个JpaRepositoryFactory,
// 它指定了要使用 SimpleJpaRepository 作为 repositoryBaseClass
Object target = getTargetRepository(information);
// Create proxy
// SimpleJpaRepository 并不能满足用户自定义JpaRepository的所有方法,所以要为其创建
// 一个代理对象,这样就可以增加更过的方法调用拦截器,对于那些自定义JpaRepository接口
// 中定义但在SimpleJpaRepository的方法,就会由这些拦截器来处理。
// 这里的代理同时也考虑了对异常,和事务的处理
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
if (MethodInvocationValidator.supports(repositoryInterface)) {
result.addAdvice(new MethodInvocationValidator());
}
result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
postProcessors.forEach(processor -> processor.postProcess(result, information));
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));
composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
T repository = (T) result.getProxy(classLoader);
if (LOG.isDebugEnabled()) {
LOG.debug("Finished creation of repository instance for {}.", repositoryInterface.getName());
}
// 这里返回的是SimpleJpaRepository对象的代理对象
return repository;
}
从上面分析可见,方法getRepository
创建了一个SimpleJpaRepository
对象,该对象知道自己要针对哪个领域实体/数据库表进行操作,然后方法getRepository
又创建了该SimpleJpaRepository
对象的一个代理对象,附着上有关的Interceptor
,返回该代理对象。
最后,当真正的JpaRepository
对象(也就是上面分析中所提到的SimpleJpaRepository
代理对象)被创建之后,包裹该对象的那个 JpaRepositoryFactoryBean
对象就是最终我们要使用的bean的FactoryBean
了,在Spring
容器中,对应用户自定义的JpaRepository
接口,保存bean
的实际上是一个JpaRepositoryFactoryBean
。
现在,因为首次注入导致的bean
的实例化过程已经结束了,可以进行真正的注入了。根据上面的分析,每个用户自定义JpaRepository
接口在容器中的bean
实际上保存的是一个JpaRepositoryFactoryBean
对象,这是一个FactoryBean
。对这样一个bean
进行注入时,会调用FactoryBean#getObject
获取真正要注入的SimpleJpaRepository
代理对象。
总结
从上面的分析可以看到,虽然用户没有为每个自定义的JpaRepository
接口提供实现类,但是Spring Data JPA
最终将每个这样的bean
最终映射到了一个统一的实现类SimpleJpaRepository
的代理对象,而这个代理对象能支持所有每个自定义的JpaRepository
接口定义的功能和SimpleJpaRepository
类中定义的所有功能。
问题 : 那些在用户自定义
JpaRepository
中定义但是并不属于SimpleJpaRepository
的方法,是怎样转换成相应的SQL的?