spring-data-JPA 是如何启动的

spring-data-JPA源码部分有两个很重要的部分:1、识别repositories接口 2、将接口添加代理实现类并托管spring管理。
目的是将范围内的接口准备作为springbean进行处理(有beanFactory辅助)

如果在启动类上添加了@EnableJpaRepositories注解则我们按照如下思路分析

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(JpaRepositoriesRegistrar.class)//引入了JpaRepositoriesRegistrar
public @interface EnableJpaRepositories {
}

JpaRepositoriesRegistrar继承了RepositoryBeanDefinitionRegistrarSupport并最终继承了ImportBeanDefinitionRegistrar的重要方法registerBeanDefinitions。JpaRepositoriesRegistrar这个类本身指定好EnableJpaRepositories注解以及给出Extension,在Extension中主要的作用是限定本次注册的factorybean是JpaRepositoryFactoryBean

@Override
protected Class<? extends Annotation> getAnnotation() {
return EnableJpaRepositories.class;
}

/* 
* (non-Javadoc)
* @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getExtension()
*/
@Override
protected RepositoryConfigurationExtension getExtension() {
return new JpaRepositoryConfigExtension();
}

ImportBeanDefinitionRegistrar的作用是 是把目标点类或者annotation注册成bean。

public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
        Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
        Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
        // Guard against calls for sub-classes
        if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) {
            return;
        }
        AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource(
                annotationMetadata, getAnnotation(), resourceLoader, environment, registry);
        RepositoryConfigurationExtension extension = getExtension();
        RepositoryConfigurationUtils.exposeRegistration(extension, registry, configurationSource);
        RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader,
                environment);
        //主要是这里面,Registers the found repositories in the given 根据给定的内容注册beans
        delegate.registerRepositoriesIn(registry, extension);
    }

具体分析代码

public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
      RepositoryConfigurationExtension extension) {
   extension.registerBeansForRoot(registry, configurationSource);
   RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
         environment);
   List<BeanComponentDefinition> definitions = new ArrayList<>();
   if (LOG.isDebugEnabled()) {
      LOG.debug("Scanning for repositories in packages {}.",
            configurationSource.getBasePackages().stream().collect(Collectors.joining(", ")));
   }
   //extension.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)
   //这行代码能够扫描出所有的repository接口,下面是跟踪代码记录点
   //org.springframework.data.repository.config.RepositoryConfigurationExtension#getRepositoryConfigurations(T, org.springframework.core.io.ResourceLoader, boolean)
   //org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getRepositoryConfigurations(T, org.springframework.core.io.ResourceLoader, boolean)
   //org.springframework.data.repository.config.RepositoryConfigurationSource#getCandidates
   //org.springframework.data.repository.config.RepositoryConfigurationSourceSupport#getCandidates 这里获得了所有符合条件的repository接口,向下看
   for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
         .getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {
             //这里通过BeanDefinitionBuilder注册的bean是JpaRepositoryFactoryBean,这个bean不是最后被代理的Repository接口
             //注册bean的方式是spring的方式,把bean的定义信息加入定义列表,初始化bean时会自动创建JpaRepositoryFactoryBean,并执行afterPropertiesSet方法
             //(实现了InitializingBean接口),beanfactory的目的最终是创建bean的
             //spring启动的第一步也是初始化BeanFactory
             //Spring将配置文件(或者注解描述的信息)的信息解析成为一个个的BeanDefinition对象并装入到容器的Bean定义注册表(BeanDefinitionRegistry)中,但此时Bean还未初始化
      BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
      extension.postProcess(definitionBuilder, configurationSource);
      if (isXml) {
         extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
      } else {
         extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
      }
      AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
      String beanName = configurationSource.generateBeanName(beanDefinition);
      if (LOG.isDebugEnabled()) {
         LOG.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName, configuration.getRepositoryInterface(),
               configuration.getRepositoryFactoryBeanClassName());
      }
      beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
      registry.registerBeanDefinition(beanName, beanDefinition);
      definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
   }
   if (LOG.isDebugEnabled()) {
      LOG.debug("Finished repository scanning.");
   }
   return definitions;
}

RepositoryConfigurationSourceSupport
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
        //这个构造方法要去把扫描过滤器构建好 下看
        RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
        scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
        scanner.setEnvironment(environment);
        scanner.setResourceLoader(loader);
        getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));
        return Streamable.of(() -> getBasePackages().stream()//
                .flatMap(it -> scanner.findCandidateComponents(it).stream()));
    //scanner.findCandidateComponents(it)发现候选人
    //org.springframework.data.repository.config.RepositoryComponentProvider#findCandidateComponents
    //org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#findCandidateComponents
    //org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
    //这里面有判断if (isCandidateComponent(metadataReader)) { 根据过滤器选择class
    //Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); 所有的类
    //第二次才进来根据下面的过滤器选择了repostory接口
}
//过滤器
//repository的规则
//构建扫描过滤器
//是Repository的接口 new InterfaceTypeFilter(Repository.class)
//有RepositoryDefinition注解 new AnnotationTypeFilter(RepositoryDefinition.class, true, true)
//排除NoRepositoryBean注解 addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
public RepositoryComponentProvider(Iterable<? extends TypeFilter> includeFilters, BeanDefinitionRegistry registry) {

   super(false);

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

   this.registry = registry;

   if (includeFilters.iterator().hasNext()) {
      for (TypeFilter filter : includeFilters) {
         addIncludeFilter(filter);
      }
   } else {
      super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
      super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
   }
   addExcludeFilter(new AnnotationTypeFilter(NoRepositoryBean.class));
}

至此,我们仅仅做到了扫描并识别出所有集成了Repository接口的JPA接口,并吧他们作为JpaRepositoryFactoryBean进行注册(初始化),但是不要指望在这里找到实现JPA接口代理实现类

自动生成代理Bean实现,并托管spring管理.实例化JpaRepositoryFactoryBean时,由于接口实现了InitializingBean接口,FactoryBean初始化后执行了afterPropertiesSet,FactoryBean和bean是一整套的springbean生成体系

public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
      implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
      BeanFactoryAware, ApplicationEventPublisherAware {
    //这个成员变量存储了要被自动生成实现的repository接口(比如coffeeRepostory)
   private final Class<? extends T> repositoryInterface;
//org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean#afterPropertiesSet
public void afterPropertiesSet() {
   this.factory = createRepositoryFactory();
   this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
   this.factory.setNamedQueries(namedQueries);
   this.factory.setEvaluationContextProvider(evaluationContextProvider);
   this.factory.setBeanClassLoader(classLoader);
   this.factory.setBeanFactory(beanFactory);
   if (publisher != null) {
      this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
   }
   repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
   RepositoryFragments customImplementationFragment = customImplementation //
         .map(RepositoryFragments::just) //
         .orElseGet(RepositoryFragments::empty);
   RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
         .orElseGet(RepositoryFragments::empty) //
         .append(customImplementationFragment);
   this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
   // Make sure the aggregate root type is present in the MappingContext (e.g. for auditing)
   this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));
   //这里将代理生成接口的实现
   this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
   if (!lazyInit) {
      this.repository.get();
   }
}


public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
   if (LOG.isDebugEnabled()) {
      LOG.debug("Initializing repository instance for {}…", repositoryInterface.getName());
   }
   Assert.notNull(repositoryInterface, "Repository interface must not be null!");
   Assert.notNull(fragments, "RepositoryFragments must not be null!");
   RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
   RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
   RepositoryInformation information = getRepositoryInformation(metadata, composition);
   validate(information, composition);
   //这里返回了SimpleJpaRepository
   Object target = getTargetRepository(information);
   // 开始创建代理
   ProxyFactory result = new ProxyFactory();
   result.setTarget(target);//设置实现实体,这个实体类就是SimpleJpaRepository
   result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
   if (MethodInvocationValidator.supports(repositoryInterface)) {
      result.addAdvice(new MethodInvocationValidator());
   }
  //这些切点实现了接口中方法的执行!
   result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
   result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
   postProcessors.forEach(processor -> processor.postProcess(result, information));
   result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
   ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
   result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));
   composition = composition.append(RepositoryFragment.implemented(target));
   result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
   //生成代理类
   T repository = (T) result.getProxy(classLoader);
   if (LOG.isDebugEnabled()) {
      LOG.debug("Finished creation of repository instance for {}.", repositoryInterface.getName());
   }
   return repository;
}

没有显示加@EnableJpaRepositories注解,JPA如何自动配置
spring-boot-autoconfigure包内的spring.factories文件内容,包含了JPA的,解释了为什么没有添加@EnableJpaRepositories仍然能用jpa

//JpaRepositoriesAutoConfiguration
@Configuration
    @ConditionalOnBean(DataSource.class) //当有DataSource的bean
@ConditionalOnClass(JpaRepository.class)//当classpath中有JpaRepository这个类
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, //没有加载过JpaRepositoryFactoryBean、 JpaRepositoryConfigExtension两个bean
      JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)//有这个配置“spring.data.jpa.repositories.enabled=true”但不做强制检查
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
//看这个类JpaRepositoriesAutoConfigureRegistrar -> AbstractRepositoryConfigurationSourceSupport#registerBeanDefinitions 熟悉的方法
//new RepositoryConfigurationDelegate(getConfigurationSource(registry),
 //     this.resourceLoader, this.environment).registerRepositoriesIn(registry,
  //          getRepositoryConfigurationExtension());
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {

}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容