SpringBoot 自動配置:Spring Data JPA

前言

不知道從啥時候開始項目上就一直用MyBatis,其實我我的更喜歡JPA些,由於JPA看起來OO的思想更強烈些,因此這才最近把JPA拿出來再看一看,使用起來也很簡單,除了定義Entity實體外,聲明本身的業務接口繼承JpaRepository接口,什麼邏輯也不用寫,基本的增刪改查,分頁,排序就都搞定了。java

我在實現JpaRepository接口時就有個疑問,那麼實現類是什麼?若是用過MyBatis確定也知道,是接口和實現類之間有一個代理類專門來處理這塊的業務,那麼JPA這塊是否也會有一個代理類來處理一樣的業務呢? 整體來講咱們有兩個疑問,關鍵字分別是:接口實現類,代理類是什麼mysql

工做原理分析

首先從spring-boot-autoconfiguration.jar中下的spring.factories中咱們能夠看到JPA的自動配置須要從JpaRepositoriesAutoConfiguration開始着手。 我先畫了一張總的Spring Data JPA自動配置流程圖,能夠有個大概的認識,下面會從源代碼層面再來讀一讀其工做原理,和關鍵代碼都分佈在那裏。
Spring Data JPA 自動配置.jpgspring

JpaRepositoriesAutoConfiguration 自動配置

由於咱們在pom中導入了spring-data-jpa.jar,數據庫驅動jar包爲系統默認jar,也就是說他們會出如今程序運行的classpath上,而且咱們在yml文件中配置了數據源,因此在springboot程序啓動中,springboot自動配置中關於JPA的自動配置就已經開始工做了,具體的自動配置類會從JpaRepositoriesAutoConfiguration開始。sql

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可能看起來有點眼熟哦。springboot

JpaRepositoryFactoryBean位於org.springframework.data.jpa.repository.support包下app

用戶自定義的JpaRepository做爲bean注入Spring容器中

JpaRepositoriesRegistrar中靜態內部類使用了@EnableJpaRepositories開啓JPA。若是不是SpringBoot項目中該註解是須要手動開啓。ide

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#registerBeanDefinitionsspring-boot

@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。
截屏2021-01-27 21.16.40.png
從上面兩段代碼分析來看,無論用戶建立多少個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;
	}

}

截屏2021-01-27 21.30.11.png
這段代碼咱們會關注兩個地方,一個是構造函數,構造函數的參數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的代理對象。

截屏2021-01-27 22.47.15.png

總結

上面我本身提到的兩個問題,到了這裏咱們就有一個明確的答案了,首先回答代理是什麼,從上面調試代碼能夠看出來repository的h屬性是JdkDynamicAopProxy對象。當程序執行的時候會經過調用JdkDynamicAopProxy.invoke(),好比說調用JpaRepository.findAll(), 代理對象的建立邏輯都隱藏在JdkDynamicAopProxy中,而在這裏這個代理對象就是SimpleJpaRepository對象,也是你的自定義JpaRepository的實現類。

SimpleJpaRepository對象位於 org.springframework.data.jpa.repository.support包下。

相關文章
相關標籤/搜索