【源码】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());@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[] { determineImport() };}private String determineImport() {// 自定引入JpaRepositoriesRegistrarreturn 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*/@Overrideprotected Class<? extends Annotation> getAnnotation() {return EnableJpaRepositories.class;}/*** 指定EnableJpaRepositoriesConfiguration配置类* @return*/@Overrideprotected Class<?> getConfiguration() {return EnableJpaRepositoriesConfiguration.class;}/*** 指定RepositoryConfigurationExtension配置扩展* @return*/@Overrideprotected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {return new JpaRepositoryConfigExtension();}@Overrideprotected BootstrapMode getBootstrapMode() {return (this.bootstrapMode == null) ? BootstrapMode.DEFAULT : this.bootstrapMode;}@Overridepublic 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));}}@EnableJpaRepositoriesprivate static class EnableJpaRepositoriesConfiguration {}}

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

package org.springframework.boot.autoconfigure.data;/*** 实现ImportBeanDefinitionRegistrar接口*/
public abstract class AbstractRepositoryConfigurationSourceSupportimplements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {private ResourceLoader resourceLoader;private BeanFactory beanFactory;private Environment environment;/*** ImportBeanDefinitionRegistrar接口的方法,在ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass() ->* loadBeanDefinitionsFromRegistrars()【执行实现ImportBeanDefinitionRegistrar接口的类的* registerBeanDefinitions()方法,注册其他bean】*/@Overridepublic 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());}@Overridepublic 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*/@NonNulldefault 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 {@Overridepublic 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.classClass<?> repositoryInterface = loadRepositoryInterface(configuration,getConfigurationInspectionClassLoader(loader));if (repositoryInterface == null) {result.add(configuration);continue;}// 解析接口的Repository元信息,保存对应Repository<T, ID>中T及ID的类型元信息RepositoryMetadata metadata = AbstractRepositoryMetadata.getMetadata(repositoryInterface);// 判断是否为Repositoryboolean 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*/@Overridepublic Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {// 定义一个RepositoryComponentProvider对象,传入包含的过滤器。// getIncludeFilters()在子类中实现,// 子类有AnnotationRepositoryConfigurationSource和XmlRepositoryConfigurationSourceRepositoryComponentProvider 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注解*/@Overridepublic 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));}/*** 自定义存储库接口检测并触发对它们的注释检测*/@Overrideprotected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {// 是非存储库接口,不是Repository.classboolean isNonRepositoryInterface = !ClassUtils.isGenericRepositoryInterface(beanDefinition.getBeanClassName());// 不是顶级类型boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass();// 是考虑嵌套存储库boolean isConsiderNestedRepositories = isConsiderNestedRepositoryInterfaces();return isNonRepositoryInterface && (isTopLevelType || isConsiderNestedRepositories);}@Overridepublic 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*/@Overridepublic 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<>();// 运行时环境,可以获取系统变量和配置文件信息@Nullableprivate Environment environment;// @Conditional条件判断的计算器。@Conditional可以添加在@Bean上,满足@Conditional条件的bean才会被加载到Spring容器中@Nullableprivate ConditionEvaluator conditionEvaluator;// 资源匹配模式解析器@Nullableprivate ResourcePatternResolver resourcePatternResolver;// MetadataReader的工厂类,用于读取类文件的元数据@Nullableprivate MetadataReaderFactory metadataReaderFactory;// 提供对META-INF/spring.components中定义的候选者的访问控制@Nullableprivate 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、@Controllerthis.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实例*/@Overridepublic 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的原型,即名称*/@Nullableprivate 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.ComponentString 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条件或没有@Conditionalif (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/**/*.classString 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 {// 如果在排除的过滤器中,返回falsefor (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}// 如果在包含的过滤器中,满足@Conditional注解的配置要求或没有@Conditional注解,返回true,否则返回falsefor (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<>();@Overridepublic String getModuleName() {return "JPA";}/*** 获取要使用的repository工厂类的名字*/@Overridepublic String getRepositoryFactoryBeanClassName() {return JpaRepositoryFactoryBean.class.getName();}@Overrideprotected String getModulePrefix() {return getModuleName().toLowerCase(Locale.US);}/*** 获取识别注解。识别@Entity和@MappedSuperclass。一个用于实体类、一个用于实体类的父类* @return*/@Overrideprotected Collection<Class<? extends Annotation>> getIdentifyingAnnotations() {return Arrays.asList(Entity.class, MappedSuperclass.class);}/*** 获取识别类型,识别JpaRepository接口类。父类RepositoryConfigurationExtensionSupport.isStrictRepositoryCandidate()方法调用* @return*/@Overrideprotected 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类,如GoodsRepositoryCollection<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.classconfigurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);// 创建一个BeanDefinitionBuilderBeanDefinitionBuilder 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中获取beanDefinitionRootBeanDefinition beanDefinition = (RootBeanDefinition) definitionBuilder.getBeanDefinition();beanDefinition.setTargetType(getRepositoryFactoryBeanType(configuration));beanDefinition.setResourceDescription(configuration.getResourceDescription());// 获取bean的名称,如goodsRepositoryString 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的beanClassBeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName());builder.getRawBeanDefinition().setSource(configuration.getSource());// 构造方法参数值为接口名,如com.jingai.jpa.dao.GoodsRepositorybuilder.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.propertiesNamedQueriesBeanDefinitionBuilder 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.hqwc.cn/news/701634.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单用户模式破解root密码

目录 一. 破解root密码 1. 查看操作系统版本 2.重启系统&#xff0c;进入grub菜单&#xff0c;选择要使用的内核&#xff0c;按e进入​编辑 3. 找到linux16那一行&#xff0c;把光标移动到最后&#xff0c;添加 init/bin/sh 然后ctrlx保存退出会自动进入系统 4. 进入系统后…

C++ 并发编程指南(11)原子操作 | 11.6、计算机内存结构

文章目录 一、计算机内存结构1、内存的基本组成2、内存的类型3、内存的结构层次4、CPU架构5、局部性原理6、总结 前言 在探讨计算机的运行效率和数据处理能力时&#xff0c;内存结构无疑是一个至关重要的部分。内存&#xff0c;作为计算机系统中的关键组件&#xff0c;承担着存…

详细分清Session,Cookie和Token之间的区别,以及JWT是什么东西

Cookie Cookie是一种小型的文本文件&#xff0c;由网站在用户访问时存储在其计算机或移动设备上&#xff0c;Cookie主要用于跟踪、识别和存储有关用户的信息。 简单来说Cookie就是用来存储某些后端发送给前端的数据&#xff0c;例如我们登陆后&#xff0c;后端会返回一个登录…

网站有存在哪些类型的漏洞,网站漏洞存在哪些危害,该怎么解决网站漏洞问题

在数字化日益发展的今天&#xff0c;随着互联网的普及和深入&#xff0c;网站安全已成为企业、组织乃至个人都必须高度重视的安全问题。而网站漏洞作为威胁网站安全的重要因素之一&#xff0c;其类型多种多样&#xff0c;不仅可能导致数据泄露、系统崩溃&#xff0c;还可能为黑…

CVPR2022人脸识别Partial FC论文及代码学习笔记

论文链接&#xff1a;https://openaccess.thecvf.com/content/CVPR2022/papers/An_Killing_Two_Birds_With_One_Stone_Efficient_and_Robust_Training_CVPR_2022_paper.pdf 代码链接&#xff1a;insightface/recognition/arcface_torch at master deepinsight/insightface G…

量化研究---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…

BakedSDF: Meshing Neural SDFs for Real-Time View Synthesis 论文阅读

&#xff08;水一篇博客&#xff09; 项目主页 BakedSDF: Meshing Neural SDFs for Real-Time View Synthesis 作者介绍 是 Mildenhall 和 Barron 参与的工作&#xff08;都是谷歌的&#xff09;&#xff0c;同时一作是 Lipman 的学生&#xff0c;VolSDF 的一作。本文引用…

五分钟“手撕”时间复杂度与空间复杂度

目录 一、算法效率 什么是算法 如何衡量一个算法的好坏 算法效率 二、时间复杂度 时间复杂度的概念 大O的渐进表示法 推导大O阶方法 常见时间复杂度计算举例 三、空间复杂度 常见时间复杂度计算举例 一、算法效率 什么是算法 算法(Algorithm)&#xff1a;就是定…

24/05/14总结

签到2&#xff1a; 签到界面上有时间显示&#xff0c;签到码输入框&#xff0c;开始签到&#xff0c;当倒计时结束&#xff0c;老师端和学生端都会显示签到结果&#xff0c;所以签到结果需要建表&#xff1a;&#xff08;签到了的学生和未签到的学生&#xff0c; 这次签到的时间…

Elasticsearch优化手段

ES 的默认配置已经提供了良好的开箱即用的体验&#xff0c;但是仍有一些优化手段去继续提升它的使用性能。 一 General recommendations 通用建议。 01 Dont return large result sets 不要返回大量的结果集。ES 是一个搜索引擎&#xff0c;擅长于返回匹配度较高的几个文…

1.柔性数组

1.柔性数组 我们先来介绍一下什么是柔性数组&#xff1a; 在C语言中&#xff0c;柔性数组&#xff08;Flexible Array&#xff09;并不是一个标准的术语&#xff0c;但它通常指的是结构体中最后一个元素是一个没有指定大小的数组。这种结构体设计允许在运行时动态分配数组的大…

ES6之正则扩展

正则表达式扩展 u修饰符&#xff08;Unicode模式&#xff09;y修饰符&#xff08;Sticky或粘连模式&#xff09;s修饰符&#xff08;dotAll模式&#xff09;Unicode属性转义正则实例的flags属性字符串方法与正则表达式的整合 javascript的常用的正则表达式 验证数字邮箱验证手机…