JpaRepository动态代理执行原理
本文基于spring-boot-starter-data-jpa:2.7.17分析
SpringBoot 里集成Jpa自动配置是如何处理的
通过分析SpringBoot 自动配置核心源码可以找到JpaRepositoriesRegistrar类,这个类的父类是抽象类AbstractRepositoryConfigurationSourceSupport。 抽象类AbstractRepositoryConfigurationSourceSupport 上面写了一段注释:用来自动配置Spring Data Repositories
class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {
}
这个类同时实现了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;
}
这里我去掉了打印logger的代码,只留下了核心代码,这一步分析完之后,也就可以回答第一个问题了。
写了一个接口集成JpaRepository就实现了CURD,这是什么原理?
当我们处理业务操作时,注入一个BookRepository接口, 这时你会发现实际注入的实现类是SimpleJpaRepository。这里是怎么实现的。
从分析前面第一个问题时,最后得出结论,注入Spring容器时是一个BeanDefinition, 而这个BeanDefintion的beanClass类型是JpqRepositoryFactoryBean, 相信大家对FactoryBean就不陌生了。 而JpaRepositoryFactoryBean既是一个Factory, 又是一个Bean。 说他是一个Factory,那么就从这个类里寻找下getObject方法。
JpqRepositoryFactoryBean的父类是TransactionalRepositoryFactoryBeanSupport
TransactionalRepositoryFactoryBeanSupport的父类是RepositoryFactoryBeanSupport
RepositoryFactoryBeanSupport类
从上面截图里看到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里没有实现的查询方法,仅靠约定就能实现的是什么原理?
public interface BookRepository extends JpaRepository<Book, Integer> {
List<Book> findByName(String name);
List<Book> findByAuthor(String author);
}
当我们在repo接口里按照契约定义这两个方法,那么jpa就帮我们实现了,那么jpa是怎么帮我们实现呢,答案是AbstractJpaQuery在做。
AbstractJpaQuery抽象类相关实现类
前面注入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。
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()));
}
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
博客地址: | http://www.cnblogs.com/sword-successful/ |
博客版权: | 本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。 如果文中有不妥或者错误的地方还望高手的你指出,以免误人子弟。如果觉得本文对你有所帮助不如【推荐】一下!如果你有更好的建议,不如留言一起讨论,共同进步! 再次感谢您耐心的读完本篇文章。 |
热门相关:离婚合约:前妻的秘密 厨道仙途 我的嫂子 酒吧2016 课外辅导