中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

直接調用userMapper接口的方法是什么

發布時間:2021-10-21 09:22:52 來源:億速云 閱讀:150 作者:iii 欄目:web開發

本篇內容主要講解“直接調用userMapper接口的方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“直接調用userMapper接口的方法是什么”吧!

老規矩,先上案例代碼,這樣大家可以更加熟悉是如何使用的,看過Mybatis系列的小伙伴,對這段代碼差不多都可以背下來了。

哈哈~,有點夸張嗎?不夸張的,就這行代碼。

public class MybatisApplication {         public static final String URL = "jdbc:mysql://localhost:3306/mblog";         public static final String USER = "root";         public static final String PASSWORD = "123456";              public static void main(String[] args) {             String resource = "mybatis-config.xml";             InputStream inputStream = null;             SqlSession sqlSession = null;             try {                 inputStream = Resources.getResourceAsStream(resource);                 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);                 sqlSession = sqlSessionFactory.openSession();                 //今天主要這行代碼                 UserMapper userMapper = sqlSession.getMapper(UserMapper.class);                 System.out.println(userMapper.selectById(1));                  } catch (Exception e) {                 e.printStackTrace();             } finally {                 try {                     inputStream.close();                 } catch (IOException e) {                     e.printStackTrace();                 }                 sqlSession.close();             }         }

看源碼有什么用?

通過源碼的學習,我們可以收獲Mybatis的核心思想和框架設計,另外還可以收獲設計模式的應用。

前兩篇文章我們已經Mybatis配置文件解析到獲取SqlSession,下面我們來分析從SqlSession到userMapper:

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

前面那篇文章已經知道了這里的sqlSession使用的是默認實現類DefaultSqlSession。所以我們直接進入DefaultSqlSession的getMapper方法。

//DefaultSqlSession中   private final Configuration configuration; //type=UserMapper.class @Override public <T> T getMapper(Class<T> type) {   return configuration.getMapper(type, this); }

這里有三個問題:

直接調用userMapper接口的方法是什么

問題1:getMapper返回的是個什么對象?

上面可以看出,getMapper方法調用的是Configuration中的getMapper方法。然后我們進入Configuration中

//Configuration中   protected final MapperRegistry mapperRegistry = new MapperRegistry(this); ////type=UserMapper.class public <T> T getMapper(Class<T> type, SqlSession sqlSession) {     return mapperRegistry.getMapper(type, sqlSession); }

這里也沒做什么,繼續調用MapperRegistry中的getMapper:

//MapperRegistry中 public class MapperRegistry {   //主要是存放配置信息   private final Configuration config;   //MapperProxyFactory 的映射   private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();    //獲得 Mapper Proxy 對象   //type=UserMapper.class,session為當前會話   public <T> T getMapper(Class<T> type, SqlSession sqlSession) {     //這里是get,那就有add或者put     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);     }   }      //解析配置文件的時候就會調用這個方法,   //type=UserMapper.class   public <T> void addMapper(Class<T> type) {     // 判斷 type 必須是接口,也就是說 Mapper 接口。     if (type.isInterface()) {         //已經添加過,則拋出 BindingException 異常         if (hasMapper(type)) {             throw new BindingException("Type " + type + " is already known to the MapperRegistry.");         }         boolean loadCompleted = false;         try {             //添加到 knownMappers 中             knownMappers.put(type, new MapperProxyFactory<>(type));             //創建 MapperAnnotationBuilder 對象,解析 Mapper 的注解配置             MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);             parser.parse();             //標記加載完成             loadCompleted = true;         } finally {             //若加載未完成,從 knownMappers 中移除             if (!loadCompleted) {                 knownMappers.remove(type);             }         }     } } }

MapperProxyFactory對象里保存了mapper接口的class對象,就是一個普通的類,沒有什么邏輯。

在MapperProxyFactory類中使用了兩種設計模式:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2. 單例模式methodCache(注冊式單例模式)。

  3. 工廠模式getMapper()。

繼續看MapperProxyFactory中的newInstance方法。

public class MapperProxyFactory<T> {       private final Class<T> mapperInterface;       private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();            public MapperProxyFactory(Class<T> mapperInterface) {         this.mapperInterface = mapperInterface;       }      public T newInstance(SqlSession sqlSession) {       //創建MapperProxy對象       final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);       return newInstance(mapperProxy);     }     //最終以JDK動態代理創建對象并返回      protected T newInstance(MapperProxy<T> mapperProxy) {         return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);     }     }

從代碼中可以看出,依然是穩穩的基于 JDK Proxy 實現的,而 InvocationHandler 參數是 MapperProxy 對象。

//UserMapper 的類加載器 //接口是UserMapper //h是mapperProxy對象 public static Object newProxyInstance(ClassLoader loader,                                           Class<?>[] interfaces,                                        InvocationHandler h){ }

問題2:為什么就可以調用他的方法?

上面調用newInstance方法時候創建了MapperProxy對象,并且是當做newProxyInstance的第三個參數,所以MapperProxy類肯定實現了InvocationHandler。

進入MapperProxy類中:

//果然實現了InvocationHandler接口   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;     }     //調用userMapper.selectById()實質上是調用這個invoke方法     @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {       try {         //如果是Object的方法toString()、hashCode()等方法           if (Object.class.equals(method.getDeclaringClass())) {           return method.invoke(this, args);         } else if (method.isDefault()) {           //JDK8以后的接口默認實現方法             return invokeDefaultMethod(proxy, method, args);         }       } catch (Throwable t) {         throw ExceptionUtil.unwrapThrowable(t);       }       //創建MapperMethod對象       final MapperMethod mapperMethod = cachedMapperMethod(method);       //下一篇再聊       return mapperMethod.execute(sqlSession, args);     }   }

也就是說,getMapper方法返回的是一個JDK動態代理對象(類型是$Proxy+數字)。這個代理對象會繼承Proxy類,實現被代理的接口UserMpper,里面持有了一個MapperProxy類型的觸發管理類。

當我們調用UserMpper的方法時候,實質上調用的是MapperProxy的invoke方法。

userMapper=$Proxy6@2355。

直接調用userMapper接口的方法是什么

為什么要在MapperRegistry中保存一個工廠類?

原來他是用來創建并返回代理類的。這里是代理模式的一個非常經典的應用。

直接調用userMapper接口的方法是什么

MapperProxy如何實現對接口的代理?

JDK動態代理

我們知道,JDK動態代理有三個核心角色:

  • 被代理類(即就是實現類)

  • 接口

  • 實現了InvocationHanndler的觸發管理類,用來生成代理對象。

被代理類必須實現接口,因為要通過接口獲取方法,而且代理類也要實現這個接口。

直接調用userMapper接口的方法是什么

而Mybatis中并沒有Mapper接口的實現類,怎么被代理呢?它忽略了實現類,直接對Mapper接口進行代理。

MyBatis動態代理:

在Mybatis中,JDK動態代理為什么不需要實現類呢?

直接調用userMapper接口的方法是什么

這里我們的目的其實就是根據一個可以執行的方法,直接找到Mapper.xml中statement ID ,方便調用。

最后返回的userMapper就是MapperProxyFactory的創建的代理對象,然后這個對象中包含了MapperProxy對象,

問題3:到底是怎么根據Mapper.java找到Mapper.xml的?

最后我們調用userMapper.selectUserById(),本質上調用的是MapperProxy的invoke()方法。

請看下面這張圖:

直接調用userMapper接口的方法是什么

如果根據(接口+方法名找到Statement ID  ),這個邏輯在InvocationHandler子類(MapperProxy類)中就可以完成了,其實也就沒有必要在用實現類了。

到此,相信大家對“直接調用userMapper接口的方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

南京市| 内乡县| 集贤县| 礼泉县| 高碑店市| 浦东新区| 库车县| 凤庆县| 东宁县| 汉源县| 井冈山市| 襄汾县| 剑阁县| 苏尼特左旗| 崇仁县| 宾川县| 秦皇岛市| 临清市| 类乌齐县| 泾源县| 武安市| 沾化县| 南乐县| 定安县| 温泉县| 乌什县| 北碚区| 静宁县| 赤水市| 阜南县| 库伦旗| 凯里市| 崇义县| 英超| 白玉县| 常山县| 和田市| 石门县| 页游| 耒阳市| 福贡县|