1. 程式人生 > 實用技巧 >【Vue CLI】手把手教你擼外掛

【Vue CLI】手把手教你擼外掛

為其他物件提供一種代理以控制對這個物件的訪問。

代理模式分為:靜態代理、動態代理(JDK代理、cglib代理)

解決問題:

在面向物件系統中,有些物件由於某些原因(比如物件建立開銷很大,或者某些操作需要安全控制,或者需要程序外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此物件時加上一個對此物件的訪問層。

UML:

靜態代理:

代理物件和被代理物件需要實現相同的介面或繼承自相同的父類

 1 // 抽象介面
 2 interface IApp {
 3     void open();
 4 }
 5 
 6 public class AppImpl implements
IApp { 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 @Override
24 public void open() { 25 System.out.println("查詢APP路徑"); 26 // 呼叫被代理物件的方法 27 target.open(); 28 // System.out.println("xxx"); 29 } 30 }
View Code

Client:

1 public static void main(String[] args) {
2     // 被代理物件
3     AppImpl app = new AppImpl();
4     //代理物件
5     AppProxy appProxy = new
AppProxy(app); 6 7 appProxy.open(); 8 }
View Code

執行結果:

缺點:需要建立多個代理類,增加閱讀的複雜性

如果介面增加方法、代理物件和目標物件都需要修改

動態代理:

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類。