下一篇: spring jpa 源码学习(2)—调用流程简介
写在前边:
作为mybatis的拥护者,一直对于jpa、hibernate这种对象全映射的orm框架不甚理睬.但难免会有工作需要必须使用的时候,所以抽时间看了下相关实现,还是在帮助自己加深理解orm模型上有很大帮助的.
分析
- 启动流程
-
先根据springboot的自动加载机制进行加载jps自动化配置
image.png
-
看下JpaRepositoriesAutoConfiguration类
@Configuration
@ConditionalOnBean(DataSource.class)
@ConditionalOnClass(JpaRepository.class)
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
JpaRepositoryConfigExtension.class })
@ConditionalOnProperty(prefix = "spring.data.jpa.repositories", name = "enabled", havingValue = "true", matchIfMissing = true)
@Import(JpaRepositoriesAutoConfigureRegistrar.class)
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
public class JpaRepositoriesAutoConfiguration {
}
-
根据spring的import标签,我们找到JpaRepositoriesAutoConfigureRegistrar此注册类,进行respositor的注册.看下此类的bean结构
image.png
我们看到了ImportBeanDefinitionRegistrar这个熟悉的spring中bean注册类,此类的作用和BeanDefinitionRegistryPostProcessor类似,都是可以自定义解析生成自己的bean定义,其核心方法是
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
看下jpa中的实现:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
new RepositoryConfigurationDelegate(getConfigurationSource(registry),
this.resourceLoader, this.environment).registerRepositoriesIn(registry,
getRepositoryConfigurationExtension());
}
看到RepositoryConfigurationDelegate类,'Delegate'明显的委派器模式,其中getConfigurationSource(registry)方法可以跟进去看到
return new AnnotationRepositoryConfigurationSource(metadata, getAnnotation(),
this.resourceLoader, this.environment, beanDefinitionRegistry) {
@Override
public Streamable<String> getBasePackages() {
return AbstractRepositoryConfigurationSourceSupport.this
.getBasePackages();
}
};
此处getBasePackages获取所有spring环境加载的包路径,看下最后调用BasePackages类的get方法,获取spring的扫描的包路径(spring的bean加载也依赖于此处),如我的demo为springboot环境,basepackage就是启动类的同级目录(常识,不了解的同学可脑补下哈)
static final class BasePackages {
private final List<String> packages;
private boolean loggedBasePackageInfo;
BasePackages(String... names) {
List<String> packages = new ArrayList<>();
for (String name : names) {
if (StringUtils.hasText(name)) {
packages.add(name);
}
}
this.packages = packages;
}
public List<String> get() {
if (!this.loggedBasePackageInfo) {
if (this.packages.isEmpty()) {
if (logger.isWarnEnabled()) {
logger.warn("@EnableAutoConfiguration was declared on a class "
+ "in the default package. Automatic @Repository and "
+ "@Entity scanning is not enabled.");
}
}
else {
if (logger.isDebugEnabled()) {
String packageNames = StringUtils
.collectionToCommaDelimitedString(this.packages);
logger.debug("@EnableAutoConfiguration was declared on a class "
+ "in the package '" + packageNames
+ "'. Automatic @Repository and @Entity scanning is "
+ "enabled.");
}
}
this.loggedBasePackageInfo = true;
}
return this.packages;
}
}
还有一个比较重要点是getRepositoryConfigurationExtension()
此处采用一种扩展方式,方便以后不同的扩展点实现,此处默认返回的是在JpaRepositoriesAutoConfigureRegistrar定义的JpaRepositoryConfigExtension,jpa扩展点
@Override
protected RepositoryConfigurationExtension getRepositoryConfigurationExtension() {
return new JpaRepositoryConfigExtension();
}
看下JpaRepositoryConfigExtension的部分代码
@Override
public String getRepositoryFactoryBeanClassName() {
return JpaRepositoryFactoryBean.class.getName();
}
返回的是JpaRepositoryFactoryBean此工厂类,此类为生成所有dao层接口代理类的核心工厂.
- 下面进入核心的委派器RepositoryConfigurationDelegate,其核心方法:
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
extension.registerBeansForRoot(registry, configurationSource);
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
//1.核心点,获取所有需要注册点dao接口定义,创建bean定义
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {
//2.类定义构造器
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
String beanName = configurationSource.generateBeanName(beanDefinition);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName,
configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName());
}
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
//3.制定beanFactory类,放入spring环境中
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
return definitions;
}
如上,我标注了三部分核心代码,我们简单分析下:
==第一处:extension.getRepositoryConfigurations(configurationSource,resourceLoader, inMultiStoreMode)==
核心含义是找到所有候选的dao接口对象,其中有个for循环有必要说一下:
==for (BeanDefinition candidate : configSource.getCandidates(loader))==
这里循环的是什么呢?看下 configSource.getCandidates()-->获取所有需要加载的候选者定义,
public Streamable<BeanDefinition> getCandidates(ResourceLoader loader) {
RepositoryComponentProvider scanner = new RepositoryComponentProvider(getIncludeFilters(), registry);
scanner.setConsiderNestedRepositoryInterfaces(shouldConsiderNestedRepositories());
scanner.setEnvironment(environment);
scanner.setResourceLoader(loader);
getExcludeFilters().forEach(it -> scanner.addExcludeFilter(it));
return Streamable.of(() -> getBasePackages().stream()//
.flatMap(it -> scanner.findCandidateComponents(it).stream()));
}
看出RepositoryComponentProvider这个scanner负责扫描所有类,其内部添加了相关的filter
if (includeFilters.iterator().hasNext()) {
for (TypeFilter filter : includeFilters) {
addIncludeFilter(filter);
}
} else {
super.addIncludeFilter(new InterfaceTypeFilter(Repository.class));
super.addIncludeFilter(new AnnotationTypeFilter(RepositoryDefinition.class, true, true));
}
核心逻辑是添加注解和Repository接口定义两个过滤器,然后从classpath环境中找到所有候选类.
==第二处:builder.build(configuration)==
此处将会将上一步获取到到接口类型包装成jpa工厂类到bean定义,如下:
public BeanDefinitionBuilder build(RepositoryConfiguration<?> configuration) {
.....
//此处获取到factorybeanName根据前面到扩展点中定义为JpaRepositoryFactoryBean
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());
这样上一步获取到到接口类信息,都转换为JpaRepositoryFactoryBean对应到工厂bean定义.
==第三处:将刚才生成到所有工厂bean定义放入spring注册工厂中,等待spring调用==
-
下面逻辑将会进去JpaRepositoryFactoryBean类中,闯进dao接口的真正代理类,先看下此类的依赖关系:
image.png
可以看到熟悉的InitializingBean、FactoryBean、BeanFactoryAware..等spring等扩展类,根据InitializingBean等特性,我们重点看afterPropertiesSet方法
public void afterPropertiesSet() {
//1.创建repositoryFactory,后续等实例bean由此工厂创造
this.factory = createRepositoryFactory();
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(evaluationContextProvider);
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
RepositoryFragments customImplementationFragment = customImplementation //
.map(RepositoryFragments::just) //
.orElseGet(RepositoryFragments::empty);
RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments //
.orElseGet(RepositoryFragments::empty) //
.append(customImplementationFragment);
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
//2.创建对应的respository
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
if (!lazyInit) {
this.repository.get();
}
}
跟下这段代码
1. createRepositoryFactory()
创建repository工厂,看下核心逻辑,最后返回JpaRepositoryFactory工厂:
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new JpaRepositoryFactory(entityManager);
}
返回工厂类,核心的实现类entityManager也在此时注入,看下这个的图谱

其中最底层的SessionImpl是我们比较直观的一个实现,但实际常用的我们会发现其实是通过SharedEntityManagerCreator创建对应的代理类实现
public static EntityManager createSharedEntityManager(EntityManagerFactory emf, @Nullable Map<?, ?> properties,
boolean synchronizedWithTransaction, Class<?>... entityManagerInterfaces) {
ClassLoader cl = null;
if (emf instanceof EntityManagerFactoryInfo) {
cl = ((EntityManagerFactoryInfo) emf).getBeanClassLoader();
}
Class<?>[] ifcs = new Class<?>[entityManagerInterfaces.length + 1];
System.arraycopy(entityManagerInterfaces, 0, ifcs, 0, entityManagerInterfaces.length);
ifcs[entityManagerInterfaces.length] = EntityManagerProxy.class;
return (EntityManager) Proxy.newProxyInstance(
(cl != null ? cl : SharedEntityManagerCreator.class.getClassLoader()),
ifcs, new SharedEntityManagerInvocationHandler(emf, properties, synchronizedWithTransaction));
}
这块逻辑细扣的话就是hibernate对注解的@Entity(name="role")的相关实现,我们先看主线,此时代理类生成的就是invokeHandler是SharedEntityManagerInvocationHandler,所以实际sql执行的时候,最后都是从此handler进行执行的,具体在后面会做介绍,我们先把此afterPropertiesSet分析完
- this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse))-->去创建对应的实现方法(当然这里有Lazy类,对应this.repository.get()方法才会真正调用,相当于实现了懒加载逻辑),看下核心代码,代码主要做了
- 获取默认的SimpleJpaRepository类,然后对其加强,生成代理类,此类中jpa通用的方法都以包括在内.
- 生成对SimpleJpaRepository代理类,然后进行各种加强
- 重点QueryExecutorMethodInterceptor将dao的每一个方法转化为Query类,对用户自定义类要进行包装增强
public <T> T getRepository(Class<T> repositoryInterface, RepositoryFragments fragments) {
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
Assert.notNull(fragments, "RepositoryFragments must not be null!");
RepositoryMetadata metadata = getRepositoryMetadata(repositoryInterface);
RepositoryComposition composition = getRepositoryComposition(metadata, fragments);
RepositoryInformation information = getRepositoryInformation(metadata, composition);
validate(information, composition);
//1.此处生成默认的jpa实现SimpleJpaRepository类
Object target = getTargetRepository(information);
// Create proxy
ProxyFactory result = new ProxyFactory();
result.setTarget(target);
result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);
if (MethodInvocationValidator.supports(repositoryInterface)) {
result.addAdvice(new MethodInvocationValidator());
}
result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE);
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);
//2.各种加强
postProcessors.forEach(processor -> processor.postProcess(result, information));
result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
//3.重点逻辑,将dao的每一个方法转化为Query类
result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));
composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
//返回最终的代理类
return (T) result.getProxy(classLoader);
}
看下QueryExecutorMethodInterceptor这个类:
public QueryExecutorMethodInterceptor(RepositoryInformation repositoryInformation,
ProjectionFactory projectionFactory) {
this.resultHandler = new QueryExecutionResultHandler();
Optional<QueryLookupStrategy> lookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey,
RepositoryFactorySupport.this.evaluationContextProvider);
if (!lookupStrategy.isPresent() && repositoryInformation.hasQueryMethods()) {
throw new IllegalStateException("You have defined query method in the repository but "
+ "you don't have any query lookup strategy defined. The "
+ "infrastructure apparently does not support query methods!");
}
//重点mapMethodsToQuery方法,进行方法对应Query类转化
this.queries = lookupStrategy //
.map(it -> mapMethodsToQuery(repositoryInformation, it, projectionFactory)) //
.orElse(Collections.emptyMap());
}
此处mapMethodsToQuery方法细节不再展开,有兴趣可以RepositoryFactorySupport在找到lookupQuery方法,跟下去,此方法将生成每个dao方法对应RepositoryQuery类,然后返回,其中有个重要逻辑在AbstractSharedSessionContract类的createQuery(String queryString)方法,将会构造最重要的QueryImpl类
@Override
public QueryImplementor createQuery(String queryString) {
checkOpen();
checkTransactionSynchStatus();
delayedAfterCompletion();
try {
final QueryImpl query = new QueryImpl(
this,
getQueryPlan( queryString, false ).getParameterMetadata(),
queryString
);
query.setComment( queryString );
applyQuerySettingsAndHints( query );
return query;
}
catch (RuntimeException e) {
throw exceptionConverter.convert( e );
}
}
好了,到这里jpa加载过程主要核心的逻辑就梳理大概梳理完了,总结下:
- JpaRepositoriesAutoConfiguration类通过配置文件自动加载
- import标签引入JpaRepositoriesAutoConfigureRegistrar类,加载所有的dao类定义
- 通过将JpaRepositoryFactoryBean工厂类注入spring上下文中,调用其afterPropertiesSet方法进行实际类产出.
- afterPropertiesSet方法中传教repositoryFactory工厂,注入实际数据库操作的EntityMananger类,此类实际为SharedEntityManagerCreator创造的代理类,核心逻辑在SharedEntityManagerInvocationHandler中
- 最后,通过上面的工厂类,创建dao中每个方法对应的Query对象,用来后续实际查询使用.
网友评论