cglib動態代理。上一篇在寫aop的時候提了一下代理模式。今天來看一下,cglib動態代理。
首先,動態代理是通過反射進行執行的。運行時編譯。其次,它也具備代理模式的共性,都是在主業務邏輯開始之前和之後進行一些操作,不需要修改原來的業務代碼。
拿http://blog.csdn.net/xiaohai0504/article/details/6832990做介紹
1. 我們創建一個對Table操作的DAO類,提供了CQUD方法。
BookServiceBean.java
- package com.tech.cglibx;
- public class BookServiceBean {
- public void create(){
- System.out.println("create() is running !");
- }
- public void query(){
- System.out.println("query() is running !");
- }
- public void update(){
- System.out.println("update() is running !");
- }
- public void delete(){
- System.out.println("delete() is running !");
- }
- }
OK,它就是一個javaBean,提供了CQUD方法的javaBean。
下面我們創建一個DAO工廠,用來生成DAO實例。
- package com.tech.cglibx;
- public class BookServiceFactory {
- private static BookServiceBean service = new BookServiceBean();
- private BookServiceFactory() {
- }
- public static BookServiceBean getInstance() {
- return service;
- }
- }
接下來我們創建客戶端,用來調用CQUD方法。
- public class Client {
- public static void main(String[] args) {
- BookServiceBean service = BookServiceFactory.getInstance();
- doMethod(service);
- }
- public static void doMethod(BookServiceBean service){
- service.create();
- service.update();
- service.query();
- service.delete();
- }
- }
OK,完成了,CQUD方法完全被調用了。
當然這裏並沒有CGlib的任何內容。問題不會這麼簡單的就結束,新的需求來臨了。
2. one day,Boss告訴我們這些方法不能開放給用戶,只有“zhangsan”纔有權使用。怎麼辦,難道我們要在每個方法上面進行判斷嗎?好像這麼做也太那啥了吧?對了,Proxy可能是最好的解決辦法。jdk的代理就可以解決了。 好了我們來動手改造吧。等等jdk的代理需要實現接口,這樣, 我們的dao類需要改變了。既然不想改動dao又要使用代理,我們這就請出CGlib。
我們只需新增一個權限驗證的方法攔截器。
主業務邏輯不變:
package com.baidu.cglib.service;
public class BookServiceBean {
public void create() {
System.out.println("create() is running !");
}
public void query() {
System.out.println("query() is running !");
}
public void update() {
System.out.println("update() is running !");
}
public void delete() {
System.out.println("delete() is running !");
}
}
增加cglib動態代理類:
package com.baidu.cglib.intercept;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyCglibProxy implements MethodInterceptor{
public Enhancer enhancer = new Enhancer(); //用來製作一個對象的代理對象
private String name;
public MyCglibProxy(String name) {
this.name = name;
}
// 冗餘代碼,在fctory中並沒有調用該方法
// public Object getDaoBean(Class clas) {
// enhancer.setSuperclass(clas);
// enhancer.setCallback(this);
// return enhancer.create();
// }
public Object intercept(Object obj, Method method, Object[] arg,
MethodProxy methodProxy) throws Throwable {
System.out.println(obj.getClass()); // EnhancerByCGLIB類型。就是指的是類中enhancer
System.out.println(arg.getClass());
System.out.println(method.getName());
if(!"zhangsan".equals(name)) {
System.out.println("you have no power");
return null;
}
Object result = methodProxy.invokeSuper(obj,arg);
return result;
}
}
enhancer的setSuperClass(clas),作用就是設置這個enhancer生成的代理對象是繼承clas對象的。由此可以看出,cglib動態代理模式的實現其實是通過繼承來做的。然後同這裏是在factory類中調用的。
fctory代碼:
package com.baidu.cglib.factory;
import net.sf.cglib.proxy.Enhancer;
import com.baidu.cglib.intercept.MyCglibProxy;
import com.baidu.cglib.service.BookServiceBean;
public class BookServiceFactory {
private static BookServiceBean service = new BookServiceBean();
private BookServiceFactory() {}
public static BookServiceBean getBean() {
return service;
}
public static BookServiceBean getProxyInstance(MyCglibProxy myProxy) {
Enhancer en = new Enhancer();
en.setSuperclass(BookServiceBean.class);
en.setCallback(myProxy); // 設置回調,在myproxy中調用Bookservice中的方法
return (BookServiceBean)en.create();
}
}
客戶端測試代碼:
package com.baidu.cglib.client;
import com.baidu.cglib.factory.BookServiceFactory;
import com.baidu.cglib.intercept.MyCglibProxy;
import com.baidu.cglib.service.BookServiceBean;
public class Client {
public static void main(String[] args) {
BookServiceBean service = BookServiceFactory.getBean();
doMethod(service);
BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("zhangsan"));
service2.create();
}
public static void doMethod(BookServiceBean service) {
service.create();
service.update();
service.query();
service.delete();
}
}
最後生成:
create() is running !
update() is running !
query() is running !
delete() is running !
class com.baidu.cglib.service.BookServiceBean$$EnhancerByCGLIB$$f98390c1
class [Ljava.lang.Object;
create
create() is running !
這時,所有的業務邏輯就只能有zhangsan去執行了。但是現在如果要開放業務邏輯中的query()方法給所有的人應該怎麼做?這時候就需要用方法過濾器(CallbackFilter),CallbackFilter可以明確表明,被代理的類中不同的方法,被哪個攔截器所攔截。
下面我們就來做個過濾器用來過濾query方法。
package com.baidu.cglib.intercept;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.CallbackFilter;
public class MyProxyFilter implements CallbackFilter{
public int accept(Method method) {
if(!"query".equalsIgnoreCase(method.getName())) {
<span style="white-space:pre"> </span>return 0;
}
return 1;
}
}
return 0或者1對應了攔截器的順序。在factory類中新增加一個被攔截器攔截的方法。
package com.baidu.cglib.factory;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.NoOp;
import com.baidu.cglib.intercept.MyCglibProxy;
import com.baidu.cglib.intercept.MyProxyFilter;
import com.baidu.cglib.service.BookServiceBean;
public class BookServiceFactory {
private static BookServiceBean service = new BookServiceBean();
private BookServiceFactory() {}
public static BookServiceBean getBean() {
return service;
}
public static BookServiceBean getProxyInstance(MyCglibProxy myProxy) {
Enhancer en = new Enhancer();
en.setSuperclass(BookServiceBean.class);
en.setCallback(myProxy); // 設置回調,在myproxy中調用Bookservice中的方法
return (BookServiceBean)en.create();
}
public static BookServiceBean getAuthInstanceByFilter(MyCglibProxy myProxy) {
Enhancer en = new Enhancer();
en.setSuperclass(BookServiceBean.class);
en.setCallbacks(new Callback[] {myProxy,NoOp.INSTANCE});
en.setCallbackFilter(new MyProxyFilter());
return (BookServiceBean)en.create();
}
}
setCallbacks中定義了所使用的攔截器,其中NoOp.INSTANCE是CGlib所提供的實際是一個沒有任何操作的攔截器, 他們是有序的,一定要和CallbackFilter裏面的順序一致。上面return返回(0/1)的就是返回的順序。也就是說如果調用query方法就使用NoOp.INSTANCE進行攔截。
重寫client類:
package com.baidu.cglib.client;
import com.baidu.cglib.factory.BookServiceFactory;
import com.baidu.cglib.intercept.MyCglibProxy;
import com.baidu.cglib.service.BookServiceBean;
public class Client {
public static void main(String[] args) {
BookServiceBean service = BookServiceFactory.getBean();
doMethod(service);
BookServiceBean service2 = BookServiceFactory.getProxyInstance(new MyCglibProxy("zhangsan"));
service2.create();
BookServiceBean service3 = BookServiceFactory.getAuthInstanceByFilter(new MyCglibProxy("jhon"));
service3.query();
service3.create();
}
public static void doMethod(BookServiceBean service) {
service.create();
service.update();
service.query();
service.delete();
}
}