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

news2024/7/4 5:48:10

  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容器中;

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4种现象表明你的血糖控制良好!

如果你出现以下4种现象&#xff0c;恭喜你&#xff0c;说明你的血糖控制的不错&#xff0c;需要继续坚持。 1.饥饿感减少&#xff0c;我们的脏腑能够吸收血液中的糖分了&#xff0c;就用不着饿了。&#xff0c;血液中的糖能够得到充分的利用&#xff0c;血糖自然降下去。 2.体…

找到字符串中所有字母异位词 ---- 滑动窗口

题目链接 题目: 分析: 要找的是在s中和p是异位词的子串, 也就是说子串大小和p相同, 那么就是窗口大小固定的滑动窗口问题可以使用哈希数组来记录每个元素出现的个数, 定义hash1存放p中的各元素个数定义left 0; right 0;进窗口 让right指向的元素进窗口, 即更新hash2中的元素…

vue3自定义指令​(通过指令钩子获得dom和钩子参数)

实现文本框自动获得焦点 Index.vue: <script setup> import { ref, onMounted } from vue import ./index.cssconst vFocus {mounted: (el, binding) > {el.focus()console.log(binding)} }onMounted(() > {}) </script><template><div class&qu…

C#委托以及在事件驱动编程中的使用

C#中的委托&#xff08;Delegate&#xff09;是一种类型&#xff0c;它可以存储对方法的引用&#xff0c;并且可以像其他类型一样传递给方法。委托提供了一种灵活的方式来实现事件处理、回调函数和多播委托等功能。以下是关于C#委托的详细介绍&#xff1a; 定义&#xff1a; …

量化研究---A股赚钱日历,上证指数为例,提供源代码

今天把A股的全部数据导出做了一些赚钱日历分析&#xff0c;看那个月赚钱容易&#xff0c;那个月赚钱困难 导入需要的库 import pandas as pdimport matplotlib.pyplot as pltimport quantstats as qsfrom trader_tool.index_data import index_datafrom trader_tool import j…

风电功率预测 | 基于BP神经网络的风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测 | 基于BP神经网络的风电功率预测(附matlab完整源码)完整代码风电功率预测 | 基于BP神经网络的风电功率预测(附matlab完整源码) 基于BP神经网络的风电功率预测是一种常见的方法,它利用BP神经网络模型来预测风电场的发电功率。下面是一个基于BP神经…

HTML常用标签-表格标签

表格标签 1 常规表格2 单元格跨行3 单元格跨行 1 常规表格 table标签 代表表格 thead标签 代表表头 可以省略不写 tbody标签 代表表体 可以省略不写 tfoot标签 代表表尾 可以省略不写 tr标签 代表一行 td标签 代表行内的一格 th标签 自带加粗和居中效果的td 代码 <h…

【启程Golang之旅】环境设置、工具安装与代码实践

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

mysql,sqlserver数据库查询表,获得表结构,结构类型说明,获得这些数据,可以拿去创建表

mysql&#xff0c;sqlserver数据库查询表&#xff0c;获得表结构&#xff0c;结构类型说明&#xff0c;获得这些数据&#xff0c;可以拿去创建表 //表名p_order select * from information_schema.COLUMNS where TABLE_NAMEp_order;1、TABLE_CATALOG &#xff0c;nvarchar(128…

C++ | Leetcode C++题解之第89题格雷编码

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> grayCode(int n) {vector<int> ret(1 << n);for (int i 0; i < ret.size(); i) {ret[i] (i >> 1) ^ i;}return ret;} };

47 tcp网络程序

网路聊天 API详解 下面用到的API&#xff0c;都在sys/socket.h中 socket (): socket() 打开一个网络通讯端口&#xff0c;如果成功的话&#xff0c;就像open() 一样返回一个文件描述符应用程序可以像读文件一样用read/write在网络上收发数据如果调用出错返回-1对于IPv4&am…

读人工智能时代与人类未来笔记03_演变

1. 演变 1.1. 每个社会都找到了属于自己的一套适应世界的方法 1.1.1. 适应的核心&#xff0c;是有关人类心智与现实之间关系的概念 1.1.2. 人类认识周围环境的能力 1.1.2.1. 这种能力通过知识获得&#xff0c;同时也受到知识…

研发管理-选择研发管理系统-研发管理系统哪个好

选择研发管理系统-研发管理系统哪个好 选择研发管理系统时&#xff0c;并没有一个绝对的“最好”的系统&#xff0c;因为每个企业的需求和情况都是独特的。然而&#xff0c;我可以向您介绍一些在市场上广受欢迎且功能强大的研发管理系统&#xff0c;供您参考&#xff1a; 1、彩…

OpenAI 推出 GPT-4o:实现多模态 AI 交互

一、前言 OpenAI 推出了其最新的 AI 模型——GPT-4o&#xff0c;此次发布的并非 GPT-4.5 或 GPT-5&#xff0c;而是一款全新的“全模态模型(Omnimodel)”。这是一个将文本、语音和视觉能力集成到单一无缝 AI 体验中的突破性发展。 GPT-4o 于 2024 年 5 月 14 日发布&#xff0…

生产消费者模型-环形队列与信号量

文章目录 前言一、怎样的环形队列&#xff1f;二、什么是信号量三、使用步骤信号量的接口函数1. sem_init2.sem_destroy3.sem_wait4.sem_post 环形队列的设计测试用例 前言 之前我们使用互斥锁和条件变量实现过一个生产者消费者模型&#xff0c;那么那个生产消费者模型具有一个…

Github20K星开源团队协作工具:Zulip

Zulip&#xff1a;让团队协作的每一次交流&#xff0c;都精准高效。- 精选真开源&#xff0c;释放新价值。 概览 随着远程工作的兴起和团队协作的需求不断增加&#xff0c;群组聊天软件成为了日常工作中不可或缺的一部分。Zulip 是github上一个开源的团队协作工具&#xff0c;…

【问题实操】银河高级服务器操作系统实例分享,网卡drop问题分析

1.服务器环境以及配置 系统环境 物理机/虚拟机/云/容器 物理机 网络环境 外网/私有网络/无网络 私有网络 硬件环境 机型 华鲲振宇 TG225B1 处理器 kunpeng 920 内存 1024GB 主板型号 TG225B1 HZKY 整机类型/架构 aarch64 固件版本 6.57 软件环境 具体操作系…

谷歌Gemini时代来了!加固搜索护城河、赋能全家桶,Gemini 1.5 Pro升级至200万token

3 月中旬&#xff0c;谷歌宣布 Google I/O 定档北京时间 5 月 15 日凌晨 1 点。而当大会开幕时间临近&#xff0c;本应是讨论度最高的时候&#xff0c;「宿敌」OpenAI 却半路杀出&#xff0c;抢先一天&#xff0c;仅耗时 27 分钟就发布了颠覆性巨作 GPT-4o&#xff0c;将新一轮…

HTTP代理可以应用在那些领域呢

HTTP代理是IP代理领域中一个重要组成部分&#xff0c;它基于HTTP协议传输&#xff0c;使用海外服务器帮助用户绕开访问限制&#xff0c;浏览查看海外资讯信息。 HTTP代理可以应用在哪些领域呢&#xff1f; 1.保护使用者隐私 当今越来越数据被上传到网络云端上&#xff0c;用户…

Gone框架介绍17 - 创建一个可运行在生产环境的Web项目

gone是可以高效开发Web服务的Golang依赖注入框架 github地址&#xff1a;https://github.com/gone-io/gone 文档原地址&#xff1a;https://goner.fun/zh/guide/auto-gen-priest.html 请帮忙在github上点个 ⭐️吧&#xff0c;这对我很重要 &#xff1b;万分感谢&#xff01;&a…