Java動(dòng)態(tài)代理靜態(tài)代理實(shí)例分析
代理模式:為其他對(duì)象提供一種代理以控制某個(gè)對(duì)象的訪問(wèn)。用在:在某些情況下,一個(gè)客戶不想或者不能直接訪問(wèn)另一個(gè)對(duì)象,而代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之前起到中介的作用,代理對(duì)象還可以完成它附加的操作。
例子:就像房東、租客、中介的關(guān)系。中介(代理對(duì)象)為房東(真實(shí)對(duì)象)出租房子,租客(客戶)通過(guò)中介(代理對(duì)象)來(lái)找房子租房子,中介完成了租房以后可以收取中介費(fèi)(附加操作)。
先看看靜態(tài)代理模式,通過(guò)上面對(duì)代理模式的理解,可以了解到代理模式:即不直接通過(guò)new一個(gè)真實(shí)對(duì)象來(lái)調(diào)用方法,而是通過(guò)代理對(duì)象來(lái)調(diào)用一個(gè)方法,所以代理對(duì)象包含真實(shí)對(duì)象的引用。下面看一下代碼
接口:Subject包含一個(gè)方法
package com.example.designpattern.proxy; public interface Subject { void request(); }
RealSubject類,實(shí)現(xiàn)了Subject接口,為了簡(jiǎn)單起見(jiàn),方法簡(jiǎn)單的輸出一句話:
package com.example.designpattern.proxy; public class RealSubject implements Subject { //真是角色實(shí)現(xiàn)了 public void request() { System.out.println("From real subject"); } }
代理類ProxySubject,也要實(shí)現(xiàn)Subject接口,實(shí)現(xiàn)Subject里面的方法,但是在這里里面是通過(guò)調(diào)用真實(shí)對(duì)象來(lái)實(shí)現(xiàn)的。
package com.example.designpattern.proxy; public class ProxySubject implements Subject { private RealSubject realSubject; //代理角色內(nèi)部引用了真實(shí)角色 //代理角色實(shí)現(xiàn)目標(biāo)動(dòng)作 public void request() { this.preRequest(); //在真實(shí)角色操作之前所附加的操作 if (realSubject == null){ realSubject = new RealSubject(); } realSubject.request(); // 真實(shí)角色所完成的事情 this.afterRequet(); //在真實(shí)角色操作之后附加的操作 } //代理角色之前完成的動(dòng)作 private void preRequest(){ System.out.println("pre request"); } //代理角色之后完成的動(dòng)作 private void afterRequet(){ System.out.println("after request"); } }
客戶調(diào)用者
package com.example.designpattern.proxy; public class Client { public static void main(String[] args) { ProxySubject proxy = new ProxySubject(); //通過(guò)代理對(duì)象來(lái)調(diào)用方法 proxy.request(); } }
靜態(tài)代理:
可以運(yùn)行一下這些代碼哦, 可以在Client類中看到,是通過(guò)代理ProxySubject的對(duì)象proxy來(lái)調(diào)用方法的,在代理類ProxySubject中,有一個(gè)真實(shí)對(duì)象的引用,在代理對(duì)象的中request()方法調(diào)用了真實(shí)對(duì)象的方法。這樣的模式叫做代理模式。
優(yōu)點(diǎn)是:
1. 代理模式能將代理對(duì)象與真實(shí)對(duì)象被調(diào)用的目標(biāo)對(duì)象分離。
2. 一定程度上降低了系統(tǒng)的耦合度,擴(kuò)展性好。
代理類中包含了對(duì)真實(shí)主題的引用,這樣做也有缺點(diǎn):
1. 真實(shí)對(duì)象與代理類一一對(duì)應(yīng),增加真實(shí)類也要增加代理類,這樣做會(huì)快速的增加類的數(shù)量,使得系統(tǒng)變得復(fù)雜。
2. 設(shè)計(jì)代理以前真實(shí)主題必須事先存在,不太靈活。
采用動(dòng)態(tài)代理可以解決以上問(wèn)題,動(dòng)態(tài)代理是相對(duì)于靜態(tài)代理來(lái)說(shuō)的。
可能你也會(huì)說(shuō)怎么樣實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建實(shí)例,以前我們創(chuàng)建實(shí)例不都是通過(guò)new 的方式來(lái)實(shí)現(xiàn)的嗎?
Hello hi = new Hello();
那么動(dòng)態(tài)創(chuàng)建實(shí)例是由Java提供的功能,就不需要我們?nèi)ew 對(duì)象,他已經(jīng)定義好了靜態(tài)方法Proxy.newProxyInstance(),只要傳入?yún)?shù)調(diào)用就可以。Java文檔里面有哦,如圖:
Java標(biāo)準(zhǔn)庫(kù)提供了一種動(dòng)態(tài)代理(DynamicProxy)的機(jī)制:可以在運(yùn)行期動(dòng)態(tài)創(chuàng)建某個(gè)interface的實(shí)例。
參數(shù)解釋:
Proxy.newProxyInstance( ClassLoader loader, // 傳入ClassLoader Class<?>[] interfaces, // 傳入要調(diào)用的接口的方法數(shù)組 InvocationHandler h); //傳入InvocationHandler 的實(shí)例
下面看一下動(dòng)態(tài)代理例子代碼:
Subject 接口
package design.dynamicproxy; public interface Subject { void request(String str); }
RealSubject類 實(shí)現(xiàn) Subject 接口
package design.dynamicproxy; public class RealSubject implements Subject { @Override public void request(String str) { System.out.println("From Real Subject!" + " args:" + str ); } }
動(dòng)態(tài)代理類DynamicSubject 實(shí)現(xiàn)了InvocationHandler,重寫invoke()方法
package design.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 該代理類的內(nèi)部屬性時(shí)Object類型,實(shí)際使用時(shí),使用該類的構(gòu)造方法傳遞一個(gè)對(duì)象 * 此外該類還實(shí)現(xiàn)了invoke() 方法,該方法中的method.invoke() 其實(shí)就是要調(diào)用被代理對(duì)象的要執(zhí)行的方法 * 方法參數(shù)是object,表示該方法從屬于object對(duì)象,通過(guò)動(dòng)態(tài)代理類,我們可以在執(zhí)行真是對(duì)象的 * 方法前后可以加入一些額外的方法 */ public class DynamicSubject implements InvocationHandler { //引入的類型是Object的,可以隨便傳入任何一個(gè)對(duì)象 private Object object; public DynamicSubject(Object object){ this.object = object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling:" + method); //等價(jià)于realSubject的request() 方法,如果這里不調(diào)用的話,不會(huì)調(diào)用Method 對(duì)象中的方法 method.invoke(object, args); System.out.println("after calling:" + method); return null; } }
Client類
package design.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); InvocationHandler handler = new DynamicSubject(realSubject); Class<?> classType = handler.getClass(); //下面的代碼一次性生成代理 // 動(dòng)態(tài)生成了class com.sun.proxy.$Proxy0 的實(shí)例, Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(),handler); subject.request("eather"); System.out.println(subject.getClass()); //輸出class com.sun.proxy.$Proxy0,可以看到Proxy.newProxyInstance() 是系統(tǒng)自動(dòng)生成的實(shí)例 } }
在Client中可以看到,我們這里調(diào)用方法的是 subject.request("eather"); 這個(gè)對(duì)象subject 不是通過(guò)new DynamicSubject()生成的,而是Java內(nèi)部寫好的方法在運(yùn)行時(shí)動(dòng)態(tài)生成對(duì)象;可能有人說(shuō)
InvocationHandler handler = new DynamicSubject(realSubject);
這里不是通過(guò)new new DynamicSubject(realSubject); 生成了一個(gè)對(duì)象嗎? 是的,但是它是InvocationHandler 類型的,主要是傳遞一個(gè)InvocationHandler類型參數(shù)給Proxy.newProxyInstance(); 即最后一個(gè)參數(shù)。通過(guò)Client類的最后一句輸出可以看到它是 class com.sun.proxy.$Proxy0 ,這是Java運(yùn)行時(shí)生成的。
解決了靜態(tài)代理的難題:1. 真實(shí)對(duì)象與代理類一一對(duì)應(yīng),增加真實(shí)類也要增加代理類,這樣做會(huì)快速的增加類的數(shù)量,使得系統(tǒng)變得復(fù)雜。 為什么這么說(shuō)呢, 因?yàn)榇眍愐玫念愋褪荗bject的,可以隨便傳入任何一個(gè)對(duì)象,當(dāng)真實(shí)類增加時(shí),代理類不用增加,new DynamicSubject(object); new的時(shí)候把要傳入的對(duì)象傳進(jìn)去即可。
下面是Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h); 這個(gè)方法的源碼啦,可以看看,深入了解一下
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. 生成一個(gè)代理類對(duì)象 */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. 使用指定的調(diào)用處理程序調(diào)用其構(gòu)造函數(shù)。就是使用InvocationHandler 實(shí)例調(diào)用【要調(diào)用方法的那個(gè)類】的構(gòu)造方法 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } } ); } return cons.newInstance(new Object[]{ h } ); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 深度剖析java動(dòng)態(tài)靜態(tài)代理原理源碼
- Java代理模式實(shí)例詳解【靜態(tài)代理與動(dòng)態(tài)代理】
- java代理模式(靜態(tài)代理、動(dòng)態(tài)代理、cglib代理)
- Java靜態(tài)代理與動(dòng)態(tài)代理案例詳解
- 代理模式:JAVA靜態(tài)代理和動(dòng)態(tài)代理的實(shí)例和實(shí)現(xiàn)詳解
- JAVA代理,靜態(tài),動(dòng)態(tài)詳解
- Java?靜態(tài)代理與動(dòng)態(tài)代理解析
- Java的動(dòng)態(tài)代理和靜態(tài)代理詳解
- 深入理解Java動(dòng)態(tài)代理與靜態(tài)代理
相關(guān)文章
SpringBoot實(shí)現(xiàn)連接nacos并支持多環(huán)境部署
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)連接nacos并支持多環(huán)境部署方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06Spring Boot啟動(dòng)過(guò)程(五)之Springboot內(nèi)嵌Tomcat對(duì)象的start教程詳解
這篇文章主要介紹了Spring Boot啟動(dòng)過(guò)程(五)之Springboot內(nèi)嵌Tomcat對(duì)象的start的相關(guān)資料,需要的朋友可以參考下2017-04-04SpringBoot如何獲取application.properties中自定義的值
這篇文章主要介紹了SpringBoot獲取application.properties中的自定義的值,目錄結(jié)構(gòu)文件代碼給大家列舉的非常詳細(xì),需要的朋友可以參考下2021-09-09Java多維數(shù)組和Arrays類方法總結(jié)詳解
這篇文章主要介紹了Java多維數(shù)組和Arrays類方法總結(jié)詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03RocketMQ消息過(guò)濾與查詢的實(shí)現(xiàn)
這篇文章主要介紹了RocketMQ消息過(guò)濾與查詢的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Hibernate雙向多對(duì)多映射關(guān)系配置代碼實(shí)例
這篇文章主要介紹了Hibernate雙向多對(duì)多映射關(guān)系配置代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10Spring Cloud下OAUTH2注銷的實(shí)現(xiàn)示例
本篇文章主要介紹了Spring Cloud下OAUTH2注銷的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Java中documentHelper解析xml獲取想要的數(shù)據(jù)
本文主要介紹了Java中documentHelper解析xml獲取想要的數(shù)據(jù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02