更新時間:2022-05-06 10:25:00 來源:動力節(jié)點 瀏覽1368次
動力節(jié)點小編來告訴大家Java動態(tài)代理原理是什么。Java動態(tài)代理機制的出現(xiàn),給Java開發(fā)者帶來了不必手工編寫代理類,只需指定一組接口和委托類對象,就可以動態(tài)獲取代理類。代理類負責將所有的方法調(diào)用分配給委托對象進行反射執(zhí)行,在分派執(zhí)行的過程中,開發(fā)者還可以根據(jù)需要調(diào)整委托類對象及其功能,這是一個非常靈活靈活的代理框架。現(xiàn)在讓我們開始學習動態(tài)代理。
動態(tài)代理簡述在java動態(tài)代理機制中,有兩個重要的類或接口,一個是InvocationHandler(Interface)、另一個是Proxy(Class)。
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.
每個動態(tài)代理類都必須實現(xiàn) InvocationHandler 這個接口,并且代理類的每個實例都關(guān)聯(lián)一個處理程序,當我們通過代理對象調(diào)用一種方式時,對該方法的調(diào)用會轉(zhuǎn)發(fā)到 InvocationHandler 這個接口的 invoke 方法來進行調(diào)用. 我們來看看 InvocationHandler 做這個接口的唯一方法 invoke 方法:
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
該方法接受三個參數(shù)并返回一個 Object 類型,它們的含義如下:
proxy: 指我們所代表的真實對象 method: 指我們要調(diào)用真實對象的方法 Method object args: 指在真實對象上調(diào)用方法時接受的參數(shù) Back to Object真實對象方法的返回類型,以上內(nèi)容將在下面的例子中進一步理解。
the value to return from the method invocation on the proxy instance.
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 此類用于動態(tài)創(chuàng)建代理對象。我們經(jīng)常使用 newProxyInstance 這個方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
參數(shù)理解:
// One ClassLoader object , Defined by which ClassLoader Object to load the generated proxy object
loader - the class loader to define the proxy class
// One Interface An array of objects , What is the set of interfaces that I'm going to provide to the object that I need to proxy
interfaces - the list of interfaces for the proxy class to implement
// One InvocationHandler object , Represents when my dynamic proxy object is calling a method , Which one does it relate to InvocationHandler On the object
h - the invocation handler to dispatch method invocations to
返回結(jié)果的理解:代理對象的一個??實例
a proxy instance with the specified invocation handler of a proxy class that is defined by the specified class loader and that implements the specified interfaces
普通Java代理我們創(chuàng)建一個Java項目用于測試和理解動態(tài)代理,項目結(jié)構(gòu)如下:
1.我們定義一個接口Interface,添加兩個方法。
package com.huhx.proxy;
public interface Interface {
void getMyName();
String getNameById(String id);
}
2.定義一個實現(xiàn)上述接口的真實類,RealObject:
package com.huhx.proxy;
public class RealObject implements Interface {
@Override
public void getMyName() {
System.out.println("my name is huhx");
}
@Override
public String getNameById(String id) {
System.out.println("argument id: " + id);
return "huhx";
}./* Welcome to join java communication Q Your appearance :909038429 Blow water and chat together
}
3.... 和 、 定義一個代理對象,它也實現(xiàn)了上面的接口接口:
package com.huhx.proxy;
public class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
@Override
public void getMyName() {
System.out.println("proxy getmyname");
proxied.getMyName();
}
@Override
public String getNameById(String id) {
System.out.println("proxy getnamebyid");
return proxied.getNameById(id);
}
}
4.SimpleMain在Main方法中,測試上述結(jié)果:
package com.huhx.proxy;
public class SimpleMain {
private static void consume(Interface iface) {
iface.getMyName();
String name = iface.getNameById("1");
System.out.println("name: " + name);
}
public static void main(String[] args) {
consume(new RealObject());
System.out.println("========================================================");
consume(new SimpleProxy(new RealObject()));
}
}
5.... 和 、 運行結(jié)果如下:
my name is huhx
argument id: 1
name: huhx
========================================================
proxy getmyname
my name is huhx
proxy getnamebyid
argument id: 1
name: huhx
Java動態(tài)代理完成了上面簡單的Java代理,現(xiàn)在我們開始學習Java動態(tài)代理,它比代理的思想更進了一步,因為它可以動態(tài)創(chuàng)建代理并動態(tài)處理對代理方法的調(diào)用。在動態(tài)代理上進行的所有調(diào)用都被重定向到單個調(diào)用處理程序,它的工作是揭示調(diào)用的類型并確定相應的對策。下面我們通過案例加深對Java動態(tài)代理的理解:
1.創(chuàng)建一個繼承 InvocationHandler 的處理器:DynamicProxyHandler
package com.huhx.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;
public class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
System.out.println("dynamic proxy handler constuctor: " + proxied.getClass());
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("dynamic proxy name: " + proxy.getClass());
System.out.println("method: " + method.getName());
System.out.println("args: " + Arrays.toString(args));
Object invokeObject = method.invoke(proxied, args);
if (invokeObject != null) {
System.out.println("invoke object: " + invokeObject.getClass());
} else {
System.out.println("invoke object is null");
}
return invokeObject;
}
}
2.我們寫一個測試Main方法,DynamicProxyMain:
package com.huhx.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import com.huhx.proxy.Interface;
import com.huhx.proxy.RealObject;
public class DynamicProxyMain {
public static void consumer(Interface iface) {
iface.getMyName();
String name = iface.getNameById("1");
System.out.println("name: " + name);
}
public static void main(String[] args) throws Exception, SecurityException, Throwable {
RealObject realObject = new RealObject();
consumer(realObject);
System.out.println("==============================");
// A dynamic proxy
ClassLoader classLoader = Interface.class.getClassLoader();
Class<?>[] interfaces = new Class[] { Interface.class };
InvocationHandler handler = new DynamicProxyHandler(realObject);
Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler);
System.out.println("in dynamicproxyMain proxy: " + proxy.getClass());
consumer(proxy);
}./* Welcome to join java communication Q Your appearance :909038429 Blow water and chat together
}
3. ... 和 、 運行結(jié)果如下:
my name is huhx
argument id: 1
name: huhx
==============================
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getNameById
args: [1]
argument id: 1
invoke object: class java.lang.String
name: huhx
從以上輸出,我們可以得出以下結(jié)論:
與代理對象InvocationHandler相關(guān)聯(lián),只有當代理對象調(diào)用一個方法時,才進行invoke方法invoke 的三個參數(shù)的理解:對象代理是代理的對象,方法方法是真實對象調(diào)用的方法class , Object[] args 是真實對象中調(diào)用方法的參數(shù)
1.動態(tài)代理的關(guān)鍵代碼是Proxy.newProxyInstance(classLoader, interfaces, handler),我們跟進源碼看看:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
// handler Can't be empty
if (h == null) {
throw new NullPointerException();
}
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.
*/
// adopt loader And interface , Get a proxy Class object
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
// Create an instance of the proxy object
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
2.我們來看newInstance方法的源碼:
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
try {
return cons.newInstance(new Object[] {h} );
} catch (IllegalAccessException | InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString());
}
}
}
3....和、 當我們通過代理對象調(diào)用一種方式時,對該方法的調(diào)用轉(zhuǎn)發(fā) InvocationHandler 這個接口的 invoke 方法進行調(diào)用。體現(xiàn)這句話的代碼,在源碼中沒有找到,所以在測試類的main方法中添加如下代碼:
if (proxy instanceof Proxy) {
InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null);
System.out.println("--------------------------------------");
}
這段代碼的輸出如下,而且,在上面,調(diào)用代理對象getMyName的方法輸出是一樣的,不知道!jvm 是否按此方式判斷到底:
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
通過上述介紹,相信大家對Java動態(tài)代理原理已經(jīng)有所了解,大家如果想了解更多相關(guān)知識,不妨來關(guān)注一下動力節(jié)點的Java在線學習,里面還有更豐富的知識等著大家去學習,希望對大家能夠有所幫助哦。