前三篇詳細總結了Mybatis的基本特性、常用配置、對映器,相對於Hibernate,對映器的配置相對複雜,但有很好的靈活性和擴充套件性,可以應對各種業務場景。熟練掌握這些內容,可以流暢的使用MyBatis進行開發了。
後面準備介紹MyBatis的解析和執行原理以及自定義外掛,今天看了書籍的這部分,都會涉及到反射和動態代理這些基礎,本篇文章總結下這些,便於理解原理。
文章索引:
通過本篇的介紹,你會了解到:
- 反射和動態代理是解決什麼問題的
- Class物件
- 反射能做什麼
- 動態代理的實現方式:JDK動態代理、CGLIB
理解反射和動態代理
反射
首先看看官網對反射的定義:
可以通過java程式碼,獲取當前載入類的欄位、方法、建構函式等資訊,並在安全限制內,使用反射欄位、方法、建構函式進行操作。
簡單來說,可以在執行時獲得程式中每一個型別的成員資訊。程式中定義的物件,其型別都是在編譯期確定的,而反射可以動態地建立物件,並訪問或呼叫其成員。
動態代理
所謂代理,是一個人或組織代替另一個人或組織做事,主要有3個角色:訪問者、代理人、被代理人,訪問者經由代理人,與被代理人互動,中間會加入一些自己的處理。
所謂的動態代理,是說在編譯時不需要定義代理類,而是在執行時建立,這個是關鍵:在執行時建立代理類。
Class物件
Class類是一個實實在在的類,存在於java.lang包中,用來表示執行時型別資訊。Class物件表示自定義類的型別資訊,比如建立一個User類,JVM就會建立一個User對應的Class物件,儲存User類相關的型別資訊,該物件儲存在jvm堆中,作為訪問方法區中User型別資訊的介面。
在使用自定義類時,會首先檢查這個類的Class物件是否已經載入,如果沒有載入,預設的類載入器就會先根據類名查詢.class檔案,Class物件就會被載入到記憶體。
可以通過下面3種方法獲取Class物件:
- 使用Class類的forName靜態方法;
- 直接獲取某一個物件的class;
- 呼叫某個物件的getClass()方法;
Class物件是反射的基礎,提供了獲取類資訊的方法,後面會介紹。
反射提供的功能
java反射框架主要提供以下內容:
- 在執行時判斷物件所屬的類;
- 在執行時建立物件;
- 在執行時獲取類包含的成員變數、方法、父類、介面等資訊;
- 在執行時呼叫一個物件的方法;
下面舉例說明相關功能
建立例項:
//獲取String所對應的Class物件
Class<?> c = User.class;
//獲取String類帶一個String引數的構造器
Constructor constructor = c.getConstructor(String.class);
//根據構造器建立例項
User user = (User)constructor.newInstance("calm");
複製程式碼
獲取方法:
//返回類或介面宣告的所有方法,包括私有的,但不包括繼承的方法
public Method[] getDeclaredMethods() throws SecurityException
//所有public方法,包括繼承的方法
public Method[] getMethods() throws SecurityException
//返回一個特定的方法,第一個引數為方法名稱,後面的引數為方法引數對應Class的物件
public Method getMethod(String name, Class<?>... parameterTypes)
複製程式碼
呼叫方法:
Class<?> userClass=User.class;
Object obj = userClass.newInstance();
Method method =klass.getMethod("addRole",String.class);
method.invoke(obj,"超級管理員");
複製程式碼
JDK動態代理
JDK本身提供了動態代理的實現,要求被代理者必須實現介面。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,InvocationHandler h)
複製程式碼
第一個引數為類載入器,第二個引數是被代理者實現的介面列表,第三個引數是實現了InvocationHandler介面的物件。
InvocationHandler是一個介面,用於規範執行被代理者的方法,可在執行方法前後,新增公共的處理程式碼。生成的動態代理類包含一個InvocationHandler屬性,呼叫對應方法時,會觸發invoke方法的呼叫。
public class JDKProxy implements InvocationHandler {
private Object targetObject;//被代理物件
public Object newProxy(Object targetObject) {
this.targetObject = targetObject;
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)//invoke方法
throws Throwable {
Object ret = null;
ret = method.invoke(targetObject, args);
return ret;
}
}
複製程式碼
測試程式碼:
JDKProxy jdkProxy=new JDKProxy();
UserService userService = (UserService)
jdkProxy.newProxy(new UserServiceImp());
userService.addRole("超級管理員");
複製程式碼
JDK動態代理的基本原理是根據定義好的規則,用傳入的介面建立一個新類。
CGLIB動態代理
JDK動態代理要求必須有介面,CGLIB(Code Generate Library)動態代理沒有這個要求,它是通過建立一個被代理類的子類,然後使用ASM位元組碼庫修改程式碼來實現的。
public class CGLibProxy implements MethodInterceptor {
private Object targetObject; //被代理物件
public Object createProxyObject(Object obj) {
this.targetObject = obj;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;
}
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object obj = null;
obj = method.invoke(targetObject, args);
return obj;
}
}
複製程式碼
測試程式碼:
CGLibProxy cgLibProxy=new CGLibProxy();
UserService userService = (UserService)
cgLibProxy.newProxy(new UserServiceImp());
userService.addRole("超級管理員");
複製程式碼
ASM 是一個 Java 位元組碼操控框架。它能夠以二進位制形式修改已有類或者動態生成類。不過ASM在建立class位元組碼的過程中,操縱的級別是底層JVM的彙編指令級別,這要求ASM使用者要對class組織結構和JVM彙編指令有一定的瞭解。另外可以使用javassist框架操作位元組碼,它對開發者提供的介面比較優化。
瞭解了反射和動態代理,對後面介紹MyBatis的解析和執行原理有很大幫助,下一篇會重點介紹。
歡迎掃描下方二維碼,關注我的個人微信公眾號 ~