码迷,mamicode.com
首页 > 其他好文 > 详细

mybatis-deffload

时间:2019-10-05 12:21:07      阅读:145      评论:0      收藏:0      [点我收藏+]

标签:rap   直接   final   执行器   添加   invoke   factory   from   protect   

1. 概述

本文,我们来分享 SQL 执行的第五部分,延迟加载的功能的实现,涉及 executor/loader 包。整体类图如下:技术图片

  • 从类图,我们发现,延迟加载的功能,是通过动态代理实现的。也就是说,通过拦截指定方法,执行数据加载,从而实现延迟加载。
  • 并且,MyBatis 提供了 Cglib 和 Javassist 两种动态代理的创建方式。

在 《精尽 MyBatis 源码分析 —— SQL 执行(四)之 ResultSetHandler》 方法中,我们已经看到延迟加载相关的代码,下面让我们一处一处来看看。

另外,如果胖友并未使用过 MyBatis 的延迟加载的功能,可以先看看 《【MyBatis框架】高级映射-延迟加载》 文章。

2. ResultLoader

在 DefaultResultSetHandler 的 #getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) 方法中,我们可以看到 ResultLoader 的身影。代码如下:

// DefaultResultSetHandler.java

// 获得嵌套查询的值
private Object getNestedQueryConstructorValue(ResultSet rs, ResultMapping constructorMapping, String columnPrefix) throws SQLException {
// 获得内嵌查询的编号
final String nestedQueryId = constructorMapping.getNestedQueryId();
// 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, constructorMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
// 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = constructorMapping.getJavaType();
// <x> 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// 加载结果
value = resultLoader.loadResult();
}
return value;
}
  • <x> 处,因为是结果对象的构造方法中使用的值,无法使用延迟加载的功能,所以使用 ResultLoader 直接加载。

org.apache.ibatis.executor.loader.ResultLoader ,结果加载器。

2.1 构造方法

// ResultLoader.java

protected final Configuration configuration;
protected final Executor executor;
protected final MappedStatement mappedStatement;
/**
* 查询的参数对象
*/
protected final Object parameterObject;
/**
* 结果的类型
*/
protected final Class<?> targetType;
protected final ObjectFactory objectFactory;
protected final CacheKey cacheKey;
protected final BoundSql boundSql;
/**
* ResultExtractor 对象
*/
protected final ResultExtractor resultExtractor;
/**
* 创建 ResultLoader 对象时,所在的线程
*/
protected final long creatorThreadId;

/**
* 是否已经加载
*/
protected boolean loaded;
/**
* 查询的结果对象
*/
protected Object resultObject;

public ResultLoader(Configuration config, Executor executor, MappedStatement mappedStatement, Object parameterObject, Class<?> targetType, CacheKey cacheKey, BoundSql boundSql) {
this.configuration = config;
this.executor = executor;
this.mappedStatement = mappedStatement;
this.parameterObject = parameterObject;
this.targetType = targetType;
this.objectFactory = configuration.getObjectFactory();
this.cacheKey = cacheKey;
this.boundSql = boundSql;
// 初始化 resultExtractor
this.resultExtractor = new ResultExtractor(configuration, objectFactory);
// 初始化 creatorThreadId
this.creatorThreadId = Thread.currentThread().getId();
}
  • 重点属性,看添加了中文注释的。

2.2 loadResult

#loadResult() 方法,加载结果。代码如下:

// ResultLoader.java

public Object loadResult() throws SQLException {
// <1> 查询结果
List<Object> list = selectList();
// <2> 提取结果
resultObject = resultExtractor.extractObjectFromList(list, targetType);
// <3> 返回结果
return resultObject;
}
  • <1> 处,调用 #selectList() 方法,查询结果。详细解析,见 「2.3 selectList」 。
  • <2> 处,调用 ResultExtractor#extractObjectFromList(List<Object> list, Class<?> targetType) 方法,提取结果。详细解析,见 「3. ResultExtractor」 。
  • <3> 处,返回结果。

2.3 selectList

#selectList() 方法,查询结果。代码如下:

// ResultLoader.java

private <E> List<E> selectList() throws SQLException {
// <1> 获得 Executor 对象
Executor localExecutor = executor;
if (Thread.currentThread().getId() != this.creatorThreadId || localExecutor.isClosed()) {
localExecutor = newExecutor();
}
// <2> 执行查询
try {
return localExecutor.query(mappedStatement, parameterObject, RowBounds.DEFAULT, Executor.NO_RESULT_HANDLER, cacheKey, boundSql);
} finally {
// <3> 关闭 Executor 对象
if (localExecutor != executor) {
localExecutor.close(false);
}
}
}
  • <1> 处,如果当前线程不是创建线程,则调用 #newExecutor() 方法,创建 Executor 对象,因为 Executor 是非线程安全的。代码如下:

    // ResultLoader.java

    private Executor newExecutor() {
    // 校验 environment
    final Environment environment = configuration.getEnvironment();
    if (environment == null) {
    throw new ExecutorException("ResultLoader could not load lazily. Environment was not configured.");
    }
    // 校验 ds
    final DataSource ds = environment.getDataSource();
    if (ds == null) {
    throw new ExecutorException("ResultLoader could not load lazily. DataSource was not configured.");
    }
    // 创建 Transaction 对象
    final TransactionFactory transactionFactory = environment.getTransactionFactory();
    final Transaction tx = transactionFactory.newTransaction(ds, null, false);
    // 创建 Executor 对象
    return configuration.newExecutor(tx, ExecutorType.SIMPLE);
    }
  • <2> 处,调用 Executor#query(...) 方法,执行查询。

  • <3> 处,如果是新创建的 Executor 对象,则调用 Executor#close() 方法,关闭 Executor 对象。

2.4 wasNull

#wasNull() 方法,是否结果为空。代码如下:

// ResultLoader.java

public boolean wasNull() {
return resultObject == null;
}

3. ResultExtractor

org.apache.ibatis.executor.ResultExtractor ,结果提取器。代码如下:

// ResultExtractor.java

public class ResultExtractor {

private final Configuration configuration;
private final ObjectFactory objectFactory;

public ResultExtractor(Configuration configuration, ObjectFactory objectFactory) {
this.configuration = configuration;
this.objectFactory = objectFactory;
}

/**
* 从 list 中,提取结果
*
* @param list list
* @param targetType 结果类型
* @return 结果
*/
public Object extractObjectFromList(List<Object> list, Class<?> targetType) {
Object value = null;
// 情况一,targetType 就是 list ,直接返回
if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
value = list;
// 情况二,targetType 是集合,添加到其中
} else if (targetType != null && objectFactory.isCollection(targetType)) {
// 创建 Collection 对象
value = objectFactory.create(targetType);
// 将结果添加到其中
MetaObject metaObject = configuration.newMetaObject(value);
metaObject.addAll(list);
// 情况三,targetType 是数组
} else if (targetType != null && targetType.isArray()) {
// 创建 array 数组
Class<?> arrayComponentType = targetType.getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
// 赋值到 array 中
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
value = array;
} else {
value = list.toArray((Object[]) array);
}
// 情况四,普通对象,取首个对象
} else {
if (list != null && list.size() > 1) {
throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
} else if (list != null && list.size() == 1) {
value = list.get(0);
}
}
return value;
}

}
  • 分成四种情况,胖友看下代码注释。

4. ResultLoaderMap

在 DefaultResultSetHandler 的 #getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) 方法中,我们可以看到 ResultLoaderMap 的身影。代码如下:

// DefaultResultSetHandler.java

// 获得嵌套查询的值
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
// 获得内嵌查询的编号
final String nestedQueryId = propertyMapping.getNestedQueryId();
// 获得属性名
final String property = propertyMapping.getProperty();
// 获得内嵌查询的 MappedStatement 对象
final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
// 获得内嵌查询的参数类型
final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
// 获得内嵌查询的参数对象
final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
Object value = null;
if (nestedQueryParameterObject != null) {
// 获得 BoundSql 对象
final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
// 获得 CacheKey 对象
final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
final Class<?> targetType = propertyMapping.getJavaType();
// <y> 检查缓存中已存在
if (executor.isCached(nestedQuery, key)) {
// 创建 DeferredLoad 对象,并通过该 DeferredLoad 对象从缓存中加载结采对象
executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
// 返回已定义
value = DEFERED;
// 检查缓存中不存在
} else {
// 创建 ResultLoader 对象
final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);
// <x> 如果要求延迟加载,则延迟加载
if (propertyMapping.isLazy()) {
// 如果该属性配置了延迟加载,则将其添加到 `ResultLoader.loaderMap` 中,等待真正使用时再执行嵌套查询并得到结果对象。
lazyLoader.addLoader(property, metaResultObject, resultLoader);
// 返回已定义
value = DEFERED;
// 如果不要求延迟加载,则直接执行加载对应的值
} else {
value = resultLoader.loadResult();
}
}
}
return value;
}
  • <x> 处,因为是结果对象的 setting 方法中使用的值,可以使用延迟加载的功能,所以使用 ResultLoaderMap 记录。最终会创建结果对象的代理对象,而 ResultLoaderMap 对象会传入其中,作为一个参数。从而能够,在加载该属性时,能够调用 ResultLoader#loadResult() 方法,加载结果。
  • 另外,在 <y> 处,检查缓存中已存在,则会调用 Executor#deferLoad(...) 方法,尝试加载结果。代码如下:

    老艿艿:此处是插入,?? 找不到适合放这块内容的地方了,哈哈哈。

    // 该方法在 BaseExecutor 抽象类中实现
    // BaseExecutor.java

    /**
    * DeferredLoad( 延迟加载 ) 队列
    */
    protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;

    @Override
    public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
    // 如果执行器已关闭,抛出 ExecutorException 异常
    if (closed) {
    throw new ExecutorException("Executor was closed.");
    }
    // 创建 DeferredLoad 对象
    DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
    // 如果可加载,则执行加载
    if (deferredLoad.canLoad()) {
    deferredLoad.load();
    // 如果不可加载,则添加到 deferredLoads 中
    } else {
    deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));
    }
    }

    private static class DeferredLoad {

    private final MetaObject resultObject;
    private final String property;
    private final Class<?> targetType;
    private final CacheKey key;
    private final PerpetualCache localCache;
    private final ObjectFactory objectFactory;
    private final ResultExtractor resultExtractor;

    // issue #781
    public DeferredLoad(MetaObject resultObject,
    String property,
    CacheKey key,
    PerpetualCache localCache,
    Configuration configuration,
    Class<?> targetType) {
    this.resultObject = resultObject;
    this.property = property;
    this.key = key;
    this.localCache = localCache;
    this.objectFactory = configuration.getObjectFactory();
    this.resultExtractor = new ResultExtractor(configuration, objectFactory);
    this.targetType = targetType;
    }

    public boolean canLoad() {
    return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
    }

    public void load() {
    @SuppressWarnings("unchecked")
    // we suppose we get back a List
    // 从缓存 localCache 中获取
    List<Object> list = (List<Object>) localCache.getObject(key);
    // 解析结果
    Object value = resultExtractor.extractObjectFromList(list, targetType);
    // 设置到 resultObject 中
    resultObject.setValue(property, value);
    }

    }
    • 代码比较简单,胖友自己瞅瞅。

org.apache.ibatis.executor.loader.ResultLoaderMap , ResultLoader 的映射。该映射,最终创建代理对象时,会作为参数传入代理。

4.1 构造方法

// ResultLoaderMap.java

/**
* LoadPair 的映射
*/
private final Map<String, LoadPair> loaderMap = new HashMap<>();

4.2 addLoader

#addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) 方法,添加到 loaderMap 中。代码如下:

// ResultLoaderMap.java

public void addLoader(String property, MetaObject metaResultObject, ResultLoader resultLoader) {
String upperFirst = getUppercaseFirstProperty(property);
// 已存在,则抛出 ExecutorException 异常
if (!upperFirst.equalsIgnoreCase(property) && loaderMap.containsKey(upperFirst)) {
throw new ExecutorException("Nested lazy loaded result property ‘" + property +
"‘ for query id ‘" + resultLoader.mappedStatement.getId() +
" already exists in the result map. The leftmost property of all lazy loaded properties must be unique within a result map.");
}
// 创建 LoadPair 对象,添加到 loaderMap 中
loaderMap.put(upperFirst, new LoadPair(property, metaResultObject, resultLoader));
}

/**
* 使用 . 分隔属性,并获得首个字符串,并大写
*
* @param property 属性
* @return 字符串 + 大写
*/
private static String getUppercaseFirstProperty(String property) {
String[] parts = property.split("\\.");
return parts[0].toUpperCase(Locale.ENGLISH);
}
  • 其中,LoadPair 是 ResultLoaderMap 的内部静态类。代码如下:

    // ResultLoaderMap.java

    public static class LoadPair implements Serializable {

    private static final long serialVersionUID = 20130412;

    /**
    * Name of factory method which returns database connection.
    */
    private static final String FACTORY_METHOD = "getConfiguration";
    /**
    * Object to check whether we went through serialization..
    */
    private final transient Object serializationCheck = new Object();
    /**
    * Meta object which sets loaded properties.
    */
    private transient MetaObject metaResultObject;
    /**
    * Result loader which loads unread properties.
    */
    private transient ResultLoader resultLoader;
    /**
    * Wow, logger.
    */
    private transient Log log;
    /**
    * Factory class through which we get database connection.
    */
    private Class<?> configurationFactory;
    /**
    * Name of the unread property.
    */
    private String property;
    /**
    * ID of SQL statement which loads the property.
    */
    private String mappedStatement;
    /**
    * Parameter of the sql statement.
    */
    private Serializable mappedParameter;

    private LoadPair(final String property, MetaObject metaResultObject, ResultLoader resultLoader) {
    this.property = property;
    this.metaResultObject = metaResultObject;
    this.resultLoader = resultLoader;

    /* Save required information only if original object can be serialized. */
    // 当 `metaResultObject.originalObject` 可序列化时,则记录 mappedStatement、mappedParameter、configurationFactory 属性
    if (metaResultObject != null && metaResultObject.getOriginalObject() instanceof Serializable) {
    final Object mappedStatementParameter = resultLoader.parameterObject;

    /* @todo May the parameter be null? */
    if (mappedStatementParameter instanceof Serializable) {
    this.mappedStatement = resultLoader.mappedStatement.getId();
    this.mappedParameter = (Serializable) mappedStatementParameter;

    this.configurationFactory = resultLoader.configuration.getConfigurationFactory();
    } else {
    Log log = this.getLogger();
    if (log.isDebugEnabled()) {
    log.debug("Property [" + this.property + "] of ["
    + metaResultObject.getOriginalObject().getClass() + "] cannot be loaded "
    + "after deserialization. Make sure it‘s loaded before serializing "
    + "forenamed object.");
    }
    }
    }
    }

    // ... 暂时省略其它方法
    }

4.3 load

#load(String property) 方法,执行指定属性的加载。代码如下:

// ResultLoaderMap.java

public boolean load(String property) throws SQLException {
// 获得 LoadPair 对象,并移除
LoadPair pair = loaderMap.remove(property.toUpperCase(Locale.ENGLISH));
// 执行加载
if (pair != null) {
pair.load();
return true; // 加载成功
}
return false; // 加载失败
}
  • 调用 LoadPair#load() 方法,执行加载。代码如下:
// ResultLoaderMap.java

public void load() throws SQLException {
/* These field should not be null unless the loadpair was serialized.
* Yet in that case this method should not be called. */
// 若 metaResultObject 或 resultLoader 为空,抛出 IllegalArgumentException 异常
if (this.metaResultObject == null) {
throw new IllegalArgumentException("metaResultObject is null");
}
if (this.resultLoader == null) {
throw new IllegalArgumentException("resultLoader is null");
}

// 执行加载
this.load(null);
}

public void load(final Object userObject) throws SQLException {
if (this.metaResultObject == null || this.resultLoader == null) { // <1>
if (this.mappedParameter == null) {
throw new ExecutorException("Property [" + this.property + "] cannot be loaded because "
+ "required parameter of mapped statement ["
+ this.mappedStatement + "] is not serializable.");
}

// 获得 Configuration 对象
final Configuration config = this.getConfiguration();
// 获得 MappedStatement 对象
final MappedStatement ms = config.getMappedStatement(this.mappedStatement);
if (ms == null) {
throw new ExecutorException("Cannot lazy load property [" + this.property
+ "] of deserialized object [" + userObject.getClass()
+ "] because configuration does not contain statement ["
+ this.mappedStatement + "]");
}

// 获得对应的 MetaObject 对象
this.metaResultObject = config.newMetaObject(userObject);
// 创建 ResultLoader 对象
this.resultLoader = new ResultLoader(config, new ClosedExecutor(), ms, this.mappedParameter,
metaResultObject.getSetterType(this.property), null, null);
}

/* We are using a new executor because we may be (and likely are) on a new thread
* and executors aren‘t thread safe. (Is this sufficient?)
*
* A better approach would be making executors thread safe. */
if (this.serializationCheck == null) { // <2>
final ResultLoader old = this.resultLoader;
this.resultLoader = new ResultLoader(old.configuration, new ClosedExecutor(), old.mappedStatement,
old.parameterObject, old.targetType, old.cacheKey, old.boundSql);
}

// <3>
this.metaResultObject.setValue(property, this.resultLoader.loadResult());
}
  • <1> 和 <2> 处,胖友可以暂时无视,主要用于延迟加载在序列化和反序列化的时候,一般很少碰到。当然,感兴趣的胖友,可以调试下 org.apache.ibatis.submitted.lazy_deserialize.LazyDeserializeTest 单元测试类。
  • 【重点】<3> 处,调用 ResultLoader#loadResult() 方法,执行查询结果。
  • <3> 处,调用 MetaObject#setValue(String name, Object value) 方法,设置属性。

4.4 loadAll

#loadAll() 方法,执行所有属性的加载。代码如下:

// ResultLoaderMap.java

public void loadAll() throws SQLException {
// 遍历 loaderMap 属性
final Set<String> methodNameSet = loaderMap.keySet();
String[] methodNames = methodNameSet.toArray(new String[methodNameSet.size()]);
for (String methodName : methodNames) {
// 执行加载
load(methodName);
}
}

4.5 其它方法

ResultLoaderMap 还有其它方法,比较简单,胖友可以自己看看。

5. ProxyFactory

在 DefaultResultSetHandler 的 #createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) 方法中,我们可以看到 ProxyFactory 的身影。代码如下:

// DefaultResultSetHandler.java

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
// useConstructorMappings ,表示是否使用构造方法创建该结果对象。此处将其重置
this.useConstructorMappings = false; // reset previous mapping result
final List<Class<?>> constructorArgTypes = new ArrayList<>(); // 记录使用的构造方法的参数类型的数组
final List<Object> constructorArgs = new ArrayList<>(); // 记录使用的构造方法的参数值的数组
// 创建映射后的结果对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
if (resultObject != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
// 如果有内嵌的查询,并且开启延迟加载,则创建结果对象的代理对象
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
// issue gcode #109 && issue #149
if (propertyMapping.getNestedQueryId() != null && propertyMapping.isLazy()) {
resultObject = configuration.getProxyFactory().createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); // <X>
break;
}
}
}
// 判断是否使用构造方法创建该结果对象
this.useConstructorMappings = resultObject != null && !constructorArgTypes.isEmpty(); // set current mapping result
return resultObject;
}
  • <x> 处,调用 ProxyFactory#createProxy(...) 方法,创建结果对象的代理对象。

org.apache.ibatis.executor.loader.ProxyFactory ,代理工厂接口,用于创建需要延迟加载属性的结果对象。代码如下:

// ProxyFactory.java

public interface ProxyFactory {

// 设置属性,目前是空实现。可以暂时无视该方法
void setProperties(Properties properties);

// 创建代理对象
Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

}
  • ProxyFactory 有 JavassistProxyFactory 和 CglibProxyFactory 两个实现类,默认使用前者。原因见如下代码:

    // Configuration.java

    protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

5.1 JavassistProxyFactory

org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory ,实现 ProxyFactory 接口,基于 Javassist 的 ProxyFactory 实现类。

5.1.1 构造方法

// JavassistProxyFactory.java

private static final Log log = LogFactory.getLog(JavassistProxyFactory.class);

private static final String FINALIZE_METHOD = "finalize";
private static final String WRITE_REPLACE_METHOD = "writeReplace";

public JavassistProxyFactory() {
try {
// 加载 javassist.util.proxy.ProxyFactory 类
Resources.classForName("javassist.util.proxy.ProxyFactory");
} catch (Throwable e) {
throw new IllegalStateException("Cannot enable lazy loading because Javassist is not available. Add Javassist to your classpath.", e);
}
}

5.1.2 createDeserializationProxy

#createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建支持反序列化的代理对象。代码如下:

// JavassistProxyFactory.java

public Object createDeserializationProxy(Object target, Map<String, ResultLoaderMap.LoadPair> unloadedProperties, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedDeserializationProxyImpl.createProxy(target, unloadedProperties, objectFactory, constructorArgTypes, constructorArgs);
}
  • 因为实际场景下,不太使用该功能,所以本文暂时无视。

5.1.3 createProxy 普通方法

#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建代理对象。代码如下:

// JavassistProxyFactory.java

@Override
public Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return EnhancedResultObjectProxyImpl.createProxy(target, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
}

5.1.4 crateProxy 静态方法

#crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 静态方法,创建代理对象。代码如下:

// JavassistProxyFactory.java

static Object crateProxy(Class<?> type, MethodHandler callback, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
// 创建 javassist ProxyFactory 对象
ProxyFactory enhancer = new ProxyFactory();
// 设置父类
enhancer.setSuperclass(type);

// 根据情况,设置接口为 WriteReplaceInterface 。和序列化相关,可以无视
try {
type.getDeclaredMethod(WRITE_REPLACE_METHOD); // 如果已经存在 writeReplace 方法,则不用设置接口为 WriteReplaceInterface
// ObjectOutputStream will call writeReplace of objects returned by writeReplace
if (log.isDebugEnabled()) {
log.debug(WRITE_REPLACE_METHOD + " method was found on bean " + type + ", make sure it returns this");
}
} catch (NoSuchMethodException e) {
enhancer.setInterfaces(new Class[]{WriteReplaceInterface.class}); // 如果不存在 writeReplace 方法,则设置接口为 WriteReplaceInterface
} catch (SecurityException e) {
// nothing to do here
}

// 创建代理对象
Object enhanced;
Class<?>[] typesArray = constructorArgTypes.toArray(new Class[constructorArgTypes.size()]);
Object[] valuesArray = constructorArgs.toArray(new Object[constructorArgs.size()]);
try {
enhanced = enhancer.create(typesArray, valuesArray);
} catch (Exception e) {
throw new ExecutorException("Error creating lazy proxy. Cause: " + e, e);
}

// <x> 设置代理对象的执行器
((Proxy) enhanced).setHandler(callback);
return enhanced;
}
  • 常见的基于 Javassist 的 API ,创建代理对象。
  • <x> 处,设置代理对象的执行器。该执行器,就是 EnhancedResultObjectProxyImpl 对象。详细解析,见 「5.1.5 EnhancedResultObjectProxyImpl」 。

5.1.5 EnhancedResultObjectProxyImpl

EnhancedResultObjectProxyImpl ,是 JavassistProxyFactory 的内部静态类,实现 javassist.util.proxy.MethodHandler 接口,方法处理器实现类。

5.1.5.1 构造方法

// JavassistProxyFactory.java

private static class EnhancedResultObjectProxyImpl implements MethodHandler {

private final Class<?> type;
private final ResultLoaderMap lazyLoader;
private final boolean aggressive;
private final Set<String> lazyLoadTriggerMethods;
private final ObjectFactory objectFactory;
private final List<Class<?>> constructorArgTypes;
private final List<Object> constructorArgs;

private EnhancedResultObjectProxyImpl(Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
this.type = type;
this.lazyLoader = lazyLoader;
this.aggressive = configuration.isAggressiveLazyLoading();
this.lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods();
this.objectFactory = objectFactory;
this.constructorArgTypes = constructorArgTypes;
this.constructorArgs = constructorArgs;
}

// ... 暂时省略无关方法
}
  • 涉及的 aggressive 和 lazyLoadTriggerMethods 属性,在 Configuration 定义如下:

    // Configuration.java

    /**
    * 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods)
    */
    protected boolean aggressiveLazyLoading;

    /**
    * 指定哪个对象的方法触发一次延迟加载。
    */
    protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));

5.1.5.2 createProxy

#createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建代理对象,并设置方法处理器为 EnhancedResultObjectProxyImpl 对象。代码如下:

因为方法名 createProxy 一直在重复,所以这里艿艿说下调用链:

「5.1.3 createProxy 普通方法」 => 「5.1.4.2 createProxy」 => 「5.1.4 createProxy 静态方法」

// JavassistProxyFactory.java

public static Object createProxy(Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
final Class<?> type = target.getClass();
// 创建 EnhancedResultObjectProxyImpl 对象
EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl(type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
// 创建代理对象
Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs);
// 将 target 的属性,复制到 enhanced 中
PropertyCopier.copyBeanProperties(type, target, enhanced);
return enhanced;
}
  • 代码比较简单,胖友仔细瞅瞅,不要绕晕噢。

5.1.5.3 invoke

#invoke(Object enhanced, Method method, Method methodProxy, Object[] args) 方法,执行方法。代码如下:

// JavassistProxyFactory.java

@Override
public Object invoke(Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable {
final String methodName = method.getName();
try {
synchronized (lazyLoader) {
// 忽略 WRITE_REPLACE_METHOD ,和序列化相关
if (WRITE_REPLACE_METHOD.equals(methodName)) {
Object original;
if (constructorArgTypes.isEmpty()) {
original = objectFactory.create(type);
} else {
original = objectFactory.create(type, constructorArgTypes, constructorArgs);
}
PropertyCopier.copyBeanProperties(type, enhanced, original);
if (lazyLoader.size() > 0) {
return new JavassistSerialStateHolder(original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs);
} else {
return original;
}
} else {
if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) {
// <1.1> 加载所有延迟加载的属性
if (aggressive || lazyLoadTriggerMethods.contains(methodName)) {
lazyLoader.loadAll();
// <1.2> 如果调用了 setting 方法,则不在使用延迟加载
} else if (PropertyNamer.isSetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
lazyLoader.remove(property); // 移除
// <1.3> 如果调用了 getting 方法,则执行延迟加载
} else if (PropertyNamer.isGetter(methodName)) {
final String property = PropertyNamer.methodToProperty(methodName);
if (lazyLoader.hasLoader(property)) {
lazyLoader.load(property);
}
}
}
}
}
// <2> 继续执行原方法
return methodProxy.invoke(enhanced, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
}
  • <1.1> 处,如果满足条件,则调用 ResultLoaderMap#loadAll() 方法,加载所有延迟加载的属性。
  • <1.2> 处,如果调用了 setting 方法,则调用 ResultLoaderMap#remove(String property) 方法,不在使用延迟加载。因为,具体的值都设置了,无需在延迟加载了。
  • <1.3> 处,如果调用了 getting 方法,则调用 ResultLoaderMap#load(String property) 方法,执行指定属性的延迟加载。此处,就会去数据库中查询,并设置到对应的属性。
  • <2> 处,继续执行原方法。

5.2 CglibProxyFactory

org.apache.ibatis.executor.loader.cglib.CglibProxyFactory ,实现 ProxyFactory 接口,基于 Cglib 的 ProxyFactory 实现类。

mybatis-deffload

标签:rap   直接   final   执行器   添加   invoke   factory   from   protect   

原文地址:https://www.cnblogs.com/siye1989/p/11624263.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!