CGlib動態代理-簡單介紹

pphzzs007發表於2011-09-06
CGlib簡單介紹
CGlib概述:
cglib(Code Generation Library)是一個強大的,高效能,高質量的Code生成類庫。它可以在執行期擴充套件Java類與實現Java介面。
cglib封裝了asm,可以在執行期動態生成新的class。
cglib用於AOP,jdk中的proxy必須基於介面,cglib卻沒有這個限制。

CGlib應用:
以一個例項在簡單介紹下cglib的應用。
我們模擬一個虛擬的場景,關於資訊的管理。

1)原始需求是任何人可以操作資訊的create,update,delete,query操作。
InfoManager.java--封裝對資訊的操作
public class InfoManager {
// 模擬查詢操作
public void query() {
System.out.println("query");
}
// 模擬建立操作
public void create() {
System.out.println("create");
}
// 模擬更新操作
public void update() {
System.out.println("update");
}
// 模擬刪除操作
public void delete() {
System.out.println("delete");
}
}
InfoManagerFactory.java--工廠類
public class InfoManagerFactory {
private static InfoManager manger = new InfoManager();
/**
* 建立原始的InfoManager
*
* @return
*/
public static InfoManager getInstance() {
return manger;
}
}
client.java--供客戶端呼叫
public class Client {

public static void main(String[] args) {
Client c = new Client();
c.anyonecanManager();
}

/**
* 模擬:沒有任何許可權要求,任何人都可以操作
*/
public void anyonecanManager() {
System.out.println("any one can do manager");
InfoManager manager = InfoManagerFactory.getInstance();
doCRUD(manager);
separatorLine();
}

/**
* 對Info做增加/更新/刪除/查詢操作
*
* @param manager
*/
private void doCRUD(InfoManager manager) {
manager.create();
manager.update();
manager.delete();
manager.query();
}

/**
* 加一個分隔行,用於區分
*/
private void separatorLine() {
System.out.println("################################");
}

}
至此,沒有涉及到cglib的內容,因為需求太簡單了,但是接下來,需求發生了改變,要求:

2)只有一個叫“maurice”的使用者登入,才允許對資訊進行create,update,delete,query的操作。
怎麼辦?難道在每個方法前,都加上一個許可權判斷嗎?這樣重複邏輯太多了,於是乎想到了Proxy(代理模式),但是原先的InfoManager也沒有實現介面,不能採用jdk的proxy。那麼cglib在這邊就要隆重登場。
一旦使用cgblig,只需要新增一個MethodInterceptor的類以及修改factory程式碼就可以實現這個需求。
AuthProxy.java--許可權校驗代理類
public class AuthProxy implements MethodInterceptor {

private String name; // 會員登入名

public AuthProxy(String name) {
this.name = name;
}

/**
* 許可權校驗,如果會員名為:maurice,則有許可權做操作,否則提示沒有許可權
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (!"maurice".equals(this.name)) {
System.out.println("AuthProxy:you have no permits to do manager!");
return null;
}
return proxy.invokeSuper(obj, args);
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}
InfoManagerFactory.java--程式碼變動如下:
public class InfoManagerFactory {

/**
* 建立帶有許可權檢驗的InfoManager
*
* @param auth
* @return
*/
public static InfoManager getAuthInstance(AuthProxy auth) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(InfoManager.class);
enhancer.setCallback(auth);
return (InfoManager) enhancer.create();
}


}
client.java--程式碼修改如下
public class Client {

public static void main(String[] args) {
Client c = new Client();
c.haveNoAuthManager();
c.haveAuthManager();
}

/**
* 模擬:登入會員沒有許可權
*/
public void haveNoAuthManager() {
System.out.println("the loginer's name is not maurice,so have no permits do manager ");
InfoManager noAuthManager = InfoManagerFactory.getAuthInstance(new AuthProxy("maurice1"));
doCRUD(noAuthManager);
separatorLine();
}

/**
* 模擬:登入會員有許可權
*/
public void haveAuthManager() {
System.out.println("the loginer's name is maurice,so have permits do manager ");
InfoManager authManager = InfoManagerFactory.getAuthInstance(new AuthProxy("maurice"));
doCRUD(authManager);
separatorLine();
}

/**
* 對Info做增加/更新/刪除/查詢操作
*
* @param manager
*/
private void doCRUD(InfoManager manager) {
manager.create();
manager.update();
manager.delete();
manager.query();
}

/**
* 加一個分隔行,用於區分
*/
private void separatorLine() {
System.out.println("################################");
}

}
執行下程式碼,發現這時client端中已經加上了許可權校驗。
同樣是InfoManager,為什麼這時能多了許可權的判斷呢?Factory中enhancer.create()返回的到底是什麼物件呢?這個疑問將在第三部分CGlib中解釋。
這邊的程式碼,其實是介紹了cglib中的enhancer功能.

到這裡,參照上面的程式碼,就可以使用cglib帶來的aop功能了。但是為了更多介紹下cglib的功能,模擬需求再次發生變化:

3)由於query功能使用者maurice才能使用,招來其他使用者的強烈的抱怨,所以許可權再次變更,只有create,update,delete,才需要許可權保護,query任何人都可以使用。
怎麼辦?採用AuthProxy,使得InfoManager中的所有方法都被代理,加上了許可權的判斷。當然,最容易想到的辦法,就是在AuthProxy的intercept的方法中再做下判斷,如果代理的method是query,不需要許可權驗證。這麼做,可以,但是一旦邏輯比較複雜的時候,intercept這個方法要做的事情會很多,邏輯會異常的複雜。
幸好,cglib還提供了CallbackFilter。使用CallbackFilter,可以明確表明,被代理的類(InfoManager)中不同的方法,被哪個攔截器(interceptor)攔截。
AuthProxyFilter.java
public class AuthProxyFilter implements CallbackFilter {

private static final int AUTH_NEED = 0;
private static final int AUTH_NOT_NEED = 1;

/**
* <pre>
* 選擇使用的proxy
* 如果呼叫query函式,則使用第二個proxy
* 否則,使用第一個proxy
* </pre>
*/
@Override
public int accept(Method method) {
if ("query".equals(method.getName())) {
return AUTH_NOT_NEED;
}
return AUTH_NEED;
}

}
這段程式碼什麼意思?其中的accept方法的意思是說,如果代理的方法是query(),那麼使用第二個攔截器去攔截,如果代理的方法不是query(),那麼使用第一個攔截器去攔截。所以我們只要再寫一個攔截器,不做許可權校驗就行了。(其實,cglib中的NoOp.INSTANCE就是一個空的攔截器,只要配置上這個就可以了。)
InfoManagerFactory.java--程式碼修改如下:(配置不同的攔截器和filter)
public class InfoManagerFactory {

/**
* 建立不同許可權要求的InfoManager
*
* @param auth
* @return
*/
public static InfoManager getSelectivityAuthInstance(AuthProxy auth) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(InfoManager.class);
enhancer.setCallbacks(new Callback[] { auth, NoOp.INSTANCE });
enhancer.setCallbackFilter(new AuthProxyFilter());
return (InfoManager) enhancer.create();
}

}
記住:setCallbacks中的攔截器(interceptor)的順序,一定要和CallbackFilter裡面指定的順序一致!!切忌。

Client.java
public class Client {

public static void main(String[] args) {
Client c = new Client();
c.selectivityAuthManager();
}

/**
* 模擬:沒有許可權的會員,可以作查詢操作
*/
public void selectivityAuthManager() {
System.out.println("the loginer's name is not maurice,so have no permits do manager except do query operator ");
InfoManager authManager = InfoManagerFactory.getSelectivityAuthInstance(new AuthProxy("maurice1"));
doCRUD(authManager);
separatorLine();
}

/**
* 對Info做增加/更新/刪除/查詢操作
*
* @param manager
*/
private void doCRUD(InfoManager manager) {
manager.create();
manager.update();
manager.delete();
manager.query();
}

/**
* 加一個分隔行,用於區分
*/
private void separatorLine() {
System.out.println("################################");
}

}
此時,對於query的許可權校驗已經被去掉了。


通過一個模擬需求,簡單介紹了cglib aop功能的使用。
CGlib應用非常廣,在spring,hibernate等框架中,被大量的使用。


CGlib原理:
cglib神奇嗎?其實一旦瞭解cglib enhancer的原理,一切就真相大白了。
剛才在第二部分中,有個疑問:enhancer.create()到底返回了什麼物件?

其實在剛才的例子中,cglib在程式碼執行期,動態生成了InfoManager的子類,並且根據CallbackFilter的accept方法,覆寫了InfoManager中的所有方法--去執行相應的MethodInterceptor的intercept方法。

相關文章