最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解析Mybatis Porxy動(dòng)態(tài)代理和sql解析替換問(wèn)題

 更新時(shí)間:2021年04月30日 08:29:54   作者:十七  
這篇文章主要介紹了Mybatis Porxy動(dòng)態(tài)代理和sql解析替換,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

JDK常用核心原理

概述

在 Mybatis 中,常用的作用就是講數(shù)據(jù)庫(kù)中的表的字段映射為對(duì)象的屬性,在進(jìn)入Mybatis之前,原生的 JDBC 有幾個(gè)步驟:導(dǎo)入 JDBC 驅(qū)動(dòng)包,通過(guò) DriverManager 注冊(cè)驅(qū)動(dòng),創(chuàng)建連接,創(chuàng)建 Statement,增刪改查,操作結(jié)果集,關(guān)閉連接

過(guò)程詳解

首先進(jìn)行類的加載,通過(guò) DriverManager 注冊(cè)驅(qū)動(dòng)

Class.forName("com.mysql.jdbc.Driver");
Connection connection = DriverManager.getConnection("");

為什么在這里可以直接注冊(cè)進(jìn)去,com.mysql.jdbc.Driver 被加載到 Driver.class ,在 DriverManager 中,首先有一個(gè)靜態(tài)代碼塊來(lái)進(jìn)行初始化加載 Driver

static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

通過(guò) loadInitialDrivers(),來(lái)加載 Driver,拿出 jdbc.drivers,通過(guò) ServiceLoader 讀取 Driver.class,讀取拿出 driver 和 所有迭代器,一直迭代

private static void loadInitialDrivers() {
    String drivers;
    // 訪問(wèn)修飾符,在這里把 jdbc.drivers 拿出來(lái)
    try {
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
        drivers = null;
    }

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
        public Void run() {
            // 讀取拿出 driver 和 所有迭代器
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();
            
            // 一直進(jìn)行迭代
            try{
                while(driversIterator.hasNext()) {
                    driversIterator.next();
                }
            } catch(Throwable t) {
            // Do nothing
            }
            return null;
        }
    });

    println("DriverManager.initialize: jdbc.drivers = " + drivers);

    if (drivers == null || drivers.equals("")) {
        return;
    }
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
        try {
            println("DriverManager.Initialize: loading " + aDriver);
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
}

從 Driver 加載完后,就可以得到一個(gè)和數(shù)據(jù)庫(kù)的連接 connection ,connection 就可以創(chuàng)建一個(gè) Statement,Statement 就可以進(jìn)行執(zhí)行 sql 語(yǔ)句,將結(jié)果返回一個(gè)結(jié)果集,獲取出來(lái)的結(jié)果集遍歷放進(jìn)一個(gè) List 集合中

Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("select * from mybatis.user");
    while (resultSet.next()) {
      int id = resultSet.getInt(1);
      String username = resultSet.getString(2);
      list.add(new User(id,username));
}

在原生的 JDBC 直接操作中,繁雜的步驟在業(yè)務(wù)代碼中不會(huì)使用,而 Mybatis 可以在更好的便利度上使用

JDK動(dòng)態(tài)代理

sql語(yǔ)句解析替換

在 JDK 動(dòng)態(tài)代理中,利用了 Proxy 這個(gè)類來(lái)實(shí)現(xiàn),在 Proxy 中,有著 newProxyInstance() 方法,創(chuàng)建一個(gè)動(dòng)態(tài)代理實(shí)例

interface UserMapper {
  @Select("select * from mybatis.user where id =#{id}")
  List<User> selectUserList();
}

public static void main(String[] args) {
  UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(
    JDKMybatis.class.getClassLoader(),
    new Class<?>[]{UserMapper.class},
    new InvocationHandler() {
      /**
       * 在 invoke() 方法中就可以進(jìn)行查找 method,args
       * @param proxy  動(dòng)態(tài)代理
       * @param method 方法
       * @param args   參數(shù)
       */
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 把注解類獲取,可以查出注解的值等多種其他值
        Select annotation = method.getAnnotation(Select.class);
        if (annotation != null) {
          String[] value = annotation.value();
          System.out.println(Arrays.toString(value));
        }
        return null;
      }
    });

  userMapper.selectUserList(1);
}

newProxyInstance() 的創(chuàng)建需要三個(gè)參數(shù),查看源碼,可以知道需要 ClassLoader 類加載器,interfaces 接口(Mapper 接口),InvocationHandler 處理器,來(lái)進(jìn)行處理

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

把 sql 語(yǔ)句中的參數(shù)取出來(lái)放進(jìn) args,這時(shí)需要一個(gè) Map 來(lái)進(jìn)行傳值
問(wèn)題
當(dāng)在通過(guò)反射獲取方法的參數(shù)名,method.getParameters() 獲取出來(lái)的參數(shù)都是 arg0,arg1...無(wú)意義參數(shù)

在Java8之前,代碼編譯為class文件后,方法參數(shù)的類型是固定的,但參數(shù)名稱卻丟失了,在編譯的時(shí)候,需要有編譯的選項(xiàng),javac -parameters 默認(rèn)是關(guān)閉的,需要在 idea 中設(shè)置開(kāi)啟,開(kāi)啟完成后,重新編譯源文件

這種方式只能臨時(shí)解決當(dāng)前環(huán)境設(shè)置,在其他人運(yùn)行代碼時(shí)還是要重新設(shè)置
另一種解決方式,在pom文件中添加編譯參數(shù):

<plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
            <compilerArgument>-parameters</compilerArgument>
            <source>1.8</source>
            <target>1.8</target>
        </configuration>
    </plugin>
</plugins>

編譯完成后,重新執(zhí)行,再次通過(guò)method.getParameters()獲取參數(shù):

解析原來(lái)的 sql ,就要把 #{} 給替換掉,這時(shí)候可以使用 StringBuffer 類來(lái)實(shí)現(xiàn)替換

private static String parseSql(String sql, Map<String, Object> argsNameMap) {
  // 定義為常量數(shù)組
  char[] str = {'#', '{'};
  StringBuilder stringBuilder = new StringBuilder();
  for (int i = 0; i < sql.length(); i++) {
    char aloneParseSql = sql.charAt(i);

    if (str[0] == aloneParseSql) {
      int nextIndex = i + 1;
      char nextChar = sql.charAt(nextIndex);

      // # 后應(yīng)該是 { ,不匹配直接拋出異常
      if (str[1] != nextChar) {
        throw new RuntimeException(String.format(
          "此處應(yīng)該是:#{\n sql:%s\n index:%d",
          stringBuilder.toString(), nextIndex));
      }
      /*
        1 已經(jīng)解析完的下標(biāo)
        2 解析完的 #{} 內(nèi)的參數(shù)名
        3 把對(duì)應(yīng)的 argsName 的值 argsValue 取出來(lái)
        4 追加到原來(lái)的 stringBuilder 中的 sql 語(yǔ)句后面
       */
      StringBuilder partStringBuilder = new StringBuilder();
      i = partParseSql(partStringBuilder, sql, nextIndex);
      String argsName = partStringBuilder.toString();
      Object argsValue = argsNameMap.get(argsName);
      stringBuilder.append(argsValue.toString());
    }

    // 如果沒(méi)有條件,直接追加
    stringBuilder.append(aloneParseSql);
  }
  return stringBuilder.toString();
}

在其中需要把需要替換的值,再用 StringBuffer 類來(lái)實(shí)現(xiàn)

private static int partParseSql(StringBuilder partStringBuilder, String sql, int nextIndex) {
  // 由于 nextIndex 當(dāng)前指針指向的是 { 所以要加一位,把后面內(nèi)容解析
  nextIndex++;
  char[] str = {'}'};
  for (; nextIndex < sql.length(); nextIndex++) {
    char indexSql = sql.charAt(nextIndex);
    if (str[0] != indexSql) {
      partStringBuilder.append(indexSql);
    }
    if (str[0] == indexSql) {
      return nextIndex;
    }
  }
  throw new RuntimeException(String.format(
    "缺少:}\n index:%d",
    nextIndex));
}

再重新在 invoke 方法中進(jìn)行調(diào)用,完成 sql 語(yǔ)句的動(dòng)態(tài)拼裝

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  // 把注解類獲取,可以查出注解的值等多種其他值
  Select annotation = method.getAnnotation(Select.class);
  Map<String, Object> argsNameMap = MapBuildArgsName(method, args);
  if (annotation != null) {
    String[] value = annotation.value();
    String sql = value[0];
    sql = parseSql(sql, argsNameMap);
    System.out.println(sql);
  }
  return null;
}

到此這篇關(guān)于Mybatis(一)Porxy動(dòng)態(tài)代理和sql解析替換的文章就介紹到這了,更多相關(guān)Mybatis(一)Porxy動(dòng)態(tài)代理和sql解析替換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot多環(huán)境切換的靈活配置詳細(xì)教程

    SpringBoot多環(huán)境切換的靈活配置詳細(xì)教程

    在真實(shí)項(xiàng)目開(kāi)發(fā)的時(shí)候,一定會(huì)有多個(gè)環(huán)境,下面這篇文章主要給大家介紹了關(guān)于SpringBoot多環(huán)境切換靈活配置的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-04-04
  • Spring中的@Autowired、@Qualifier和@Primary注解詳解

    Spring中的@Autowired、@Qualifier和@Primary注解詳解

    這篇文章主要介紹了Spring中的@Autowired、@Qualifier和@Primary注解詳解,@Autowired?注解,可以對(duì)類成員變量、方法和構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動(dòng)裝配的工作,@Autowired?是默認(rèn)根據(jù)?byType?進(jìn)行自動(dòng)裝配的,需要的朋友可以參考下
    2023-11-11
  • Maven 生成打包可執(zhí)行jar包的方法步驟

    Maven 生成打包可執(zhí)行jar包的方法步驟

    這篇文章主要介紹了Maven 生成打包可執(zhí)行jar包的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Java多線程Callable接口實(shí)現(xiàn)代碼示例

    Java多線程Callable接口實(shí)現(xiàn)代碼示例

    相信大家對(duì)Java編程中如何創(chuàng)建線程已經(jīng)不陌生了,這篇文章就向朋友們介紹實(shí)現(xiàn)callable接口,具體實(shí)例詳見(jiàn)正文。
    2017-10-10
  • 一文教會(huì)你用mybatis查詢數(shù)據(jù)庫(kù)數(shù)據(jù)

    一文教會(huì)你用mybatis查詢數(shù)據(jù)庫(kù)數(shù)據(jù)

    MyBatis本身是一個(gè)數(shù)據(jù)庫(kù)連接框架,可以認(rèn)為是JDBC的升級(jí)版,下面這篇文章主要給大家介紹了關(guān)于mybatis查詢數(shù)據(jù)庫(kù)數(shù)據(jù)的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • SpringMVC 中配置 Swagger 插件的教程(分享)

    SpringMVC 中配置 Swagger 插件的教程(分享)

    下面小編就為大家分享一篇SpringMVC 中配置 Swagger 插件的教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • J2EE Servlet基礎(chǔ)在瀏覽器上運(yùn)行HelloServlet的方法

    J2EE Servlet基礎(chǔ)在瀏覽器上運(yùn)行HelloServlet的方法

    這篇文章主要介紹了J2EE Servlet基礎(chǔ)在瀏覽器上運(yùn)行HelloServlet的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Java中工具Jstack的使用實(shí)例

    Java中工具Jstack的使用實(shí)例

    jstack用于生成java虛擬機(jī)當(dāng)前時(shí)刻的線程快照,下面這篇文章主要給大家介紹了關(guān)于Java中工具Jstack使用的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-04-04
  • Java簡(jiǎn)單工廠模式定義與用法實(shí)例分析

    Java簡(jiǎn)單工廠模式定義與用法實(shí)例分析

    這篇文章主要介紹了Java簡(jiǎn)單工廠模式定義與用法,結(jié)合實(shí)例形式分析了java簡(jiǎn)單工廠模式的相關(guān)定義與使用技巧,并給出了原理類圖進(jìn)行總結(jié),需要的朋友可以參考下
    2019-07-07
  • java發(fā)送javax.mail郵件實(shí)例講解

    java發(fā)送javax.mail郵件實(shí)例講解

    這篇文章主要為大家介紹了java發(fā)送javax.mail郵件實(shí)例講解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-01-01

最新評(píng)論

中山市| 启东市| 临泉县| 蕲春县| 手游| 珲春市| 乌苏市| 浏阳市| 福鼎市| 新余市| 仁布县| 华安县| 湄潭县| 北京市| 永靖县| 伊宁县| 革吉县| 绿春县| 绥棱县| 枣阳市| 北宁市| 明星| 大石桥市| 德昌县| 梁平县| 崇阳县| 临泽县| 竹北市| 黄梅县| 潞西市| 德令哈市| 东阳市| 长寿区| 尚义县| 侯马市| 科尔| 交口县| 堆龙德庆县| 合作市| 亳州市| 巴林左旗|