【源码】Spring Data JPA原理解析之Repository的自动注入(一)

  Spring Data JPA系列

1、SpringBoot集成JPA及基本使用

2、Spring Data JPA Criteria查询、部分字段查询

3、Spring Data JPA数据批量插入、批量更新真的用对了吗

4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作

5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用

6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)

前言

前面5篇分享了Spring Data JPA的用法,在Spring Data JPA中的Repository类不需要添加任何注解,为何就能够自动注入到Spring的IOC容器中呢?为何只需要继承JpaRepository或JJpaRepositoryImplementation接口,就能够使用系统的CRUD接口?为何只需要在接口方法中使用@Query注解,就能够实现对应数据库表的操作呢?让我们一起从源码中寻找答案吧!本篇基于Spring-boot-2.7.x的源码分析。

JpaRepositoriesAutoConfiguration

在Spring Boot框架中,通常通过XxxAutoConfiguration类自动注入某个插件的功能,Spring Data JPA也是如此。通常XxxAutoConfiguration类是在对应插件的starter包里面的,而Spring Data JPA的starter包下的META-INF并没有对应的spring.factories文件。如下:

搜索引用该类的地方:

说明JpaRepositoriesAutoConfiguration是spring boot自带的,但为何要使用Spring Data JPA的时候,还需要引入spring-boot-starter-data-jpa依赖呢?以下为JpaRepositoriesAutoConfiguration的代码:

package org.springframework.boot.autoconfigure.data.jpa;

@AutoConfiguration(after = { HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
@ConditionalOnBean(DataSource.class)
// 需要有JpaRepository类
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
		matchIfMissing = true)
// 自动引入内部类JpaRepositoriesImportSelector,在JpaRepositoriesImportSelector中,引入JpaRepositoriesRegistrar
@Import(JpaRepositoriesImportSelector.class)
public class JpaRepositoriesAutoConfiguration {

	// 省略其他代码

	static class JpaRepositoriesImportSelector implements ImportSelector {

		private static final boolean ENVERS_AVAILABLE = ClassUtils.isPresent(
				"org.springframework.data.envers.repository.config.EnableEnversRepositories",
				JpaRepositoriesImportSelector.class.getClassLoader());

		@Override
		public String[] selectImports(AnnotationMetadata importingClassMetadata) {
			return new String[] { determineImport() };
		}

		private String determineImport() {
			// 自定引入JpaRepositoriesRegistrar
			return ENVERS_AVAILABLE ? EnversRevisionRepositoriesRegistrar.class.getName()
					: JpaRepositoriesRegistrar.class.getName();
		}

	}

}

JpaRepositoriesAutoConfiguration要自动引入,条件是需要有JpaRepository类,而该类是在spring-data-jpa包下,所以需要额外导入spring-boot-starter-data-jpa依赖。

JpaRepositoriesAutoConfiguration自动注入时,会自动注入JpaRepositoriesImportSelector,该类实现了ImportSelector接口。Spring的配置类解析的时候,会执行ConfigurationClassParser.processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports),在该方法中,判断importCandidates是否继承于ImportSelector接口,如果是,调用ImportSelector的selectImports()方法,获取需要额外自动加入容器的类。

在JpaRepositoriesImportSelector中的selectImports()方法中,返回JpaRepositoriesRegistrar.class.getName(),自动注入JpaRepositoriesRegistrar类。

JpaRepositoriesRegistrar源码如下:

package org.springframework.boot.autoconfigure.data.jpa;

class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {

	private BootstrapMode bootstrapMode = null;

	/**
	 * 指定@EnableJpaRepositories注解,所以在启动类中可以不添加该注解
	 * @return
	 */
	@Override
	protected Class<? extends Annotation> getAnnotation() {
		return EnableJpaRepositories.class;
	}

	/**
	 * 指定EnableJpaRepositoriesConfiguration配置类
	 * @return
	 */
	@Override
	protected Class<?> getConfiguration() {
		return EnableJpaRepositoriesConfiguration.class;
	}

	/**
	 * 指定RepositoryConfigurationExtension配置扩展
	 * @return
	 */
	@Override
	protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
		return new JpaRepositoryConfigExtension();
	}

	@Override
	protected BootstrapMode getBootstrapMode() {
		return (this.bootstrapMode == null) ? BootstrapMode.DEFAULT : this.bootstrapMode;
	}

	@Override
	public void setEnvironment(Environment environment) {
		super.setEnvironment(environment);
		configureBootstrapMode(environment);
	}

	private void configureBootstrapMode(Environment environment) {
		String property = environment.getProperty("spring.data.jpa.repositories.bootstrap-mode");
		if (StringUtils.hasText(property)) {
			this.bootstrapMode = BootstrapMode.valueOf(property.toUpperCase(Locale.ENGLISH));
		}
	}

	@EnableJpaRepositories
	private static class EnableJpaRepositoriesConfiguration {

	}

}

父类AbstractRepositoryConfigurationSourceSupport的核心源码代码如下:

package org.springframework.boot.autoconfigure.data;

/**
 * 实现ImportBeanDefinitionRegistrar接口
 */
public abstract class AbstractRepositoryConfigurationSourceSupport
		implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {

	private ResourceLoader resourceLoader;

	private BeanFactory beanFactory;

	private Environment environment;

	/**
	 * ImportBeanDefinitionRegistrar接口的方法,在ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass() ->
	 * loadBeanDefinitionsFromRegistrars()【执行实现ImportBeanDefinitionRegistrar接口的类的
	 * registerBeanDefinitions()方法,注册其他bean】
	 */
	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {
		RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
				getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
		// 子类JpaRepositoriesRegistrar.getRepositoryConfigurationExtension()返回JpaRepositoryConfigExtension
		// 自动注入JpaRepositoryConfigExtension对象
		delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
	}

	@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		registerBeanDefinitions(importingClassMetadata, registry, null);
	}

	private AnnotationRepositoryConfigurationSource getConfigurationSource(BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {
		AnnotationMetadata metadata = AnnotationMetadata.introspect(getConfiguration());
		return new AutoConfiguredAnnotationRepositoryConfigurationSource(metadata, getAnnotation(), this.resourceLoader,
				this.environment, registry, importBeanNameGenerator) {
		};
	}

	
	protected abstract RepositoryConfigurationExtension getRepositoryConfigurationExtension();

	// 省略其他

}

通过以上的源码,Spring中会自动注入JpaRepositoryConfigExtension类。

JpaRepositoryConfigExtension

JpaRepositoryConfigExtension为Jpa repository配置扩展类,类的结构如下:

2.1 RepositoryConfigurationExtension的源码如下:

package org.springframework.data.repository.config;

/**
 * SPI实现对存储库bean定义注册过程的特定于存储库的扩展
 */
public interface RepositoryConfigurationExtension {
    /**
	 * 在所有Spring Data模块中唯一标识模块的字符串。不得包含任何空格。模块的描述性名称小写,且空格转化为“-”
	 */
	default String getModuleIdentifier() {

		return getModuleName().toLowerCase(Locale.ENGLISH).replace(' ', '-');
	}

	/**
	 * 返回模块的描述性名称
	 * @return
	 */
	String getModuleName();

	/**
	 * 返回{@link BeanRegistryAotProcessor}类型,该类型负责在本机运行时提供Spring Data Repository基础结构组件所需的AOT/本机配置。
	 * @return
	 */
	@NonNull
	default Class<? extends BeanRegistrationAotProcessor> getRepositoryAotProcessor() {
		return RepositoryRegistrationAotProcessor.class;
	}

	/**
	 * 通过给定的RepositoryConfigurationSource获得所有的RepositoryConfiguration对象
	 * @param configSource RepositoryConfigurationSource对象,封装了repository配置的源(XML/Annotation)
	 * @param loader 用于加载资源
	 * @param strictMatchesOnly 是否仅返回严格匹配的repository。为true将导致检查所处理的存储库接口和域类型是否由当前存储管理
	 */
	<T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(
			T configSource, ResourceLoader loader, boolean strictMatchesOnly);

	/**
	 * 返回Spring Data命名查询的默认位置
	 */
	String getDefaultNamedQueryLocation();

	/**
	 * 获取要使用的repository工厂类的名字
	 */
	String getRepositoryFactoryBeanClassName();

	/**
	 * 回调以注册{@literal repositories}根节点的其他bean定义。这通常包括必须独立于要创建的存储库数量设置一次的bean。
	 * 将在注册任何存储库bean定义之前调用。
	 */
	void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConfigurationSource configurationSource);

	/**
	 * 回调用于对{@link BeanDefinition}进行后处理,并在必要时调整配置。
	 */
	void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource config);

	/**
	 * 回调用于对从注释构建的{@link BeanDefinition}进行后处理,并在必要时调整配置。
	 */
	void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config);

	/**
	 * 回调用于对从XML构建的{@link BeanDefinition}进行后处理,并在必要时调整配置。
	 */
	void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config);
}

2.2 RepositoryConfigurationExtensionSupport核心源码如下:

/**
 * RepositoryConfigurationExtension的基本实现,以简化接口的实现。将根据实现者提供的模块前缀默认命名查询位置
 * (请参见{getModulePrefix())。截断后处理方法,因为默认情况下可能不需要它们。
 */
public abstract class RepositoryConfigurationExtensionSupport implements RepositoryConfigurationExtension {

	@Override
	public String getModuleName() {
		return StringUtils.capitalize(getModulePrefix());
	}

	public <T extends RepositoryConfigurationSource> Collection<RepositoryConfiguration<T>> getRepositoryConfigurations(
			T configSource, ResourceLoader loader) {
		return getRepositoryConfigurations(configSource, loader, false);
	}

	/**
	 * 通过给定的RepositoryConfigurationSource获得所有的RepositoryConfiguration对象
	 * @param configSource RepositoryConfigurationSource对象,封装了repository配置的源(XML/Annotation)
	 * @param loader 用于加载资源
	 * @param strictMatchesOnly 是否仅返回严格匹配的repository。为true将导致检查所处理的存储库接口和域类型是否由当前存储管理
	 */
	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<>();
		// configSource.getCandidates(loader)返回所有定义的Repository接口类
		for (BeanDefinition candidate : configSource.getCandidates(loader)) {

			RepositoryConfiguration<T> configuration = getRepositoryConfiguration(candidate, configSource);
			// 获取Repository接口类,如GoodsRepository.class
			Class<?> repositoryInterface = loadRepositoryInterface(configuration,
					getConfigurationInspectionClassLoader(loader));

			if (repositoryInterface == null) {
				result.add(configuration);
				continue;
			}

			// 解析接口的Repository元信息,保存对应Repository<T, ID>中T及ID的类型元信息
			RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface);
			// 判断是否为Repository
			boolean qualifiedForImplementation = !strictMatchesOnly || configSource.usesExplicitFilters()
					|| isStrictRepositoryCandidate(metadata);

			if (qualifiedForImplementation && useRepositoryConfiguration(metadata)) {
				result.add(configuration);
			}
		}

		return result;
	}

}

该类最核心的方法是getRepositoryConfigurations()方法,通过该方法,执行configSource.getCandidates()从configSource获取要自动注入Spring容器的Repository Bean。

2.2.1 通过configSource.getCandidates()方法,获取Repository Bean

代码在RepositoryConfigurationSourceSupport中实现,代码如下:

public abstract class RepositoryConfigurationSourceSupport implements RepositoryConfigurationSource {

	/**
	 * 返回要为其创建存储库实例的存储库接口的源BeanDefinition
	 */
	@Override
	public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {

		// 定义一个RepositoryComponentProvider对象,传入包含的过滤器。
		// getIncludeFilters()在子类中实现,
		// 子类有AnnotationRepositoryConfigurationSource和XmlRepositoryConfigurationSource
		RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
		scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
		scanner.setEnvironment(environment);
		scanner.setResourceLoader(loader);
		// 添加排除的类型过滤器
		getExcludeFilters().forEach(scanner::addExcludeFilter);

		return Streamable.of(() -> getBasePackages().stream()//
				// 遍历基础包,根据RepositoryComponentProvider定义时添加的包含filter和排除filter,扫描beanDefinition对象
				.flatMap(it -> scanner.findCandidateComponents(it).stream()));
	}

}

2.2.1.1 在该方法中,先new一个RepositoryComponentProvider对象,其中的getIncludeFilter()和getExcludeFilters()分别用于添加扫描时要包含和排除的过滤器。对于使用注解实现的JPA,实现在子类AnnotationRepositoryConfigurationSource,该类主要职责是解析@EnableJpaRepositories注解的信息,其中的包含和排除的过滤器就是在该注解中配置的includeFilters和excludeFilters。

RepositoryComponentProvider代码如下:

/**
 * 自定义类路径扫描候选组件提供程序ClassPathScanningCandidateComponentProvider,扫描给定基本接口的接口类。
 * 不使用ClassPathScanningCandidateComponentProvider中的默认过滤器。 
 * 跳过添加@NoRepositoryBean的接口
 */
class RepositoryComponentProvider extends ClassPathScanningCandidateComponentProvider {

	private boolean considerNestedRepositoryInterfaces;
	private BeanDefinitionRegistry registry;

	public RepositoryComponentProvider(Iterable<? extends TypeFilter> includeFilters, BeanDefinitionRegistry registry) {
        // 父类的构造方法中传入false,即不适用默认的过滤器。即仅使用RepositoryComponentProvider添加的过滤器
		super(false);

		Assert.notNull(includeFilters, "Include filters must not be null");
		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");

		// DefaultListableBeanFactory对象
		this.registry = registry;
		// 添加指定的类型过滤器
		if (includeFilters.iterator().hasNext()) {
			for (TypeFilter filter : includeFilters) {
				addIncludeFilter(filter);
			}
		} else {
			// 如果没有类型过滤器,则添加Repository接口的类型过滤器以及添加@RepositoryDefinition注解的类型过滤器
			super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
			// AnnotationTypeFilter类似InterfaceTypeFilter类,前面是用于过滤特定注解,后面用于过滤特定接口
			super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
		}
		// 不扫描添加@NoRepositoryBean注解的类
		addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
	}

	/**
	 * 重写父类的方法,在添加入参的TypeFilter以外,还需要同时满足继承Repository,或添加了@RepositoryDefinition注解
	 */
	@Override
	public void addIncludeFilter(TypeFilter includeFilter) {

		List<TypeFilter> filterPlusInterface = new ArrayList<>(2);
		filterPlusInterface.add(includeFilter);
		filterPlusInterface.add(new InterfaceTypeFilter(Repository.class));
		// 添加TypeFilter及继承Repository的类型过滤器
		super.addIncludeFilter(new AllTypeFilter(filterPlusInterface));

		List<TypeFilter> filterPlusAnnotation = new ArrayList<>(2);
		filterPlusAnnotation.add(includeFilter);
		filterPlusAnnotation.add(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
		// 添加TypeFilter及添加@RepositoryDefinition注解
		super.addIncludeFilter(new AllTypeFilter(filterPlusAnnotation));
	}

	/**
	 * 自定义存储库接口检测并触发对它们的注释检测
	 */
	@Override
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		// 是非存储库接口,不是Repository.class
		boolean isNonRepositoryInterface = !ClassUtils.isGenericRepositoryInterface(beanDefinition.getBeanClassName());
		// 不是顶级类型
		boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass();
		// 是考虑嵌套存储库
		boolean isConsiderNestedRepositories = isConsiderNestedRepositoryInterfaces();

		return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories);
	}

	@Override
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		// 查找所有的Repository类
		Set<BeanDefinition> candidates = super.findCandidateComponents(basePackage);

		for (BeanDefinition candidate : candidates) {
			if (candidate instanceof AnnotatedBeanDefinition) {
				// 处理通用注解,解析@Lazy、@Primary、@DependOn、@Role、@Description中的value,添加到BeanDefinition中
				AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
			}
		}

		return candidates;
	}

	/**
	 * 接口类型过滤器,对应类必须的接口,且为构造方法传入的targetType或targetType的子类。
	 * 在super.match()中会遍历继承树,判断是否为targetType,即父类为targetType
	 */
	private static class InterfaceTypeFilter extends AssignableTypeFilter {

		public InterfaceTypeFilter(Class<?> targetType) {
			super(targetType);
		}

		/*
		 * 返回对应的类是否为接口,且为构造方法传入的targetType或targetType的子类。
		 * 在super.match()中会遍历继承树,判断是否为targetType,即父类为targetType
		 */
		@Override
		public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
				throws IOException {

			return metadataReader.getClassMetadata().isInterface() && super.match(metadataReader, metadataReaderFactory);
		}
	}

	/**
	 * 匹配所有的类型过滤器
	 */
	private static class AllTypeFilter implements TypeFilter {

		private final List<TypeFilter> delegates;

		public AllTypeFilter(List<TypeFilter> delegates) {

			Assert.notNull(delegates, "TypeFilter deleages must not be null");
			this.delegates = delegates;
		}

		/*
		 * 遍历所有的TypeFilter,只要一个不匹配,则返回false
		 */
		public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
				throws IOException {

			for (TypeFilter filter : delegates) {
				if (!filter.match(metadataReader, metadataReaderFactory)) {
					return false;
				}
			}

			return true;
		}
	}
}

在构造方法中,由于没有添加@EnableJpaRepositories注解注解,也就没有添加对应的includeFilters,所以走else分支,添加了Repository接口的类型过滤器以及@RepositoryDefinition注解的类型过滤器。然后添加了@NoRepositoryBean注解的排除过滤器。即会扫描实现Repository接口或添加@RepositoryDefinition注解的类,且类中不能添加@NoRepositoryBean注解。

另外,父类ClassPathScanningCandidateComponentProvider的构造方法中,如果传入的值为true,会添加@Component注解类的自动扫描。

2.2.1.2 然后调用RepositoryComponentProvider的findCandidateComponents(it)方法。该方法调用父类ClassPathScanningCandidateComponentProvider的findCandidateComponents()方法。

ClassPathScanningCandidateComponentProvider的代码如下:

package org.springframework.context.annotation;

/**
 * 从基本包中扫描候选组件的组件提供程序。可以使用{@link CandidateComponentsIndex the index}(如果可用)
 * 扫描类路径,否则,通过应用排除(exclude)和包含(include)过滤器来识别候选组件。
 * 特定注解类型过滤器(AnnotationTypeFilter)、可转化的类型过滤器(AssignableTypeFilter,为特定类型的子类)支持
 * CandidateComponentsIndex的筛选:如果指定了任何其他包含筛选器,则会忽略CandidateComponentsIndex,而使用类路径扫描。
 */
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {

	// 默认资源模式。扫描所有的.class文件
	static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";


	protected final Log logger = LogFactory.getLog(getClass());

	private String resourcePattern = DEFAULT_RESOURCE_PATTERN;

	// 包含的过滤器,满足条件的类会被扫描注册成bean。默认为添加@Component、@Repository、
	// @Service、@Controller、@ManagedBean、@Named注解的类,后面两个需要引入相关依赖
	private final List<TypeFilter> includeFilters = new LinkedList<>();

	// 不包含的过滤器,满足条件的类会被忽略,不扫描
	private final List<TypeFilter> excludeFilters = new ArrayList<>();

	// 运行时环境,可以获取系统变量和配置文件信息
	@Nullable
	private Environment environment;

	// @Conditional条件判断的计算器。@Conditional可以添加在@Bean上,满足@Conditional条件的bean才会被加载到Spring容器中
	@Nullable
	private ConditionEvaluator conditionEvaluator;

	// 资源匹配模式解析器
	@Nullable
	private ResourcePatternResolver resourcePatternResolver;

	// MetadataReader的工厂类,用于读取类文件的元数据
	@Nullable
	private MetadataReaderFactory metadataReaderFactory;

	// 提供对META-INF/spring.components中定义的候选者的访问控制
	@Nullable
	private CandidateComponentsIndex componentsIndex;

	protected ClassPathScanningCandidateComponentProvider() {
	}

	/**
	 * 创建一个标注环境的扫描组件提供器。useDefaultFilters用于指定是否应用默认的过滤器。如果为true,会自动包含对
	 * @Component、@Repository、@Service、@Controller的支持
	 */
	public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
		this(useDefaultFilters, new StandardEnvironment());
	}

	public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
		// 如果为true,调用registerDefaultFilters(),注册默认的过滤器,包含对
		// @Component、@Repository、@Service、@Controller的支持
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		setEnvironment(environment);
		setResourceLoader(null);
	}

	/**
	 * 添加一个包含的类型过滤器,用于指定对应类型的组件在被包含的扫描结果中
	 */
	public void addIncludeFilter(TypeFilter includeFilter) {
		this.includeFilters.add(includeFilter);
	}

	/**
	 * 添加一个排除的类型过滤器,用于指定对应类型的组件不在被包含的扫描结果中
	 */
	public void addExcludeFilter(TypeFilter excludeFilter) {
		this.excludeFilters.add(0, excludeFilter);
	}

	/**
	 * 重置过滤器,清空包含和排除的过滤器集合,重写注册默认的类型过滤器
	 */
	public void resetFilters(boolean useDefaultFilters) {
		this.includeFilters.clear();
		this.excludeFilters.clear();
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
	}

	/**
	 * 注册默认的过滤器,包含对@Component、@Repository、@Service、@Controller、@ManagedBean、@Named的过滤
	 */
	@SuppressWarnings("unchecked")
	protected void registerDefaultFilters() {
		// 添加@Component过滤,@Repository、@Service、@Controller都添加了@Component,
		// 也会过滤@Repository、@Service、@Controller
		this.includeFilters.add(new AnnotationTypeFilter(Component.class));
		ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
		try {
			// 添加@ManagedBean过滤
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
			logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
		}
		try {
			// 添加@Named过滤
			this.includeFilters.add(new AnnotationTypeFilter(
					((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
			logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

	/**
	 * 设置用于加载资源的ResourceLoader实例
	 */
	@Override
	public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
		// 如果resourceLoader实现了ResourcePatternResolver,直接返回,否则创建一个PathMatchingResourcePatternResolver对象
		this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
		this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
		// 读取配置文件META-INF/spring.components中的信息,如果有信息,创建一个CandidateComponentsIndex对象并保存配置信息返回
		this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
	}

	/**
	 * 设置用于创建MetadataReader实例的工厂。允许自定义如何读取类的元数据
	 */
	public void setMetadataReaderFactory(MetadataReaderFactory metadataReaderFactory) {
		this.metadataReaderFactory = metadataReaderFactory;
	}

	/**
	 * 返回一个带缓存功能的MetadataReaderFactory工厂,可以将某个类对应的MetadataReader进行缓存
	 * @return
	 */
	public final MetadataReaderFactory getMetadataReaderFactory() {
		if (this.metadataReaderFactory == null) {
			this.metadataReaderFactory = new CachingMetadataReaderFactory();
		}
		return this.metadataReaderFactory;
	}

	/**
	 * 扫描指定包及其子包的候选组件
	 */
	public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		// 如果componentsIndex不为null,即META-INF/spring.components中有配置信息,
		// 且include过滤器都支持@Indexed注解,从配置中扫描
		if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
			return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
		}
		else {
			// 扫描特定包
			return scanCandidateComponents(basePackage);
		}
	}

	/**
	 * 判断包含的过滤器是否支持都支持@Indexed注解,都支持返回true,否则返回false
	 */
	private boolean indexSupportsIncludeFilters() {
		for (TypeFilter includeFilter : this.includeFilters) {
			if (!indexSupportsIncludeFilter(includeFilter)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 确认指定的filter是否支持@Indexed注解或者是javax.开头的注解
	 */
	private boolean indexSupportsIncludeFilter(TypeFilter filter) {
		if (filter instanceof AnnotationTypeFilter) {
			Class<? extends Annotation> annotation = ((AnnotationTypeFilter) filter).getAnnotationType();
			return (AnnotationUtils.isAnnotationDeclaredLocally(Indexed.class, annotation) ||
					annotation.getName().startsWith("javax."));
		}
		if (filter instanceof AssignableTypeFilter) {
			Class<?> target = ((AssignableTypeFilter) filter).getTargetType();
			return AnnotationUtils.isAnnotationDeclaredLocally(Indexed.class, target);
		}
		return false;
	}

	/**
	 * 获取TypeFilter的原型,即名称
	 */
	@Nullable
	private String extractStereotype(TypeFilter filter) {
		if (filter instanceof AnnotationTypeFilter) {
			return ((AnnotationTypeFilter) filter).getAnnotationType().getName();
		}
		if (filter instanceof AssignableTypeFilter) {
			return ((AssignableTypeFilter) filter).getTargetType().getName();
		}
		return null;
	}

	private Set<BeanDefinition> addCandidateComponentsFromIndex(CandidateComponentsIndex index, String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			Set<String> types = new HashSet<>();
			for (TypeFilter filter : this.includeFilters) {
				// 获取filter的名称。全限定名,包名加类名,如:org.springframework.stereotype.Component
				String stereotype = extractStereotype(filter);
				if (stereotype == null) {
					throw new IllegalArgumentException("Failed to extract stereotype from " + filter);
				}
				// 查找basePackage包下所有stereotype类型的类的名称
				types.addAll(index.getCandidateTypes(basePackage, stereotype));
			}
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (String type : types) {
				// 获取类中元数据读取器,包含类的资源信息、类元模型、类中添加的注解元模型
				MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(type);
				// 判断给定的MetadataReader是否是一个候选组件。该组件不在排除过滤器中,
				//是在包含的过滤器中,且满足@Conditional条件或没有@Conditional
				if (isCandidateComponent(metadataReader)) {
					ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
					sbd.setSource(metadataReader.getResource());
					// 确定给定的bean定义是否符合候选条件。默认实现检查类是否不是接口,是否不依赖于封闭类可以在子类中重写
					if (isCandidateComponent(sbd)) {
						if (debugEnabled) {
							logger.debug("Using candidate component class from index: " + type);
						}
						candidates.add(sbd);
					}
					else {
						if (debugEnabled) {
							logger.debug("Ignored because not a concrete top-level class: " + type);
						}
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because matching an exclude filter: " + type);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

	private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<>();
		try {
			// 包的查询路径,如:classpath*:com/jingai/jpa/**/*.class
			String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
					resolveBasePackage(basePackage) + '/' + this.resourcePattern;
			// 获得符合查询条件的所有类的资源
			Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
			boolean traceEnabled = logger.isTraceEnabled();
			boolean debugEnabled = logger.isDebugEnabled();
			for (Resource resource : resources) {
				if (traceEnabled) {
					logger.trace("Scanning " + resource);
				}
				if (resource.isReadable()) {
					try {
						MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
						if (isCandidateComponent(metadataReader)) {
							ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
							sbd.setSource(resource);
							if (isCandidateComponent(sbd)) {
								if (debugEnabled) {
									logger.debug("Identified candidate component class: " + resource);
								}
								candidates.add(sbd);
							}
							else {
								if (debugEnabled) {
									logger.debug("Ignored because not a concrete top-level class: " + resource);
								}
							}
						}
						else {
							if (traceEnabled) {
								logger.trace("Ignored because not matching any filter: " + resource);
							}
						}
					}
					catch (Throwable ex) {
						throw new BeanDefinitionStoreException(
								"Failed to read candidate component class: " + resource, ex);
					}
				}
				else {
					if (traceEnabled) {
						logger.trace("Ignored because not readable: " + resource);
					}
				}
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
		}
		return candidates;
	}

	protected String resolveBasePackage(String basePackage) {
		return ClassUtils.convertClassNameToResourcePath(getEnvironment().resolveRequiredPlaceholders(basePackage));
	}

	/**
	 * 判断给定的MetadataReader是否是一个候选组件。该组件不在排除过滤器中,
	 * 是在包含的过滤器中,且满足@Conditional条件或没有@Conditional
	 */
	protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
		// 如果在排除的过滤器中,返回false
		for (TypeFilter tf : this.excludeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return false;
			}
		}
		// 如果在包含的过滤器中,满足@Conditional注解的配置要求或没有@Conditional注解,返回true,否则返回false
		for (TypeFilter tf : this.includeFilters) {
			if (tf.match(metadataReader, getMetadataReaderFactory())) {
				return isConditionMatch(metadataReader);
			}
		}
		return false;
	}

	/**
	 * 判断是否满足@Conditional注解的配置要求,如果不满足,返回false。没有添加或满足,返回true
	 */
	private boolean isConditionMatch(MetadataReader metadataReader) {
		if (this.conditionEvaluator == null) {
			this.conditionEvaluator =
					new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
		}
		return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
	}

	/**
	 * 确定给定的bean定义是否符合候选条件。默认实现检查类是否不是接口,是否不依赖于封闭类可以在子类中重写。
	 */
	protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
		AnnotationMetadata metadata = beanDefinition.getMetadata();
		// 确定基础类是独立的,即它是顶级类或可以独立于封闭类构造的嵌套类(静态内部类)
		// 返回底层类是否表示具体类,即既不是接口也不是抽象类,或者是抽象类,但添加了@Lockup注解
		return (metadata.isIndependent() && (metadata.isConcrete() ||
				(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
	}

}

在findCandidateComponents()方法中,执行else分支,调用scanCandidateComponents(),扫描特定包下的组件,传入此方法的basePackage的值没有设置的情况下,默认传入的是SpringBoot主方法所在的包。如本Spring Data JPA系列篇章传入的basePackage为com.jingai.jpa。

1)获得packageSearchPath,值为classpath*:com/jingai/jpa/**/*.class,即扫描com.jingai.jpa包及其子包所有class类;

2)遍历class类,获取类对应的MetadataReader。其中MetadataReader用于访问类元数据的简单外观,由ASM{@link org.springframework.ASM.ClassReader}读取。包含类文件资源信息、类元数据、注解元数据;

3)调用isCandidateComponent()方法,判断该组件不在排除excludeFilters过滤器中,是在包含includeFilters的过滤器中,且满足@Conditional条件或没有@Conditional。

结合上面调用构造函数时创建的过滤器可知:

默认的ClassPathScanningCandidateComponentProvider,

includeFilters包含添加了@Component、@Repository、@Service、@Controller注解的类【@Repository、@Service、@Controller注解添加了@Component,也会扫描】,会扫描。excludeFilters为空。该默认方式也是Spring默认处理的方式,所以在类中添加上面的注解,即可自动注入到Spring IOC容器;

对于RepositoryComponentProvider,继承了ClassPathScanningCandidateComponentProvider,传入构造方法的值为false,不会添加默认的过滤器,只添加了自己的过滤器,includeFilters包含实现了Repository接口、添加@RepositoryDefinition注解的类,会扫描。excludeFilters包含@NoRepositoryBean注解,不会扫描。即只扫描跟JPA的Repository相关的类。

在项目中,Repository实现了JpaRepositoryImplementation或JpaRepository接口,它们都继承了Repository接口。

此处有一个优化点,最好指定Repository所在的包,省得扫描整个SpringBoot启动类所在的包及其子包来获取Repository类;

4)调用isCandidateComponent(),确定给定的bean定义是否符合候选条件。默认实现检查类是否不是接口,是否不依赖于封闭类可以在子类中重写;

通过findCandidateComponents()方法,找出所有满足条件的组件,即bean。

2.2.2 遍历2.2.1中找到的所有满足条件的组件

组件实现Repository接口或添加了@RepositoryDefinition注解,且不能添加@NoRepositoryBean注解的类,调用loadRepositoryInterface()方法,获取Repository接口类;

调用AbstractRepositoryMetadata.getMetadata(repositoryInterface),解析接口的Repository元信息,保存对应Repository<T, ID>中T及ID的类型元信息;

进行元数据判断,此处的strictMatchesOnly为false,所以都会加入到集合中返回;

2.3 JpaRepositoryConfigExtension源码如下

public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport {

	private static final Class<?> PAB_POST_PROCESSOR = PersistenceAnnotationBeanPostProcessor.class;
	private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
	private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions";
	private static final String JPA_METAMODEL_CACHE_CLEANUP_CLASSNAME = "org.springframework.data.jpa.util.JpaMetamodelCacheCleanup";
	private static final String ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";

	private final Map<Object, String> entityManagerRefs = new LinkedHashMap<>();

	@Override
	public String getModuleName() {
		return "JPA";
	}

	/**
	 * 获取要使用的repository工厂类的名字
	 */
	@Override
	public String getRepositoryFactoryBeanClassName() {
		return JpaRepositoryFactoryBean.class.getName();
	}

	@Override
	protected String getModulePrefix() {
		return getModuleName().toLowerCase(Locale.US);
	}

	/**
	 * 获取识别注解。识别@Entity和@MappedSuperclass。一个用于实体类、一个用于实体类的父类
	 * @return
	 */
	@Override
	protected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {
		return Arrays.asList(Entity.class, MappedSuperclass.class);
	}

	/**
	 * 获取识别类型,识别JpaRepository接口类。父类RepositoryConfigurationExtensionSupport.isStrictRepositoryCandidate()方法调用
	 * @return
	 */
	@Override
	protected Collection<Class<?>> getIdentifyingTypes() {
		return Collections.<Class<?>> singleton(JpaRepository.class);
	}

    // 省略其他

}

在上面的2.2.2最后的元数据判断时,如果传入的strictMatchesOnly为true,会调用JpaRepositoryConfigExtension.getIdentifyingTypes()方法,判断Repository<T, ID>接口中的T是否添加了@Entity或@MappedSuperclass注解。

此处的getRepositoryFactoryBeanClassName()方法返回JpaRepositoryFactoryBean类名。

Repository加入Spring IOC容器

Spring启动时,通过ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars() -> ImportBeanDefinitionRegistrar.registerBeanDefinitions() -> AbstractRepositoryConfigurationSourceSupport.registerBeanDefinitions() -> RepositoryConfigurationDelegate.registerRepositoriesIn(),一步一步调用,执行到RepositoryConfigurationDelegate的registerRepositoriesIn()方法,代码如下:

public class RepositoryConfigurationDelegate {
	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(), configurationSource.getBootstrapMode().name()));
		}

		extension.registerBeansForRoot(registry, configurationSource);

		RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension,
				configurationSource, resourceLoader, environment);

		if (logger.isDebugEnabled()) {
			logger.debug(LogMessage.format("Scanning for %s repositories in packages %s.", //
					extension.getModuleName(), //
					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",
				() -> configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
		watch.start();

		// 获取所有的Repository类,如GoodsRepository
		Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension
				.getRepositoryConfigurations(configurationSource, resourceLoader, 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) {
			// key为Repository类的接口名称,如com.jingai.jpa.dao.entity.GoodsRepository.class
			configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);
			// 创建一个BeanDefinitionBuilder
			BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
			// definitionBuilder中添加transactionManager、entityManager等属性信息
			extension.postProcess(definitionBuilder, configurationSource);

			if (isXml) {
				extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
			} else {
				// definitionBuilder中添加enableDefaultTransactions属性信息
				extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
			}

			// 从builder中获取beanDefinition
			RootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition();
			beanDefinition.setTargetType(getRepositoryFactoryBeanType(configuration));
			beanDefinition.setResourceDescription(configuration.getResourceDescription());

			// 获取bean的名称,如goodsRepository
			String beanName = configurationSource.generateBeanName(beanDefinition);

			if (logger.isTraceEnabled()) {
				logger.trace(LogMessage.format(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName,
						configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName()));
			}
			// 保存
			metadataByRepositoryBeanName.put(beanName, builder.buildMetadata(configuration));
			// 自动注册goodsRepository,添加到bean容器
			registry.registerBeanDefinition(beanName, beanDefinition);
			definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
		}

		potentiallyLazifyRepositories(configurationsByRepositoryName, registry, 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"));
		}

		registerAotComponents(registry, extension, metadataByRepositoryBeanName);

		return definitions;
	}
}

3.1 通过RepositoryConfigurationExtension.getRepositoryConfigurations()方法,也就是前面讲解的RepositoryConfigurationExtensionSupport的getRepositoryConfigurations()方法,获取所有实现Repository接口的类,封装成RepositoryConfiguration;

3.2 遍历RepositoryConfiguration,调用builder.build()方法,创建一个BeanDefinitionBuilder对象;其中build()方法如下:

class RepositoryBeanDefinitionBuilder {

	public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		Assert.notNull(resourceLoader, "ResourceLoader must not be null");

		// configuration.getRepositoryFactoryBeanClassName()返回org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean
		// 创建一个BeanDefinitionBuilder对象,其factoryMethodName为null
		// BeanDefinitionBuilder.rootBeanDefinition()方法会将"org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean"
		// 赋值给BeanDefinition的beanClass
		BeanDefinitionBuilder builder = BeanDefinitionBuilder
				.rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName());

		builder.getRawBeanDefinition().setSource(configuration.getSource());
		// 构造方法参数值为接口名,如com.jingai.jpa.dao.GoodsRepository
		builder.addConstructorArgValue(configuration.getRepositoryInterface());
		// 配置属性。在RepositoryComponentProvider的findCandidateComponents()方法中,调用
		// AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate)
		// 解析类的@Lazy、@Primary、@DependOn、@Role、@Description等注解得到的值
		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));
		// extension.getDefaultNamedQueryLocation()返回classpath*:META-INF/jpa-named-queries.properties
		NamedQueriesBeanDefinitionBuilder definitionBuilder = new NamedQueriesBeanDefinitionBuilder(
				extension.getDefaultNamedQueryLocation());
		configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations);

		String namedQueriesBeanName = BeanDefinitionReaderUtils
				.uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry);
		BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource());
		registry.registerBeanDefinition(namedQueriesBeanName, namedQueries);

		builder.addPropertyValue("namedQueries", new RuntimeBeanReference(namedQueriesBeanName));

		registerCustomImplementation(configuration).ifPresent(it -> {
			builder.addPropertyReference("customImplementation", it);
			builder.addDependsOn(it);
		});

		String fragmentsBeanName = registerRepositoryFragments(configuration);
		builder.addPropertyValue("repositoryFragments", new RuntimeBeanReference(fragmentsBeanName));

		return builder;
	}
}

核心逻辑是执行configuration.getRepositoryFactoryBeanClassName()返回org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean,创建一个BeanDefinitionBuilder对象,其factoryMethodName为null
BeanDefinitionBuilder.rootBeanDefinition()方法会将"org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean"赋值给BeanDefinition的beanClass。

3.3 获取builder中的BeanDefinition对象,执行registry.registerBeanDefinition(beanName, beanDefinition),将Repository类及对应的beanDefinition添加到Spring IOC容器中;

小结

本篇源码比较多,做一个简单总结:

1、在SpringBoot中引入spring-boot-starter-data-jpa依赖时,会自动注入JpaRepositoriesAutoConfiguration,从而注入JpaRepositoryConfigExtension;

2、在JpaRepositoryConfigExtension的父类RepositoryConfigurationExtensionSupport的getRepositoryConfigurations()方法,获取所有实现Repository接口的类,封装成RepositoryConfiguration;

3、SpringBoot启动时,会执行RepositoryConfigurationDelegate的registerRepositoriesIn()方法,在该方法中,调用2中的方法,获取所有实现Repository接口的类,创建类的BeanDefinition对象,其中beanClass为"org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean"字符串。并执行registry.registerBeanDefinition(beanName, beanDefinition),将Repository类及对应的beanDefinition添加到Spring IOC容器中;

限于篇幅,本篇就先讲解到这里。

关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/625298.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

电脑没有网络连接怎么办?4招轻松完成网络连接!

“我的电脑开机后发现连接不上网络&#xff0c;尝试了很多次也不行&#xff0c;这是因为什么呢&#xff1f;有什么比较好的解决方法吗&#xff1f;” 当电脑无法连接到网络时&#xff0c;可能会给我们的工作和生活带来诸多不便。然而&#xff0c;大多数网络连接问题都可以通过一…

C#实现长方体棱锥圆柱棱柱圆锥展开折叠旋转缩放

C#实现长方体棱锥圆柱棱柱圆锥展开折叠旋转缩放 C#实现 模型边数 长方体 棱锥 圆柱 棱柱 圆锥 实现功能 展开 折叠 颜色 边框颜色 旋转 缩放 大小 视图方向 项目获取&#xff1a; 项目获取&#xff1a;typora: typora/img (gitee.com) 备用项目获取链接1&#xff1a;yife…

Linux的进程间通信 管道 进程池

目录 前言 进程间通信的基本概念 管道 匿名管道 pipe函数 cfc 管道的四种情况 管道的五种特征 进程池 ProcessPool.cpp&#xff1a; Task.cpp&#xff1a; 前言 ubuntu系统的默认用户名不为root的解决方案&#xff08;但是不建议&#xff09;&#xff1a;轻量应用服…

安卓悬浮窗----可移动的悬浮窗

目录 前言一、添加对悬浮窗功能的支持二、通过service实现悬浮窗2.1 窗口属性和标志2.2 窗口移动 三、完整代码 前言 记录一下基础的悬浮窗实现&#xff0c;分为几个重要的点进行阐述。 一、添加对悬浮窗功能的支持 app要实现悬浮窗功能&#xff0c;首先app要添加对悬浮窗功…

【瑞萨RA6M3】2. UART 实验

https://blog.csdn.net/qq_35181236/article/details/132789258 使用 uart9 配置 打印 void hal_entry(void) {/* TODO: add your own code here */fsp_err_t err;uint8_t c;/* 配置串口 */err g_uart9.p_api->open(g_uart9.p_ctrl, g_uart9.p_cfg);while (1){g_uart9.…

扫码枪与Input的火花

文章目录 前言一、需求&#xff1a;交互细节二、具体实现两个核心的函数&#xff1a;自动聚焦 三&#xff0c;扩展知识input 与 change的区别 前言 在浏览器扫描条形码获取条形的值&#xff0c;再操作对应的逻辑。这是比较常见的业务&#xff0c;这里记录实际操作。 其中PC端…

spacy NER 位置信息不考虑空格!!!

texts ["疫情期间&#xff0c;俄罗斯 联邦军队医疗机构的负责人Saanvi Alia在方城县启动了远程医疗服务。","疫情期间&#xff0c;俄罗斯 联 邦 军队医疗机构的负责人Saanvi Alia在方城县启动了远程医疗服务。","疫情期间&#xff0c;俄罗 斯 联 邦 …

PR对比模板|手机竖屏分辨率视频效果前后对比模板剪辑素材

Premiere Pro前后对比效果模板&#xff0c;适用于化妆前后对比、视频调色效果前后对比、同一地方人物活场景变化等视频制作剪辑使用。 主要特点&#xff1a; 只需将图像或视频导入占位符&#xff0c;编辑前后文本&#xff0c;并使用控件微调动画计时。 可以打开或关闭前后屏幕…

LeetCode2095删除链表的中间节点

题目描述 给你一个链表的头节点 head 。删除 链表的 中间节点 &#xff0c;并返回修改后的链表的头节点 head 。长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点&#xff08;下标从 0 开始&#xff09;&#xff0c;其中 ⌊x⌋ 表示小于或等于 x 的最大整数。对于 n 1、…

Linux防火墙iptalbes

1 iptalbes 1.1 概念 防火墙(Firewall)是一种隔离技术&#xff0c;用于安全管理与筛选的软件和硬件设备&#xff0c;使计算机内网和外网分开&#xff0c;可以防止外部网络用户以非法手段通过外部网络进入内部网络&#xff0c;保护内网免受外部非法用户的侵入。 1.2 SELinux …

Linux文件相关

权限&#xff1a; 超级用户root 可以做任何事情不受限制 普通用户[用户名]做有限的事情 超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是“$” 拓展&#xff1a; 用户的切换 su [用户名] 只是简单的换了一个账号&#xff0c;环境没变 su - 改变…

实验十 智能手机互联网程序设计(微信程序方向)实验报告

实验目的和要求 完成以下页面设计。 二、实验步骤与结果&#xff08;给出对应的代码或运行结果截图&#xff09; Wxml <view class"container"> <view class"header"> <view class"logo"…

遇到难题 暗区突围掉宝Twitch绑定关联账号显示404

Twitch作为一个广受欢迎的直播平台&#xff0c;经常会举办各种与游戏相关的互动活动&#xff0c;如“掉宝活动”&#xff0c;其中就包括了与《暗区突围》的合作。这类活动允许观众在观看指定的Twitch直播时&#xff0c;通过将他们的Twitch账号与《暗区突围》游戏账号绑定&#…

2024年3月 电子学会青少年等级考试机器人理论真题六级

202403 青少年等级考试机器人理论真题六级 第 1 题 下列选项中&#xff0c;属于URL的是&#xff1f;&#xff08; &#xff09; A&#xff1a;192.168.1.10 B&#xff1a;www.baidu.com C&#xff1a;http://www.kpcb.org.cn/h-col-147.html D&#xff1a;fe80::7998:ffc8…

springMVC基础使用(示例)

maven依赖&#xff08;javax.servlet-api版本与spring-webmvc班恩要匹配不然会报java.lang.NoSuchMethodError: javax.servlet.http.HttpServletRespons&#xff09;&#xff1a; <dependencies><dependency><groupId>javax.servlet</groupId><arti…

贪心 -力扣860.柠檬水找零力扣2208.将数组和减半的最少操作次数力扣179.最大数力扣376.摆动序列

目录 力扣860.柠檬水找零 力扣2208.将数组和减半的最少操作次数 力扣179.最大数 力扣376.摆动序列 贪心策略&#xff0c;局部最优->全局最优 1.把解决问题的过程分为若干步骤 2.解决每一步的时候&#xff0c;都选择当前看起来“最优秀的”解法 3.希望能够得到全局最优解…

硬盘架构原理及其算法RAID工作原理写惩罚

一、硬盘的架构以及寻址原理 硬盘工作原理&#xff1a; 硬盘寻址原理&#xff1a;逻辑顺序磁道、盘片、扇区&#xff08;顺序CHS&#xff09; 二、机械硬盘算法 读取算法 寻道算法 个人与企业适合的算法和寻道 个人使用的机械硬盘适合的寻道算法和读取算法是&#xff1a…

Matlab如何批量导出多张高质量论文插图?科研效率UpUp第9期

上一期文章中&#xff0c;分享了Matlab导出高质量论文插图的方法&#xff08;Matlab如何导出高质量论文插图&#xff1f;科研效率UpUp第8期&#xff09;。 进一步&#xff0c;假如我们想要批量导出多张高质量无变形论文插图&#xff0c;该如何操作呢&#xff1f; ​也很简单&…

工作太闲怎么办?有没有什么副业推荐?

如果您的工作太闲&#xff0c;可以考虑参加一些副业&#xff0c;利用您的空余时间进行一些有意义的活动。以下是一些副业建议 1. 在线兼职 可以通过一些在线平台寻找兼职工作&#xff0c;如做在线调查、参与评估、进行数据输入等。 2.做任务 还可以做下百度的致米宝库&#…

vue获取路由的值

1&#xff0c;此方法获取到请求地址后面的值 如 /name123&age12 2&#xff0c;此方法获取到请地址&#xff1f;后面的值 例如?name123&age12 二者的区别&#xff0c;第一个是直接在路径后面拼接&#xff0c;第二种就是正规的http请求。 路径带&#xff1f;号的