(1)什麼是代理?
大道理上講代理是一種軟體設計模式,目的地希望能做到代碼重用。具體上講,代理這種設計模式是通過不直接通路被代理對象的方式,而通路被代理對象的方法。這個就好比 商戶---->明星經紀人(代理)---->明星這種模式。我們可以不通過直接與明星對話的情況下,而通過明星經紀人(代理)與其産生間接對話。
(2)什麼情況下使用代理?
(1)設計模式中有一個設計原則是開閉原則,是說對修改關閉對擴充開放,我們在工作中有時會接手很多前人的代碼,裡面代碼邏輯讓人摸不着頭腦(sometimes the code is really like shit),這時就很難去下手修改代碼,那麼這時我們就可以通過代理對類進行增強。
(2)我們在使用RPC架構的時候,架構本身并不能提前知道各個業務方要調用哪些接口的哪些方法 。那麼這個時候,就可用通過動态代理的方式來建立一個中間人給用戶端使用,也友善架構進行搭建邏輯,某種程度上也是用戶端代碼和架構松耦合的一種表現。
(3)Spring的AOP機制就是采用動态代理的機制來實作切面程式設計。
(3)靜态代理和動态代理
我們根據加載被代理類的時機不同,将代理分為靜态代理和動态代理。如果我們在代碼編譯時就确定了被代理的類是哪一個,那麼就可以直接使用靜态代理;如果不能确定,那麼可以使用類的動态加載機制,在代碼運作期間加載被代理的類這就是動态代理,比如RPC架構和Spring AOP機制。
(4)動态代理類型
- JDK動态代理
- CGLIB動态代理
JDK動态代理示例
public interface Manager {
public void modify(String msg);
}
public class ManagerImpl implements Manager {
@Override
public void modify(String msg) {
System.out.println("*******modify()方法被調用" + msg);
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class BusinessHandler implements InvocationHandler {
public Object delagate = null;
public Object bind(Object obj){
this.delagate = obj;
return Proxy.newProxyInstance(this.delagate.getClass().getClassLoader(), this.delagate.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do something before method");
Object ret = method.invoke(this.delagate, args);
System.out.println("do something after method");
return ret;
}
}
public class TestJDKProxy {
public static void main(String[] args) {
// 元對象(被代理對象)
ManagerImpl managerImpl = new ManagerImpl();
// 業務代理類
BusinessHandler securityHandler = new BusinessHandler();
Manager managerProxy = (Manager) securityHandler.bind(managerImpl);
managerProxy.modify("hello steve!");
}
}
CGLIB動态代理示例
public interface BookFacade {
public void addBook();
}
public class BookFacadeImpl implements BookFacade {
@Override
public void addBook() {
System.out.println("增加圖書的普通方法...");
}
}
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 BookFacadeCglib implements MethodInterceptor {
private Object target;
/**
* 建立代理對象
*
* @param target
* @return
*/
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回調方法
enhancer.setCallback(this);
// 建立代理對象
return enhancer.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("事物開始");
proxy.invokeSuper(obj, args);
System.out.println("事物結束");
return null;
}
}
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib = new BookFacadeCglib();
BookFacade bookCglib = (BookFacade) cglib.getInstance(new BookFacadeImpl());
bookCglib.addBook();
}
}
兩者差別
一、簡單來說:
- JDK動态代理隻能對實作了接口的類生成代理,而不能針對類
- CGLIB是針對類實作代理,主要是對指定的類生成一個子類,覆寫其中的方法(繼承)
二、Spring在選擇用JDK還是CGLiB的依據:
- 當Bean實作接口時,Spring就會用JDK的動态代理
- 當Bean沒有實作接口時,Spring使用CGlib是實作
- 可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
三、CGlib比JDK快?
- 使用CGLib實作動态代理,CGLib底層采用ASM位元組碼生成架構,使用位元組碼技術生成代理類,比使用Java反射效率要高。唯一需要注意的是,CGLib不能對聲明為final的方法進行代理,因為CGLib原理是動态生成被代理類的子類。
- 在對JDK動态代理與CGlib動态代理的代碼實驗中看,1W次執行下,JDK7及8的動态代理性能比CGlib要好20%左右。