【Vue CLI】手把手教你擼外掛
為其他物件提供一種代理以控制對這個物件的訪問。
代理模式分為:靜態代理、動態代理(JDK代理、cglib代理)
解決問題:
在面向物件系統中,有些物件由於某些原因(比如物件建立開銷很大,或者某些操作需要安全控制,或者需要程序外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此物件時加上一個對此物件的訪問層。
UML:
靜態代理:
代理物件和被代理物件需要實現相同的介面或繼承自相同的父類
1 // 抽象介面 2 interface IApp { 3 void open(); 4 } 5 6 public class AppImpl implementsView CodeIApp { 7 8 @Override 9 public void open() { 10 System.out.println("開啟APP"); 11 } 12 } 13 14 // 代理類 15 public class AppProxy implements IApp{ 16 17 private IApp target; 18 19 public AppProxy(IApp target) { 20 this.target = target; 21 } 22 23 @Override24 public void open() { 25 System.out.println("查詢APP路徑"); 26 // 呼叫被代理物件的方法 27 target.open(); 28 // System.out.println("xxx"); 29 } 30 }
Client:
1 public static void main(String[] args) { 2 // 被代理物件 3 AppImpl app = new AppImpl(); 4 //代理物件 5 AppProxy appProxy = newView CodeAppProxy(app); 6 7 appProxy.open(); 8 }
執行結果:
缺點:需要建立多個代理類,增加閱讀的複雜性
如果介面增加方法、代理物件和目標物件都需要修改
動態代理:
JDK代理:
代理物件不需要實現介面,但是目標物件需要實現介面
代理物件利用JDK的API動態生成,代理類:java.lang.reflect.Proxy
JDK代理的實現只需要使用以下方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h):
loader:目標物件的類載入器
interfaces:代理類實現的介面列表
h:由代理例項的呼叫處理程式實現的介面,當在代理例項上呼叫方法時,方法呼叫將被編碼並分派到其呼叫處理程式的 invoke 方法
介面及實現類:
1 interface IApp { 2 void open(); 3 } 4 5 public class AppImpl implements IApp { 6 @Override 7 public void open() { 8 System.out.println("開啟APP"); 9 } 10 }View Code
代理類:
1 public class ProxyFactory { 2 3 private Object target; 4 5 public ProxyFactory(Object target) { 6 this.target = target; 7 } 8 9 public Object getProxyInstance() { 10 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 11 target.getClass().getInterfaces(), 12 new InvocationHandler() { 13 @Override 14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 15 System.out.println("JDK代理開始"); 16 Object res = method.invoke(target, args); 17 System.out.println("JDK代理結束"); 18 return res; 19 } 20 }); 21 } 22 }View Code
client:
1 public static void main(String[] args) { 2 // 被代理物件 3 IApp app = new AppImpl(); 4 //代理物件 5 IApp proxyInstance = (IApp) new ProxyFactory(app).getProxyInstance(); 6 System.out.println("proxyInstance = " + proxyInstance.getClass()); 7 8 proxyInstance.open(); 9 }View Code
缺點:JDK代理只能代理介面,不能對類進行代理。並且,如果實現類中新增了介面中沒有的方法,這些方法是無法被代理的(因為無法被呼叫)。
cglib代理:
cglib是針對類來實現代理的,對代理的目標類生成一個子類,並覆蓋其中方法實現增強,因為底層是基於建立被代理類的子類,所以它避免了JDK動態代理類的缺陷。但因為採用的是繼承,所以不能代理final類。(final類不可被繼承)
匯入Maven依賴:
cglib 是基於asm 位元組修改技術。匯入 cglib 會間接匯入 asm, ant, ant-launcher 三個jar 包
1 <dependency> 2 <groupId>cglib</groupId> 3 <artifactId>cglib</artifactId> 4 <version>3.2.5</version> 5 </dependency>View Code
實現類:
1 public class App { 2 public void open() { 3 System.out.println("開啟APP"); 4 } 5 }View Code
代理類:
1 import java.lang.reflect.Method; 2 3 import net.sf.cglib.proxy.Enhancer; 4 import net.sf.cglib.proxy.MethodInterceptor; 5 import net.sf.cglib.proxy.MethodProxy; 6 7 public class ProxyFactory implements MethodInterceptor { 8 9 //維護一個目標物件 10 private Object target; 11 12 //構造器,傳入一個被代理的物件 13 public ProxyFactory(Object target) { 14 this.target = target; 15 } 16 17 //返回一個代理物件: 是 target 物件的代理物件 18 public Object getProxyInstance() { 19 //1. 建立一個工具類 20 Enhancer enhancer = new Enhancer(); 21 //2. 設定父類 22 enhancer.setSuperclass(target.getClass()); 23 //3. 設定回撥函式 24 enhancer.setCallback(this); 25 //4. 建立子類物件,即代理物件 26 return enhancer.create(); 27 } 28 29 //重寫 intercept 方法,會呼叫目標物件的方法 30 @Override 31 public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable { 32 System.out.println("cglib代理開始"); 33 Object res = method.invoke(target, args); 34 System.out.println("cglib代理提交"); 35 return res; 36 } 37 }View Code
client:
1 public static void main(String[] args) { 2 // 被代理物件 3 App target = new App(); 4 //代理物件 5 App proxyInstance = (App) new ProxyFactory(target).getProxyInstance(); 6 7 proxyInstance.open(); 8 }View Code
由於是繼承方式,static方法,private方法,final方法等不能被代理
cglib會預設代理Object中equals、toString、hashCode、clone等方法。比JDK代理多了clone
總結:
靜態代理在編譯時產生class位元組碼檔案,可以直接使用,效率高。
JDK代理必須實現 InvocationHandler 介面,通過 invoke 呼叫被代理類介面方法是通過反射的方式,比較消耗系統性能,但可以減少代理類的數量,使用更靈活。
cglib代理無需實現介面,通過生成類位元組碼實現代理,比反射稍快,不存在效能問題,應用更加廣泛。但cglib會繼承目標物件,需要重寫方法,所以目標物件不能為final類。