JpaRepository动态代理执行原理

十念  金牌会员 | 2024-1-15 03:08:21 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 903|帖子 903|积分 2709


本文基于spring-boot-starter-data-jpa:2.7.17分析

SpringBoot 里集成Jpa自动配置是如何处理的

通过分析SpringBoot 自动配置核心源码可以找到JpaRepositoriesRegistrar类,这个类的父类是抽象类AbstractRepositoryConfigurationSourceSupport。 抽象类AbstractRepositoryConfigurationSourceSupport 上面写了一段注释:用来自动配置Spring Data Repositories
  1. class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
  2.        
  3. }
复制代码

这个类同时实现了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容器中使用。
  1. public abstract class AbstractRepositoryConfigurationSourceSupport
  2.                 implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {
  3.     //这里我删除了成员变量和其他方法,留下这两个核心方法       
  4.         @Override
  5.         public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
  6.                         BeanNameGenerator importBeanNameGenerator) {
  7.                 RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
  8.                                 getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
  9.                 //这里声明了委托类 RepositoryConfigurationDelegate,
  10.                 //进一步调用了其 registerRepositoriesIn 方法
  11.                 delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
  12.         }
  13.         @Override
  14.         public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  15.                 registerBeanDefinitions(importingClassMetadata, registry, null);
  16.         }
  17. }
复制代码
接下来看RepositoryConfigurationDelegate类的核心方法registerRepositoriesIn
  1. /**
  2. * registry也就是这里用到的是Spring容器DefaultListableBeanFactory
  3. * extension 也就是实现类JpaRepositoryConfigExtension实例
  4. * **/
  5. public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension) {       
  6.         extension.registerBeansForRoot(registry, configurationSource);
  7.         RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension,
  8.                         configurationSource, resourceLoader, environment);
  9.         List<BeanComponentDefinition> definitions = new ArrayList<>();
  10.         ApplicationStartup startup = getStartup(registry);
  11.         StartupStep repoScan = startup.start("spring.data.repository.scanning");
  12.         repoScan.tag("dataModule", extension.getModuleName());
  13.         repoScan.tag("basePackages",
  14.                         () -> configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
  15.         //关键代码1: 这里会找到所有的自定义的Repository, 比如我这个demo里的BookRepository, FilmRepository
  16.         Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension
  17.                         .getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode);
  18.         Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap<>(configurations.size());
  19.         for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : configurations) {
  20.                 configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);
  21.         //关键代码2: 这个builder.build方法里就要产生JpqRepositoryFactoryBean这个对象了
  22.                 BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
  23.                 extension.postProcess(definitionBuilder, configurationSource);
  24.                 if (isXml) {
  25.                         extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
  26.                 } else {
  27.                         extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
  28.                 }
  29.                 //关键代码3: 创建出来beanDefinition
  30.                 //1. 这里的beanDefinition的实例化对象类型是RootBeanDefinition
  31.                 //2. beanDefinition对象的beanClass是JpaRepositoryFactoryBean 这一步很重要,因为后面从Spring容器中提取时就是这个类
  32.                 AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
  33.                 beanDefinition.setResourceDescription(configuration.getResourceDescription());
  34.                 //beanName也就是自定义的repository, 比如bookRepository
  35.                 String beanName = configurationSource.generateBeanName(beanDefinition);
  36.                 //给当前beanDefinition设置一个属性,key是常量字符串"factoryBeanObjectType",value值是repository接口全限定名com.codingbetter.jpa.bookRepository
  37.                 beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
  38.                 //关键代码4: 这里会把自定义的Repository注册到Spring容器中
  39.                 registry.registerBeanDefinition(beanName, beanDefinition);
  40.                 definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
  41.         }
  42.         potentiallyLazifyRepositories(configurationsByRepositoryName, registry, configurationSource.getBootstrapMode());
  43.         repoScan.tag("repository.count", Integer.toString(configurations.size()));
  44.         repoScan.end();
  45.         return definitions;
  46. }
复制代码

OK,  看了这几个代码片段后,也就能回答上面第二个问题了。

那些JpaRepository里没有实现的查询方法,仅靠约定就能实现的是什么原理?
  1. /**
  2. * RepositoryFactoryBeanSupport#afterPropertiesSet
  3. */
  4. public void afterPropertiesSet() {
  5.         //创建RepositoryFactorySupport, 这个方法在子类JpaRepositoryFactoryBean中实现,父类中只声明了抽象方法名称,交给子类实现
  6.         //子类创建并返回JpaRepositoryFactory类
  7.         //也就是this.factory就是JpaRepositoryFactory实例化类型
  8.         this.factory = createRepositoryFactory();
  9.         this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
  10.         this.factory.setNamedQueries(namedQueries);
  11.         this.factory.setEvaluationContextProvider(
  12.                         evaluationContextProvider.orElseGet(() -> QueryMethodEvaluationContextProvider.DEFAULT));
  13.         this.factory.setBeanClassLoader(classLoader);
  14.         this.factory.setBeanFactory(beanFactory);
  15.         if (publisher != null) {
  16.                 this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
  17.         }
  18.         repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
  19.         this.repositoryFactoryCustomizers.forEach(customizer -> customizer.customize(this.factory));
  20.         RepositoryFragments customImplementationFragment = customImplementation //
  21.                         .map(RepositoryFragments::just) //
  22.                         .orElseGet(RepositoryFragments::empty);
  23.         RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
  24.                         .orElseGet(RepositoryFragments::empty) //
  25.                         .append(customImplementationFragment);
  26.         this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
  27.         //关键代码: 这里就会创建SimpleJpaRepository
  28.         //Lazy.of()的参数是委托java.util.function.Supplier
  29.         //这里会调用JpaRepositoryFactory#getRepository()
  30.         this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
  31.         this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));
  32.         if (!lazyInit) {
  33.         //这里的repository是一个Lazy类型,而在上面Lazy中,入参,通过factory.getRepsitory中保存了
  34.         //SimpleJpaRepository引用,所以这里的this.repository.get就是返回SimpleJpaRepository
  35.                 this.repository.get();
  36.         }
  37. }
复制代码
当我们在repo接口里按照契约定义这两个方法,那么jpa就帮我们实现了,那么jpa是怎么帮我们实现呢,答案是AbstractJpaQuery在做。
AbstractJpaQuery抽象类相关实现类

前面注入SimpleJpaRepository类时, RepositoryFactorySupport.getRepository方法里注入了两个关键的拦截器和查询有关,分别是QueryExecutorMethodInterceptor和ImplementationMethodExecutionInterceptor
  1. /**
  2. * RepositoryFactorySupport#getRepository
  3. * 第一个参数也就是我们自定义的bookRepository全路径
  4. */
  5. public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
  6.         ApplicationStartup applicationStartup = getStartup();
  7.         StartupStep repositoryInit = onEvent(applicationStartup, "spring.data.repository.init", repositoryInterface);
  8.         repositoryBaseClass.ifPresent(it -> repositoryInit.tag("baseClass", it.getName()));
  9.         StartupStep repositoryMetadataStep = onEvent(applicationStartup, "spring.data.repository.metadata",
  10.                         repositoryInterface);
  11.         RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
  12.         repositoryMetadataStep.end();
  13.         StartupStep repositoryCompositionStep = onEvent(applicationStartup, "spring.data.repository.composition",
  14.                         repositoryInterface);
  15.         repositoryCompositionStep.tag("fragment.count", String.valueOf(fragments.size()));
  16.         RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
  17.         //关键代码1:这一步指定了repositoryBaseClass为SimpleJpaRepository
  18.         RepositoryInformation information = getRepositoryInformation(metadata, composition);
  19.         repositoryCompositionStep.tag("fragments", () -> {
  20.                 StringBuilder fragmentsTag = new StringBuilder();
  21.                 for (RepositoryFragment<?> fragment : composition.getFragments()) {
  22.                         if (fragmentsTag.length() > 0) {
  23.                                 fragmentsTag.append(";");
  24.                         }
  25.                         fragmentsTag.append(fragment.getSignatureContributor().getName());
  26.                         fragmentsTag.append(fragment.getImplementation().map(it -> ":" + it.getClass().getName()).orElse(""));
  27.                 }
  28.                 return fragmentsTag.toString();
  29.         });
  30.         repositoryCompositionStep.end();
  31.         StartupStep repositoryTargetStep = onEvent(applicationStartup, "spring.data.repository.target", repositoryInterface);
  32.        
  33.         //关键代码2:看到这句代码是不是很熟悉,这里target就是SimpleJpaRepository
  34.         //getTargetRepository方法在子类JpaRepositoryFactory中实现
  35.         Object target = getTargetRepository(information);
  36.         repositoryTargetStep.tag("target", target.getClass().getName());
  37.         repositoryTargetStep.end();
  38.         RepositoryComposition compositionToUse = composition.append(RepositoryFragment.implemented(target));
  39.         validate(information, compositionToUse);
  40.         StartupStep repositoryProxyStep = onEvent(applicationStartup, "spring.data.repository.proxy", repositoryInterface);
  41.        
  42.         //关键代码3:创建代理,设置Target和Interface
  43.         ProxyFactory result = new ProxyFactory();
  44.         result.setTarget(target);
  45.         result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
  46.         if (MethodInvocationValidator.supports(repositoryInterface)) {
  47.                 result.addAdvice(new MethodInvocationValidator());
  48.         }
  49.         result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
  50.         if (!postProcessors.isEmpty()) {
  51.                 StartupStep repositoryPostprocessorsStep = onEvent(applicationStartup, "spring.data.repository.postprocessors",
  52.                                 repositoryInterface);
  53.                 postProcessors.forEach(processor -> {
  54.                         StartupStep singlePostProcessor = onEvent(applicationStartup, "spring.data.repository.postprocessor",
  55.                                         repositoryInterface);
  56.                         singlePostProcessor.tag("type", processor.getClass().getName());
  57.                         processor.postProcess(result, information);
  58.                         singlePostProcessor.end();
  59.                 });
  60.                 repositoryPostprocessorsStep.end();
  61.         }
  62.         if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
  63.                 result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
  64.         }
  65.         Optional<QueryLookupStrategy> queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
  66.                         evaluationContextProvider);
  67.         result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy,
  68.                         namedQueries, queryPostProcessors, methodInvocationListeners));
  69.         result.addAdvice(new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
  70.         //关键代码4:从代理里获取repository, 也就是SimpleJpaRepository
  71.         T repository = (T) result.getProxy(classLoader);
  72.         repositoryProxyStep.end();
  73.         repositoryInit.end();
  74.         return repository;
  75. }
复制代码
QueryExecutorMethodInterceptor拦截器的构造函数
  1. /***
  2. * RepositoryFactorySupport#getRepositoryInformation
  3. */
  4. private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
  5.                         RepositoryComposition composition) {
  6.         RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, composition);
  7.         return repositoryInformationCache.computeIfAbsent(cacheKey, key -> {
  8.                 //这一步调用getRepositoryBaseClass, 而这个方法会由子类重写
  9.                 Class<?> baseClass = repositoryBaseClass.orElse(getRepositoryBaseClass(metadata));
  10.                 return new DefaultRepositoryInformation(metadata, baseClass, composition);
  11.         });
  12. }
  13. /**
  14. * JpaRepositoryFactory#getRepositoryBaseClass
  15. * 这一步创建了SimpleJpaRepository
  16. */
  17. protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
  18.         return SimpleJpaRepository.class;
  19. }
复制代码
这里PartTreeJpaQuery类里有个关键类PartTree, 在这个类里会看到根据契约find, count, exists去查询数据, 会根据正则表达式去匹配查询方法, 对吧,看到这些方法是不是就很熟悉了。
PartTreeJpaQuery
  1. public interface BookRepository extends JpaRepository<Book, Integer> {
  2.     List<Book> findByName(String name);
  3.     List<Book> findByAuthor(String author);
  4. }
复制代码
PartTree
  1. public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
  2.         Optional<QueryLookupStrategy> queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
  3.                         evaluationContextProvider);
  4.         result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy, namedQueries, queryPostProcessors, methodInvocationListeners));
  5.         result.addAdvice(new ImplementationMethodExecutionInterceptor(information, compositionToUse, methodInvocationListeners));
  6.         T repository = (T) result.getProxy(classLoader);
  7.         repositoryProxyStep.end();
  8.         repositoryInit.end();
  9.         return repository;
  10. }
复制代码
AbstractJpaQuery#execute
  1. private final Map<Method, RepositoryQuery> queries;
  2. public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
  3.                         ProjectionFactory projectionFactory,
  4.                         Optional<QueryLookupStrategy> queryLookupStrategy,
  5.                         NamedQueries namedQueries,
  6.                         List<QueryCreationListener<?>> queryPostProcessors,
  7.                         List<RepositoryMethodInvocationListener> methodInvocationListeners) {
  8.         this.repositoryInformation = repositoryInformation;
  9.         this.namedQueries = namedQueries;
  10.         this.queryPostProcessors = queryPostProcessors;
  11.         this.invocationMulticaster = methodInvocationListeners.isEmpty() ? NoOpRepositoryInvocationMulticaster.INSTANCE
  12.                         : new DefaultRepositoryInvocationMulticaster(methodInvocationListeners);
  13.         this.resultHandler = new QueryExecutionResultHandler(RepositoryFactorySupport.CONVERSION_SERVICE);
  14.        
  15.         /***
  16.         **这个queries是一个map数组,保存了你的自定义查询方法。
  17.     **这里也就能明白RepositoryQuery的设计, 一个自定义方法就代表一个RepositoryQuery
  18.         */
  19.         this.queries = queryLookupStrategy
  20.                                 .map(it -> mapMethodsToQuery(repositoryInformation, it, projectionFactory)) //
  21.                                 .orElse(Collections.emptyMap());
  22. }
复制代码

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

十念

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表