【三】MyBatis-Spring最全源码详解之SQL执行流程

本文深入剖析MyBatis-Spring的SQL执行流程,从JdkDynamicAopProxy.invoke开始,经过ReflectiveMethodInvocation.proceed(),进而到MapperMethod的execute过程,详细讲解了动态SQL替换、PreparedStatement的准备和执行,以及事务管理和一级缓存的工作原理。通过对PersonMapper.selectByName方法的实例分析,揭示了MyBatis-Spring如何处理数据库查询并返回结果。
摘要由CSDN通过智能技术生成

目录

1. JdkDynamicAopProxy#invoke

2. ReflectiveMethodInvocation.proceed()

3. invokeJoinpoint

3.1 SqlCommand

3.2 MethodSignature

4 MapperMethod#execute

4.1 代理方法执行

4.2 目标方法执行

4.2.1 准备查询事宜

4.2.2 动态SQL替换

4.3 prepareStatement

4.4 执行和返回

5. 附录:项目文档


前面两篇系列文章已经详细介绍了Mapper接口的扫描过程和对应bean的创建过程。本文重点介绍mybatis执行SQL的过程。


DaoService的selectByName方法,拿到的personMapper对象已经是上文介绍的那个superMapperProxy对象。执行personMapper.selectByName(personName)方法,入参personName是"Hodey",其实就是执行的superMapperProxy的InvocationHandler.invoke(Object proxy, Method method, Object[] args)方法。

public String selectByName(String personName){
    PersonInfo personInfo = personMapper.selectByName(personName);
    return personInfo.toString();
}

我们进入JdkDynamicAopProxy的invoke方法(代理类为什么是JdkDynamicAopProxy,请参见上一篇文章)

1. JdkDynamicAopProxy#invoke

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

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

     // 如果原始方法是equals(),hashcode(),则直接执行代理类的对应方法返回。
     // 如果原始方法所在类实现了DecoratingProxy.class接口,做相应的处理后返回类对象
     // 如果原始方法是Advised.class的实现类,那就直接返回对应的代理结果。
     // 以上4条并不重要!读不懂不影响后续的理解。
    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            return equals(args[0]);
        }
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            return hashCode();
        }
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        // 主流程不会进入该分支
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 根据配置,为给定方法获取一个拦截表。
        // persistenceExceptionTranslationInterceptor
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // ReflectiveMethodInvocation是Spring AOP执行回调的载体。它会将代理对象,目标对象,目标方法,目标方法的入参,目标对象/方法所在类类型和拦截列表封装在一起。
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 重点在这里
            retVal = invocation.proceed();
        }
        
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
                returnType != Object.class && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            retVal = proxy;
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

TargetSource targetSource就是Mapper的代理对象MapperProxy@5773。try语句块中的前4个if..else分支不重要,读不懂没关系。先往下走,this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)根据配置,为给定方法method获取一个拦截表。这里获取到的chain中只有一个拦截器persistenceExceptionTranslationInterceptor,这个拦截器就是为代理targetSource而生的。接下来的invocation对象仅仅是将proxy, target, method, args, targetClass, chain这些参数用ReflectiveMethodInvocation类型封装起来。retVal = invocation.proceed()才是重点需要介绍的方法,它处理完成后会得到SQL查询的结果。最后通过method.getReturnType()拿到所需的查询对象类型,将得到的结果转换成该返回值类型后返回上层完成结果的输出。

2. ReflectiveMethodInvocation.proceed()

public Object proceed() throws Throwable {
    // 这里其实就是找到当前需要哪个拦截器去执行
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 重点在这里
        return invokeJoinpoint();
    }
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    // 因为persistenceExceptionTranslationInterceptor没有实现InterceptorAndDynamicMethodMatcher,故会走else分支
    // PersistenceExceptionTranslator是异常处理器用于处理DataAccessException 。如果后续流程中出现了异常,那么该拦截器可以进行相应的异常转换处理。    
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {        
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            return proceed();
        }
    }
    else {
        // PersistenceExceptionTranslationInterceptor.invoke处理完成后又回走进该方法,但是那个时候currentInterceptorIndex=0了。
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

process方法比较绕,处理过程也很“奇葩”。从整体上看作者是使用了责任链模式+观察者模式的实现。下面我们一起来分析这个奇葩的实现:

this.currentInterceptorIndex默认值是-1。假如interceptorsAndDynamicMethodMatchers.size=1。那就会先去执行this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)即get(0)的那个dm.interceptor.invoke(this)。然后调用最后一行((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)。实际执行的是PersistenceExceptionTranslationInterceptor的invoke方法。

// org.springframework.dao.support.PersistenceExceptionTranslationInterceptor#invoke
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        return mi.proceed();
    }
    // 这估计是我第一次在系列文章中对异常进行保留和注释
    // 只要后续过程中有RuntimeException ex发生,那么PersistenceExceptionTranslationInterceptor就能进行相应的处理
    catch (RuntimeException ex) {
        // Let it throw raw if the type of the exception is on the throws clause of the method.
        if (!this.alwaysTranslate && ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) {
            throw ex;
        } 
        else {
            PersistenceExceptionTranslator translator = this.persistenceExceptionTranslator;
            if (translator == null) {
                translator = detectPersistenceExceptionTranslators(this.beanFactory);
                this.persistenceExceptionTranslator = translator;
            }
            throw DataAccessUtils.translateIfNecessary(ex, tran
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值