proxy代理模式
Ⅰ Chrome瀏覽器怎麼設置代理
操作步驟如下:
1、打開瀏覽器,打開右上角下拉菜單,找到點擊打開設置選項:
Ⅱ java設計模式之代理模式:
很簡單:對外部提供統一的介面方法,而代理類在介面中實現對真實類的附加操作行為,從而可以在不影響外部調用情況下,進行系統擴展。也就是說,我要修改真實角色的操作的時候,盡量不要修改他,而是在外部在「包」一層進行附加行為,即代理類。
例如:介面A有一個介面方法operator(),真是角色:RealA實現介面A,則必須實現介面方法operator()。客戶端Client調用介面A的介面方法operator()。
現在新需求來了,需要修改RealA中的operator()的操作行為。怎麼辦呢?如果修改RealA就會影響原有系統的穩定性,還要重新測試。這是就需要代理類實現附加行為操作。創建代理ProxyA實現介面A,並將真實對象RealA注入進來。ProxyA實現介面方法operator(),可以增加附加行為,然後調用真實對象的operator()。從而達到了「對修改關閉,對擴展開放」,保證了系統的穩定性。我們看客戶端Client調用仍是介面A的介面方法operator(),只不過實例變為了ProxyA類了而已。
建議看看《軟體秘笈:設計模式那點事》,當中講解的很詳細,例子也很生動,讀了收獲很大!希望以上內容對你有幫助!
Ⅲ 編程的Proxy模式,為什麼在調用者跟被調用者之間加一個代理類就能降低兩個類之間的耦合度
問題:編程的Proxy模式,為什麼在調用者跟被調用者之間加一個代理類就能降低兩個類回之間的耦合度答?
回答:有了代理類,調用者不必再關心使用的是哪個具體類的服務,所以與具體類的耦合就降低了。當然代理類和具體類之間的耦合是不可能降低的。
Ⅳ 我的瀏覽器啟用了代理伺服器,怎麼關閉
1、打開IE瀏覽器,點擊右上角圖標,點擊「工具」,選擇下拉中的「Internet選項專」。屬
2、在彈出的Internet選項窗口中,點擊「連接」,選擇「區域網設置」。
3、在彈出的「區域網LAN設置」中,勾去「為LAN使用代理伺服器」,點擊確定。
通過上述步驟,我們就可以取消掉之前的設置了,不管使用哪個瀏覽器設置的芝麻代理伺服器,都可以通過IE瀏覽器「設置」下的「Internet選項」來解決問題。
Ⅳ java中的代理模式一般和什麼模式相結合
Java編程的目標是實現現實不能完成的,優化現實能夠完成的,是一種虛擬技術。生活中的方方面面都可以虛擬到代碼中。代理模式所講的就是現實生活中的這么一個概念:中介。
代理模式的定義:給某一個對象提供一個代理,並由代理對象控制對原對象的引用。
代理模式包含如下角色:
ISubject:抽象主題角色,是一個介面。該介面是對象和它的代理共用的介面。
RealSubject:真實主題角色,是實現抽象主題介面的類。
Proxy:代理角色,內部含有對真實對象RealSubject的引用,從而可以操作真實對象。代理對象提供與真實對象相同的介面,以便在任何時刻都能代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當於對真實對象進行封裝。
實現動態代理的關鍵技術是反射。
靜態代理
代理模式有幾種,虛擬代理,計數代理,遠程代理,動態代理。主要分為兩類,靜態代理和動態代理。靜態代理比較簡單,是由程序員編寫的代理類,並在程序運行前就編譯好的,而不是由程序動態產生代理類,這就是所謂的靜態。
考慮這樣的場景,管理員在網站上執行操作,在生成操作結果的同時需要記錄操作日誌,這是很常見的。此時就可以使用代理模式,代理模式可以通過聚合和繼承兩種方式實現:
<pre name="code" class="java">/**方式一:聚合式靜態代理 * @author Goser (mailto:[email protected]) * @Since 2016年9月7日 */ //1.抽象主題介面 public interface Manager { void doSomething(); } //2.真實主題類 public class Admin implements Manager { public void doSomething() { System.out.println("Admin do something."); } } //3.以聚合方式實現的代理主題 public class AdminPoly implements Manager{ private Admin admin; public AdminPoly(Admin admin) { super(); this.admin = admin; } public void doSomething() { System.out.println("Log:admin操作開始"); admin.doSomething(); System.out.println("Log:admin操作結束"); } } //4.測試代碼 Admin admin = new Admin(); Manager m = new AdminPoly(admin); m.doSomething(); //方式二:繼承式靜態代理 //與上面的方式僅代理類和測試代碼不同 //1.代理類 public class AdminProxy extends Admin { @Override public void doSomething() { System.out.println("Log:admin操作開始"); super.doSomething(); System.out.println("Log:admin操作開始"); } } //2.測試代碼 AdminProxy proxy = new AdminProxy(); proxy.doSomething();</pre>
聚合實現方式中代理類聚合了被代理類,且代理類及被代理類都實現了同一個介面,可實現靈活多變。繼承式的實現方式則不夠靈活。
比如,在管理員操作的同時需要進行許可權的處理,操作內容的日誌記錄,操作後數據的變化三個功能。三個功能的排列組合有6種,也就是說使用繼承要編寫6個繼承了Admin的代理類,而使用聚合,僅需要針對許可權的處理、日誌記錄和數據變化三個功能編寫代理類,在業務邏輯中根據具體需求改變代碼順序即可。
動態代理
一般來說,對代理模式而言,一個主題類與一個代理類一一對應,這也是靜態代理模式的特點。
但是,也存在這樣的情況,有n各主題類,但是代理類中的「前處理、後處理」都是一樣的,僅調用主題不同。也就是說,多個主題類對應一個代理類,共享「前處理,後處理」功能,動態調用所需主題,大大減小了程序規模,這就是動態代理模式的特點。
JDK動態代理
實現
<pre name="code" class="java">//1. 抽象主題 public interface Moveable { void move() throws Exception; } //2. 真實主題 public class Car implements Moveable { public void move() throws Exception { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽車行駛中…"); } } //3.事務處理器 public class TimeHandler implements InvocationHandler { private Object target; public TimeHandler(Object target) { super(); this.target = target; } /** * 參數: *proxy 被代理的對象 *method 被代理對象的方法 *args 方法的參數 * 返回: *Object 方法返回值 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); System.out.println("汽車開始行駛…"); method.invoke(target, args); long stopTime = System.currentTimeMillis(); System.out.println("汽車結束行駛…汽車行駛時間:" + (stopTime - startTime) + "毫秒!"); return null; } } //測試類 public class Test { public static void main(String[] args) throws Exception{ Car car = new Car(); InvocationHandler h = new TimeHandler(car); Class<?> cls = car.getClass(); /** *loader 類載入器 *interfaces 實現介面 *h InvocationHandler */ Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h); m.move(); } }</pre>
代碼講解:
在測試代碼中,Proxy.newProxyInstance()方法需要3個參數:類載入器(要進行代理的類)、被代理類實現的介面,事務處理器。所以先實例化Car,實例化InvocationHandler的子類TimeHandler,將各參數傳入Proxy的靜態方法newProxyInstance()即可獲得Car的代理類,前面的靜態代理,代理類是我們編寫好的,而動態代理則不需要我們去編寫代理類,是在程序中動態生成的。
JDK動態代理步驟
1. 創建一個實現InvocationHandler介面的類,它必須實現invoke()方法
2. 創建被代理的類及介面
3. 調用Proxy的靜態方法,創建一個代理類
4. 通過代理調用方法
而為什麼要進行如此操作,可以從Proxy和InvocationHandler的源碼中找打答案。對源碼不感興趣的可以將下面的源碼部分小節略過。
JDK動態代理原理與源碼
ewProxyInstance()方法的源碼:
<pre name="code" class="java"> public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException{ if (h == null) { throw new NullPointerException(); } final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /*查找或生成指定的代理類*/ Class<?> cl = getProxyClass0(loader, intfs); /*用指定的調用處理程序調用它的構造函數.*/ try { //獲得類的構造函數 final Constructor<?> cons =cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { //當需要代理的類實現了一個非public的介面時,因為這樣的介面需要特殊的許可權,因此調用doPrivilege(native 修飾的方法)創建代理實例。 return AccessController.doPrivileged(newPrivilegedAction<Object>() { public Object run() { return newInstance(cons,ih); } }); } else { return newInstance(cons,ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }</pre>
可以看到,獲得代理類的代碼是
Class<?>cl = getProxyClass0(loader,intfs)
並由此獲得代理類的構造函數,生成代理類的實例返回給該方法的調用者。
繼續跟進getProxyClass0()方法:
<pre name="code" class="java">/** 生成代理類。調用該方法前必須使用checkproxyaccess方法執行許可權檢查。*/ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //檢查實現的介面數,65535這個數字好特殊,埠數好像也是這個,這個數字是由虛擬機所決定的,2^16-1個 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果代理類已經通過實現給定介面的類載入器創建了,則返回緩存中的該類的副本;否則將通過ProxyClassFactory創建代理類 return proxyClassCache.get(loader, interfaces); }</pre>
還是沒有看到代理類是怎麼生成的,只知道代理類是從
roxyClassCache
中取得的,這個變數是與緩存相關的一個對象,查看該變數的聲明與初始化:
<pre name="code" class="java">private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());</pre>
可以發現
roxyClassCache
是個用來緩存代理類的類變數,大家知道類變數的特點是與類一一對應,在一個虛擬機中類只有一個,對應著在一個虛擬機中類變數也只有一個,且在此處,在Proxy類被載入的時候就賦值了。在賦值操作的參數中有ProxyClassFactory()這么一個構造函數,這個是動態代理中的關鍵:生成代理類的類文件位元組碼。繼續跟進去,找到代理類的生成之處了:
<pre name="code" class="java"> /** 根據給定的類載入器和介面數組生成代理類的工廠類*/ private static final class ProxyClassFactory implements BiFunction<ClassLoader,Class<?>[], Class<?>> { // 所有代理類名稱的前綴 private static final String proxyClassNamePrefix = "$Proxy"; //用於生成唯一代理類名稱的下一個序號 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader,Class<?>[] interfaces) { Map<Class<?>, Boolean>interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* 驗證類載入器將此介面的名稱解析為實際對象的名稱。*/ Class<?> interfaceClass =null; try { interfaceClass = Class.forName(intf.getName(),false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from classloader"); } /* 驗證類對象確實是一個介面。*/ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /*確保介面唯一*/ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // 代理類的包名 /*記錄非公開代理介面的包,以便將代理類定義在同一個包中。確認所有非公共代理介面都在同一個包中。*/ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { String name =intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces fromdifferent packages"); } } } if (proxyPkg == null) { // 如果沒有非公開的代理介面,使用com.sun.proxy作為包名 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* 生成代理類名的序號*/ long num = nextUniqueNumber.getAndIncrement(); //生成全類名 String proxyName = proxyPkg + proxyClassNamePrefix + num; /*生成代理類位元組碼 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces); try { return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }</pre>
ProxyClassFactory
中,可以看到產生代理類的具體邏輯,大致上是,根據傳遞的被代理類及其實現的介面生成代理類的位元組碼載入到緩存中,但是載入到緩存中只是一個.java文件也不能用,所以底層還有編譯等操作。到這里,可以大致的看清JDK中動態代理的面孔了,實現的步驟為:
1. 創建代理類的源碼;
2. 對源碼進行編譯成位元組碼;
3. 將位元組碼載入到內存;
4. 實例化代理類對象並返回給調用者;
底層的代碼我們看不到,但是我們可以查看其生成的位元組碼:
<pre name="code" class="java">//獲得位元組碼的測試方法 byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", Car.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(System.getProperty("user.dir") + "\\$Proxy1.class"); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } //生成的位元組碼: importcn.com.goser.proxy.imooc.staticproxy.Moveable; importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy1 extends Proxy implements Moveable { private static Method m1; private static Method m3; private static Method m0; private static Method m2; public $Proxy1(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final void move() throws Exception { try { this.h.invoke(this, m3, null); return; } catch (Exception localException) { throw localException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final int hashCode() throws { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } public final String toString() throws { try { return (String)this.h.invoke(this, m2, null); } catch (RuntimeException localRuntimeException) { throw localRuntimeException; } catch (Throwable localThrowable) { } throw new UndeclaredThrowableException(localThrowable); } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("cn.com.goser.proxy.imooc.staticproxy.Moveable").getMethod("move", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch () { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch () { } throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } }</pre>
生成的位元組碼比較長,但是在位元組碼中最關鍵的信息是代理類的聲明:
ublic final class $Proxy1 extends Proxy
可以看到生成的代理類是繼承了Proxy類的,這就是說明了為什麼使用JDK動態代理不能實現繼承式動態代理,原因是Java不允許多繼承,而生成的代理類本身就已經繼承了Proxy類。
至此,JDK的動態代理的使用及底層原理分析完畢,揭下動態代理的神秘面紗,果然是枚美女。
至於最底層的native方法是怎麼動態生成代理類的位元組碼我們也可以簡單的模擬一下,先分析下模擬的步驟:首先要生成一段代理類的源碼,然後將源碼編譯後生成代理類的實例返回給調用者。依據此步驟開始編寫我們的模擬代碼:
<pre name="code" class="java">/** * JDK java.lang.reflect.Proxy的模擬 * @author Goser (mailto:[email protected]) * @Since 2016年9月7日 */ public class Proxy { private static final String RT = "\r\n"; public static Object newProxyInstance() throws Exception{ //聲明一段源碼 String sourceCode = "packagecn.com.goser.proxy.jdk.simulate;"+ RT + "importcn.com.goser.proxy.imooc.staticproxy.Admin;" + RT + "importcn.com.goser.proxy.imooc.staticproxy.Manager;" + RT + "//以聚合方式實現的代理主題" + RT + "public class $Proxy0 implementsManager{" + RT + " privateAdmin admin;" + RT + " public$Proxy0(Admin admin) {" + RT + " super();" + RT + " this.admin= admin;" + RT + " }" + RT + " publicvoid doSomething() {" + RT + " System.out.println(\"Log:admin操作開始\");" + RT + " admin.doSomething();" + RT + " System.out.println(\"Log:admin操作結束\");" + RT + " }" + RT + "}"; String filename = System.getProperty("user.dir") + "/src/main/java/cn/com/goser/proxy/jdk/simulate/$Proxy0.java"; File file = new File(filename); //使用org.apache.commons.io.FileUtils.writeStringToFile()將源碼寫入磁碟 //編寫到處,可以運行一下程序,可以在當前目錄中看到生成的.java文件 FileUtils.writeStringToFile(file,sourceCode); //獲得當前系統中的編譯器 JavaCompiler complier = ToolProvider.getSystemJavaCompiler(); //獲得文件管理者 StandardJavaFileManager fileMgr =complier.getStandardFileManager(null, null, null); Iterable its =fileMgr.getJavaFileObjects(filename); //編譯任務 CompilationTask task = complier.getTask(null, fileMgr, null, null, null, its); //開始編譯,執行完可在當前目錄下看到.class文件 task.call(); fileMgr.close(); //load到內存 ClassLoader loader = ClassLoader.getSystemClassLoader(); Class cls = loader.loadClass("cn.com.goser.proxy.jdk.simulate.$Proxy0"); //生成代理類對象 Constructor ct = cls.getConstructor(Admin.class); return ct.newInstance(new Admin()); } } class test{ public static void main(String[] args) throws Exception { Manager m = (Manager)Proxy.newProxyInstance(); m.doSomething(); } }</pre>
運行測試代碼,結果和手工編寫的結果一致,完成了JDK中動態代理的實現模擬。
cglib動態代理
前面分析到,因為Java只允許單繼承,而JDK生成的代理類本身就繼承了Proxy類,因此,使用JDK實現的動態代理不能完成繼承式的動態代理,但是我們可以使用cglib來實現繼承式的動態代理。
大名鼎鼎的Spring中就含有cglib動態代理,在此也以Spring中自帶的cglib完成動態代理的實現:
<pre name="code" class="java">//1.具體主題 public class Train{ public void move(){ System.out.println("火車行駛中…"); } } //2.生成代理 public class CGLibProxy implements MethodInterceptor { private Enhancer enhancer = new Enhancer(); public Object getProxy(Class<?> clazz){ enhancer.setSuperclass(clazz); enhancer.setCallback(this); return enhancer.create(); } /** * 攔截所有目標類方法的調用 * 參數: * obj目標實例對象 *method 目標方法的反射對象 * args方法的參數 * proxy代理類的實例 */ 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; } } //3.測試 public class Test { public static void main(String[] args) { CGLibProxy proxy = new CGLibProxy(); Train t = (Train) proxy.getProxy(Train.class); t.move(); } }</pre>
Ⅵ 如何使用Proxy模式及Java內建的動態代理機制
在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼通過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。
在java的動態代理機制中,有兩個重要的類或介面,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和介面是實現我們動態代理所必須用到的。首先我們先來看看java的API幫助文檔是怎麼樣對這兩個類進行描述的:
InvocationHandler:
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.
每一個動態代理類都必須要實現InvocationHandler這個介面,並且每個代理類的實例都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個介面的 invoke 方法來進行調用。我們來看看InvocationHandler這個介面的唯一一個方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
我們看到這個方法一共接受三個參數,那麼這三個參數分別代表什麼呢?
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy:指代我們所代理的那個真實對象
method:指代的是我們所要調用真實對象的某個方法的Method對象
args:指代的是調用真實對象某個方法時接受的參數
如果不是很明白,等下通過一個實例會對這幾個參數進行更深的講解。
接下來我們來看看Proxy這個類:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
Proxy這個類的作用就是用來動態創建一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.
這個方法的作用就是得到一個動態的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行載入
interfaces:一個Interface對象的數組,表示的是我將要給我需要代理的對象提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理對象就宣稱實現了該介面(多態),這樣我就能調用這組介面中的方法了
h:一個InvocationHandler對象,表示的是當我這個動態代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上
好了,在介紹完這兩個介面(類)以後,我們來通過一個實例來看看我們的動態代理模式是什麼樣的:
首先我們定義了一個Subject類型的介面,為其聲明了兩個方法:
public interface Subject
{
public void rent();
public void hello(String str);
}
接著,定義了一個類來實現這個介面,這個類就是我們的真實對象,RealSubject類:
public class RealSubject implements Subject
{
@Override
public void rent()
{
System.out.println("I want to rent my house");
}
@Override
public void hello(String str)
{
System.out.println("hello: " + str);
}
}
下一步,我們就要定義一個動態代理類了,前面說個,每一個動態代理類都必須要實現 InvocationHandler 這個介面,因此我們這個動態代理類也不例外:
public class DynamicProxy implements InvocationHandler
{
//這個就是我們要代理的真實對象
private Object subject;
// 構造方法,給我們要代理的真實對象賦初值
public DynamicProxy(Object subject)
{
this.subject = subject;
}
@Override
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
//在代理真實對象前我們可以添加一些自己的操作
System.out.println("before rent house");
System.out.println("Method:" + method);
// 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
method.invoke(subject, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println("after rent house");
return null;
}
}
最後,來看看我們的Client類:
public class Client
{
public static void main(String[] args)
{
// 我們要代理的真實對象
Subject realSubject = new RealSubject();
// 我們要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法的
InvocationHandler handler = new DynamicProxy(realSubject);
/*
* 通過Proxy的newProxyInstance方法來創建我們的代理對象,我們來看看其三個參數
* 第一個參數 handler.getClass().getClassLoader() ,我們這里使用handler這個類的ClassLoader對象來載入我們的代理對象
* 第二個參數realSubject.getClass().getInterfaces(),我們這里為代理對象提供的介面是真實對象所實行的介面,表示我要代理的是該真實對象,這樣我就能調用這組介面中的方法了
* 第三個參數handler, 我們這里將這個代理對象關聯到了上方的 InvocationHandler 這個對象上
*/
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
System.out.println(subject.getClass().getName());
subject.rent();
subject.hello("world");
}
}
我們先來看看控制台的輸出:
$Proxy0
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house
before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house
我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句列印出來的,那麼為什麼我們返回的這個代理對象的類名是這樣的呢?
Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
.getClass().getInterfaces(), handler);
可能我以為返回的這個代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結果卻不是,首先我們解釋一下為什麼我們這里可以將其轉化為Subject類型的對象?原因就是在newProxyInstance這個方法的第二個參數上,我們給這個代理對象提供了一組什麼介面,那麼我這個代理對象就會實現了這組介面,這個時候我們當然可以將這個代理對象強制類型轉化為這組介面中的任意一個,因為這里的介面是Subject類型,所以就可以將其轉化為Subject類型了。
同時我們一定要記住,通過 Proxy.newProxyInstance 創建的代理對象是在jvm運行時動態生成的一個對象,它並不是我們的InvocationHandler類型,也不是我們定義的那組介面的類型,而是在運行是動態生成的一個對象,並且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的標號。
接著我們來看看這兩句
subject.rent();
subject.hello("world");
這里是通過代理對象來調用實現的那種介面中的方法,這個時候程序就會跳轉到由這個代理對象關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 對象又接受了一個 RealSubject類型的參數,表示我要代理的就是這個真實對象,所以此時就會調用 handler 中的invoke方法去執行:
public Object invoke(Object object, Method method, Object[] args)
throws Throwable
{
//在代理真實對象前我們可以添加一些自己的操作
System.out.println("before rent house");
System.out.println("Method:" + method);
// 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
method.invoke(subject, args);
//在代理真實對象後我們也可以添加一些自己的操作
System.out.println("after rent house");
return null;
}
我們看到,在真正通過代理對象來調用真實對象的方法的時候,我們可以在該方法前後添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:
public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
正好就是我們的Subject介面中的兩個方法,這也就證明了當我通過代理對象來調用方法的時候,起實際就是委託由其關聯到的 handler 對象的invoke方法中來調用,並不是自己來真實調用,而是通過代理的方式來調用的。
這就是我們的java動態代理機制
Ⅶ 代理模式的實現方式
下面我舉個例子來說明這一切:
場景:業務類CompanyManager在調用Save方法的時候需要調用SecurityManager類判斷許可權是否足夠
第一種實現方式,我們通常會這樣做:直接在CompanyManager類中調用ISecurityManager介面的IsPass方法判斷許可權。
public class SimpleCompanyManager : ICompanyManager
{
可通過外部注入的屬性#region 可通過外部注入的屬性
public string UserName { get; set; }
public CompanyDao Dao { get; set; }
#endregion
public void Save()
{
//判斷許可權
ISecurityManager security = new SecurityManager();
if (security.IsPass(UserName))
{
//執行業務方法
//.
//調用DAO層方法
Dao.Save();
}
else
{
//執行其它業務方法
Console.WriteLine("您沒有該許可權");
}
}
}
這樣CompanyManager類與ISecurityManager或SecurityManager會發生業務性耦合。聰明的朋友會發現在GOF(設計模式)中有一種模式(代理模式)可以解除這種耦合。
代理模式(Proxy Pattern):什麼是代理模式?是給某一個對象提供一個代理對象,並由代理對象控制對源對象的引用。代理就是一個人或一個機構代表另一個人或者一個機構採取行動。某些情況下,客戶不想或者不能夠直接引用一個對象,代理對象可以在客戶和目標對象直接起到中介的作用。客戶端分辨不出代理主題對象與真實主題對象。代理模式可以並不知道真正的被代理對象,而僅僅持有一個被代理對象的介面,這時候代理對象不能夠創建被代理對象,被代理對象必須有系統的其他角色代為創建並傳入
CompanyManager
public class CompanyManager : ICompanyManager
{
可通過外部注入的屬性#region 可通過外部注入的屬性
public string UserName { get; set; }
public CompanyDao Dao { get; set; }
#endregion
public void Save()
{
//執行業務方法
//.
//調用DAO層方法
Dao.Save();
}
}
CompanyProxyManager
public class CompanyProxyManager : ICompanyManager
{
public string UserName { get; set; }
private ICompanyManager target = new CompanyManager();
public void Save()
{
//判斷許可權
ISecurityManager security = new SecurityManager();
if (security.IsPass(UserName))
{
//調用目標對象Save方法
target.Save();
}
else
{
Console.WriteLine("您沒有該許可權");
}
}
}
這樣,CompanyManager類就不必與判斷許可權的類SecurityManager耦合
代理模式作用就是接觸業務耦合,保證業務的獨立性
摘自 劉冬的博客園
Ⅷ 如何使用Proxy模式及Java內建的動態代理機制
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyMain {
public static void main(String[] args) {
Service service = new ServiceImpl();
Service proxy = (Service) Proxy.newProxyInstance(service.getClass().getClassLoader(),
service.getClass().getInterfaces(), new ServiceInvocationHandler(service));
System.out.println(proxy.sayHi("Arvin"));
}
}
class ServiceInvocationHandler implements InvocationHandler {
private final Service target;
public ServiceInvocationHandler(Service target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
interface Service {
String sayHi(String name);
}
class ServiceImpl implements Service {
@Override
public String sayHi(String name) {
return "Hi, " + name;
}
}
Ⅸ java:代理模式 是實現了InvocationHandler那個類是代理類還是$Proxy0類是代理類呢
$Proxy0是代理類,是JDK生成的。
Ⅹ java中代理模式的問題
兩個new都是實例化一個對象
new ProxySubject() 是代理對象實列,但每個代理都要有一個真實的實列所以需要傳遞一個真實實列給它 。 及new RealSubject()