激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術(shù)及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 深度剖析java動態(tài)靜態(tài)代理原理源碼

深度剖析java動態(tài)靜態(tài)代理原理源碼

2019-07-04 17:04chen_hao Java教程

這篇文章主要介紹了深度剖析java動態(tài)靜態(tài)代理原理源碼,關(guān)于Java中的動態(tài)代理,我們首先需要了解的是一種常用的設計模式--代理模式,而對于代理,根據(jù)創(chuàng)建代理類的時間點,又可以分為靜態(tài)代理和動態(tài)代理。,需要的朋友可以參

正文

關(guān)于Java中的動態(tài)代理,我們首先需要了解的是一種常用的設計模式--代理模式,而對于代理,根據(jù)創(chuàng)建代理類的時間點,又可以分為靜態(tài)代理和動態(tài)代理。

靜態(tài)代理
1、靜態(tài)代理

靜態(tài)代理:由程序員創(chuàng)建或特定工具自動生成源代碼,也就是在編譯時就已經(jīng)將接口,被代理類,代理類等確定下來。在程序運行之前,代理類的.class文件就已經(jīng)生成。

2、靜態(tài)代理簡單實現(xiàn)

根據(jù)上面代理模式的類圖,來寫一個簡單的靜態(tài)代理的例子。我這兒舉一個比較粗糙的例子,假如一個班的同學要向老師交班費,但是都是通過班長把自己的錢轉(zhuǎn)交給老師。這里,班長就是代理學生上交班費,

班長就是學生的代理。

首先,我們創(chuàng)建一個Person接口。這個接口就是學生(被代理類),和班長(代理類)的公共接口,他們都有上交班費的行為。這樣,學生上交班費就可以讓班長來代理執(zhí)行。

/**
* 創(chuàng)建Person接口
* @author ChenHao
*/
public interface Person {
//上交班費
void giveMoney();
}

Student類實現(xiàn)Person接口。Student可以具體實施上交班費的動作。

public class Student implements Person {
private String name;
public Student(String name) {
this.name = name;
}
@Override
public void giveMoney() {
System.out.println(name + "上交班費50元");
}
}

StudentsProxy類,這個類也實現(xiàn)了Person接口,但是還另外持有一個學生類對象,由于實現(xiàn)了Peson接口,同時持有一個學生對象,那么他可以代理學生類對象執(zhí)行上交班費(執(zhí)行g(shù)iveMoney()方法)行為。

/**
* 學生代理類,也實現(xiàn)了Person接口,保存一個學生實體,這樣既可以代理學生產(chǎn)生行為
* @author ChenHao
*
*/
public class StudentsProxy implements Person{
//被代理的學生
Student stu;
public StudentsProxy(Student stu) {
this.stu = stu;
}
//代理上交班費,調(diào)用被代理學生的上交班費行為
public void giveMoney() {
stu.giveMoney();
}
}

下面測試一下,看如何使用代理模式:

public class StaticProxyTest {
public static void main(String[] args) {
//被代理的學生張三,他的班費上交有代理對象monitor(班長)完成
Student zhangsan = new Student("張三");
//生成代理對象,并將張三傳給代理對象
Person monitor = new StudentsProxy(zhangsan);
//班長代理上交班費
monitor.giveMoney();
}
}

運行結(jié)果:

深度剖析java動態(tài)靜態(tài)代理原理源碼

這里并沒有直接通過張三(被代理對象)來執(zhí)行上交班費的行為,而是通過班長(代理對象)來代理執(zhí)行了。這就是代理模式。

代理模式最主要的就是有一個公共接口(Person),一個具體的類(Student),一個代理類(StudentsProxy),代理類持有具體類的實例,代為執(zhí)行具體類實例方法。上面說到,代理模式就是在訪問實際對象時引入一定程度的間接性,因為這種間接性,可以附加多種用途。這里的間接性就是指不直接調(diào)用實際對象的方法,那么我們在代理過程中就可以加上一些其他用途。就這個例子來說,加入班長在幫張三上交班費之前想要先反映一下張三最近學習有很大進步,通過代理模式很輕松就能辦到:

這里并沒有直接通過張三(被代理對象)來執(zhí)行上交班費的行為,而是通過班長(代理對象)來代理執(zhí)行了。這就是代理模式。

代理模式最主要的就是有一個公共接口(Person),一個具體的類(Student),一個代理類(StudentsProxy),代理類持有具體類的實例,代為執(zhí)行具體類實例方法。上面說到,代理模式就是在訪問實際對象時引入一定程度的間接性,因為這種間接性,可以附加多種用途。這里的間接性就是指不直接調(diào)用實際對象的方法,那么我們在代理過程中就可以加上一些其他用途。就這個例子來說,加入班長在幫張三上交班費之前想要先反映一下張三最近學習有很大進步,通過代理模式很輕松就能辦到:

/**
* 學生代理類,也實現(xiàn)了Person接口,保存一個學生實體,這樣既可以代理學生產(chǎn)生行為
* @author ChenHao
*
*/
public class StudentsProxy implements Person{
//被代理的學生
Student stu;
public StudentsProxy(Student stu) {
this.stu = stu;
}
//代理上交班費,調(diào)用被代理學生的上交班費行為
public void giveMoney() {
System.out.println("張三最近學習有進步!");
stu.giveMoney();
}
}

深度剖析java動態(tài)靜態(tài)代理原理源碼

模式優(yōu)缺點

優(yōu)點

1、代理模式能夠協(xié)調(diào)調(diào)用者和被調(diào)用者,在一定程度上降低了系統(tǒng)的耦合度。

2、代理對象可以在客戶端和目標對象之間起到中介的作用,這樣起到了的作用和保護了目標對象的

缺點

1、由于在客戶端和真實主題之間增加了代理對象,因此有些類型的代理模式可能會造成請求的處理速度變慢。
2、實現(xiàn)代理模式需要額外的工作,有些代理模式的實現(xiàn)非常復雜。

動態(tài)代理

1.動態(tài)代理

前面介紹了靜態(tài)代理,雖然靜態(tài)代理模式很好用,但是靜態(tài)代理還是存在一些局限性的,比如使用靜態(tài)代理模式需要程序員手寫很多代碼,這個過程是比較浪費時間和精力的。一旦需要代理的類中方法比較多,或者需要同時代理多個對象的時候,這無疑會增加很大的復雜度。

代理類在程序運行時創(chuàng)建的代理方式被成為動態(tài)代理。 我們上面靜態(tài)代理的例子中,代理類(studentProxy)是自己定義好的,在程序運行之前就已經(jīng)編譯完成。然而動態(tài)代理,代理類并不是在Java代碼中定義的,而是在運行時根據(jù)我們在Java代碼中的“指示”動態(tài)生成的。相比于靜態(tài)代理, 動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理,而不用修改每個代理類中的方法。

2、動態(tài)代理簡單實現(xiàn)

在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過這個類和這個接口可以生成JDK動態(tài)代理類和動態(tài)代理對象。

public class DynamicProxyTest {
interface IHello {
void sayHello();
}
static class Hello implements IHello {
@Override
public void sayHello() {
System.out.println("hello world");
}
}
static class DynamicProxy implements InvocationHandler {
Object originalObj;
Object bind(Object originalObj) {
this.originalObj = originalObj;
return Proxy.newProxyInstance(originalObj.getClass().getClassLoader(), originalObj.getClass().getInterfaces(), this);
}
/**
*Object proxy是代理的對象, Method method是真實對象中調(diào)用方法的Method類, Object[] args是真實對象中調(diào)用方法的參數(shù)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("welcome");
return method.invoke(originalObj, args);
}
}
public static void main(String[] args) {
IHello hello = (IHello) new DynamicProxy().bind(new Hello());
hello.sayHello();
}
}

運行結(jié)果如下:

welcome 
hello world

動態(tài)代理原理分析

上面說到,動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理,而不用修改每個代理類中的方法。是因為所有被代理執(zhí)行的方法,都是通過在InvocationHandler中的invoke方法調(diào)用的,所以我們只要在invoke方法中統(tǒng)一處理,就可以對所有被代理的方法進行相同的操作了。上述代碼里,唯一的“黑厘子”就是Proxy.newProxyInstance()方法,除此之外再沒有任何特殊之處。

在JDK動態(tài)代理中涉及如下角色:

業(yè)務接口Interface、業(yè)務實現(xiàn)類target、業(yè)務處理類Handler、JVM在內(nèi)存中生成的動態(tài)代理類$Proxy0

動態(tài)代理原理圖:

深度剖析java動態(tài)靜態(tài)代理原理源碼

說白了,動態(tài)代理的過程是這樣的:

1.Proxy通過傳遞給它的參數(shù)(interfaces/invocationHandler)生成代理類$Proxy0;

2.Proxy通過傳遞給它的參數(shù)(ClassLoader)來加載生成的代理類$Proxy0的字節(jié)碼文件;

動態(tài)代理的關(guān)鍵代碼就是Proxy.newProxyInstance(classLoader, interfaces, handler),我們跟進源代碼看看

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  // handler不能為空
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.
*/
  // 通過loader和接口,得到代理的Class對象
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 {
       // 創(chuàng)建代理對象的實例
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}

我們看一下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());
}
}
}

講解完了代理類的生成源碼,我們一定想要看看代理類的代碼是什么樣的,下面提供一個生成代理類的方法供大家使用:

/**
* 代理類的生成工具 
* @author ChenHao
* @since 2019-4-2 
*/
public class ProxyGeneratorUtils {
/**
* 把代理類的字節(jié)碼寫到硬盤上 
* @param path 保存路徑 
*/
public static void writeProxyClassToHardDisk(String path) {
// 第一種方法
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true); 
// 第二種方法 
// 獲取代理類的字節(jié)碼 
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", UserServiceImpl.class.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ProxyGeneratorUtils.writeProxyClassToHardDisk("C:/x/$Proxy11.class");
}
} 

此時就會在指定的C盤x文件夾下生成代理類的.class文件,我們看下反編譯后的結(jié)果:

package org.fenixsoft.bytecode;
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 DynamicProxyTest.IHello
{
private static Method m3;
private static Method m1;
private static Method m0;
private static Method m2;
/**
*注意這里是生成代理類的構(gòu)造方法,方法參數(shù)為InvocationHandler類型,看到這,是不是就有點明白
*super(paramInvocationHandler),是調(diào)用父類Proxy的構(gòu)造方法。
*父類持有:protected InvocationHandler h;
*Proxy構(gòu)造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws 
{
super(paramInvocationHandler);
}
/**
* 
*這里調(diào)用代理對象的sayHello方法,直接就調(diào)用了InvocationHandler中的invoke方法,并把m3傳了進去。
*this.h.invoke(this, m3, null); this.h就是父類Proxy中保存的InvocationHandler實例變量
*來,再想想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象,
*再聯(lián)系到InvacationHandler中的invoke方法。嗯,就是這樣。
*/
public final void sayHello()
throws 
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
// 此處由于版面原因,省略equals()、hashCode()、toString()三個方法的代碼
// 這3個方法的內(nèi)容與sayHello()非常相似。
static
{
try
{
m3 = Class.forName("org.fenixsoft.bytecode.DynamicProxyTest$IHello").getMethod("sayHello", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}

這個代理類的實現(xiàn)代碼也很簡單,它為傳入接口中的每一個方法,以及從 java.lang.Object中繼承來的equals()、hashCode()、toString()方法都生成了對應的實現(xiàn) ,并且統(tǒng)一調(diào)用了InvocationHandler對象的invoke()方法(代碼中的“this.h”就是父類Proxy中保存的InvocationHandler實例變量)來實現(xiàn)這些方法的內(nèi)容,各個方法的區(qū)別不過是傳入的參數(shù)和Method對象有所不同而已,所以無論調(diào)用動態(tài)代理的哪一個方法,實際上都是在執(zhí)行InvocationHandler.invoke()中的代理邏輯。

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 免费观看一级欧美大 | 亚洲一区成人在线 | 国产精品久久久久久模特 | 国产毛片在线看 | 国产亚洲欧美在线视频 | 毛片免费网 | 有色视频在线观看 | 国产高潮好爽好大受不了了 | 国产免费乱淫av | 亚洲精品日韩色噜噜久久五月 | 亚洲一区二区三区精品在线观看 | 夜间福利视频 | 特大黑人videos与另类娇小 | av电影免费播放 | 久久国产不卡 | 久久亚洲一区二区三区成人国产 | 特黄一区二区三区 | 素人视频免费观看 | 国产精品久久久不卡 | 成人午夜久久 | 色婷婷a v | 国产精品亚洲欧美一级在线 | 天天曰夜夜操 | 一本精品999爽爽久久久 | 久久久一区二区三区精品 | 369看片你懂的小视频在线观看 | 一级免费特黄视频 | 午夜视频在线观 | japanese massage tube| 成人一级毛片 | 一区二区国产在线 | 欧美一极视频 | 99re久久最新地址获取 | 免费久久久久 | 国产午夜小视频 | 神马福利网 | 国产一区日韩一区 | 国产影院一区 | 亚洲精品欧美在线 | 欧美在线另类 | 一级做受大片免费视频 |