SpringBoot 自動配置之Spring Data JPA


前言

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

我在實現JpaRepository接口時就有個疑問,那么實現類是什么?如果用過MyBatis肯定也知道,是接口和實現類之間有一個代理類專門來處理這塊的業務,那么JPA這塊是否也會有一個代理類來處理同樣的業務呢? 總體來說我們有兩個疑問,關鍵字分別是:接口實現類,代理類是什么

工作原理分析

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

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。
截屏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包下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM