spring-data-JPA源码解读
spring-data-JPA源码部分有两个很重要的部分:1、识别repositories接口 2、将接口添加代理实现类并托管spring管理
JpaRepositoriesRegistrar
目的是将范围内的接口准备作为springbean进行处理(有beanFactory辅助)
如果在启动类上添加了@EnableJpaRepositories注解则我们按照如下思路分析
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)//引入了JpaRepositoriesRegistrar
public @interface EnableJpaRepositories {
JpaRepositoriesRegistrar继承了RepositoryBeanDefinitionRegistrarSupport并最终继承了ImportBeanDefinitionRegistrar的重要方法registerBeanDefinitions
JpaRepositoriesRegistrar这个类本身指定好EnableJpaRepositories注解以及给出Extension,在Extension中主要的作用是限定本次注册的factorybean是JpaRepositoryFactoryBean
@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableJpaRepositories.class;
} /*
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
*/
@Override
protected RepositoryConfigurationExtension getExtension() {
return new JpaRepositoryConfigExtension();
}
ImportBeanDefinitionRegistrar的作用是:
Interface to be implemented by types that register additional bean definitions whenprocessing @{@link Configuration} classes. Useful when operating at the bean definition
接口被实现成类型时需注册成新的bean定义的过程
level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfigurati……
spring在启动过程中都是将配置文件(或者注解描述的信息)的信息解析成为一个个的BeanDefinition对象并装入到容器的Bean定义注册表(BeanDefinitionRegistry)中,但此时Bean还未初始化
我们跟随重要代码片段向下走:
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
// Guard against calls for sub-classes
if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
return;
}
AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(
annotationMetadata, getAnnotation(), resourceLoader, environment, registry);
RepositoryConfigurationExtension extension = getExtension();
RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);
RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
environment);
//主要是这里面,Registers the found repositories in the given 根据给定的内容注册beans
delegate.registerRepositoriesIn(registry, extension);
}
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
extension.registerBeansForRoot(registry, configurationSource);
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
if (LOG.isDebugEnabled()) {
LOG.debug("Scanning for repositories in packages {}.",
configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
}
//extension.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)
//这行代码能够扫描出所有的repository接口,下面是跟踪代码记录点
//org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryConfigurations(T, org.springframework.core.io.ResourceLoader, boolean)
//org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getRepositoryConfigurations(T, org.springframework.core.io.ResourceLoader, boolean)
//org.springframework.data.repository.config.RepositoryConfigurationSource#getCandidates
//org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getCandidates 这里获得了所有符合条件的repository接口,向下看
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {
//这里通过BeanDefinitionBuilder注册的bean是JpaRepositoryFactoryBean,这个bean不是最后被代理的Repository接口
//注册bean的方式是spring的方式,把bean的定义信息加入定义列表,初始化bean时会自动创建JpaRepositoryFactoryBean,并执行afterPropertiesSet方法
//(实现了InitializingBean接口),beanfactory的目的最终是创建bean的
//spring启动的第一步也是初始化BeanFactory
//Spring将配置文件(或者注解描述的信息)的信息解析成为一个个的BeanDefinition对象并装入到容器的Bean定义注册表(BeanDefinitionRegistry)中,但此时Bean还未初始化
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
String beanName = configurationSource.generateBeanName(beanDefinition);
if (LOG.isDebugEnabled()) {
LOG.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName, configuration.getRepositoryInterface(),
configuration.getRepositoryFactoryBeanClassName());
}
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
if (LOG.isDebugEnabled()) {
LOG.debug("Finished repository scanning.");
}
return definitions;
}
RepositoryConfigurationSourceSupport
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
//这个构造方法要去把扫描过滤器构建好 下看
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
scanner.setEnvironment(environment);
scanner.setResourceLoader(loader);
getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));
return Streamable.of(() -> getBasePackages().stream()//
.flatMap(it -> scanner.findCandidateComponents(it).stream()));
//scanner.findCandidateComponents(it)发现候选人
//org.springframework.data.repository.config.RepositoryComponentProvider#findCandidateComponents
//org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
//org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
//这里面有判断if (isCandidateComponent(metadataReader)) { 根据过滤器选择class
//Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 所有的类
//第二次才进来根据下面的过滤器选择了repostory接口
}
//过滤器
//repository的规则
//构建扫描过滤器
//是Repository的接口 new InterfaceTypeFilter(Repository.class)
//有RepositoryDefinition注解 new AnnotationTypeFilter(RepositoryDefinition.class, true, true)
//排除NoRepositoryBean注解 addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
public RepositoryComponentProvider(Iterable<? extends TypeFilter> includeFilters, BeanDefinitionRegistry registry) { super(false); Assert.notNull(includeFilters, "Include filters must not be null!");
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!"); this.registry = registry; if (includeFilters.iterator().hasNext()) {
for (TypeFilter filter : includeFilters) {
addIncludeFilter(filter);
}
} else {
super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
}
addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
}
至此,我们仅仅做到了扫描并识别出所有集成了Repository接口的JPA接口,并吧他们作为JpaRepositoryFactoryBean进行注册(初始化),但是不要指望在这里找到实现JPA接口代理实现类
自动生成代理Bean实现,并托管spring管理
实例化JpaRepositoryFactoryBean时,由于接口实现了InitializingBean接口,FactoryBean初始化后执行了afterPropertiesSet,FactoryBean和bean是一整套的springbean生成体系
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware {
//这个成员变量存储了要被自动生成实现的repository接口(比如coffeeRepostory)
private final Class<? extends T> repositoryInterface;
//org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#afterPropertiesSet
public void afterPropertiesSet() {
this.factory = createRepositoryFactory();
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(evaluationContextProvider);
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
RepositoryFragments customImplementationFragment = customImplementation //
.map(RepositoryFragments::just) //
.orElseGet(RepositoryFragments::empty);
RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
.orElseGet(RepositoryFragments::empty) //
.append(customImplementationFragment);
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
// Make sure the aggregate root type is present in the MappingContext (e.g. for auditing)
this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));
//这里将代理生成接口的实现
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
if (!lazyInit) {
this.repository.get();
}
}
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
if (LOG.isDebugEnabled()) {
LOG.debug("Initializing repository instance for {}…", repositoryInterface.getName());
}
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
Assert.notNull(fragments, "RepositoryFragments must not be null!");
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
RepositoryInformation information = getRepositoryInformation(metadata, composition);
validate(information, composition);
//这里返回了SimpleJpaRepository
Object target = getTargetRepository(information);
// 开始创建代理
ProxyFactory result = new ProxyFactory();
result.setTarget(target);//设置实现实体,这个实体类就是SimpleJpaRepository
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
if (MethodInvocationValidator.supports(repositoryInterface)) {
result.addAdvice(new MethodInvocationValidator());
}
//这些切点实现了接口中方法的执行!
result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
postProcessors.forEach(processor -> processor.postProcess(result, information));
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));
composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
//生成代理类
T repository = (T) result.getProxy(classLoader);
if (LOG.isDebugEnabled()) {
LOG.debug("Finished creation of repository instance for {}.", repositoryInterface.getName());
}
return repository;
}
不通过@EnableJpaRepositories注解,JPA如何自动配置
如果不熟悉springboot启动自动配置的同学,请移步上一篇文章阅读
spring-boot-autoconfigure包内的spring.factories文件内容,包含了JPA的,解释了为什么没有添加@EnableJpaRepositories仍然能用jpa
//JpaRepositoriesAutoConfiguration
@Configuration
@ConditionalOnBean(DataSource.class) //当有DataSource的bean
@ConditionalOnClass(JpaRepository.class)//当classpath中有JpaRepository这个类
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, //没有加载过JpaRepositoryFactoryBean、 JpaRepositoryConfigExtension两个bean
JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)//有这个配置“spring.data.jpa.repositories.enabled=true”但不做强制检查
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
//看这个类JpaRepositoriesAutoConfigureRegistrar -> AbstractRepositoryConfigurationSourceSupport#registerBeanDefinitions 熟悉的方法
//new RepositoryConfigurationDelegate(getConfigurationSource(registry),
// this.resourceLoader, this.environment).registerRepositoriesIn(registry,
// getRepositoryConfigurationExtension());
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration { }
spring-data-JPA源码解读的更多相关文章
- Spring启动流程—源码解读
https://blog.csdn.net/yangliuhbhd/article/details/80790761 Spring的AbstractApplicationContext的refresh ...
- spring data jpa 全面解析(实践 + 源码分析)
前言 本文将从示例.原理.应用3个方面介绍spring data jpa. 以下分析基于spring boot 2.0 + spring 5.0.4版本源码 概述 JPA是什么? JPA (Java ...
- 【spring boot 系列】spring data jpa 全面解析(实践 + 源码分析)
前言 本文将从示例.原理.应用3个方面介绍spring data jpa. 以下分析基于spring boot 2.0 + spring 5.0.4版本源码 概述 JPA是什么? JPA (Java ...
- Spring源码-循环依赖源码解读
Spring源码-循环依赖源码解读 笔者最近无论是看书还是从网上找资料,都没发现对Spring源码是怎么解决循环依赖这一问题的详解,大家都是解释了Spring解决循环依赖的想法(有的解释也不准确,在& ...
- spring IOC DI AOP MVC 事务, mybatis 源码解读
demo https://gitee.com/easybao/aop.git spring DI运行时序 AbstractApplicationContext类的 refresh()方法 1: pre ...
- Spring事务源码解读
一.Spring事务使用 1.通过maven方式引入jar包 <dependency> <groupId>com.alibaba</groupId> <art ...
- Spring IoC源码解读——谈谈bean的几种状态
阅读Spring IoC部分源码有一段时间了,经过不断的单步调试和参阅资料,对Spring容器中bean管理有了一定的了解.这里从bean的几个状态的角度出发,研究下IoC容器. 一.原材料 Xml中 ...
- spring security 实践 + 源码分析
前言 本文将从示例.原理.应用3个方面介绍 spring data jpa. 以下分析基于spring boot 2.0 + spring 5.0.4版本源码 概述 Spring Security 是 ...
- Spring Data JPA:解析CriteriaBuilder
源码 在Spring Data JPA相关的文章[地址]中提到了有哪几种方式可以构建Specification的实例,该处需要借助CriteriaBuilder,回顾一下Specification中t ...
- spring boot(五):spring data jpa的使用
在上篇文章springboot(二):web综合开发中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jpa 常见用法以及注意事项 使用spr ...
随机推荐
- 《深入理解Java虚拟机》笔记03 -- 垃圾收集器
收集器可以大致分为:单线程收集器, 并发收集器和并行收集器. 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态. 并发(Concurrent):指用户线程与垃圾收集 ...
- 最短路(floyd/dijkstra/bellmanford/spaf 模板)
floyd/dijkstra/bellmanford/spaf 模板: 1. floyd(不能处理负权环,时间复杂度为O(n^3), 空间复杂度为O(n^2)) floyd算法的本质是dp,用dp[k ...
- Codevs 1159 最大全0子矩阵
1159 最大全0子矩阵 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description 在一个0,1方阵中找出其中最大的全 ...
- web前端篇:CSS使用,样式表特征,选择器
目录 web前端篇:CSS使用,样式表特征,选择器 1.CSS介绍 2.CSS使用 3.样式表特征 4.CSS选择器 5.选择器的优先级 6.练习题: web前端篇:CSS使用,样式表特征,选择器 1 ...
- 洛谷 P3381 【模板】最小费用最大流
题目描述 如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用. 输入输出格式 输入格式: 第一行包含四个正整数\(N.M.S.T\) ...
- urlencode()和urldecode()
urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%.一般用在url链接地址编码urldecode()函数与urlencode()函数原理相反,用于解码已编 ...
- Maven项目编译版本的问题和Spring中没有导入核心包
idea中maven项目的编译: 方案1:maven的settings.xml中指定全局默认编译版本 <profile> <id>jdk-1.8</id> < ...
- CC18:二叉树平衡检查
题目 实现一个函数,检查二叉树是否平衡,平衡的定义如下,对于树中的任意一个结点,其两颗子树的高度差不超过1. 给定指向树根结点的指针TreeNode* root,请返回一个bool,代表这棵树是否平衡 ...
- 洛谷 P3676 小清新数据结构题
https://www.luogu.org/problemnew/show/P3676 这题被我当成动态dp去做了,码了4k,搞了一个换根的动态dp #include<cstdio> #i ...
- OAuthLogin2.0
开源第三方登录组件OAuthLogin2.0 支持QQ,阿里巴巴,淘宝,京东,蘑菇街,有赞等平台 Nuget地址:https://www.nuget.org/packages/OAuthLogin ...