侧边栏壁纸
博主头像
soulballad博主等级

技术文章记录及总结

  • 累计撰写 169 篇文章
  • 累计创建 26 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

【源码分析-Spring Boot】-10.Spring Boot Jpa 查询流程及实现原理

soulballad
2020-07-21 / 0 评论 / 0 点赞 / 77 阅读 / 7,947 字
温馨提示:
本文最后更新于 2022-03-03,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Spring Boot Jpa:【从零开始学Spring Boot】-10.Spring Boot Jpa操作数据库

1.@Repository 如何加载的?

SpringBooApplication 应用启动时,会调用 createApplicationContext 方法,这里指定了默认 web 应用的类型是 AnnotationConfigServletWebServerApplicationContext。在 AnnotationConfigServletWebServerApplicationContext 的构造函数中,调用了 AnnotatedBeanDefinitionReader 的构造方法,最终通过 registerAnnotationConfigProcessors 方法将一些和注解扫描相关的 Processor 注册到 context 中,其中有一个类是 ConfigurationClassPostProcessor,这个比较关键。

在调用 refreshContext 方法时,最终会调用到 AbstractApplicationContext 的 refresh 方法,在这个流程中,invokeBeanFactoryPostProcessors 方法触发了 ConfigurationClassPostProcessor,将注解进行扫描,从而注册到 registry 中。

2.UserRepository 的动态代理

UserRepository 继承自 JpaRepository,JpaRepository 有一个 FactoryBean 叫 JpaRepositoryFactoryBean,它实现了InitializingBean 接口,在 afterPropertiesSet 中进行了代理操作。同时它也实现了 FactoryBean 接口,提供一个 getObject 方法来获取 bean 的实例。

在 factory.getRepository 方法中,有一个 getRepositoryInformation 方法,它的实现如下

private RepositoryInformation getRepositoryInformation(RepositoryMetadata metadata,
                                                       RepositoryComposition composition) {

    RepositoryInformationCacheKey cacheKey = new RepositoryInformationCacheKey(metadata, composition);

    return repositoryInformationCache.computeIfAbsent(cacheKey, key -> {

        // 这里的 baseClass 为 SimpleJpaRepository
        Class<?> baseClass = repositoryBaseClass.orElse(getRepositoryBaseClass(metadata));

        return new DefaultRepositoryInformation(metadata, baseClass, composition);
    });
}

这里的 getRepositoryBaseClass 获取一个 baseClass,实际返回一个 SimpleJpaRepository.class,这个 baseClass 在后面作为被代理对象使用。

在 getTargetRepositoryViaReflection 方法中,根据这个 baseClass,通过反射创建对象

protected final <R> R getTargetRepositoryViaReflection(RepositoryInformation information,
      Object... constructorArguments) {
	// 获取到 baseClass,即为 SimpleJpaRepository
   Class<?> baseClass = information.getRepositoryBaseClass();
   return getTargetRepositoryViaReflection(baseClass, constructorArguments);
}

	protected final <R> R getTargetRepositoryViaReflection(Class<?> baseClass, Object... constructorArguments) {
		Optional<Constructor<?>> constructor = ReflectionUtils.findConstructor(baseClass, constructorArguments);
		// 通过反射创建对象对象
		return constructor.map(it -> (R) BeanUtils.instantiateClass(it, constructorArguments)).orElseThrow(() -> new IllegalStateException(String.format(
						"No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these",
						baseClass, Arrays.stream(constructorArguments).map(Object::getClass).collect(Collectors.toList()))));
	}

然后将这个对象作为 target 放到 result 中,result 又添加了一些 advisor 和 advice,这些在查询时被构建成链接器链

// 获取到一个 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());
}
// 添加 advisor
result.addAdvisor(ExposeInvocationInterceptor.ADVISOR);

postProcessors.forEach(processor -> processor.postProcess(result, information));

if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) {
   result.addAdvice(new DefaultMethodInvokingMethodInterceptor());
}

// 添加 advice
ProjectionFactory projectionFactory = getProjectionFactory(classLoader, beanFactory);
result.addAdvice(new QueryExecutorMethodInterceptor(information, projectionFactory));

composition = composition.append(RepositoryFragment.implemented(target));
result.addAdvice(new ImplementationMethodExecutionInterceptor(composition));
// 获取代理对象
T repository = (T) result.getProxy(classLoader);

最终生成的代理对象即为如下所示

3.Jpa 查询流程是怎样的?

这里以 UserServiceImpl#findUserByName 说一下 jpa 的查询流程

在 UserServiceImpl 调用了 UserRepository,UserRepository 是一个代理对象,它被 JdkDynamicAopProxy 所代理,所以执行 UserRepository 中方法时,会调用 JdkDynamicAopProxy 中 invoke 方法。

@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   Object oldProxy = null;
   boolean setProxyContext = false;

   TargetSource targetSource = this.advised.targetSource;
   Object target = null;

   try {
      if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
         // The target does not implement the equals(Object) method itself.
         return equals(args[0]);
      }
      else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
         // The target does not implement the hashCode() method itself.
         return hashCode();
      }
      else if (method.getDeclaringClass() == DecoratingProxy.class) {
         // There is only getDecoratedClass() declared -> dispatch to proxy config.
         return AopProxyUtils.ultimateTargetClass(this.advised);
      }
      else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
         // Service invocations on ProxyConfig with the proxy config...
         return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
      }

      Object retVal;

      if (this.advised.exposeProxy) {
         // Make invocation available if necessary.
         oldProxy = AopContext.setCurrentProxy(proxy);
         setProxyContext = true;
      }

      // Get as late as possible to minimize the time we "own" the target,
      // in case it comes from a pool.
       // 获取目标对象
      target = targetSource.getTarget();
      Class<?> targetClass = (target != null ? target.getClass() : null);

      // Get the interception chain for this method.
       // 构建拦截链
      List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

      // Check whether we have any advice. If we don't, we can fallback on direct
      // reflective invocation of the target, and avoid creating a MethodInvocation.
      if (chain.isEmpty()) {
         // We can skip creating a MethodInvocation: just invoke the target directly
         // Note that the final invoker must be an InvokerInterceptor so we know it does
         // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
         Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
         retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
      }
      else {
         // We need to create a method invocation...
         MethodInvocation invocation =
               new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
         // Proceed to the joinpoint through the interceptor chain.
         retVal = invocation.proceed();
      }

      // Massage return value if necessary.
      Class<?> returnType = method.getReturnType();
      if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
         // Special case: it returned "this" and the return type of the method
         // is type-compatible. Note that we can't help if the target sets
         // a reference to itself in another returned object.
         retVal = proxy;
      }
      else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
         throw new AopInvocationException(
               "Null return value from advice does not match primitive return type for: " + method);
      }
      return retVal;
   }
   finally {
      if (target != null && !targetSource.isStatic()) {
         // Must have come from TargetSource.
         targetSource.releaseTarget(target);
      }
      if (setProxyContext) {
         // Restore old proxy.
         AopContext.setCurrentProxy(oldProxy);
      }
   }
}

在 JdkDynamicAopProxy 中会通过 getInterceptorsAndDynamicInterceptionAdvice 获取到一条链,实际上它是一个拦截器链,它由一下几个部分组成:

  • ExposeInvocationInterceptor: 将当前的invocation设置到上下文中
  • CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor: 判断是自定义方法还是jpa中方法,如果是自定义方法直接执行下一个拦截器;否则绑定资源再执行下一个拦截器
  • PersistenceExceptionTranslationInterceptor: 捕获RuntimeException,出现异常之后拦截器才生效
  • TransactionInterceptor: 给后面要执行的拦截器添加后置事务处理
  • DefaultMethodInvokingMethodInterceptor: 判断是否 defaultMethod,如果不是走下一个拦截器;否则使用MethodHandle执行
  • RepositoryFactorySupport$QueryExecutorMethodInterceptor: 执行自定义查询
  • RepositoryFactorySupport$ImplementationMethodExecutionInterceptor:拦截 RepositoryComposition
  • PersistenceExceptionTranslationInterceptor:异常处理拦截器

最终在 QueryExecutorMethodInterceptor 中调用 doInvoke 方法执行自定义查询

@Nullable
private Object doInvoke(MethodInvocation invocation) throws Throwable {

    Method method = invocation.getMethod();
	
    if (hasQueryFor(method)) {
        // 执行查询
        return queries.get(method).execute(invocation.getArguments());
    }
	// 继续执行下一个拦截器
    return invocation.proceed();
}

在 execute 中,通过调用 AbstractJpaQuery#execute -> AbstractJpaQuery#doExecute -> JpaQueryExecution#execute -> JpaQueryExecution.SingleEntityExecution#doExecute -> AbstractProducedQuery#getSingleResult -> AbstractProducedQuery#list -> AbstractProducedQuery#doList -> org.hibernate.internal.SessionImpl#list,使用 hibernate 完成查询。

0

评论区