1. 程式人生 > >帶你用例項學習代理模式:靜態代理、動態代理(JDK、CGlib)以及區別和優缺點

帶你用例項學習代理模式:靜態代理、動態代理(JDK、CGlib)以及區別和優缺點

Spring AOP的核心技術就是動態代理,所以小編學習並整理了代理模式的材料,供大家一起學習。

1、代理模式滿足的三個必要條件:

  • 兩個角色:執行者、被代理物件
  • 這個過程必須要做,但是自己不能做或者不想做,交給專業的人(媒婆)
  • 執行者必須拿到被代理物件的引用(需要知道你要什麼資訊)
  •  

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

 

3、靜態代理:

 

a.定義一個介面

public interface Count(){

public void queryCount();

}

 

b.業務邏輯類實現介面

public class CountImpl implements Count(){

@Override

public void queryCount(){

System.out.println("實現業務邏輯類");

}

}

 

c.定義業務代理類

public class CountProxy implements Count(){

private CountImpl countImpl;

 

public CountProxy(CountImpl countImpl){

this.countImpl = countImpl;

}

 

@Override

public void queryCount(){

System.out.println("在呼叫代理業務類之前執行");

countImpl.queryCount();

System.out.println("在呼叫代理業務類之後執行");

}

}

 

d.呼叫

public static void main(String []args){

CountImpl countImpl = new CountImpl();

CountProxy countProxy = new CountProxy(countImpl);

countProxy.queryCount();

}

缺點:一個代理類只能代理一個業務介面,如果要代理多個業務介面需要定義多個實現類和代理類,如果在呼叫代理業務類前後的程式碼是一樣的,則多個代理類會有很多冗餘程式碼。

 

4、動態代理:根據傳進來的業務實現類和方法名進行具體呼叫

JDK動態代理

a.定義業務邏輯介面

public interface BookFacade(){

public void addBook();

}

 

b.定義業務實現類

public class BookFacadeImpl implements BookFacade(){

@Override

public void addBook(){

System.out.printin("增加圖書的方法");

}

}

 

c.建立動態代理類:InvocationHandler是呼叫管理介面

public class BookFacadeProxy implements InvocationHandler {

// 業務實現類物件,用來呼叫具體的業務方法

private Object target;

 

// 繫結業務物件並返回一個代理類

public Object bind(Object targer) {

this.targer = targer;

 

// 通過反射機制,建立一個代理類物件並返回例項,使用者進行方法呼叫時使用

// 建立代理物件時,需要傳遞該業務類的類載入器(用來獲取業務實現類的元資料,呼叫真的的業務方法)、介面、handler實現類

return Proxy.newProxyInstance(this.targer.getClass().getClassLoader(), this.targer.getClass().getInterfaces(), this);

}

 

// 包裝呼叫方法:進行預處理、呼叫後處理

public Object invoke(Object proxy, Method method, Object []args) throws Throwable {

Object result = null;

System.out.println("進行預處理");

// 呼叫真正的業務方法

method.invoke(targer, args)

System.out.println("進行呼叫後處理");

}

}

 

d.在使用時,先建立一個業務實現類物件和一個代理類物件,然後定義介面引用(向上轉型)並用代理物件.bind(業務實現類物件)的返回值進行賦值,最後通過介面引用呼叫真實業務方法。(介面引用指向一個綁定了業務類的代理類物件,所以通過介面名呼叫的是被代理的方法們)

public static void main(String []args) {

BookFacadeImpl bookFacadeImpl = new BookFacadeImpl();

BookFacadeProxy proxy = new BookFacadeProxy();

BookFacade bookFacade = (BookFacade)proxy.bind(bookFacadeImpl);

bookFacade.addBook();

}

缺點:JDK動態代理的代理物件在建立時,需要有業務實現類所實現的介面作為引數(因為後面代理方法需要根據介面內的方法名進行呼叫)。如果業務實現類沒有實現介面而是直接定義介面的話,就無法使用JDK動態代理。並且如果業務實現類中新增了介面中沒有的方法,這些方法也是無法被代理的(因為無法呼叫)。

 

CGlib動態代理:是根據針對類來實現代理的,原理是對指定的業務類生成一個子類,並覆蓋其中業務方法實現代理,(因為採用的是繼承,所以不能對final修飾的類進行代理)。

 

a.定義業務類,無需實現介面

public calss BookFacadeImpl1 {

public void addBook() {

System.out.println("增加一本書");

}

}

 

b.實現MethodInterceptor方法代理介面,建立代理類

public class BookFacadeCglib implements MethodInterceptor {

// 業務物件類,代理方法中進行真正的業務方法呼叫

private Object targer;

// 相當於JDK動態代理的bind 繫結

public Object getInstance(Object targer) {

this.targer = targer;

// 建立加強器,用來建立動態代理

Enhancer enhancer = new Enhancer();

// 加強器要指定代理的業務類(即:為下面生成的代理類指定父類)

enhancer.setSupperclass(this.targer.getClass());

// 設定回撥:對於代理類上所有方法的呼叫,都會呼叫CallBack,

enhancer.setCallBack(this);

// 建立動態代理物件並返回

return enhancer.create();

}

// 實現回撥方法

public Object intercept(Object obj, Method method, Object []args, MethodProxy proxy) {

System.out.println("預處理方法");

// 呼叫父類的方法

proxy.invokeSuper(obj, args);

System.out.println("呼叫後方法");

}

}

 

c.建立業務類物件和代理類物件,代理類物件.getInstance(業務類物件)返回一個動態代理物件(它是業務類的子類,可以用業務類引用指向它),最後通過動態代理類物件進行方法呼叫。

public static void main(String []args) {

BookFacadeImpl1 bookFacadeImpl1 = new BookFacadeImpl1();

BookFacadeCglib bookFacadeCglib = new BookFacadeCglib();

BookFacadeImpl1 bookFacade = (BookFacadeImpl1)bookFacadeCglib.getInstance(bookFacadeImpl1);

bookFacade.addBook();

}

 

5.靜態代理、JDK動態搭理、CGlib動態代理比較:

靜態代理:通過在程式碼中顯式定義一個業務類一個代理,在代理類中對業務方法進行包裝,使用者通過代理類呼叫被包裝過的業務方法。

缺點:每個代理類只能代理一個業務類,如果有多個業務類需要代理則需要些多個代理類

JDK動態代理:通過傳進來的業務實現類和方法進行呼叫業務實現類的同名方法

缺點:如果該業務實現類沒有實現介面而是直接定義介面,或者是該業務實現類中增加了介面沒有的方法,則就無法被代理類呼叫

CGlib動態代理:通過繼承業務類,生成的動態代理類是業務類的子類,通過重寫業務方法進行呼叫

 

據說原來的CGlib比JDK的動態代理效能要高出很多,但是由於JDK6 7 8逐漸完善,差距越來越小,而且有被反超的可能