前言
不知道從啥時候開始項目上就一直用MyBatis,其實我個人更喜歡JPA些,因為JPA看起來OO的思想更強烈些,所以這才最近把JPA拿出來再看一看,使用起來也很簡單,除了定義Entity實體外,聲明自己的業務接口繼承JpaRepository接口,什么邏輯也不用寫,基本的增刪改查,分頁,排序就都搞定了。
我在實現JpaRepository接口時就有個疑問,那么實現類是什么?如果用過MyBatis肯定也知道,是接口和實現類之間有一個代理類專門來處理這塊的業務,那么JPA這塊是否也會有一個代理類來處理同樣的業務呢? 總體來說我們有兩個疑問,關鍵字分別是:接口實現類,代理類是什么。
工作原理分析
首先從spring-boot-autoconfiguration.jar中下的spring.factories中我們可以看到JPA的自動配置需要從JpaRepositoriesAutoConfiguration開始着手。 我先畫了一張總的Spring Data JPA自動配置流程圖,可以有個大概的認識,下面會從源代碼層面再來讀一讀其工作原理,和關鍵代碼都分布在那里。
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。
從上面兩段代碼分析來看,不管用戶創建多少個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;
}
}
這段代碼我們會關注兩個地方,一個是構造函數,構造函數的參數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的代理對象。
總結
上面我自己提到的兩個問題,到了這里我們就有一個明確的答案了,首先回答代理是什么,從上面調試代碼可以看出來repository的h屬性是JdkDynamicAopProxy對象。當程序執行的時候會通過調用JdkDynamicAopProxy.invoke(),比如說調用JpaRepository.findAll(), 代理對象的創建邏輯都隱藏在JdkDynamicAopProxy中,而在這里這個代理對象就是SimpleJpaRepository對象,也是你的自定義JpaRepository的實現類。
SimpleJpaRepository對象位於 org.springframework.data.jpa.repository.support包下。