MyBatis运行原理(三)接口式编程及创建代理对象原理分析
一、面向接口開發步驟
二、環境準備
數據庫表結構:
DROP TABLE IF EXISTS `t_employee`; CREATE TABLE `t_employee` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(30) DEFAULT NULL,`gender` char(1) DEFAULT NULL,`email` varchar(20) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;在pom.xml中添加依賴:
<!-- MyBatis 的依賴 --><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version></dependency><!-- 數據庫驅動 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.39</version></dependency><!-- log4j 的依賴,用于在控制臺查看執行的SQL 語句 --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!-- junit --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency>項目工程目錄:
三、開發過程
這里就不提供log4j 相關的配置信息了,有興趣的可以自行百度搜索。
員工類Employee
public class Employee {private Integer id;private String username;private Character gender;private String email;/** 省略get 、set與toString 方法 */ }接口EmployeeMapper
public interface EmployeeMapper {/**** 根據id 獲取Employee 的信息* * @param id* @return*/Employee getEmpById(Integer id); }SQL 映射文件EmployeeMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:EmployeeMapper 接口的全路徑名id:唯一標識,EmployeeMapper 接口對應的方法名resultType:返回值類型,這里返回的是Employee 對象 --> <mapper namespace="com.jas.mybatis.mapper.EmployeeMapper"><select id="getEmpById" resultType="com.jas.mybatis.bean.Employee"><!-- #{id} 表示要傳進來的參數,類似于JDBC SQL 語句中的? -->select * from t_employee where id = #{id}</select> </mapper>MyBatis 全局配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration><environments default="development"><environment id="development"><transactionManager type="JDBC"/><!-- 配置數據源 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis-study"/><property name="username" value="root"/><property name="password" value="1234"/></dataSource></environment></environments><!-- 注冊SQL 映射文件 --><mappers><mapper resource="EmployeeMapper.xml"/></mappers> </configuration>測試代碼
public class MyBatisTest {@Testpublic void helloWorld() throws Exception{String resource = "mybatis-config.xml";InputStream is = Resources.getResourceAsStream(resource);// 創建SqlSessionFactory 對象SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);// 創建SqlSession 對象SqlSession sqlSession = sqlSessionFactory.openSession();// 利用sqlSession.getMapper() 方法創建EmployeeMapper 對象EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);// 調用EmployeeMapper 中的getEmpById() 方法獲取數據庫信息Employee employee = employeeMapper.getEmpById(2);// 關閉sqlSession 對象sqlSession.close();System.out.println(employee);}}測試結果
不知道大家有沒有一個疑問:EmployeeMapper不是一個接口嗎,為什么可以生成一個對象呢?原因是在調用sqlSession.getMapper()方法時,底層利用JDK 的動態代理方式為EmployeeMapper接口生成了一個代理對象。在下面會進行其底層的原理分析。
關于JDK 動態代理可參考博文:http://blog.csdn.net/codejas/article/details/79218302
四、為什么MyBatis要提供面向接口的開發方式
到這里我們知道MyBatis 提供了面向接口方式編程,但是為什么要這么做呢?這里就要提及一些關于設計模式方面的知識。我們知道一個設計原則:面向抽象編程,其實面向接口編程就是面向抽象編程。面向抽象編程可以降低代碼之間的耦合,使代碼更加具有彈性,從而提高程序擴展性。
五、sqlSession.getMapper() 方法創建接口代理對象原理解析
這里采用DUBUG 的方式深入MyBatis 的源碼內部去探索sqlSession.getMapper(EmployeeMapper.class)創建對象的過程。
1.執行sqlSession.getMapper(EmployeeMapper.class)時,調用的是DefaultSqlSession中的getMapper(Class<T> type)方法。
/** DefaultSqlSession 中的方法 */ @Overridepublic <T> T getMapper(Class<T> type) {return configuration.<T>getMapper(type, this);}2.DefaultSqlSession中的getMapper(Class<T> type)方法調用了Configuration中的
getMapper(Class<T> type, SqlSession sqlSession)方法。
3.接著調用MapperRegistry類中的getMapper(Class<T> type, SqlSession sqlSession)方法,在這個方法中創建了一個MapperProxyFactory對象,然后調用MapperProxyFactory 類的newInstance(sqlSession)方法。
/** MapperRegistry 中的方法 */ 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);}}4.下面是在MapperProxyFactory 類中關于newInstance(sqlSession)方法的調用過程,在這個方法中首先創建了一個MapperProxy<T>對象。
/** MapperProxyFactory 中的方法 */ public T newInstance(SqlSession sqlSession) {final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);return newInstance(mapperProxy);}5.下面是創建MapperProxy<T>對象的過程,仔細看你會發現MapperProxy<T>類實現了InvocationHandler接口,這個InvocationHandler接口是Proxy的一個輔助類,用于幫助Proxy創建代理對象。
/** MapperProxy<T> 類的構造函數 */ public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {this.sqlSession = sqlSession;this.mapperInterface = mapperInterface;this.methodCache = methodCache;}6.創建完MapperProxy<T>對象后,接著調用MapperProxyFactory自身類中的另一個newInstance(MapperProxy<T> mapperProxy)方法,在newInstance(MapperProxy<T> mapperProxy) 中最終完成代理類的創建。然后一步步返回,把代理對象的引用賦給employeeMapper。到這里EmployeeMapper接口的代理對象就算是創建完成了。
/** MapperProxyFactory 中的方法 */protected T newInstance(MapperProxy<T> mapperProxy) {return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);}建議大家自己動手DEBUG 一下,只有動手了才是自己的。
最終執行結果圖:
調用過程時序圖:
六、總結
這篇博客前半部分主要介紹了MyBatis 接口式編程的使用方法,后半部分對MyBatis 接口式編程創建接口代理對象的過程進行了深入探究。如果你對后半部分的調用過程不是很理解,大可不必先急于了解其中的過程,首先會使用就可以了。學習不可一蹴而就,慢慢積累,過了一段時間,你自然就會理解了。希望這篇博客可以為你提供幫助。
總結
以上是生活随笔為你收集整理的MyBatis运行原理(三)接口式编程及创建代理对象原理分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 定外卖也能被盗刷信用卡 支付宝付款码别随
- 下一篇: 天然气开户需要带什么