關注公衆號 JavaStorm 獲取更多技術。java
爲另外一個對象提供表明,以便控制客戶對對象的訪問。其定義爲:爲另外一個對象提供替身或佔位符以訪問這個對象。具體地嗎能夠瀏覽 github.com/UniqueDong/…git
官話上講是一種設計模式,目的是但願代碼重用。跟咱們以往訪問對象的方式不一樣,代理模式不是直接經過目標對象,而是經過代理訪問咱們的目標對象以及方法。由於有的時候咱們沒法直接與目標對象創建聯繫或者,咱們要控制客戶端訪問。因此便經過代理來訪問咱們的真實對象。github
就比如「客戶」-> 「明星經紀人」-> 「明星」。咱們不是直接與明星聯繫,明星很忙的,要唱歌跳舞燙頭拍電影,給的價格足夠好,經紀人才告知明星接下這個任務。設計模式
咱們根據加載被代理類的時機不一樣,將代理分爲靜態代理和動態代理。若是咱們在代碼編譯時就肯定了被代理的類是哪個,那麼就能夠直接使用靜態代理;若是不能肯定,那麼可使用類的動態加載機制,在代碼運行期間加載被代理的類這就是動態代理,好比RPC框架和Spring AOP機制。bash
咱們按照 UML 類圖來實現一個簡單的靜態代理模式,首先先建立一個 Subject 接口。網絡
public interface Subject {
public void request();
}
複製代碼
建立一個真實對象去實現該接口app
public class RealSubjbect implements Subject {
@Override
public void request() {
System.out.println("this is RealSubjbect.request()");
}
}
複製代碼
建立咱們的代理類,持有真實對象的引用,同時實現了 Subject 接口。框架
public class ProxySubject implements Subject {
private Subject realSubjbect = null;
/** * 除了代理真實角色作該作的事,代理角色提供附加操做 * 如 */
@Override
public void request() {
preRequest(); //真實角色操做前的附加操做
if (realSubjbect == null) {
realSubjbect = new RealSubjbect();
}
realSubjbect.request();
postRequest();//真實角色操做後的附加操做
}
/** * 真實角色操做前的附加操做 */
private void postRequest() {
System.out.println("真實角色操做後的附加操做");
}
/** * 真實角色操做後的附加操做 */
private void preRequest() {
System.out.println("真實角色操做前的附加操做");
}
}
複製代碼
編寫咱們的客戶端接口,經過代理類調用。ide
public class Client {
public static void main(String[] args) {
Subject subject = new ProxySubject();
subject.request(); //代理者代替真實者作事情
}
}
複製代碼
打印的結果以下所示,咱們實現了對真實對象的控制,而且新增一些操做。就像Spring的AOP實現的功能同樣。post
真實角色操做前的附加操做
this is RealSubjbect.request()
真實角色操做後的附加操做
複製代碼
另外,若是要按照上述的方法使用代理模式,那麼真實角色(委託類)必須是事先已經存在的,並將其做爲代理對象的內部屬性。可是實際使用時,一個真實角色必須對應一個代理角色,若是大量使用會致使類的急劇膨脹;此外,若是事先並不知道真實角色(委託類),該如何使用代理呢?這個問題能夠經過Java的動態代理類來解決。
動態代理類的源碼是在程序運行期間 JVM 根據反射機制動態生成的,因此不存在代理類的字節碼文件。代理類和委託類的關係是在程序運行時肯定。
主要有兩種實現方式:
UML 類圖以下所示
Handler
實現 InvocationHandler
,同時持有真實對象的引用,會依賴咱們的業務類。Proxy.newProxyInstance()
生成,而該方法以來於 Handler
對象和 真實對象 。經過類圖其實咱們也能夠知道,當經過動態代理生成的代理字節碼調用的時候就會委託到 Handler 的 invoke 方法。同時 Handler 又持有真正的業務對象。因此能在執行調用真實的對象以前控制其行爲以及訪問。主要優勢:
經過類圖,建立 JDK 動態代理咱們能夠分爲三步。
InvocationHandler
接口,並依賴被代理類(真實對象的引用)。Proxy.newProxyInstance()
方法建立具體代理對象。如今咱們有一個需求,爲咱們的業務邏輯統一記錄調用日誌,或者事務控制,在這裏咱們那就編寫一個日誌記錄爲咱們的被代理類記錄日誌。模擬相似 Spring AOP功能,一個簡化版的例子讓你們明白其使用場景以及原理。
第一步:咱們定義本身的業務接口 OrderService
跟 ProductService
public interface OrderService {
String createOder(String no);
}
public interface ProductService {
String getProduct(String no);
}
複製代碼
業務邏輯的實現類
public class OrderServiceImpl implements OrderService {
@Override
public String createOder(String no) {
return "生成訂單" + no + "成功";
}
}
public class ProductServiceImpl implements ProductService {
@Override
public String getProduct(String no) {
return "獲取商品" + no + "成功";
}
}
複製代碼
第二步:定義咱們的 Handler 實現 JDK 的 Invocationhandler,將持有的委託引用定義 Object
類型,這樣能夠代理全部須要日誌記錄的委託類,同時咱們有一個 bind()
方法,用於設置 target 同時返回 對應的代理類讓客戶端調用。第三步的 Proxy.newProxyInstance() 咱們放在 bind 方法裏。代碼更加簡潔。
public class LoggerInterceptor implements InvocationHandler {
/** * 持有委託類的引用 */
private Object target;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
System.out.println(proxy.getClass().getInterfaces()[0]);
//經過反射調用
System.out.println("Entered " + target.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
//調用目標對象的方法
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println("執行結果:" + result.toString());
System.out.println("共耗時:" + (end - start));
return result;
}
/** * 獲取代理類:並將 target 委託類綁定到 咱們定義的Targe中 * @param target 真實的委託類 * @return */
public synchronized Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
複製代碼
最後咱們新建啓動類看效果,分別有兩個業務邏輯類須要日誌記錄。具體可看代碼註釋
public class BootStrap {
public static void main(String[] args) {
// 新建業務邏輯
OrderService orderService = new OrderServiceImpl();
ProductService productService = new ProductServiceImpl();
// 新建咱們的 日誌 Handler
LoggerInterceptor loggerInterceptor = new LoggerInterceptor();
//綁定 委託類同時生產代理類調用 。
OrderService orderServiceProxy = (OrderService) loggerInterceptor.bind(orderService);
orderServiceProxy.createOder("12927381");
ProductService productServiceProxy = (ProductService) loggerInterceptor.bind(productService);
productServiceProxy.getProduct("34010234");
}
}
複製代碼
結果打印以下所示:咱們能夠看到,每一個咱們所代理的接口執行方法先後都打印了日誌以及返回結果。其實 Spring AOP 就是經過動態代理實現。
nterface com.zero.headfirst.proxy.service.OrderService
Entered com.zero.headfirst.proxy.service.OrderServiceImpl-createOder,with arguments{12927381}
執行結果:生成訂單12927381成功
共耗時:2
interface com.zero.headfirst.proxy.service.ProductService
Entered com.zero.headfirst.proxy.service.ProductServiceImpl-getProduct,with arguments{34010234}
執行結果:獲取商品34010234成功
共耗時:1
複製代碼
CGLIB是一個強大的高性能的代碼生成包。它普遍的被許多AOP的框架使用,例如Spring AOP爲他們提供方法的interception(攔截)。CGLIB包的底層是經過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
不能是 final 修飾的類,以及final方法,能夠不定義接口。
使用方式很簡單:咱們要先添加cglib 依賴 ,新建一個類實現 MethodInterceptor 。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LoggerProxy implements MethodInterceptor {
/** * 建立代理類 * @param targetClass 委託類 * @return */
public Object bind(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
//設置回調方,當客戶端經過代理調用方法的時候會會調用咱們重寫的 intercept() 方法
enhancer.setCallback(this);
//建立代理類
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
long start = System.currentTimeMillis();
//經過反射調用
System.out.println("Entered " + o.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
Object result = methodProxy.invokeSuper(o, args);
long end = System.currentTimeMillis();
System.out.println("執行結果:" + result.toString());
System.out.println("共耗時:" + (end - start));
return result;
}
}
複製代碼
是否是很簡單?新建 Enhancer ,只要設置好 委託類以及回調類。在這裏咱們能夠利用工廠模式 建立不一樣的代理類對應的回調。這裏簡單實例就不寫了。
接下來看咱們的測試類以及結果
public class CglibBootStrap {
public static void main(String[] args) {
LoggerProxy loggerProxy = new LoggerProxy();
OrderService orderService = (OrderService) loggerProxy.bind(OrderServiceImpl.class);
orderService.createOder("12873051209g");
ProductService productProxy = (ProductService) loggerProxy.bind(ProductServiceImpl.class);
productProxy.getProduct("2780935782309");
}
}
複製代碼
打印結果以下所示
Entered com.zero.headfirst.proxy.service.OrderServiceImpl$$EnhancerByCGLIB$$54d983a1-createOder,with arguments{12873051209g}
執行結果:生成訂單12873051209g成功
共耗時:14
Entered com.zero.headfirst.proxy.service.ProductServiceImpl$$EnhancerByCGLIB$$4e2c7c36-getProduct,with arguments{2780935782309}
執行結果:獲取商品2780935782309成功
共耗時:5
複製代碼