問題:
動態代理是基於什麼原理?
知識點:
- Java是靜態的強型別語言,但是因為提供了類似反射等機制,也具備了部分動態型別的語言能力。
- 代理模式: 定義:給某個物件提供一個代理物件,並由代理物件控制對於原物件的訪問,即客戶不直接操控原物件,而是通過代理物件間接地操控原物件。(類似在上海這邊經常看到的房屋中介)
靜態代理模式程式碼:
public class ProxyDemo {
public static void main(String args[]){
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
interface Subject{
void request();
}
class RealSubject implements Subject{
public void request(){
System.out.println("request");
}
}
class Proxy implements Subject{
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("PreProcess");
subject.request();
System.out.println("PostProcess");
}
}
複製程式碼
- 反射: Java 反射機制在程式執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種 動態的獲取資訊 以及 動態呼叫物件的方法 的功能稱為 java 的反射機制。 反射機制很重要的一點就是“執行時”,其使得我們可以在程式執行時載入、探索以及使用編譯期間完全未知的 .class 檔案。換句話說,Java 程式可以載入一個執行時才得知名稱的 .class 檔案,然後獲悉其完整構造,並生成其物件實體、或對其 fields(變數)設值、或呼叫其 methods(方法)。具體可以參考:Java 反射由淺入深 | 進階必備
執行時註解利用反射會影響效能,編譯時候註解不會影響效能。 通過註解方式宣告訂閱方法,速度相比2.x會變慢,通過引入註解處理器(annotation processor),在編譯期間建立訂閱方法的索引,效能有明顯提升。
根據官方說明,註解的引入讓EventBus 3.0相比2.x效能變差3-5倍,但是引入索引,3.0相比2.x效能提高至少3倍。
詳細瞭解請參考:EventBus 3.0 最佳實踐和原理淺析
- 動態代理:
為其他物件提供一種代理以控制對這個物件的訪問。某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以再兩者之間起到中介作用。執行階段才指定代理哪個物件。
組成元素:
- 抽象類介面
- 被代理類(具體實現抽象類介面的類)
- 動態代理類,實際呼叫被代理類的方法和屬性
實現方式 - [x] JDK 自身提供的動態代理,就是主要利用了反射機制
//retrofit2.Retrofit原始碼中就使用到了
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T)
//劃重點了
Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
ServiceMethod<Object, Object> serviceMethod =
(ServiceMethod<Object, Object>) loadServiceMethod(method);
OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
return serviceMethod.callAdapter.adapt(okHttpCall);
}
});
}
複製程式碼
其他實現方式:利用位元組碼操作機制,類似ASM、GGLB(基於ASM)、Javassist等
回答問題:
這裡不作回答了,自己根據上面知識點進行總結吧。在APM功能開發的時候,動態代理使用場景及其廣泛,以及在我們針對不同機型適配的時候,反射使用場景次數較多。
/**
* 隱藏系統鍵盤
*/
public void hintSystemSoftKeyboard() {
if (getWindowToken() != null) {
((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getWindowToken(), 2);
}
if (Build.VERSION.SDK_INT >= 11) {
try {
//以下是本章重點內容,請仔細體會
Class[] arrayOfClass = new Class[1];
arrayOfClass[0] = Boolean.TYPE;
Method localMethod = null;
if (Build.VERSION.SDK_INT < 17) {
localMethod = EditText.class.getMethod("setSoftInputShownOnFocus", arrayOfClass);
} else {
localMethod = EditText.class.getMethod("setShowSoftInputOnFocus", arrayOfClass);
}
localMethod.setAccessible(true);
Object[] arrayOfObject = new Object[1];
arrayOfObject[0] = Boolean.valueOf(false);
localMethod.invoke(this, arrayOfObject);
return;
} catch (SecurityException localSecurityException) {
localSecurityException.printStackTrace();
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
localNoSuchMethodException.printStackTrace();
return;
} catch (Exception localException) {
localException.printStackTrace();
return;
}
}
setInputType(0);
}
複製程式碼
參考:
- 代理模式及Java實現動態代理
- Java 反射由淺入深 | 進階必備
- EventBus 3.0 最佳實踐和原理淺析
- Retrofit部分原始碼
- 極客時間APP核心技術第六講|動態代理是基於什麼原理?
- 自己專案中部分程式碼
宣告:此為原創,轉載請聯絡作者
作者:微信公眾號新增公眾號-遛狗的程式設計師 ,或者可以掃描以下二維碼關注相關技術文章。
當然喜愛技術,樂於分享的你也可以可以新增作者微訊號: