不知道從啥時候開始項目上就一直用MyBatis,其實我我的更喜歡JPA些,由於JPA看起來OO的思想更強烈些,因此這才最近把JPA拿出來再看一看,使用起來也很簡單,除了定義Entity實體外,聲明本身的業務接口繼承JpaRepository接口,什麼邏輯也不用寫,基本的增刪改查,分頁,排序就都搞定了。java
我在實現JpaRepository接口時就有個疑問,那麼實現類是什麼?若是用過MyBatis確定也知道,是接口和實現類之間有一個代理類專門來處理這塊的業務,那麼JPA這塊是否也會有一個代理類來處理一樣的業務呢? 整體來講咱們有兩個疑問,關鍵字分別是:接口實現類,代理類是什麼。mysql
首先從spring-boot-autoconfiguration.jar中下的spring.factories中咱們能夠看到JPA的自動配置須要從JpaRepositoriesAutoConfiguration開始着手。 我先畫了一張總的Spring Data JPA自動配置流程圖,能夠有個大概的認識,下面會從源代碼層面再來讀一讀其工做原理,和關鍵代碼都分佈在那裏。
spring
由於咱們在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
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。
從上面兩段代碼分析來看,無論用戶建立多少個JpaRepository,最終注入Spring容器的bean都是來自JpaRepositoryFactoryBean工廠來建立。每一個開發人員自定義的JpqRepository又都是針對不一樣的領域模型的,好比說UserRepository,OrderRepository,OrderLineItemRepository。
@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; } }
這段代碼咱們會關注兩個地方,一個是構造函數,構造函數的參數repositoryInterface就是用戶自定義的接口,一個是createRepositoryFactory(),Spring要建立JpaRepository的實現類,會先建立一個JpaRepositoryFactory,而後具體接口的實現類,或者叫作代理會交給該工廠類實現。
public class JpaRepositoryFactory extends RepositoryFactorySupport { }
JpaRepositoryFactory繼承了抽象類RepositoryFactorySupport,而RepositoryFactorySupport又實現了兩個Spring接口BeanClassLoaderAware,BeanFactoryAware。
RepositoryFactorySupport位於spring-data-common.jar內。
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的代理對象。
上面我本身提到的兩個問題,到了這裏咱們就有一個明確的答案了,首先回答代理是什麼,從上面調試代碼能夠看出來repository的h屬性是JdkDynamicAopProxy對象。當程序執行的時候會經過調用JdkDynamicAopProxy.invoke(),好比說調用JpaRepository.findAll(), 代理對象的建立邏輯都隱藏在JdkDynamicAopProxy中,而在這裏這個代理對象就是SimpleJpaRepository對象,也是你的自定義JpaRepository的實現類。
SimpleJpaRepository對象位於 org.springframework.data.jpa.repository.support包下。