最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

深入學(xué)習(xí)Java 動態(tài)代理

 更新時間:2019年05月31日 10:20:51   作者:我是家寶  
Java 動態(tài)代理機制的出現(xiàn),使得 Java 開發(fā)人員不用手工編寫代理類,只要簡單地指定一組接口及委托類對象,便能動態(tài)地獲得代理類。下面小編和大家來一起學(xué)習(xí)一下吧

前言

要想了解Java動態(tài)代理,首先要了解什么叫做代理,熟悉設(shè)計模式的朋友一定知道在Gof總結(jié)的23種設(shè)計模式中,有一種叫做代理(Proxy)的對象結(jié)構(gòu)型模式,動態(tài)代理中的代理,指的就是這種設(shè)計模式。

在我看來所謂的代理模式,和23種設(shè)計模式中的“裝飾模式”是一個東西。23種設(shè)計模式中將它們作為兩種模式,網(wǎng)上也有些文章講這兩種模式的異同,從細(xì)節(jié)來看,確實可以人為地區(qū)分這兩種模式,但是抽象到一定高度后,我認(rèn)為這兩種模式是完全一樣的。因此學(xué)會了代理模式,也就同時掌握了裝飾模式。

代理模式

代理模式簡單來說,就是對一個對象進行包裝,包裝后生成的對象具有和原對象一樣的方法列表,但是每個方法都可以是被包裝過的。

靜態(tài)代理

讓我們先來看一段代碼:

package common;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class SubjectImplProxy implements Subject{
private Subject target;
public SubjectImplProxy(Subject target) {
this.target=target;
}
@Override
public void sayHi() {
System.out.print("say:");
target.sayHi();
}
@Override
public void sayHello() {
System.out.print("say:");
target.sayHello();
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=new SubjectImplProxy(subject);
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}

這段代碼中首先定義了一個Subject接口,接口中有兩個方法。

然后定義了SubjectImpl類實現(xiàn)Subject接口并實現(xiàn)其中的兩個方法,到這里肯定是沒問題的。

現(xiàn)在再定義一個SubjuectImplProxy類,也實現(xiàn)Subject接口。這個SubjectImplProxy類的作用是包裝SubjectImpl類的實例,它的內(nèi)部定義一個變量target來保存一個SubjectImpl的實例。SubjectImplProxy也實現(xiàn)了接口規(guī)定的兩個方法,并且在它的實現(xiàn)版本中,都調(diào)用了SubjectImpl的實現(xiàn),但是又添加了自己的處理邏輯。

相信這段代碼不難理解,它通過對SubjectImpl進行包裝,達到了給輸出內(nèi)容添加前綴的功能。這種代理方式叫做靜態(tài)代理。

動態(tài)代理

從上面的演示中我們不難看出靜態(tài)代理的缺點:我們對SubjectImpl的兩個方法,是進行的相同的包裝,但是卻要在SubjectImplProxy里把相同的包裝邏輯寫兩次,而且以后如果Subject接口再添加新的方法,SubjectImplProxy也必須要添加新的實現(xiàn),盡管SubjectImplProxy對所有方法的包裝可能都是一樣的。

下面我把上面例子的靜態(tài)代理改成動態(tài)代理,我們來看一下區(qū)別:

package common;
import java.lang.invoke.MethodHandle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
static interface Subject{
void sayHi();
void sayHello();
}
static class SubjectImpl implements Subject{
@Override
public void sayHi() {
System.out.println("hi");
}
@Override
public void sayHello() {
System.out.println("hello");
}
}
static class ProxyInvocationHandler implements InvocationHandler{
private Subject target;
public ProxyInvocationHandler(Subject target) {
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.print("say:");
return method.invoke(target, args);
}
}
public static void main(String[] args) {
Subject subject=new SubjectImpl();
Subject subjectProxy=(Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), subject.getClass().getInterfaces(), new ProxyInvocationHandler(subject));
subjectProxy.sayHi();
subjectProxy.sayHello();
}
}

只看main方法的話,只有第二行和之前的靜態(tài)代理不同,同樣是生成一個subjectProxy代理對象,只是生成的代碼不同了。靜態(tài)代理是直接new 一個SubjectImplProxy的實例,而動態(tài)代理則調(diào)用了java.lang.reflect.Proxy.newProxyInstance()方法,我們來看一下這個方法的源碼:

public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass(loader, interfaces);  //獲取代理類的Class
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);  //constructorParams是寫死的:{ InvocationHandler.class },上邊返回的代理類Class一定是extends Proxy的,而Proxy有一個參數(shù)為InvocationHandler的構(gòu)造函數(shù)
return cons.newInstance(new Object[] { h });  //這里通過構(gòu)造函數(shù)將我們自己定義的InvocationHandler的子類傳到代理類的實例里,當(dāng)我們調(diào)用代理類的任何方法時,實際上都會調(diào)用我們定義的InvocationHandler子類重寫的invoke()函數(shù)
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}

上面的 Class<?> cl = getProxyClass(loader, interfaces); 調(diào)用的getProxyClass方法:

public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) {  //因為在class文件中,一個類保存的接口數(shù)量是用2個字節(jié)來表示的,因此java中一個類最多可以實現(xiàn)65535個接口
throw new IllegalArgumentException("interface limit exceeded");
}
Class<?> proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
// for detecting duplicates
Set<Class<?>> interfaceSet = new HashSet<>();
     //驗證interfaces里的接口是否能被類加載器加載,是否是接口,是否有重復(fù)的 
for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
/*
* Using string representations of the proxy interfaces as
* keys in the proxy class cache (instead of their Class
* objects) is sufficient because we require the proxy
* interfaces to be resolvable by name through the supplied
* class loader, and it has the advantage that using a string
* representation of a class makes for an implicit weak
* reference to the class.
*/
List<String> key = Arrays.asList(interfaceNames);  //使用interfaces列表作為key緩存在cache里,也就是實現(xiàn)了相同interfaces的代理類只會創(chuàng)建加載一次
/*
* Find or create the proxy class cache for the class loader.
*/
Map<List<String>, Object> cache;
synchronized (loaderToCache) {
cache = loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap<>();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this
* method, without further synchronization, because the mapping
* will only be removed if the class loader becomes unreachable.
*/
}
/*
* Look up the list of interfaces in the proxy class cache using
* the key. This lookup will result in one of three possible
* kinds of values:
* null, if there is currently no proxy class for the list of
* interfaces in the class loader,
* the pendingGenerationMarker object, if a proxy class for the
* list of interfaces is currently being generated,
* or a weak reference to a Class object, if a proxy class for
* the list of interfaces has already been generated.
*/
     //看看緩存里有沒有,如果有就直接取出來然后return,否則判斷根據(jù)pendingGenerationMarker判斷是否有其它線程正在生成當(dāng)前的代理類,如果有則cache.wait()等待,如果沒有則創(chuàng)建。
synchronized (cache) {
/*
* Note that we need not worry about reaping the cache for
* entries with cleared weak references because if a proxy class
* has been garbage collected, its class loader will have been
* garbage collected as well, so the entire cache will be reaped
* from the loaderToCache map.
*/
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class<?>) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
/*
* The class generation that we are waiting for should
* take a small, bounded time, so we can safely ignore
* thread interrupts here.
*/
}
continue;
} else {
/*
* No proxy class for this list of interfaces has been
* generated or is being generated, so we will go and
* generate it now. Mark it as pending generation.
*/
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
     //確認(rèn)要生成的代理類所屬的包,如果interfaces里所有接口都是public的,代理類所屬包就是默認(rèn)包;如果有interface不是public,那么所有不是public的interface必須在一個包里否則報錯。
try {
String proxyPkg = null; // package to define proxy class in
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].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 from different packages");
}
}
}
if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
}
{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;  //生成代理類的名字,proxyPkg是上面確定下來的代理類所在的包名,proxyClassNamePrefix是寫死的字符串“$Proxy”,num是一個全局唯一的long型數(shù)字,從0開始累積,每次生成新的代理類就+1,從這里也能看出生成的動態(tài)代理類的數(shù)量不能超過Long.maxValue
/*
* Verify that the class loader hasn't already
* defined a class with the chosen name.
*/

/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);  //生成一個以proxyName為類名的,實現(xiàn)了Interfaces里所有接口的類的字節(jié)碼
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);  //加載生成的類
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);

} finally {
/*
* We must clean up the "pending generation" state of the proxy
* class cache entry somehow. If a proxy class was successfully
* generated, store it in the cache (with a weak reference);
* otherwise, remove the reserved entry. In all cases, notify
* all waiters on reserved entries in this cache.
*/
       //創(chuàng)建成功,則將cache中該key的pendingGenerationMarker替換為實際的代理類的弱引用,否則也要清除pendingGenerationMarker標(biāo)記;不管是否成功,都要執(zhí)行cache.notifyAll(),讓其它要創(chuàng)建相同代理類并且執(zhí)行了cache.wait()的線程恢復(fù)執(zhí)行。
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference<Class<?>>(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass; //最后返回代理類Class
}

到這里,我們已經(jīng)把動態(tài)代理的java源代碼都解析完了,現(xiàn)在思路就很清晰了:

Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法簡單來說執(zhí)行了以下操作:

1.生成一個實現(xiàn)了參數(shù)interfaces里所有接口且繼承了Proxy的代理類的字節(jié)碼,然后用參數(shù)里的classLoader加載這個代理類。

2.使用代理類父類的構(gòu)造函數(shù) Proxy(InvocationHandler h)來創(chuàng)造一個代理類的實例,將我們自定義的InvocationHandler的子類傳入。

3.返回這個代理類實例,因為我們構(gòu)造的代理類實現(xiàn)了interfaces(也就是我們程序中傳入的subject.getClass().getInterfaces())里的所有接口,因此返回的代理類可以強轉(zhuǎn)成Subject類型來調(diào)用接口中定義的方法。

現(xiàn)在我們知道了用Proxy.newProxyInstance()返回的subjectProxy可以成功強轉(zhuǎn)成Subject類型來調(diào)用接口中定義的方法了,那么在調(diào)用方法后,代理類實例怎么進行處理的呢,這就需要看一下代理類的源碼了。但是代理類是程序動態(tài)生成字節(jié)碼加載的,

怎么看源碼呢?

沒關(guān)系,可以在main方法中加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,”true”),這樣就會把生成的代理類Class文件保存在本地磁盤上,然后再反編譯可以得到代理類的源碼:

package common;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements Test.Subject
{
private static Method m4;
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
static
{
try {
m4 = Class.forName("Test$Subject").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("Test$Subject").getMethod("sayHi", 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]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public $Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final void sayHello()
{
try
{
this.h.invoke(this, m4, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
{
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 sayHi()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
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()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
}

我們可以看到代理類內(nèi)部實現(xiàn)比較簡單,在調(diào)用每個代理類每個方法的時候,都用反射去調(diào)h的invoke方法(也就是我們自定義的InvocationHandler的子類中重寫的invoke方法),用參數(shù)傳遞了代理類實例、接口方法、調(diào)用參數(shù)列表,這樣我們在重寫的invoke方法中就可以實現(xiàn)對所有方法的統(tǒng)一包裝了。

總結(jié)

動態(tài)代理相對于靜態(tài)代理在使用上的優(yōu)點主要是能夠?qū)σ粋€對象的所有方法進行統(tǒng)一包裝,而且后期被代理的類添加方法的時候動態(tài)代理類不需要改動。

缺點是要求被代理的類必須實現(xiàn)了接口,因為動態(tài)代理類在實現(xiàn)的時候繼承了Proxy類,java不支持多繼承,因此動態(tài)代理類只能根據(jù)接口來定義方法。

最后動態(tài)代理之所以叫做動態(tài)代理是因為java在實現(xiàn)動態(tài)代理的時候,動態(tài)代理類是在運行時動態(tài)生成和加載的,相對的,靜態(tài)代理類和其他普通類一下,在類加載階段就加載了。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • JAVA內(nèi)部類示例詳解及練習(xí)

    JAVA內(nèi)部類示例詳解及練習(xí)

    這篇文章主要為大家詳細(xì)介紹了Java的內(nèi)部類,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • MyBatis批量插入/修改/刪除MySql數(shù)據(jù)

    MyBatis批量插入/修改/刪除MySql數(shù)據(jù)

    這篇文章主要給大家介紹了關(guān)于MyBatis批量插入/修改/刪除MySql數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-05-05
  • SpringMVC實現(xiàn)文件下載功能

    SpringMVC實現(xiàn)文件下載功能

    這篇文章主要為大家詳細(xì)介紹了SpringMVC實現(xiàn)文件下載功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • 詳解SpringBoot啟動項目后執(zhí)行方法的幾種方式

    詳解SpringBoot啟動項目后執(zhí)行方法的幾種方式

    在項目開發(fā)中某些場景必須要用到啟動項目后立即執(zhí)行方式的功能,本文主要聊聊實現(xiàn)立即執(zhí)行的幾種方法,具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • java通過Excel批量上傳數(shù)據(jù)的實現(xiàn)示例

    java通過Excel批量上傳數(shù)據(jù)的實現(xiàn)示例

    Excel批量上傳是常見的一種功能,本文就來介紹一下java通過Excel批量上傳數(shù)據(jù)的實現(xiàn)示例,具有一定的參考價值,感興趣的可以了解一下
    2023-10-10
  • MyEclipse配置JDK的全過程

    MyEclipse配置JDK的全過程

    這篇文章主要介紹了MyEclipse配置JDK的全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-04-04
  • SpringBoot2+Netty+WebSocket(netty實現(xiàn)websocket支持URL參數(shù))問題記錄

    SpringBoot2+Netty+WebSocket(netty實現(xiàn)websocket支持URL參數(shù))問題記錄

    Netty?是一個利用?Java?的高級網(wǎng)絡(luò)的能力,隱藏其背后的復(fù)雜性而提供一個易于使用的?API?的客戶端/服務(wù)器框架,這篇文章主要介紹了SpringBoot2+Netty+WebSocket(netty實現(xiàn)websocket,支持URL參數(shù)),需要的朋友可以參考下
    2023-12-12
  • SpringBoot部署xxl-job方法詳細(xì)講解

    SpringBoot部署xxl-job方法詳細(xì)講解

    XXL-JOB是一個分布式任務(wù)調(diào)度平臺,其核心設(shè)計目標(biāo)是開發(fā)迅速、學(xué)習(xí)簡單、輕量級、易擴展,這篇文章主要介紹了springboot整合xxl-job流程,需要的朋友可以參考下
    2023-01-01
  • Spring中自動注入的兩種方式總結(jié)

    Spring中自動注入的兩種方式總結(jié)

    Spring的核心技術(shù)IOC(Intorol of Converse控制反轉(zhuǎn))的實現(xiàn)途徑是DI(dependency Insert依賴注入)。而依賴注入(DI)的實現(xiàn)方式又有兩種,xml方式和注解方式。本文就來詳細(xì)聊聊這兩個方式,需要的可以了解一下
    2022-10-10
  • 詳解RabbitMQ延遲隊列的基本使用和優(yōu)化

    詳解RabbitMQ延遲隊列的基本使用和優(yōu)化

    這篇文章主要介紹了詳解RabbitMQ延遲隊列的基本使用和優(yōu)化,延遲隊列中的元素都是帶有時間屬性的。延遲隊列就是用來存放需要在指定時間被處理的元素的隊列,需要的朋友可以參考下
    2023-05-05

最新評論

顺义区| 东莞市| 峨山| 阿克苏市| 乌拉特前旗| 永年县| 长治县| 曲麻莱县| 台南县| 图木舒克市| 呼和浩特市| 宝丰县| 乌拉特中旗| 徐水县| 黄龙县| 丁青县| 旺苍县| 绥棱县| 杂多县| 河南省| 盐山县| 沐川县| 阜新市| 开阳县| 敦化市| 巧家县| 连云港市| 日喀则市| 桂阳县| 木里| 鹰潭市| 清水县| 临邑县| 平利县| 余姚市| 新邵县| 布尔津县| 雷山县| 南通市| 南漳县| 江津市|