原創(chuàng)作品,可以轉(zhuǎn)載,但是請標注出處地址:http://www.cnblogs.com/V1haoge/p/6715063.html

1、回顧

  之前的兩篇分別解析了類型別名注冊器和類型處理器注冊器,此二者皆是解析XML映射文件中參數(shù)類型與返回結(jié)果類型的基礎(chǔ),別名注冊器用于通過別名找到對應(yīng)的類類型,類型處理器注冊器則用于通過類類型來找到對應(yīng)了類型處理器與數(shù)據(jù)庫類型,以此來完成進出數(shù)據(jù)庫數(shù)據(jù)與java之間類型的轉(zhuǎn)換。

  我們在類型處理器注冊器一篇中已經(jīng)簡單介紹了類型處理器,那就是用于java類型與數(shù)據(jù)庫類型之間進行映射處理的工具類,這一篇中要詳細解析一下MyBatis中的類型處理器。

2、類型處理器

2.1 類架構(gòu)

  seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  從上面的圖中可以看出MyBatis中整個類型處理器實現(xiàn)架構(gòu),TypeHandler接口定義了類型處理器,而TypeReference抽象類則定義了一個類型引用,用于引用一個泛型類型(此處很抽象,不好理解,詳見后續(xù)解析),BaseTypeHandler則是類型處理器的基礎(chǔ),是所有類型處理器的公共模塊,幾乎所有的類型處理器都是通過直接繼承BaseTypeHandler來實現(xiàn)的。

2.2 類型處理器接口:TypeHandler

  TypeHandler是用于定義類型處理器的接口,內(nèi)部很是簡單:

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

 1 package org.apache.ibatis.type; 2 import java.sql.CallableStatement; 3 import java.sql.PreparedStatement; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 /** 7  * 類型處理器 8  * 
 9  */10 public interface TypeHandler<T> {11 12   //設(shè)置參數(shù)13   void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;14 15   //取得結(jié)果,供普通select用16   T getResult(ResultSet rs, String columnName) throws SQLException;17 18   //取得結(jié)果,供普通select用19   T getResult(ResultSet rs, int columnIndex) throws SQLException;20 21   //取得結(jié)果,供SP用22   T getResult(CallableStatement cs, int columnIndex) throws SQLException;23 24 }

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  通過上述源碼可以看到這個接口中定義了類型處理器基本的四個方法,其中分為兩大類,第一類是設(shè)置參數(shù)的方法setParameter(),這個方法是用于設(shè)置數(shù)據(jù)庫操作的參數(shù),例如查詢參數(shù)、刪除參數(shù)、更新參數(shù)等;另一類是用于取得結(jié)果的方法,這一類方法又細分為兩大種,第一種是從結(jié)果集中獲取結(jié)果,按照獲取的方式分為兩種:一種是通過列名(columnName)來獲取,另一種是通過列下標(columnIndex)來獲取,這兩種獲取方式正對應(yīng)我們直接使用JDBC進行數(shù)據(jù)庫查詢結(jié)果中獲取數(shù)據(jù)的兩種方式,第二種是針對存儲過程而設(shè),通過列下標的方式來獲取存儲過程輸出結(jié)果中的數(shù)據(jù)。

  總的來說類型處理器就是兩方面的作用,一方面將Java類型的參數(shù)(T prarameter)設(shè)置到數(shù)據(jù)庫操作腳本中(匹配數(shù)據(jù)庫類型jdbcType),另一種是獲取操作結(jié)果到Java類型(T)中。

2.3 類型引用:TypeReference

  這個類型引用的作用適用于獲取原生類型,Java中的原生類型又稱為基本類型,即byte、short、int、long、float、double、boolean、char八大基本數(shù)據(jù)類型。

  這個類有必要重點講解一下,同時也是為了加強一下Java中類型的概念,來看源碼:

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

 1 package org.apache.ibatis.type; 2  3 import java.lang.reflect.ParameterizedType; 4 import java.lang.reflect.Type; 5  6 /** 7  * References a generic type. 8  * 9  * @param <T> the referenced type10  * @author Simone Tripodi11  * @since 3.1.012  * 3.1新加的類型引用,為了引用一個泛型類型13  */14 public abstract class TypeReference<T> {15 16   //引用的原生類型17   private final Type rawType;18 19   protected TypeReference() {20     rawType = getSuperclassTypeParameter(getClass());21   }22 23   Type getSuperclassTypeParameter(Class<?> clazz) {24     //得到泛型T的實際類型25     Type genericSuperclass = clazz.getGenericSuperclass();26     if (genericSuperclass instanceof Class) {27       // try to climb up the hierarchy until meet something useful28       if (TypeReference.class != genericSuperclass) {29         return getSuperclassTypeParameter(clazz.getSuperclass());30       }31       throw new TypeException("'" + getClass() + "' extends TypeReference but misses the type parameter. "32         + "Remove the extension or add a type parameter to it.");33     }34     //獲取泛型<T>中的T類型35     Type rawType = ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];36     // TODO remove this when Reflector is fixed to return Types37     if (rawType instanceof ParameterizedType) {38       rawType = ((ParameterizedType) rawType).getRawType();39     }40     return rawType;41   }42 43   public final Type getRawType() {44     return rawType;45   }46 47   @Override48   public String toString() {49     return rawType.toString();50   }51 52 }

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  這個抽象類也是被BaseTypeHandler所繼承的,也就意味著幾乎所有的內(nèi)置類型處理器都繼承了這個類,那么這個類型引用的目的到底是什么呢?

  這個問題稍后再說,我們先解析下源碼:

  這個類在其無參構(gòu)造器中通過調(diào)用getSuperclassTypeParameter()方法為其內(nèi)部定義的final型字段rawType賦值,其參數(shù)是getClass()方法的結(jié)果,這是Object類中定義的方法,這個方法返回的是當前類(實例)的類類型。

  重點在于getSuperclassTypeParameter()方法中:

    第一步:通過給定參數(shù)的getGenericSuperclass()方法來獲取該類類型的上一級類型(直接超類,父類,即參數(shù)類類型繼承的類的類型)并帶有參數(shù)類型,即帶泛型。如果要獲取不帶泛型的父類可使用getSuperclass()方法。

    第二步:判斷第一步獲取的類型是否是Class類的實例

  Class類的實例有哪些呢?

  其實每一個類都是Class類的實例,Class類是對Java中類的抽象,它本身也是一個類,但它是出于其他類上一層次的類,是類的頂層抽象。從JDK文檔中可獲知“Instances of the class represent classes and interfaces in a running Java application.”(意為:Class的實例表示的是在一個運行的應(yīng)用中的所有類和接口)

 ,那么我們就明白了,Class類的實例就是接口與類。那么Java中有哪些不是Class類的實例呢?泛型類,不錯,如果一個類是泛型類,那么他就不再是Class類的實例,為什么呢?

  泛型類是Java中一種獨特的存在,它一般用于傳遞類,類似于一般方法中傳遞對象的概念,它不是簡單的類,而是一種帶有抽象概念性質(zhì)的一種類,它會通過所傳遞的類(參數(shù)化類)來指定當前類所代表的的屬于基本類型中的哪一類類型。(通過兩種類型來確定具體的類型)

    第二步:如果第一步獲取的類型式帶泛型的類型,那么判斷不成立,則會直接執(zhí)行第35行代碼,將該類型強轉(zhuǎn)為參數(shù)化類型,使用其getActualTypeArguments()方法來獲取其參數(shù)類型(泛型類型),因為該方法獲取的泛型類型可能不是一個,所以返回的是一個數(shù)組,但是我們這里只會獲取到一個,所以取第一個即可。

    但是如果第一步獲取的類型不帶泛型,那么就會進入條件內(nèi)部執(zhí)行,再次判斷,獲取的類型是否是TypeReference類型,如果不是該類型,則執(zhí)行遞歸操作,如果是該類型,那么說明第一步通過getGenericSuperclass()獲取帶泛型的類型時為獲取到泛型(因為MyBatis中TypeReference是泛型類),則說明程序出錯,此處拋出類型異常,提示丟失泛型。

    第三步:如果第二步判斷不通過,則會執(zhí)行地35行代碼,來獲取參數(shù)類型,然后對獲取的參數(shù)類型進行判斷如果該類型還是參數(shù)化類型(仍然帶有泛型,即泛型嵌套的模式),那么就需要再次執(zhí)行g(shù)etActualTypeArguments()方法來獲取其泛型類型(參數(shù)類型),最后將該類型返回(賦值給字段)

  為什么只會獲取兩次呢?因為,通過之前的類架構(gòu)我們已經(jīng)明白,具體的類型處理器最多只會存在兩層繼承。

  最后說一下,這個類型引用的目的,它就是為了持有這個具體的類型處理器所處理的Java類型的原生類型。我們可以看到在該類中還有兩個方法getRawType()和toString()方法,這兩個方法都是public修飾的,是對外公開的方法,那么也就意味著這個原生類型是為了被外部調(diào)用而設(shè)。

  通過檢索發(fā)現(xiàn),getRawType()方法重點被調(diào)用的地方在TypeHandlerRegistry(類型處理器注冊器)中,在沒有指定JavaType而只有TypeHandler的情況下,調(diào)用該TypeHandler的getRawType()方法來獲取其原生類型(即參數(shù)類型)來作為其JavaType來進行類型處理器的注冊。

2.4 基礎(chǔ)類型處理器:BaseTypeHandler

  BaseTypeHandler繼承了TypeReference抽象類,實現(xiàn)了TypeHandler接口,它本身仍然是抽象類,在它內(nèi)部簡單的實現(xiàn)了TypeHandler接口中定義的四個方法中的部分功能,所謂部分功能是指只實現(xiàn)了所有類型處理器公共部分,具體的不同處理部分則還是交由具體的類型處理器來自己實現(xiàn),所有它內(nèi)部再次定義了四個抽象類,用來指導具體類型處理器的實現(xiàn)。

  BaseTypeHandler中主要對設(shè)置參數(shù)與獲取返回結(jié)果時數(shù)據(jù)位null的情況進行了處理,具體的參數(shù)設(shè)置方法與結(jié)果獲取方法都是由具體的類型處理器來實現(xiàn)的。

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

1     //非NULL情況,怎么設(shè)參數(shù)還得交給不同的子類完成2   public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;3 4     //以下3個方法是取得可能為null的結(jié)果,具體交給子類完成5   public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;6 7   public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;8 9   public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  上面的四個方法就是BaseTypeHandler中定義的抽象方法。MyBatis內(nèi)置的類型處理器幾乎都是通過繼承實現(xiàn)上面的四個方法來完成最終定義的。

2.5 類型處理器:StringTypeHandler

  我們看個簡單的例子來理解一下這個過程。下面是字符串類型處理器:StringTypeHandler的源碼

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

 1 package org.apache.ibatis.type; 2  3 import java.sql.CallableStatement; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 /** 8  * String類型處理器 9  * 調(diào)用PreparedStatement.setString, ResultSet.getString, CallableStatement.getString10  */11 public class StringTypeHandler extends BaseTypeHandler<String> {12 13   @Override14   public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)15       throws SQLException {16     ps.setString(i, parameter);17   }18 19   @Override20   public String getNullableResult(ResultSet rs, String columnName)21       throws SQLException {22     return rs.getString(columnName);23   }24 25   @Override26   public String getNullableResult(ResultSet rs, int columnIndex)27       throws SQLException {28     return rs.getString(columnIndex);29   }30 31   @Override32   public String getNullableResult(CallableStatement cs, int columnIndex)33       throws SQLException {34     return cs.getString(columnIndex);35   }36 }

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  上面的源碼完美的詮釋了之前的解析,具體的類型處理器中只需要實現(xiàn)這四個方法即可,前提是其繼承了BaseTypeHandler抽象類。

  其中設(shè)置參數(shù)的方法中具體的實現(xiàn)調(diào)用了PreparedStatement的setString()方法,這個是我們很熟悉的方法。同樣的,在獲取結(jié)果的方法中也是通過調(diào)用ResultSet的getString()方法,和CallableStatement的getString()方法來完成具體的功能。這已經(jīng)是MyBatis中最為底層的邏輯了,因為它直接調(diào)用了JDK API來實現(xiàn)功能。

2.6  未知類型處理器:UnknownTypeHandler

   這個是MyBatis中定義的一個較為特殊的類型處理器,雖然其內(nèi)部實現(xiàn)和普通的類型處理器如出一轍,但是它擁有一些特殊的地方,所以單獨拿出來說一說。

  通過類型處理器注冊器中的注冊信息可以看出這種類型處理器所對應(yīng)的JavaType是Object類型,對應(yīng)的JdbcType是OTHER類型,這個OTHER是什么類型?我們可以這么理解,市面上數(shù)據(jù)庫種類繁多,而且各有特點,這些數(shù)據(jù)庫產(chǎn)品即滿足SQL規(guī)范,同時也有各自的擴展和強化,每個數(shù)據(jù)庫內(nèi)部都有一些自定義的只在其內(nèi)部起作用的數(shù)據(jù)類型,而這些類型反映到Java中之后是Object類型時,這里就將其統(tǒng)一定義為OTHER類型。

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

 1   private static final ObjectTypeHandler OBJECT_TYPE_HANDLER = new ObjectTypeHandler(); 2  3   private TypeHandlerRegistry typeHandlerRegistry; 4  5   public UnknownTypeHandler(TypeHandlerRegistry typeHandlerRegistry) { 6     this.typeHandlerRegistry = typeHandlerRegistry; 7   } 8  9   @Override10   public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)11       throws SQLException {12     TypeHandler handler = resolveTypeHandler(parameter, jdbcType);13     handler.setParameter(ps, i, parameter, jdbcType);14   }15 16   @Override17   public Object getNullableResult(ResultSet rs, String columnName)18       throws SQLException {19     TypeHandler<?> handler = resolveTypeHandler(rs, columnName);20     return handler.getResult(rs, columnName);21   }22 23   @Override24   public Object getNullableResult(ResultSet rs, int columnIndex)25       throws SQLException {26     TypeHandler<?> handler = resolveTypeHandler(rs.getMetaData(), columnIndex);27     if (handler == null || handler instanceof UnknownTypeHandler) {28       handler = OBJECT_TYPE_HANDLER;29     }30     return handler.getResult(rs, columnIndex);31   }32 33   @Override34   public Object getNullableResult(CallableStatement cs, int columnIndex)35       throws SQLException {36     return cs.getObject(columnIndex);37   }

seo優(yōu)化培訓,網(wǎng)絡(luò)推廣培訓,網(wǎng)絡(luò)營銷培訓,SEM培訓,網(wǎng)絡(luò)優(yōu)化,在線營銷培訓

  源碼分析:在UnknownTypeHandler中的四個方法中,除針對存儲過程結(jié)果取數(shù)據(jù)的情況之外,其余三個方法的實現(xiàn)均類似,都是先通過不同的resolveTypeHandler()方法來獲取具體的TypeHandler,然后調(diào)用具體TypeHandler的對應(yīng)方法來完成功能。那么UnknownTypeHandler中的重點就集中在這三個resolveTypeHandler()方法中了。

http://www.cnblogs.com/V1haoge/p/6715063.html