十念 发表于 2024-1-15 03:08:21

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]
查看完整版本: JpaRepository动态代理执行原理