TIP本文涉及 Spring 扩展点,需要对 Spring 原理和扩展点有一定了解基础
@EnableJpaRepositories 源码跟踪
在 @EnableJpaRepositories 注解中,通过 @Import 注解把 JpaRepositoriesRegistrar 注册到 IOC 容器
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Import({JpaRepositoriesRegistrar.class})public @interface EnableJpaRepositories {
}JpaRepositoriesRegistrar 继承了 RepositoryBeanDefinitionRegistrarSupport 的注册能力,它是通过实现 ImportBeanDefinitionRegistrar 接口(一般配合 @Import 使用)的 registerBeanDefinitions 方法在上下文启动时动态注册 Bean
ImportBeanDefinitionRegistrar
这个接口允许开发者在 Spring 应用启动过程中,通过编程方式注册额外的 Bean 定义
public abstract class RepositoryBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
/** @deprecated */ @Deprecated public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { this.registerBeanDefinitions(metadata, registry, ConfigurationClassPostProcessor.IMPORT_BEAN_NAME_GENERATOR); }
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry, BeanNameGenerator generator) { Assert.notNull(metadata, "AnnotationMetadata must not be null"); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(this.resourceLoader, "ResourceLoader must not be null"); if (metadata.getAnnotationAttributes(this.getAnnotation().getName()) != null) { AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(metadata, this.getAnnotation(), this.resourceLoader, this.environment, registry, generator); RepositoryConfigurationExtension extension = this.getExtension(); RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource); RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, this.resourceLoader, this.environment); // 在这个方法实现注册 delegate.registerRepositoriesIn(registry, extension); } }}BeanDefinition 扫描
最后调用 registerRepositoriesIn 方法注册 Bean,通过断点和观察,发现 BeanDefinition 是先通过 extension.getRepositoryConfigurations 方法扫描出 BeanDefinition 封装成 RepositoryConfiguration,然后通过 builder.build 方法重新构建 BeanDefinition,我们先跟踪 getRepositoryConfigurations 方法看如何扫描出 BeanDefinition,再回来看 build 方法如何构建 BeanDefinition
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension) { if (logger.isInfoEnabled()) { logger.info(LogMessage.format("Bootstrapping Spring Data %s repositories in %s mode.", extension.getModuleName(), this.configurationSource.getBootstrapMode().name())); }
extension.registerBeansForRoot(registry, this.configurationSource); RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, this.configurationSource, this.resourceLoader, this.environment); if (logger.isDebugEnabled()) { logger.debug(LogMessage.format("Scanning for %s repositories in packages %s.", extension.getModuleName(), this.configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")))); }
StopWatch watch = new StopWatch(); ApplicationStartup startup = getStartup(registry); StartupStep repoScan = startup.start("spring.data.repository.scanning"); repoScan.tag("dataModule", extension.getModuleName()); repoScan.tag("basePackages", () -> (String)this.configurationSource.getBasePackages().stream().collect(Collectors.joining(", "))); watch.start(); // getRepositoryConfigurations 方法里扫描出 BeanDefinition 封装成 RepositoryConfiguration Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension.getRepositoryConfigurations(this.configurationSource, this.resourceLoader, this.inMultiStoreMode); List<BeanComponentDefinition> definitions = new ArrayList(); Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap(configurations.size()); Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName = new HashMap(configurations.size());
for(RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : configurations) { configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration); // definitionBuilder 是通过 configuration 构建 BeanDefinition,后面会回到这里分析 build 方法 BeanDefinitionBuilder definitionBuilder = builder.build(configuration); extension.postProcess(definitionBuilder, this.configurationSource); if (this.isXml) { extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource)this.configurationSource); } else { extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource)this.configurationSource); } // beanDefinition 是从 definitionBuilder 获取 RootBeanDefinition beanDefinition = (RootBeanDefinition)definitionBuilder.getBeanDefinition(); beanDefinition.setTargetType(this.getRepositoryFactoryBeanType(configuration)); beanDefinition.setResourceDescription(configuration.getResourceDescription()); String beanName = this.configurationSource.generateBeanName(beanDefinition); if (logger.isTraceEnabled()) { logger.trace(LogMessage.format("Spring Data %s - Registering repository: %s - Interface: %s - Factory: %s", extension.getModuleName(), beanName, configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName())); }
metadataByRepositoryBeanName.put(beanName, builder.buildMetadata(configuration)); // 把 beanDefinition 注册到 BeanDefinitionRegistry registry.registerBeanDefinition(beanName, beanDefinition); definitions.add(new BeanComponentDefinition(beanDefinition, beanName)); }
potentiallyLazifyRepositories(configurationsByRepositoryName, registry, this.configurationSource.getBootstrapMode()); watch.stop(); repoScan.tag("repository.count", Integer.toString(configurations.size())); repoScan.end(); if (logger.isInfoEnabled()) { logger.info(LogMessage.format("Finished Spring Data repository scanning in %s ms. Found %s %s repository interface%s.", watch.lastTaskInfo().getTimeMillis(), configurations.size(), extension.getModuleName(), configurations.size() == 1 ? "" : "s")); }
this.registerAotComponents(registry, extension, metadataByRepositoryBeanName); return definitions;}在 getRepositoryConfigurations 方法中已经可以看到 BeanDefinition 是从 configSource.getCandidates(loader) 中返回,继续跟踪 getCandidates 方法
public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(T configSource, ResourceLoader loader, boolean strictMatchesOnly) { Assert.notNull(configSource, "ConfigSource must not be null"); Assert.notNull(loader, "Loader must not be null"); Set<RepositoryConfiguration<T>> result = new HashSet(); // BeanDefinition 是从 configSource.getCandidates(loader) 返回 for(BeanDefinition candidate : configSource.getCandidates(loader)) { RepositoryConfiguration<T> configuration = this.<T>getRepositoryConfiguration(candidate, configSource); Class<?> repositoryInterface = this.loadRepositoryInterface(configuration, this.getConfigurationInspectionClassLoader(loader)); if (repositoryInterface == null) { result.add(configuration); } else { RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface); boolean qualifiedForImplementation = !strictMatchesOnly || configSource.usesExplicitFilters() || this.isStrictRepositoryCandidate(metadata); if (qualifiedForImplementation && this.useRepositoryConfiguration(metadata)) { result.add(configuration); } } }
return result;}public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) { // 继承了 spring 的 ClassPathScanningCandidateComponentProvider 的扫描能力,实现扫描 Repository 接口的逻辑 RepositoryComponentProvider scanner = new RepositoryComponentProvider(this.getIncludeFilters(), this.registry); scanner.setConsiderNestedRepositoryInterfaces(this.shouldConsiderNestedRepositories()); scanner.setEnvironment(this.environment); scanner.setResourceLoader(loader); Streamable var10000 = this.getExcludeFilters(); Objects.requireNonNull(scanner); var10000.forEach(scanner::addExcludeFilter); // 通过 findCandidateComponents 方法返回扫描结果 return Streamable.of(() -> this.getBasePackages().stream().flatMap((it) -> scanner.findCandidateComponents(it).stream()));}通过阅读源码,RepositoryComponentProvider 的实例扫描出 BeanDefinition,最终通过 getCandidates 方法返回,我们看 RepositoryComponentProvider 这个类
class RepositoryComponentProvider extends ClassPathScanningCandidateComponentProvider {
private static boolean isGenericRepositoryInterface(@Nullable String interfaceName) { return Repository.class.getName().equals(interfaceName); }
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { // 扫描继承 Repository 接口的接口 boolean isNonRepositoryInterface = !isGenericRepositoryInterface(beanDefinition.getBeanClassName()); boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass(); boolean isConsiderNestedRepositories = this.isConsiderNestedRepositoryInterfaces(); return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories); }
public Set<BeanDefinition> findCandidateComponents(String basePackage) { // 扫描包路径下符合 isCandidateComponent 条件的组件 Set<BeanDefinition> candidates = super.findCandidateComponents(basePackage);
for(BeanDefinition candidate : candidates) { if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate); } }
return candidates; }}RepositoryComponentProvider 继承了 spring 的 ClassPathScanningCandidateComponentProvider 的扫描能力,重写 isCandidateComponent 判断组件的方法,把 Repository 接口扫描出来,通过 findCandidateComponents 方法扫描指定包路径,Spring 底层对 ASM 源码进行了扩展和封装,通过读取字节码文件,解析符合条件的文件,把文件的信息封装成 BeanDefinition
ClassPathScanningCandidateComponentProvider
是 Spring 框架中一个非常核心的类,它主要用于在类路径下扫描并发现带有特定注解的组件
到此,我们知道了 JPA 是如何实现把 Repository 接口扫描出来,但 IOC 容器只能注册实例,接口无法实例化,带着疑问我们回到之前构建 BeanDefinition 的地方
BeanDefinition 构建
回到刚才 registerRepositoriesIn 方法里面的 builder.build 方法,继续跟踪 BeanDefinition 的构建,在 rootBeanDefinition 方法中会创建一个新的 BeanDefinition,并且把 beanClassName 设置为 JpaRepositoryFactoryBean
public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) { Assert.notNull(this.registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(this.resourceLoader, "ResourceLoader must not be null"); // rootBeanDefinition 会创建一个新的 BeanDefinition,并且设置 beanClassName 为 JpaRepositoryFactoryBean BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName()); builder.getRawBeanDefinition().setSource(configuration.getSource()); builder.addConstructorArgValue(configuration.getRepositoryInterface()); builder.addPropertyValue("queryLookupStrategyKey", configuration.getQueryLookupStrategyKey()); builder.addPropertyValue("lazyInit", configuration.isLazyInit()); builder.setLazyInit(configuration.isLazyInit()); builder.setPrimary(configuration.isPrimary()); configuration.getRepositoryBaseClassName().ifPresent((it) -> builder.addPropertyValue("repositoryBaseClass", it)); NamedQueriesBeanDefinitionBuilder definitionBuilder = new NamedQueriesBeanDefinitionBuilder(this.extension.getDefaultNamedQueryLocation()); Optional var10000 = configuration.getNamedQueriesLocation(); Objects.requireNonNull(definitionBuilder); var10000.ifPresent(definitionBuilder::setLocations); String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(this.extension.getModuleIdentifier() + ".named-queries", this.registry); BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource()); this.registry.registerBeanDefinition(namedQueriesBeanName, namedQueries); builder.addPropertyValue("namedQueries", new RuntimeBeanReference(namedQueriesBeanName)); this.registerCustomImplementation(configuration).ifPresent((it) -> { builder.addPropertyReference("customImplementation", it); builder.addDependsOn(it); }); String fragmentsBeanName = this.registerRepositoryFragments(configuration); builder.addPropertyValue("repositoryFragments", new RuntimeBeanReference(fragmentsBeanName)); return builder;}public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName) { return rootBeanDefinition(beanClassName, (String)null);}
public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName, @Nullable String factoryMethodName) { BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new RootBeanDefinition()); // 设置 beanClassName 为 JpaRepositoryFactoryBean builder.beanDefinition.setBeanClassName(beanClassName); builder.beanDefinition.setFactoryMethodName(factoryMethodName); return builder;}通过断点观察,新建的 BeanDefinition 的 beanClassName 都会被设置成 JpaRepositoryFactoryBean

JpaRepositoryFactoryBean
观察 JpaRepositoryFactoryBean 这个类,通过继承 RepositoryFactoryBeanSupport 实现了 FactoryBean、InitializingBean、BeanClassLoaderAware 等扩展接口

RepositoryFactoryBeanSupport
阅读 RepositoryFactoryBeanSupport 的源码,它主要通过实现 InitializingBean 的 afterPropertiesSet 方法,在 Bean 初始化完成后通过动态代理生成 SimpleJpaRepository 的代理对象交给 this.repository 成员变量,然后实现 FactoryBean 接口的 getObject 方法,返回这个代理对象
经常使用 JPA 的人应该对 SimpleJpaRepository 这个代理类不陌生,平时使用 CRUD 等内置方法,就是通过它调用的
InitializingBean接口是 Bean 的生命周期扩展,afterPropertiesSet方法是在 Bean 初始化之后执行FactoryBean接口是 Spring 的扩展点,当从 IOC 容器调用 getBean 方法获取 Bean 时,实际会调用getObject方法获取
所以刚才的疑问已经解决,实际注册到 IOC 容器的是 SimpleJpaRepository 这个代理类的实例
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID> implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, ApplicationEventPublisherAware {
public void afterPropertiesSet() { this.factory = this.createRepositoryFactory(); this.factory.setQueryLookupStrategyKey(this.queryLookupStrategyKey); this.factory.setNamedQueries(this.namedQueries); this.factory.setEvaluationContextProvider((QueryMethodEvaluationContextProvider)this.evaluationContextProvider.orElseGet(() -> QueryMethodEvaluationContextProvider.DEFAULT)); this.factory.setBeanClassLoader(this.classLoader); this.factory.setBeanFactory(this.beanFactory); if (this.publisher != null) { this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(this.publisher)); }
Optional var10000 = this.repositoryBaseClass; RepositoryFactorySupport var10001 = this.factory; Objects.requireNonNull(var10001); var10000.ifPresent(var10001::setRepositoryBaseClass); this.repositoryFactoryCustomizers.forEach((customizer) -> customizer.customize(this.factory)); RepositoryComposition.RepositoryFragments customImplementationFragment = (RepositoryComposition.RepositoryFragments)this.customImplementation.map((xva$0) -> RepositoryFragments.just(new Object[]{xva$0})).orElseGet(RepositoryComposition.RepositoryFragments::empty); RepositoryComposition.RepositoryFragments repositoryFragmentsToUse = ((RepositoryComposition.RepositoryFragments)this.repositoryFragments.orElseGet(RepositoryComposition.RepositoryFragments::empty)).append(customImplementationFragment); this.repositoryMetadata = this.factory.getRepositoryMetadata(this.repositoryInterface); // getRepository 方法里生成 JDK 动态代理(SimpleJpaRepository),在 getObject 方法中加载并返回 this.repository = Lazy.of(() -> (Repository)this.factory.getRepository(this.repositoryInterface, repositoryFragmentsToUse)); // 这里会校验 repository 关联的 Entity 是否存在 // Make sure the aggregate root type is present in the MappingContext (e.g. for auditing) this.mappingContext.ifPresent((it) -> it.getPersistentEntity(this.repositoryMetadata.getDomainType())); if (!this.lazyInit) { this.repository.get(); }
}
public T getObject() { // 返回代理类 return this.repository.get(); }}总结
- @EnableJpaRepositories 注解是通过
ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法在启动时动态注册 Bean - 通过继承
ClassPathScanningCandidateComponentProvider重写判断组件的方法扩展 spring 的扫描能力,扫描出继承了 Repository 接口的 BeanDefinition - 把 Repository 接口的 BeanDefinition 替换成新建 BeanClassName 为 JpaRepositoryFactoryBean 的 BeanDefinition
- 当 JpaRepositoryFactoryBean 初始化后调用
InitializingBean接口的afterPropertiesSet方法生成 SimpleJpaRepository 的动态代理对象 - IOC 容器 getBean 时,通过
FactoryBean接口的getObject方法获取到代理对象