国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 數(shù)據(jù)庫 > MySQL > 正文

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

2020-01-18 20:44:07
字體:
供稿:網(wǎ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 { /* 查詢所有用戶信息  */ List<User> 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  */ List<User> findByName(String name); /**  * 根據(jù)組合對象進行模糊查詢  * @param vo  * @return  */ List<User> 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 List<User> findAll() {  //1.獲取sqlSession對象  SqlSession sqlSession = factory.openSession();  //2.調(diào)用selectList方法  List<User> 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 List<User> findByName(String name) {  return null; } public List<User> findByQueryVo(QueryVo vo) {  return null; }

這里的關(guān)鍵代碼 List<User> list = sqlSession.selectList("com.example.dao.UserDao.findAll"),需要我們自己手動調(diào)用SqlSession里面的方法,基于動態(tài)代理的方式最后的目標(biāo)也是成功的調(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í)行查詢所有的方法List<User> 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(Class<T> type) { return configuration.<T>getMapper(type, this); }

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

 public <T> T getMapper(Class<T> 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(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) 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(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(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 MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, 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 Constructor<MethodHandles.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) { List<E> 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> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) { Cursor<T> 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, List<E> list) { Object collection = config.getObjectFactory().create(method.getReturnType()); MetaObject metaObject = config.newMetaObject(collection); metaObject.addAll(list); return collection; } @SuppressWarnings("unchecked") private <E> Object convertToArray(List<E> 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> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) { Map<K, 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 ParamMap<V> extends HashMap<String, 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í)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 务川| 广南县| 柏乡县| 淳安县| 甘谷县| 马公市| 霍林郭勒市| 玉溪市| 平遥县| 分宜县| 普宁市| 吕梁市| 曲周县| 东乌| 巫山县| 阳泉市| 阿城市| 石首市| 光泽县| 邯郸县| 沅陵县| 杭锦后旗| 新龙县| 土默特左旗| 石家庄市| 鄂尔多斯市| 青岛市| 五峰| 朝阳市| 高清| 江阴市| 安吉县| 交口县| 大邑县| 杨浦区| 常德市| 延长县| 新营市| 嘉荫县| 临颍县| 张家界市|