主頁 > 知識庫 > Mybatis mapper動態(tài)代理的原理解析

Mybatis mapper動態(tài)代理的原理解析

熱門標簽:銅陵防封電銷卡 美國反騷擾電話機器人 真人語音電話機器人 騰訊地圖標注提升 400電話可以免費申請嗎 悟空科技電話機器人 電銷卡外呼系統(tǒng)供應(yīng)商 怎么在地圖標注位置生成圖片 福建外呼系統(tǒng)定制化

前言

在開始動態(tài)代理的原理講解以前,我們先看一下集成mybatis以后dao層不使用動態(tài)代理以及使用動態(tài)代理的兩種實現(xiàn)方式,通過對比我們自己實現(xiàn)dao層接口以及mybatis動態(tài)代理可以更加直觀的展現(xiàn)出mybatis動態(tài)代理替我們所做的工作,有利于我們理解動態(tài)代理的過程,講解完以后我們再進行動態(tài)代理的原理解析,此講解基于mybatis的環(huán)境已經(jīng)搭建完成,并且已經(jīng)實現(xiàn)了基本的用戶類編寫以及用戶類的Dao接口的聲明,下面是Dao層的接口代碼

public interface UserDao {
 /*
 查詢所有用戶信息
  */
 ListUser> findAll();
 /**
  * 保存用戶
  * @param user
  */
 void save(User user);

 /**
  * 更新用戶
  * @return
  */
 void update(User user);
 /**
  * 刪除用戶
  */
 void delete(Integer userId);
 /**
  * 查找一個用戶
  * @param userId
  * @return
  */
 User findOne(Integer userId);
 /**
  * 根據(jù)名字模糊查詢
  * @param name
  * @return
  */
 ListUser> findByName(String name);
 /**
  * 根據(jù)組合對象進行模糊查詢
  * @param vo
  * @return
  */
 ListUser> findByQueryVo(QueryVo vo);
}

一、Mybatis dao層兩種實現(xiàn)方式的對比

1.dao層不使用動態(tài)代理

dao層不使用動態(tài)代理的話,就需要我們自己實現(xiàn)dao層的接口,為了簡便起見,我只是實現(xiàn)了Dao接口中的findAll方法,以此方法為例子來展現(xiàn)我們自己實現(xiàn)Dao的方式的情況,讓我們來看代碼:

public class UserDaoImpl implements UserDao{
 private SqlSessionFactory factory;
 public UserDaoImpl(SqlSessionFactory factory){
  this.factory = factory;
 }
 public ListUser> findAll() {
  //1.獲取sqlSession對象
  SqlSession sqlSession = factory.openSession();
  //2.調(diào)用selectList方法
  ListUser> list = sqlSession.selectList("com.example.dao.UserDao.findAll");
  //3.關(guān)閉流
  sqlSession.close();
  return list;
 }
 public void save(User user) {
 }
 public void update(User user) {
 }
 public void delete(Integer userId) {
 }
 public User findOne(Integer userId) {
  return null;
 }
 public ListUser> findByName(String name) {
  return null;
 }
 public ListUser> findByQueryVo(QueryVo vo) {
  return null;
 }

這里的關(guān)鍵代碼 ListUser> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我們自己手動調(diào)用SqlSession里面的方法,基于動態(tài)代理的方式最后的目標也是成功的調(diào)用到這里。

注意:如果是添加,更新或者刪除操作的話需要在方法中增加事務(wù)的提交。

2.dao層使用Mybatis的動態(tài)代理

使用動態(tài)代理的話Dao層的接口聲明完成以后只需要在使用的時候通過SqlSession對象的getMapper方法獲取對應(yīng)Dao接口的代理對象,關(guān)鍵代碼如下:

//3.獲取SqlSession對象
SqlSession session = factory.openSession();
//4.獲取dao的代理對象
UserDao mapper = session.getMapper(UserDao.class);
//5.執(zhí)行查詢所有的方法
ListUser> list = mapper.findAll();

獲取到dao層的代理對象以后通過代理對象調(diào)用查詢方法就可以實現(xiàn)查詢所有用戶列表的功能。

二、Mybatis動態(tài)代理實現(xiàn)方式的原理解析

動態(tài)代理中最重要的類:SqlSession、MapperProxy、MapperMethod,下面開始從入口方法到調(diào)用結(jié)束的過程分析。

1.調(diào)用方法的開始:

//4.獲取dao的代理對象

UserDao mapper = session.getMapper(UserDao.class); 因為SqlSesseion為接口,所以我們通過Debug方式發(fā)現(xiàn)這里使用的實現(xiàn)類為DefaultSqlSession。

2.找到DeaultSqlSession中的getMapper方法,發(fā)現(xiàn)這里沒有做其他的動作,只是將工作繼續(xù)拋到了Configuration類中,Configuration為類不是接口,可以直接進入該類的getMapper方法中

@Override
 public T> T getMapper(ClassT> type) {
 return configuration.T>getMapper(type, this);
 }

3. 找到Configuration類的getMapper方法,這里也是將工作繼續(xù)交到MapperRegistry的getMapper的方法中,所以我們繼續(xù)向下進行。

 public T> T getMapper(ClassT> type, SqlSession sqlSession) {
 return mapperRegistry.getMapper(type, sqlSession);
 }

4. 找到MapperRegistry的getMapper的方法,看到這里發(fā)現(xiàn)和以前不一樣了,通過MapperProxyFactory的命名方式我們知道這里將通過這個工廠生成我們所關(guān)注的MapperProxy的代理類,然后我們通過mapperProxyFactory.newInstance(sqlSession);進入MapperProxyFactory的newInstance方法中

public T> T getMapper(ClassT> type, SqlSession sqlSession) {
 final MapperProxyFactoryT> mapperProxyFactory = (MapperProxyFactoryT>) knownMappers.get(type);
 if (mapperProxyFactory == null) {
  throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
 }
 try {
  return mapperProxyFactory.newInstance(sqlSession);
 } catch (Exception e) {
  throw new BindingException("Error getting mapper instance. Cause: " + e, e);
 }
 }

5. 找到MapperProxyFactory的newIntance方法,通過參數(shù)類型SqlSession可以得知,上面的調(diào)用先進入第二個newInstance方法中并創(chuàng)建我們所需要重點關(guān)注的MapperProxy對象,第二個方法中再調(diào)用第一個newInstance方法并將MapperProxy對象傳入進去,根據(jù)該對象創(chuàng)建代理類并返回。這里已經(jīng)得到需要的代理類了,但是我們的代理類所做的工作還得繼續(xù)向下看MapperProxy類。

protected T newInstance(MapperProxyT> mapperProxy) {
 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
 }
 public T newInstance(SqlSession sqlSession) {
 final MapperProxyT> mapperProxy = new MapperProxyT>(sqlSession, mapperInterface, methodCache);
 return newInstance(mapperProxy);
 }

6. 找到MapperProxy類,發(fā)現(xiàn)其確實實現(xiàn)了JDK動態(tài)代理必須實現(xiàn)的接口InvocationHandler,所以我們重點關(guān)注invoke()方法,這里看到在invoke方法里先獲取MapperMethod類,然后調(diào)用mapperMethod.execute(),所以我們繼續(xù)查看MapperMethod類的execute方法。

public class MapperProxyT> implements InvocationHandler, Serializable {
 private static final long serialVersionUID = -6424540398559729838L;
 private final SqlSession sqlSession;
 private final ClassT> mapperInterface;
 private final MapMethod, MapperMethod> methodCache;
 public MapperProxy(SqlSession sqlSession, ClassT> mapperInterface, MapMethod, MapperMethod> methodCache) {
 this.sqlSession = sqlSession;
 this.mapperInterface = mapperInterface;
 this.methodCache = methodCache;
 }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 try {
  if (Object.class.equals(method.getDeclaringClass())) {
  return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
  return invokeDefaultMethod(proxy, method, args);
  }
 } catch (Throwable t) {
  throw ExceptionUtil.unwrapThrowable(t);
 }
 final MapperMethod mapperMethod = cachedMapperMethod(method);
 return mapperMethod.execute(sqlSession, args);
 }

 private MapperMethod cachedMapperMethod(Method method) {
 MapperMethod mapperMethod = methodCache.get(method);
 if (mapperMethod == null) {
  mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
  methodCache.put(method, mapperMethod);
 }
 return mapperMethod;
 }

 @UsesJava7
 private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
  throws Throwable {
 final ConstructorMethodHandles.Lookup> constructor = MethodHandles.Lookup.class
  .getDeclaredConstructor(Class.class, int.class);
 if (!constructor.isAccessible()) {
  constructor.setAccessible(true);
 }
 final Class?> declaringClass = method.getDeclaringClass();
 return constructor
  .newInstance(declaringClass,
   MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
    | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
  .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
 }
 /**
 * Backport of java.lang.reflect.Method#isDefault()
 */
 private boolean isDefaultMethod(Method method) {
 return ((method.getModifiers()
   (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
   method.getDeclaringClass().isInterface();
 }
}

7. 找到類MapperMethod類的execute方法,發(fā)現(xiàn)execute中通過調(diào)用本類中的其他方法獲取并封裝返回結(jié)果,我們來看一下MapperMethod整個類。

public Object execute(SqlSession sqlSession, Object[] args) {
 Object result;
 switch (command.getType()) {
  case INSERT: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.insert(command.getName(), param));
  break;
  }
  case UPDATE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.update(command.getName(), param));
  break;
  }
  case DELETE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.delete(command.getName(), param));
  break;
  }
  case SELECT:
  if (method.returnsVoid()  method.hasResultHandler()) {
   executeWithResultHandler(sqlSession, args);
   result = null;
  } else if (method.returnsMany()) {
   result = executeForMany(sqlSession, args);
  } else if (method.returnsMap()) {
   result = executeForMap(sqlSession, args);
  } else if (method.returnsCursor()) {
   result = executeForCursor(sqlSession, args);
  } else {
   Object param = method.convertArgsToSqlCommandParam(args);
   result = sqlSession.selectOne(command.getName(), param);
  }
  break;
  case FLUSH:
  result = sqlSession.flushStatements();
  break;
  default:
  throw new BindingException("Unknown execution method for: " + command.getName());
 }
 if (result == null  method.getReturnType().isPrimitive()  !method.returnsVoid()) {
  throw new BindingException("Mapper method '" + command.getName() 
   + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
 }
 return result;
 }

8. MapperMethod類是整個代理機制的核心類,對SqlSession中的操作進行了封裝使用。

該類里有兩個內(nèi)部類SqlCommand和MethodSignature。 SqlCommand用來封裝CRUD操作,也就是我們在xml中配置的操作的節(jié)點。每個節(jié)點都會生成一個MappedStatement類。

MethodSignature用來封裝方法的參數(shù)以及返回類型,在execute的方法中我們發(fā)現(xiàn)在這里又回到了SqlSession中的接口調(diào)用,和我們自己實現(xiàn)UerDao接口的方式中直接用SqlSession對象調(diào)用DefaultSqlSession的實現(xiàn)類的方法是一樣的,經(jīng)過一大圈的代理又回到了原地,這就是整個動態(tài)代理的實現(xiàn)過程了。

public class MapperMethod {
 private final SqlCommand command;
 private final MethodSignature method;
 public MapperMethod(Class?> mapperInterface, Method method, Configuration config) {
 this.command = new SqlCommand(config, mapperInterface, method);
 this.method = new MethodSignature(config, mapperInterface, method);
 }
 public Object execute(SqlSession sqlSession, Object[] args) {
 Object result;
 switch (command.getType()) {
  case INSERT: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.insert(command.getName(), param));
  break;
  }
  case UPDATE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.update(command.getName(), param));
  break;
  }
  case DELETE: {
  Object param = method.convertArgsToSqlCommandParam(args);
  result = rowCountResult(sqlSession.delete(command.getName(), param));
  break;
  }
  case SELECT:
  if (method.returnsVoid()  method.hasResultHandler()) {
   executeWithResultHandler(sqlSession, args);
   result = null;
  } else if (method.returnsMany()) {
   result = executeForMany(sqlSession, args);
  } else if (method.returnsMap()) {
   result = executeForMap(sqlSession, args);
  } else if (method.returnsCursor()) {
   result = executeForCursor(sqlSession, args);
  } else {
   Object param = method.convertArgsToSqlCommandParam(args);
   result = sqlSession.selectOne(command.getName(), param);
  }
  break;
  case FLUSH:
  result = sqlSession.flushStatements();
  break;
  default:
  throw new BindingException("Unknown execution method for: " + command.getName());
 }
 if (result == null  method.getReturnType().isPrimitive()  !method.returnsVoid()) {
  throw new BindingException("Mapper method '" + command.getName() 
   + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
 }
 return result;
 }

 private Object rowCountResult(int rowCount) {
 final Object result;
 if (method.returnsVoid()) {
  result = null;
 } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
  result = rowCount;
 } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
  result = (long)rowCount;
 } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
  result = rowCount > 0;
 } else {
  throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
 }
 return result;
 }

 private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
 MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
 if (void.class.equals(ms.getResultMaps().get(0).getType())) {
  throw new BindingException("method " + command.getName() 
   + " needs either a @ResultMap annotation, a @ResultType annotation," 
   + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
 }
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
 } else {
  sqlSession.select(command.getName(), param, method.extractResultHandler(args));
 }
 }

 private E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 ListE> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.E>selectList(command.getName(), param, rowBounds);
 } else {
  result = sqlSession.E>selectList(command.getName(), param);
 }
 // issue #510 Collections  arrays support
 if (!method.getReturnType().isAssignableFrom(result.getClass())) {
  if (method.getReturnType().isArray()) {
  return convertToArray(result);
  } else {
  return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
  }
 }
 return result;
 }

 private T> CursorT> executeForCursor(SqlSession sqlSession, Object[] args) {
 CursorT> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.T>selectCursor(command.getName(), param, rowBounds);
 } else {
  result = sqlSession.T>selectCursor(command.getName(), param);
 }
 return result;
 }

 private E> Object convertToDeclaredCollection(Configuration config, ListE> list) {
 Object collection = config.getObjectFactory().create(method.getReturnType());
 MetaObject metaObject = config.newMetaObject(collection);
 metaObject.addAll(list);
 return collection;
 }
 @SuppressWarnings("unchecked")
 private E> Object convertToArray(ListE> list) {
 Class?> arrayComponentType = method.getReturnType().getComponentType();
 Object array = Array.newInstance(arrayComponentType, list.size());
 if (arrayComponentType.isPrimitive()) {
  for (int i = 0; i  list.size(); i++) {
  Array.set(array, i, list.get(i));
  }
  return array;
 } else {
  return list.toArray((E[])array);
 }
 }
 private K, V> MapK, V> executeForMap(SqlSession sqlSession, Object[] args) {
 MapK, V> result;
 Object param = method.convertArgsToSqlCommandParam(args);
 if (method.hasRowBounds()) {
  RowBounds rowBounds = method.extractRowBounds(args);
  result = sqlSession.K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
 } else {
  result = sqlSession.K, V>selectMap(command.getName(), param, method.getMapKey());
 }
 return result;
 }
 public static class ParamMapV> extends HashMapString, V> {
 private static final long serialVersionUID = -2212268410512043556L;
 @Override
 public V get(Object key) {
  if (!super.containsKey(key)) {
  throw new BindingException("Parameter '" + key + "' not found. Available parameters are " + keySet());
  }
  return super.get(key);
 }
 }
 public static class SqlCommand {
 private final String name;
 private final SqlCommandType type;

 public SqlCommand(Configuration configuration, Class?> mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class?> declaringClass = method.getDeclaringClass();
  MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
   configuration);
  if (ms == null) {
  if (method.getAnnotation(Flush.class) != null) {
   name = null;
   type = SqlCommandType.FLUSH;
  } else {
   throw new BindingException("Invalid bound statement (not found): "
    + mapperInterface.getName() + "." + methodName);
  }
  } else {
  name = ms.getId();
  type = ms.getSqlCommandType();
  if (type == SqlCommandType.UNKNOWN) {
   throw new BindingException("Unknown execution method for: " + name);
  }
  }
 }
 public String getName() {
  return name;
 }
 public SqlCommandType getType() {
  return type;
 }
 private MappedStatement resolveMappedStatement(Class?> mapperInterface, String methodName,
  Class?> declaringClass, Configuration configuration) {
  String statementId = mapperInterface.getName() + "." + methodName;
  if (configuration.hasStatement(statementId)) {
  return configuration.getMappedStatement(statementId);
  } else if (mapperInterface.equals(declaringClass)) {
  return null;
  }
  for (Class?> superInterface : mapperInterface.getInterfaces()) {
  if (declaringClass.isAssignableFrom(superInterface)) {
   MappedStatement ms = resolveMappedStatement(superInterface, methodName,
    declaringClass, configuration);
   if (ms != null) {
   return ms;
   }
  }
  }
  return null;
 }
 }

 public static class MethodSignature {
 private final boolean returnsMany;
 private final boolean returnsMap;
 private final boolean returnsVoid;
 private final boolean returnsCursor;
 private final Class?> returnType;
 private final String mapKey;
 private final Integer resultHandlerIndex;
 private final Integer rowBoundsIndex;
 private final ParamNameResolver paramNameResolver;

 public MethodSignature(Configuration configuration, Class?> mapperInterface, Method method) {
  Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
  if (resolvedReturnType instanceof Class?>) {
  this.returnType = (Class?>) resolvedReturnType;
  } else if (resolvedReturnType instanceof ParameterizedType) {
  this.returnType = (Class?>) ((ParameterizedType) resolvedReturnType).getRawType();
  } else {
  this.returnType = method.getReturnType();
  }
  this.returnsVoid = void.class.equals(this.returnType);
  this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray());
  this.returnsCursor = Cursor.class.equals(this.returnType);
  this.mapKey = getMapKey(method);
  this.returnsMap = (this.mapKey != null);
  this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
  this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
  this.paramNameResolver = new ParamNameResolver(configuration, method);
 }

 public Object convertArgsToSqlCommandParam(Object[] args) {
  return paramNameResolver.getNamedParams(args);
 }

 public boolean hasRowBounds() {
  return rowBoundsIndex != null;
 }

 public RowBounds extractRowBounds(Object[] args) {
  return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
 }

 public boolean hasResultHandler() {
  return resultHandlerIndex != null;
 }

 public ResultHandler extractResultHandler(Object[] args) {
  return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
 }

 public String getMapKey() {
  return mapKey;
 }

 public Class?> getReturnType() {
  return returnType;
 }

 public boolean returnsMany() {
  return returnsMany;
 }

 public boolean returnsMap() {
  return returnsMap;
 }

 public boolean returnsVoid() {
  return returnsVoid;
 }

 public boolean returnsCursor() {
  return returnsCursor;
 }
 private Integer getUniqueParamIndex(Method method, Class?> paramType) {
  Integer index = null;
  final Class?>[] argTypes = method.getParameterTypes();
  for (int i = 0; i  argTypes.length; i++) {
  if (paramType.isAssignableFrom(argTypes[i])) {
   if (index == null) {
   index = i;
   } else {
   throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
   }
  }
  }
  return index;
 }
 private String getMapKey(Method method) {
  String mapKey = null;
  if (Map.class.isAssignableFrom(method.getReturnType())) {
  final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
  if (mapKeyAnnotation != null) {
   mapKey = mapKeyAnnotation.value();
  }
  }
  return mapKey;
 }
 }

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • Idea如何去除Mapper警告方法解析
  • Mybatis Mapper接口工作原理實例解析
  • MyBatis Mapper接受參數(shù)的四種方式代碼解析
  • mapper接口注入兩種方式詳解
  • Springboot整合通用mapper過程解析
  • 詳解MyBatis Mapper 代理實現(xiàn)數(shù)據(jù)庫調(diào)用原理
  • 如何自動生成Mybatis的Mapper文件詳解
  • 如何使用IDEA創(chuàng)建MAPPER模板過程圖解

標簽:云浮 武威 烏海 聊城 白銀 湖南 湖北 臨汾

巨人網(wǎng)絡(luò)通訊聲明:本文標題《Mybatis mapper動態(tài)代理的原理解析》,本文關(guān)鍵詞  Mybatis,mapper,動態(tài),代理,的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Mybatis mapper動態(tài)代理的原理解析》相關(guān)的同類信息!
  • 本頁收集關(guān)于Mybatis mapper動態(tài)代理的原理解析的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章