JpaRepository动态代理执行原理
https://cdn.nlark.com/yuque/0/2023/jpeg/359374/1700035243623-e8e44478-ed85-4ce6-b39b-c1959feb6473.jpeg#averageHue=%23a5d275&clientId=u0bf9f15b-727f-4&from=ui&id=u71d9fb93&originHeight=451&originWidth=790&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=39650&status=done&style=none&taskId=u8039a003-f0f5-4bf3-8828-ad6eabc4835&title=本文基于spring-boot-starter-data-jpa:2.7.17分析
SpringBoot 里集成Jpa自动配置是如何处理的
通过分析SpringBoot 自动配置核心源码可以找到JpaRepositoriesRegistrar类,这个类的父类是抽象类AbstractRepositoryConfigurationSourceSupport。 抽象类AbstractRepositoryConfigurationSourceSupport 上面写了一段注释:用来自动配置Spring Data Repositories
class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
}https://cdn.nlark.com/yuque/0/2023/png/359374/1698808080506-23050c7c-0cd8-4e44-9d50-3af58dfe359f.png#averageHue=%232c2b2b&clientId=u3ce1c2b6-d217-4&from=paste&height=512&id=u2a949e45&originHeight=768&originWidth=1758&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=119682&status=done&style=none&taskId=u62e315ba-2eef-481f-b133-d78081bf5af&title=&width=1172
这个类同时实现了4个Spring核心接口
ImportBeanDefinitionRegistrar
BeanFactoryAware
ResourceLoaderAware
EnvironmentAware
这4个接口里面,最核心的接口是ImportBeanDefinitionRegistrar, 经常看Spring源码对这个接口肯定不陌生,Spring容器启动时会调用该接口,用于动态注册BeanDefintion,允许你在应用程序上下文启动时以编程的方式定义BeanDefinition。 当实现ImportBeanDefinitionRegistrar时,需要实现其中的registerBeanDefinitions方法,这个方法允许以编程的方式注册BeanDefintion,可以根据条件、配置或者其他逻辑来动态决定要注册那些Bean。
ImportBeanDefinitionRegistrar通常和Import注解一起使用,可以在一个配置类上使用Import注解,将一个实现了ImportBeanDefinitionRegistrar接口的类指定为要注册为Bean定义的来源,在ImportBeanDefinitionRegistrar实现类中,你可以根据需要注册一个或多个BeanDefintion,然后这些Bean就可以在Spring容器中使用。
public abstract class AbstractRepositoryConfigurationSourceSupport
implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {
//这里我删除了成员变量和其他方法,留下这两个核心方法
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
BeanNameGenerator importBeanNameGenerator) {
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
//这里声明了委托类 RepositoryConfigurationDelegate,
//进一步调用了其 registerRepositoriesIn 方法
delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
registerBeanDefinitions(importingClassMetadata, registry, null);
}
}接下来看RepositoryConfigurationDelegate类的核心方法registerRepositoriesIn
/**
* registry也就是这里用到的是Spring容器DefaultListableBeanFactory
* extension 也就是实现类JpaRepositoryConfigExtension实例
* **/
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension) {
extension.registerBeansForRoot(registry, configurationSource);
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension,
configurationSource, resourceLoader, environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
ApplicationStartup startup = getStartup(registry);
StartupStep repoScan = startup.start("spring.data.repository.scanning");
repoScan.tag("dataModule", extension.getModuleName());
repoScan.tag("basePackages",
() -> configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
//关键代码1: 这里会找到所有的自定义的Repository, 比如我这个demo里的BookRepository, FilmRepository
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);
//关键代码2: 这个builder.build方法里就要产生JpqRepositoryFactoryBean这个对象了
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
//关键代码3: 创建出来beanDefinition
//1. 这里的beanDefinition的实例化对象类型是RootBeanDefinition
//2. beanDefinition对象的beanClass是JpaRepositoryFactoryBean 这一步很重要,因为后面从Spring容器中提取时就是这个类
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
beanDefinition.setResourceDescription(configuration.getResourceDescription());
//beanName也就是自定义的repository, 比如bookRepository
String beanName = configurationSource.generateBeanName(beanDefinition);
//给当前beanDefinition设置一个属性,key是常量字符串"factoryBeanObjectType",value值是repository接口全限定名com.codingbetter.jpa.bookRepository
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
//关键代码4: 这里会把自定义的Repository注册到Spring容器中
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
potentiallyLazifyRepositories(configurationsByRepositoryName, registry, configurationSource.getBootstrapMode());
repoScan.tag("repository.count", Integer.toString(configurations.size()));
repoScan.end();
return definitions;
}https://cdn.nlark.com/yuque/0/2023/png/359374/1699193668950-890dd82a-dc7a-4b5d-9184-5670b207674b.png#averageHue=%23708179&clientId=uc73bc1d9-e983-4&from=ui&id=u5e24d116&originHeight=1022&originWidth=1872&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=310813&status=done&style=none&taskId=u0a67054f-1b75-45ec-8256-d94729506c3&title=https://cdn.nlark.com/yuque/0/2023/png/359374/1699193669076-774096a1-417a-4ff4-a417-ec5552dcbcb5.png#averageHue=%237d9688&clientId=uc73bc1d9-e983-4&from=ui&id=uffeee8ce&originHeight=1118&originWidth=1885&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=334239&status=done&style=none&taskId=u70725a1a-e3a8-4ef3-b5f0-675b931cfe0&title=https://cdn.nlark.com/yuque/0/2023/png/359374/1699193669085-9b97dfea-e464-42b5-9cbd-64e613e39efd.png#averageHue=%23778774&clientId=uc73bc1d9-e983-4&from=ui&id=u6d094e5a&originHeight=1108&originWidth=1700&originalType=binary&ratio=1.25&rotation=0&showTitle=false&size=268531&status=done&style=none&taskId=u1c845b06-cb27-415b-a3fd-479bb1bf59f&title=
OK,看了这几个代码片段后,也就能回答上面第二个问题了。
那些JpaRepository里没有实现的查询方法,仅靠约定就能实现的是什么原理?
/**
* RepositoryFactoryBeanSupport#afterPropertiesSet
*/
public void afterPropertiesSet() {
//创建RepositoryFactorySupport, 这个方法在子类JpaRepositoryFactoryBean中实现,父类中只声明了抽象方法名称,交给子类实现
//子类创建并返回JpaRepositoryFactory类
//也就是this.factory就是JpaRepositoryFactory实例化类型
this.factory = createRepositoryFactory();
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(
evaluationContextProvider.orElseGet(() -> QueryMethodEvaluationContextProvider.DEFAULT));
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
this.repositoryFactoryCustomizers.forEach(customizer -> customizer.customize(this.factory));
RepositoryFragments customImplementationFragment = customImplementation //
.map(RepositoryFragments::just) //
.orElseGet(RepositoryFragments::empty);
RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
.orElseGet(RepositoryFragments::empty) //
.append(customImplementationFragment);
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
//关键代码: 这里就会创建SimpleJpaRepository
//Lazy.of()的参数是委托java.util.function.Supplier
//这里会调用JpaRepositoryFactory#getRepository()
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));
if (!lazyInit) {
//这里的repository是一个Lazy类型,而在上面Lazy中,入参,通过factory.getRepsitory中保存了
//SimpleJpaRepository引用,所以这里的this.repository.get就是返回SimpleJpaRepository
this.repository.get();
}
}当我们在repo接口里按照契约定义这两个方法,那么jpa就帮我们实现了,那么jpa是怎么帮我们实现呢,答案是AbstractJpaQuery在做。
AbstractJpaQuery抽象类相关实现类
https://cdn.nlark.com/yuque/0/2023/png/359374/1701141093498-3c055816-5b39-4cb0-b4b4-d97cb4f2d714.png#averageHue=%232d2d2d&clientId=ue845fb5b-cbc7-4&from=ui&id=uf4e46c34&originHeight=712&originWidth=1812&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=41159&status=done&style=none&taskId=uef71c4d1-6b90-4a87-95b7-a15fab0d0fe&title=
前面注入SimpleJpaRepository类时, RepositoryFactorySupport.getRepository方法里注入了两个关键的拦截器和查询有关,分别是QueryExecutorMethodInterceptor和ImplementationMethodExecutionInterceptor
/**
* RepositoryFactorySupport#getRepository
* 第一个参数也就是我们自定义的bookRepository全路径
*/
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
ApplicationStartup applicationStartup = getStartup();
StartupStep repositoryInit = onEvent(applicationStartup, "spring.data.repository.init", repositoryInterface);
repositoryBaseClass.ifPresent(it -> repositoryInit.tag("baseClass", it.getName()));
StartupStep repositoryMetadataStep = onEvent(applicationStartup, "spring.data.repository.metadata",
repositoryInterface);
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
repositoryMetadataStep.end();
StartupStep repositoryCompositionStep = onEvent(applicationStartup, "spring.data.repository.composition",
repositoryInterface);
repositoryCompositionStep.tag("fragment.count", String.valueOf(fragments.size()));
RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
//关键代码1:这一步指定了repositoryBaseClass为SimpleJpaRepository
RepositoryInformation information = getRepositoryInformation(metadata, composition);
repositoryCompositionStep.tag("fragments", () -> {
StringBuilder fragmentsTag = new StringBuilder();
for (RepositoryFragment<?> fragment : composition.getFragments()) {
if (fragmentsTag.length() > 0) {
fragmentsTag.append(";");
}
fragmentsTag.append(fragment.getSignatureContributor().getName());
fragmentsTag.append(fragment.getImplementation().map(it -> ":" + it.getClass().getName()).orElse(""));
}
return fragmentsTag.toString();
});
repositoryCompositionStep.end();
StartupStep repositoryTargetStep = onEvent(applicationStartup, "spring.data.repository.target", repositoryInterface);
//关键代码2:看到这句代码是不是很熟悉,这里target就是SimpleJpaRepository
//getTargetRepository方法在子类JpaRepositoryFactory中实现
Object target = getTargetRepository(information);
repositoryTargetStep.tag("target", target.getClass().getName());
repositoryTargetStep.end();
RepositoryComposition compositionToUse = composition.append(RepositoryFragment.implemented(target));
validate(information, compositionToUse);
StartupStep repositoryProxyStep = onEvent(applicationStartup, "spring.data.repository.proxy", repositoryInterface);
//关键代码3:创建代理,设置Target和Interface
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
if (MethodInvocationValidator.supports(repositoryInterface)) {
result.addAdvice(new MethodInvocationValidator());
}
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
if (!postProcessors.isEmpty()) {
StartupStep repositoryPostprocessorsStep = onEvent(applicationStartup, "spring.data.repository.postprocessors",
repositoryInterface);
postProcessors.forEach(processor -> {
StartupStep singlePostProcessor = onEvent(applicationStartup, "spring.data.repository.postprocessor",
repositoryInterface);
singlePostProcessor.tag("type", processor.getClass().getName());
processor.postProcess(result, information);
singlePostProcessor.end();
});
repositoryPostprocessorsStep.end();
}
if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
}
Optional<QueryLookupStrategy> queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
evaluationContextProvider);
result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy,
namedQueries, queryPostProcessors, methodInvocationListeners));
result.addAdvice(new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
//关键代码4:从代理里获取repository, 也就是SimpleJpaRepository
T repository = (T) result.getProxy(classLoader);
repositoryProxyStep.end();
repositoryInit.end();
return repository;
}QueryExecutorMethodInterceptor拦截器的构造函数
/***
* RepositoryFactorySupport#getRepositoryInformation
*/
private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
RepositoryComposition composition) {
RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, composition);
return repositoryInformationCache.computeIfAbsent(cacheKey, key -> {
//这一步调用getRepositoryBaseClass, 而这个方法会由子类重写
Class<?> baseClass = repositoryBaseClass.orElse(getRepositoryBaseClass(metadata));
return new DefaultRepositoryInformation(metadata, baseClass, composition);
});
}
/**
* JpaRepositoryFactory#getRepositoryBaseClass
* 这一步创建了SimpleJpaRepository
*/
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return SimpleJpaRepository.class;
}这里PartTreeJpaQuery类里有个关键类PartTree, 在这个类里会看到根据契约find, count, exists去查询数据, 会根据正则表达式去匹配查询方法, 对吧,看到这些方法是不是就很熟悉了。
PartTreeJpaQuery
public interface BookRepository extends JpaRepository<Book, Integer> {
List<Book> findByName(String name);
List<Book> findByAuthor(String author);
}PartTree
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
Optional<QueryLookupStrategy> queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
evaluationContextProvider);
result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy, namedQueries, queryPostProcessors, methodInvocationListeners));
result.addAdvice(new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
T repository = (T) result.getProxy(classLoader);
repositoryProxyStep.end();
repositoryInit.end();
return repository;
}AbstractJpaQuery#execute
private final Map<Method, RepositoryQuery> queries;
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
ProjectionFactory projectionFactory,
Optional<QueryLookupStrategy> queryLookupStrategy,
NamedQueries namedQueries,
List<QueryCreationListener<?>> queryPostProcessors,
List<RepositoryMethodInvocationListener> methodInvocationListeners) {
this.repositoryInformation = repositoryInformation;
this.namedQueries = namedQueries;
this.queryPostProcessors = queryPostProcessors;
this.invocationMulticaster = methodInvocationListeners.isEmpty() ? NoOpRepositoryInvocationMulticaster.INSTANCE
: new DefaultRepositoryInvocationMulticaster(methodInvocationListeners);
this.resultHandler = new QueryExecutionResultHandler(RepositoryFactorySupport.CONVERSION_SERVICE);
/***
**这个queries是一个map数组,保存了你的自定义查询方法。
**这里也就能明白RepositoryQuery的设计, 一个自定义方法就代表一个RepositoryQuery
*/
this.queries = queryLookupStrategy
.map(it -> mapMethodsToQuery(repositoryInformation, it, projectionFactory)) //
.orElse(Collections.emptyMap());
}https://cdn.nlark.com/yuque/0/2023/png/359374/1701143151358-5ea6001c-6fc3-4091-8fa9-196c3c8ac883.png#averageHue=%237c8367&clientId=ue845fb5b-cbc7-4&from=ui&id=u4087246f&originHeight=1517&originWidth=2978&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=327775&status=done&style=none&taskId=ueb1cc1af-0317-4e3c-b01c-1d013f00bcb&title=
OK 看到这里第三个问题也就知道来龙去脉了,再说一句,PartTreeJpaQuery是针对在方法头上未标注Query注解的方法,它是用jpa根据方法去识别并转化成SQL方法去执行。
总结
接触jpa时间不长,里面一些来龙去脉还在研究和摸索中,如果有描述不正确的地方,可以留言讨论哦。
参考链接
https://xie.infoq.cn/article/24c7c7874e31f9f057f80ff40
https://www.cnblogs.com/sword-successful/p/14337507.html
https://zhuanlan.zhihu.com/p/344630578
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]