5

SpringBoot 自动配置:Spring Data JPA

 3 years ago
source link: http://www.cnblogs.com/sword-successful/p/14337507.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

前言

不知道从啥时候开始项目上就一直用MyBatis,其实我个人更新JPA些,因为JPA看起来OO的思想更强烈些,所以这才最近把JPA拿出来再看一看,使用起来也很简单,除了定义Entity实体外,声明自己的业务接口继承JpaRepository接口,什么逻辑也不用写,基本的增删改查,分页,排序就都搞定了。

我在实现JpaRepository接口时就有个疑问,那么实现类是什么?如果用过MyBatis肯定也知道,是接口和实现类之间有一个代理类专门来处理这块的业务,那么JPA这块是否也会有一个代理类来处理同样的业务呢? 总体来说我们有两个疑问,关键字分别是: 接口实现类,代理类是什么

工作原理分析

首先从spring-boot-autoconfiguration.jar中下的spring.factories中我们可以看到JPA的自动配置需要从JpaRepositoriesAutoConfiguration开始着手。 我先画了一张总的Spring Data JPA自动配置流程图,可以有个大概的认识,下面会从源代码层面再来读一读其工作原理,和关键代码都分布在那里。

An6vM3Z.jpg!mobile

JpaRepositoriesAutoConfiguration 自动配置

因为我们在pom中导入了spring-data-jpa.jar,数据库驱动jar包为系统默认jar,也就是说他们会出现在程序运行的classpath上,并且我们在yml文件中配置了数据源,所以在springboot程序启动中,springboot自动配置中关于JPA的自动配置就已经开始工作了,具体的自动配置类会从JpaRepositoriesAutoConfiguration开始。

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3307/readinglist?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval\
  =true
spring.datasource.username=root
spring.datasource.password=000
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

从代码中可以看到JPA的默认实现是Hibernate,所以会先配置HibernateJpaAutoConfiguration,并且是在DataSource bean已经存在的情况下。

@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class, JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true",
		matchIfMissing = true)
//导入JpaRepositoriesRegistrar
@Import(JpaRepositoriesRegistrar.class)
//先自动配置HibernateJpaAutoConfiguration, TaskExecutionAutoConfiguration
@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class, TaskExecutionAutoConfiguration.class })
public class JpaRepositoriesAutoConfiguration {
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ LocalContainerEntityManagerFactoryBean.class, EntityManager.class, SessionImplementor.class })
@EnableConfigurationProperties(JpaProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class })
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {

}

这里你首先会看到必须是DataSource bean存在的情况下,其次还有一个关键信息就是不存在JpaRepositoryFactoryBean bean的情况下才会执行该自动配置,也就是说如果你想根据自己的业务重新实现一个FactoryBean,那么该自动配置则不会执行。 那么看起来JpaRepositoryFactoryBean可能看起来有点眼熟哦。

JpaRepositoryFactoryBean位于org.springframework.data.jpa.repository.support包下

用户自定义的JpaRepository作为bean注入Spring容器中

JpaRepositoriesRegistrar中静态内部类使用了@EnableJpaRepositories开启JPA。如果不是SpringBoot项目中该注解是需要手动开启。

class JpaRepositoriesRegistrar extends AbstractRepositoryConfigurationSourceSupport {

	@EnableJpaRepositories
	private static class EnableJpaRepositoriesConfiguration {

	}

}

JpaRepositoriesRegistrar又继承了抽象类AbstractRepositoryConfigurationSourceSupport类。这是一个ImportBeanDefinitionRegistrar,设计目的就是在SpringBoot自动发现机制中发现用户自定义的JpaRepository。在Spring容器启动中,该ImportBeanDefinitionRegistrar就会执行。

public abstract class AbstractRepositoryConfigurationSourceSupport
    implements ImportBeanDefinitionRegistrar, BeanFactoryAware, ResourceLoaderAware, EnvironmentAware {
}
在AbstractRepositoryConfigurationSourceSupport类中重写了registerBeanDefinitions方法,这个方法里又把实例化的任务交给了RepositoryConfigurationDelegate#registerRepositoriesIn()。
AbstractRepositoryConfigurationSourceSupport#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
			BeanNameGenerator importBeanNameGenerator) {
   RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(
				getConfigurationSource(registry, importBeanNameGenerator), this.resourceLoader, this.environment);
   delegate.registerRepositoriesIn(registry, getRepositoryConfigurationExtension());
}

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

    public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension) {
       
        extension.registerBeansForRoot(registry, this.configurationSource);
        RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, this.configurationSource, this.resourceLoader, this.environment);
        List<BeanComponentDefinition> definitions = new ArrayList();
        StopWatch watch = new StopWatch();
        
        watch.start();
        //extension.getRepositoryConfigurations() 会扫描相应的包并找到用户自定义JpaRepository接口
        Collection<RepositoryConfiguration<RepositoryConfigurationSource>> configurations = extension.getRepositoryConfigurations(this.configurationSource, this.resourceLoader, this.inMultiStoreMode);
        Map<String, RepositoryConfiguration<?>> configurationsByRepositoryName = new HashMap(configurations.size());
        Iterator var8 = configurations.iterator();

        while(var8.hasNext()) {
            RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration = (RepositoryConfiguration)var8.next();
            configurationsByRepositoryName.put(configuration.getRepositoryInterface(), configuration);

            //对于每个扫描找到的用户自定义JpaRepository,构建一个BeanDefinitionBuilder,
		    //就是在这个步骤中将该BeanDefinition同JpaRepositoryFactoryBean建立关系
            BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
            extension.postProcess(definitionBuilder, this.configurationSource);
            if (this.isXml) {
                extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource)this.configurationSource);
            } else {
                extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource)this.configurationSource);
            }

            //这里根据所发现的用户自定义JpaRepository接口的名字构造一个bean名称
            AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
            beanDefinition.setResourceDescription(configuration.getResourceDescription());
            String beanName = this.configurationSource.generateBeanName(beanDefinition);
            if (logger.isTraceEnabled()) {
                logger.trace(LogMessage.format("Spring Data %s - Registering repository: %s - Interface: %s - Factory: %s", extension.getModuleName(), beanName, configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName()));
            }

            //设置当前BeanDefinition的属性factoryBeanObjectType为用户自定义JpaRepository接口的全限定名
            beanDefinition.setAttribute("factoryBeanObjectType", configuration.getRepositoryInterface());

            // 现在把这个bean注册到容器
            registry.registerBeanDefinition(beanName, beanDefinition);
            definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
        }

        potentiallyLazifyRepositories(configurationsByRepositoryName, registry, this.configurationSource.getBootstrapMode());
        watch.stop();
        
        return definitions;
    }

}
RepositoryBeanDefinitionBuilder#build
public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(configuration.getRepositoryFactoryBeanClassName());
    builder.getRawBeanDefinition().setSource(configuration.getSource());
    builder.addConstructorArgValue(configuration.getRepositoryInterface());
    builder.addPropertyValue("queryLookupStrategyKey", configuration.getQueryLookupStrategyKey());
    builder.addPropertyValue("lazyInit", configuration.isLazyInit());
    builder.setLazyInit(configuration.isLazyInit());
    builder.setPrimary(configuration.isPrimary());
    configuration.getRepositoryBaseClassName().ifPresent((it) -> {
        builder.addPropertyValue("repositoryBaseClass", it);
    });
    NamedQueriesBeanDefinitionBuilder definitionBuilder = new NamedQueriesBeanDefinitionBuilder(this.extension.getDefaultNamedQueryLocation());
    configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations);
    builder.addPropertyValue("namedQueries", definitionBuilder.build(configuration.getSource()));
    this.registerCustomImplementation(configuration).ifPresent((it) -> {
        builder.addPropertyReference("customImplementation", it);
        builder.addDependsOn(it);
    });
    BeanDefinitionBuilder fragmentsBuilder = BeanDefinitionBuilder.rootBeanDefinition(RepositoryFragmentsFactoryBean.class);
    List<String> fragmentBeanNames = (List)this.registerRepositoryFragmentsImplementation(configuration).map(RepositoryFragmentConfiguration::getFragmentBeanName).collect(Collectors.toList());
    fragmentsBuilder.addConstructorArgValue(fragmentBeanNames);
    builder.addPropertyValue("repositoryFragments", ParsingUtils.getSourceBeanDefinition(fragmentsBuilder, configuration.getSource()));
    return builder;
}

我在builder()里调试了一下代码,程序执行到该方法倒数第二行代码时,可以BeanDefinitionBuilder#beanClass就是org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean。

7RrMN3e.png!mobile

从上面两段代码分析来看,不管用户创建多少个JpaRepository,最终注入Spring容器的bean都是来自JpaRepositoryFactoryBean工厂来创建。每个开发人员自定义的JpqRepository又都是针对不同的领域模型的,比如说UserRepository,OrderRepository,OrderLineItemRepository。

使用用户自定义的JpaRepository

@Autowired
private BookRepository bookRepository;

当你定义了的BookRepository后,在使用时又是如何从Spring容器中获取bean的。上面既然说了BeanDefinitionBuilder会和JpaRepositoryFactoryBean建立联系,那我们还是从JpaRepositoryFactoryBean入手。

public class JpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
		extends TransactionalRepositoryFactoryBeanSupport<T, S, ID> {

	//构造函数,这里repositoryInter就是你自定义的JpaRepository
	public JpaRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
		super(repositoryInterface);
	}

	@Override
	protected RepositoryFactorySupport doCreateRepositoryFactory() {

		Assert.state(entityManager != null, "EntityManager must not be null!");

		return createRepositoryFactory(entityManager);
	}

    //这个方法会返回JpaRepositoryFactory
	protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

		JpaRepositoryFactory jpaRepositoryFactory = new JpaRepositoryFactory(entityManager);
		jpaRepositoryFactory.setEntityPathResolver(entityPathResolver);
		jpaRepositoryFactory.setEscapeCharacter(escapeCharacter);

		if (queryMethodFactory != null) {
			jpaRepositoryFactory.setQueryMethodFactory(queryMethodFactory);
		}

		return jpaRepositoryFactory;
	}

}

mEJfUfb.png!mobile 这段代码我们会关注两个地方,一个是构造函数,构造函数的参数repositoryInterface就是用户自定义的接口,一个是createRepositoryFactory(),Spring要创建JpaRepository的实现类,会先创建一个JpaRepositoryFactory,然后具体接口的实现类,或者叫做代理会交给该工厂类实现。

public class JpaRepositoryFactory extends RepositoryFactorySupport {

}

JpaRepositoryFactory继承了抽象类RepositoryFactorySupport,而RepositoryFactorySupport又实现了两个Spring接口BeanClassLoaderAware,BeanFactoryAware。

RepositoryFactorySupport位于spring-data-common.jar内。

RepositoryFactorySupport#getRepository

public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
        
     //repositoryInterface为用户自定义的JpaRepository,这里为BookRepository。
     RepositoryMetadata metadata = this.getRepositoryMetadata(repositoryInterface);
     RepositoryComposition composition = this.getRepositoryComposition(metadata, fragments);
     RepositoryInformation information = this.getRepositoryInformation(metadata, composition);
     this.validate(information, composition);
     //target为SimpleJpaRepository。
     Object target = this.getTargetRepository(information);
     ProxyFactory result = new ProxyFactory();
     result.setTarget(target);
     result.setInterfaces(new Class[]{repositoryInterface, Repository.class, TransactionalProxy.class});
     if (MethodInvocationValidator.supports(repositoryInterface)) {
         result.addAdvice(new MethodInvocationValidator());
     }

     result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
     this.postProcessors.forEach((processor) -> {
         processor.postProcess(result, information);
     });
     if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
         result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
     }

     ProjectionFactory projectionFactory = this.getProjectionFactory(this.classLoader, this.beanFactory);
     Optional<QueryLookupStrategy> queryLookupStrategy = this.getQueryLookupStrategy(this.queryLookupStrategyKey, this.evaluationContextProvider);
     result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory, queryLookupStrategy, this.namedQueries, this.queryPostProcessors, this.methodInvocationListeners));
     composition = composition.append(RepositoryFragment.implemented(target));
     result.addAdvice(new RepositoryFactorySupport.ImplementationMethodExecutionInterceptor(information, composition, this.methodInvocationListeners));
     //repository为SimpleJpaRepository
     T repository = result.getProxy(this.classLoader);

     return repository;
 }

这是一个关键方法,this.getTargetRepository会创建一个SimpleJpaRepository对象。该对象知道自己具体操作那个领域对象,随后又基于此类创建一个代理对象,设置Interceptor对象后返回该代理对象。 当真正的SimpleJpaRepository代理对象被创建之后,包裹该对象的JpaRepositoryFactoryBean对象就是我们最终要使用bean的FactoryBean,Spring容器中,用户自定义的bean保存的实际上是一个JpaRepositoryFactoryBean。

@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
     //这里就是我们常用的CURD方法了,终于看到了庐山真面目。
}

综上所述,注入bean实例化过程就结束了,可以进行注入了,根据上面的分析,每个用户自定义的JpaRepository实际上在Spring容器中保存的是一个JpaRepositoryFactoryBean,这是一个FactoryBean。当对JpaRepository进行注入并调用时会FactoryBean#getObject()获取要调用SimpleJpaRepository的代理对象。

rYbqYjy.png!mobile

总结

上面我自己提到的两个问题,到了这里我们就有一个明确的答案了,首先回答代理是什么,从上面调试代码可以看出来repository的h属性是JdkDynamicAopProxy对象。当程序执行的时候会通过调用JdkDynamicAopProxy.invoke(),比如说调用JpaRepository.findAll(), 代理对象的创建逻辑都隐藏在JdkDynamicAopProxy中,而在这里这个代理对象就是 SimpleJpaRepository 对象,也是你的自定义JpaRepository的实现类。

SimpleJpaRepository对象位于 org.springframework.data.jpa.repository.support包下。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK