MyBatis延迟加载原理(源码剖析)

MyBatis延迟加载原理:
它的原理是,使用 CGLIB 或 Javassist( 默认 ) 创建目标对象的代理对象。当调用代理对象的延迟加载属性的 getting 方法时,进入拦截器方法。比如调用 a.getB().getName() 方法,进入拦截器的
invoke(…) 方法,发现 a.getB() 需要延迟加载时,那么就会单独发送事先保存好的查询关联 B 对象的 SQL ,把 B 查询上来,然后调用 a.setB(b) 方法,于是 a 对象 b 属性就有值了,接着完成
a.getB().getName() 方法的调用。这就是延迟加载的基本原理

代理对象的生成原理

  1. Mybatis的查询结果是由ResultSetHandler接口的handleResultSets()方法处理的。ResultSetHandler接口只有一个实现,DefaultResultSetHandler,接下来看下延迟加载相关的一个核心的方法createResultObject()
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    
    
        this.useConstructorMappings = false;
        List<Class<?>> constructorArgTypes = new ArrayList();
        List<Object> constructorArgs = new ArrayList();
        // 获取返回值结果真实对象
        Object resultObject = this.createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
        // 返回值不等于null且返回值类型指定了resultMap标签
        if (resultObject != null && !this.hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    
    
        // 对于resultMap标签里的全部子标签如:id,result,association,collection
            List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
            Iterator var9 = propertyMappings.iterator();

            while(var9.hasNext()) {
    
    
                ResultMapping propertyMapping = (ResultMapping)var9.next();
                //判断属性有没配置嵌套查询(association,collection中的select属性指定了StatemenId),且备注懒加载,如果有就创建代理对象
                if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
    
    
                // 获取代理对象
                    resultObject = this.configuration.getProxyFactory().createProxy(resultObject, lazyLoader, this.configuration, this.objectFactory, constructorArgTypes, constructorArgs);
                    break;
                }
            }
        }

        this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty();
        return resultObject;
    }

2.进入configuration.getProxyFactory()

// 默认为Javassist代理工厂
private proxyFactory = new JavassistProxyFactory();
public ProxyFactory getProxyFactory() {
    
    
        return this.proxyFactory;
    }

3.进入JavassistProxyFactory的createProxy()方法发现底层调用的是JavassistProxyFactory的静态内部类EnhancedResultObjectProxyImpl的createProxy()方法

public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    
    
			// 结果集的对象类型
            Class<?> type = target.getClass();
            // 类似JDK动态代理InvocationHandler接口的实现类一样,要实现invoke方法
            JavassistProxyFactory.EnhancedResultObjectProxyImpl callback = new JavassistProxyFactory.EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
            // 调用JavassistProxyFactory的静态方法获取代理对象
            Object enhanced = JavassistProxyFactory.crateProxy(type, callback, constructorArgTypes, constructorArgs);
            PropertyCopier.copyBeanProperties(type, target, enhanced);
            return enhanced;
        }
  1. 进入JavassistProxyFactory的静态crateProxy方法
static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    
    
        org.apache.ibatis.javassist.util.proxy.ProxyFactory enhancer = new org.apache.ibatis.javassist.util.proxy.ProxyFactory();
        enhancer.setSuperclass(type);

        try {
    
    
            type.getDeclaredMethod("writeReplace");
            if (log.isDebugEnabled()) {
    
    
                log.debug("writeReplace method was found on bean " + type + ", make sure it returns this");
            }
        } catch (NoSuchMethodException var10) {
    
    
            enhancer.setInterfaces(new Class[]{
    
    WriteReplaceInterface.class});
        } catch (SecurityException var11) {
    
    
        }

        Class<?>[] typesArray = (Class[])constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
        Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);

        Object enhanced;
        try {
    
    
        // 创建代理类
            enhanced = enhancer.create(typesArray, valuesArray);
        } catch (Exception var9) {
    
    
            throw new ExecutorException("Error creating lazy proxy.  Cause: " + var9, var9);
        }
		// 为代理类绑定处理执行器
        ((Proxy)enhanced).setHandler(callback);
        return enhanced;
    }

总结:当返回结果集有配置嵌套查询(association,collection中的select属性指定了StatemenId),且备注懒加载,如果有就基于Javassis产生的代理类

代理对象调用懒加载属性的加载原理

1.当懒加载代理对象执行方法是实际执行的是代理对象执行器EnhancedResultObjectProxyImpl的invoke方法

public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
    
    
            String methodName = method.getName();

            try {
    
    
                synchronized(this.lazyLoader) {
    
    
                    if ("writeReplace".equals(methodName)) {
    
    
                        Object original;
                        if (this.constructorArgTypes.isEmpty()) {
    
    
                            original = this.objectFactory.create(this.type);
                        } else {
    
    
                            original = this.objectFactory.create(this.type, this.constructorArgTypes, this.constructorArgs);
                        }

                        PropertyCopier.copyBeanProperties(this.type, enhanced, original);
                        if (this.lazyLoader.size() > 0) {
    
    
                            return new JavassistSerialStateHolder(original, this.lazyLoader.getProperties(), this.objectFactory, this.constructorArgTypes, this.constructorArgs);
                        }

                        return original;
                    }
				//延迟加载数量大于0
                    if (this.lazyLoader.size() > 0 && !"finalize".equals(methodName)) {
    
    
                    //aggressive 一次加载性所有需要要延迟加载属性或者包含触发延迟加载方法  
                    //对于延迟加载的两个可配置属性,一个为是否一个方法执行全部属性都加载,第二个为指定方法运行是进行全部延时加载(默认指定方法包括hashCode,toString等)
                        if (!this.aggressive && !this.lazyLoadTriggerMethods.contains(methodName)) {
    
    
                            String property;
                            // 是否为set方法
                            if (PropertyNamer.isSetter(methodName)) {
    
    
                                property = PropertyNamer.methodToProperty(methodName);
                                this.lazyLoader.remove(property);
                                // 是否为get方法
                            } else if (PropertyNamer.isGetter(methodName)) {
    
    
                                property = PropertyNamer.methodToProperty(methodName);
                                // 该属性是否设置懒加载
                                if (this.lazyLoader.hasLoader(property)) {
    
    
                                // 执行预先缓存好的sql查询,加载当前属性 ,查询后调用set方法为该属性赋值
                                    this.lazyLoader.load(property);
                                }
                            }
                        } else {
    
    
                        // 执行sql查询,加载全部属性
                            this.lazyLoader.loadAll();
                        }
                    }
                }
				// 继续执行原方法
                return methodProxy.invoke(enhanced, args);
            } catch (Throwable var10) {
    
    
                throw ExceptionUtil.unwrapThrowable(var10);
            }
        }

总结:当懒加载代理对象执行方法是会被代理对象的拦截处理器监听执行invoke方法,在invoke方法中会判断改属性是否需要延迟加载以及是否会导致全部属性延迟加载,如果会就执行事先储存好的查询sql属性结果并调用set方法为该懒加载对象的属性赋值,并继续执行原方法。

猜你喜欢

转载自blog.csdn.net/yangxiaofei_java/article/details/111148980
今日推荐