JpaRepository动态代理执行原理-小白菜博客
本文基于spring-boot-starter-data-jpa:2.7.17分析

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

通过分析SpringBoot 自动配置核心源码可以找到JpaRepositoriesRegistrar类,这个类的父类是抽象类AbstractRepositoryConfigurationSourceSupport。 抽象类AbstractRepositoryConfigurationSourceSupport 上面写了一段注释:用来自动配置Spring Data Repositories

class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
	
}

JpaRepository动态代理执行原理-小白菜博客
这个类同时实现了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;
}

image.png

JpaRepository动态代理执行原理-小白菜博客
这里我去掉了打印logger的代码,只留下了核心代码,这一步分析完之后,也就可以回答第一个问题了。

写了一个接口集成JpaRepository就实现了CURD,这是什么原理?

当我们处理业务操作时,注入一个BookRepository接口, 这时你会发现实际注入的实现类是SimpleJpaRepository。这里是怎么实现的。
JpaRepository动态代理执行原理-小白菜博客
从分析前面第一个问题时,最后得出结论,注入Spring容器时是一个BeanDefinition, 而这个BeanDefintion的beanClass类型是JpqRepositoryFactoryBean, 相信大家对FactoryBean就不陌生了。 而JpaRepositoryFactoryBean既是一个Factory, 又是一个Bean。 说他是一个Factory,那么就从这个类里寻找下getObject方法。


JpqRepositoryFactoryBean的父类是TransactionalRepositoryFactoryBeanSupport

image.png

TransactionalRepositoryFactoryBeanSupport的父类是RepositoryFactoryBeanSupport

image.png

RepositoryFactoryBeanSupport类

JpaRepository动态代理执行原理-小白菜博客
从上面截图里看到RepositoryFactoryBeanSupport类实现了Spring接口InitializingBean, 然后继续看实现接口方法afterPropertiesSet

/**
 * 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();
	}
}

JpaRepositoryFactory类的父类是RepositoryFactorySupport类

/**
 * 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;
}

RepositoryFactorySupport#getRepositoryInformation

/***
 * 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;
}

JpaRepository动态代理执行原理-小白菜博客JpaRepository动态代理执行原理-小白菜博客JpaRepository动态代理执行原理-小白菜博客
OK, 看了这几个代码片段后,也就能回答上面第二个问题了。

那些JpaRepository里没有实现的查询方法,仅靠约定就能实现的是什么原理?

public interface BookRepository extends JpaRepository<Book, Integer> {
    List<Book> findByName(String name);

    List<Book> findByAuthor(String author);
}

当我们在repo接口里按照契约定义这两个方法,那么jpa就帮我们实现了,那么jpa是怎么帮我们实现呢,答案是AbstractJpaQuery在做。

AbstractJpaQuery抽象类相关实现类
JpaRepository动态代理执行原理-小白菜博客
前面注入SimpleJpaRepository类时, RepositoryFactorySupport.getRepository方法里注入了两个关键的拦截器和查询有关,分别是QueryExecutorMethodInterceptor和ImplementationMethodExecutionInterceptor

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;
}

QueryExecutorMethodInterceptor拦截器的构造函数

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());
}

这里通过调试可以看到,map里的每一个key就是你的自定义方法,也就是findByName, value就是一个PartTreeJpaQuery。
QQ截图20231128104804.png

QueryExecutorMethodInterceptor#doInvoke

private Object doInvoke(MethodInvocation invocation) throws Throwable {

	Method method = invocation.getMethod();

	if (hasQueryFor(method)) {

		RepositoryMethodInvoker invocationMetadata = invocationMetadataCache.get(method);

		if (invocationMetadata == null) {
			invocationMetadata = RepositoryMethodInvoker.forRepositoryQuery(method, queries.get(method));
			invocationMetadataCache.put(method, invocationMetadata);
		}
        //这里会委托RepositoryMethodInvoker去调用
		return invocationMetadata.invoke(repositoryInformation.getRepositoryInterface(), invocationMulticaster, invocation.getArguments());
	}

	return invocation.proceed();
}

RepositoryMethodInvoker#doInvoke

private Object doInvoke(Class<?> repositoryInterface, RepositoryInvocationMulticaster multicaster, Object[] args)
			throws Exception {

	RepositoryMethodInvocationCaptor invocationResultCaptor = RepositoryMethodInvocationCaptor.captureInvocationOn(repositoryInterface);

	try {
   
		//这里会反射调用PartTreeJpaQuery的父类AbstractJpaQuery的execute方法
		Object result = invokable.invoke(args);

		if (result != null && ReactiveWrappers.supports(result.getClass())) {
			return new ReactiveInvocationListenerDecorator().decorate(repositoryInterface, multicaster, args, result);
		}

		if (result instanceof Stream) {
			return ((Stream<?>) result).onClose(
					() -> multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.success())));
		}

		multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.success()));

		return result;
	} catch (Exception e) {
		multicaster.notifyListeners(method, args, computeInvocationResult(invocationResultCaptor.error(e)));
		throw e;
	}
}

这里PartTreeJpaQuery类里有个关键类PartTree, 在这个类里会看到根据契约find, count, exists去查询数据, 会根据正则表达式去匹配查询方法, 对吧,看到这些方法是不是就很熟悉了。

PartTreeJpaQuery

public class PartTreeJpaQuery extends AbstractJpaQuery {
    private final PartTree tree;
}

PartTree

public class PartTree implements Streamable<OrPart> {
	private static final String KEYWORD_TEMPLATE = "(%s)(?=(\\p{Lu}|\\P{InBASIC_LATIN}))";
	//这里定义查询契约规则
	private static final String QUERY_PATTERN = "find|read|get|query|search|stream";
	private static final String COUNT_PATTERN = "count";
	private static final String EXISTS_PATTERN = "exists";
	private static final String DELETE_PATTERN = "delete|remove";
	//在这里拼接查询方法
	private static final Pattern PREFIX_TEMPLATE = Pattern.compile("^(" + QUERY_PATTERN + "|" + COUNT_PATTERN + "|" + EXISTS_PATTERN + "|" + DELETE_PATTERN + ")((\\p{Lu}.*?))??By");

}

AbstractJpaQuery#execute

@Override
public Object execute(Object[] parameters) {
	return doExecute(getExecution(), parameters);
}

private Object doExecute(JpaQueryExecution execution, Object[] values) {
    //这里通过JpaQueryExecution.execute调用查询方法
	JpaParametersParameterAccessor accessor = obtainParameterAccessor(values);

    //返回查询结果,也就是一个ArrayList<Book>
	Object result = execution.execute(this, accessor);

	ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);
	return withDynamicProjection.processResult(result, new TupleConverter(withDynamicProjection.getReturnedType()));
}

JpaRepository动态代理执行原理-小白菜博客
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