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

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

 更新時(shí)間:2019年06月18日 11:01:48   作者:chen_hao  
這篇文章主要介紹了深度剖析java動(dòng)態(tài)靜態(tài)代理原理源碼,關(guān)于Java中的動(dòng)態(tài)代理,我們首先需要了解的是一種常用的設(shè)計(jì)模式--代理模式,而對(duì)于代理,根據(jù)創(chuàng)建代理類(lèi)的時(shí)間點(diǎn),又可以分為靜態(tài)代理和動(dòng)態(tài)代理。,需要的朋友可以參考下

正文

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

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

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

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

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

班長(zhǎng)就是學(xué)生的代理。

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

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

Student類(lèi)實(shí)現(xiàn)Person接口。Student可以具體實(shí)施上交班費(fèi)的動(dòng)作。

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

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

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

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

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

運(yùn)行結(jié)果:

這里并沒(méi)有直接通過(guò)張三(被代理對(duì)象)來(lái)執(zhí)行上交班費(fèi)的行為,而是通過(guò)班長(zhǎng)(代理對(duì)象)來(lái)代理執(zhí)行了。這就是代理模式。

代理模式最主要的就是有一個(gè)公共接口(Person),一個(gè)具體的類(lèi)(Student),一個(gè)代理類(lèi)(StudentsProxy),代理類(lèi)持有具體類(lèi)的實(shí)例,代為執(zhí)行具體類(lèi)實(shí)例方法。上面說(shuō)到,代理模式就是在訪問(wèn)實(shí)際對(duì)象時(shí)引入一定程度的間接性,因?yàn)檫@種間接性,可以附加多種用途。這里的間接性就是指不直接調(diào)用實(shí)際對(duì)象的方法,那么我們?cè)诖磉^(guò)程中就可以加上一些其他用途。就這個(gè)例子來(lái)說(shuō),加入班長(zhǎng)在幫張三上交班費(fèi)之前想要先反映一下張三最近學(xué)習(xí)有很大進(jìn)步,通過(guò)代理模式很輕松就能辦到:

這里并沒(méi)有直接通過(guò)張三(被代理對(duì)象)來(lái)執(zhí)行上交班費(fèi)的行為,而是通過(guò)班長(zhǎng)(代理對(duì)象)來(lái)代理執(zhí)行了。這就是代理模式。

代理模式最主要的就是有一個(gè)公共接口(Person),一個(gè)具體的類(lèi)(Student),一個(gè)代理類(lèi)(StudentsProxy),代理類(lèi)持有具體類(lèi)的實(shí)例,代為執(zhí)行具體類(lèi)實(shí)例方法。上面說(shuō)到,代理模式就是在訪問(wèn)實(shí)際對(duì)象時(shí)引入一定程度的間接性,因?yàn)檫@種間接性,可以附加多種用途。這里的間接性就是指不直接調(diào)用實(shí)際對(duì)象的方法,那么我們?cè)诖磉^(guò)程中就可以加上一些其他用途。就這個(gè)例子來(lái)說(shuō),加入班長(zhǎng)在幫張三上交班費(fèi)之前想要先反映一下張三最近學(xué)習(xí)有很大進(jìn)步,通過(guò)代理模式很輕松就能辦到:

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

模式優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

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

2、代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用,這樣起到了的作用和保護(hù)了目標(biāo)對(duì)象的

缺點(diǎn)

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

動(dòng)態(tài)代理

1.動(dòng)態(tài)代理

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

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

2、動(dòng)態(tài)代理簡(jiǎn)單實(shí)現(xiàn)

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

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是代理的對(duì)象, Method method是真實(shí)對(duì)象中調(diào)用方法的Method類(lèi), Object[] args是真實(shí)對(duì)象中調(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();
}
}

運(yùn)行結(jié)果如下:

welcome 
hello world

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

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

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

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

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

說(shuō)白了,動(dòng)態(tài)代理的過(guò)程是這樣的:

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

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

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

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.
*/
  // 通過(guò)loader和接口,得到代理的Class對(duì)象
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)建代理對(duì)象的實(shí)例
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());
}
}
}

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

/**
* 代理類(lèi)的生成工具 
* @author ChenHao
* @since 2019-4-2 
*/
public class ProxyGeneratorUtils {
/**
* 把代理類(lèi)的字節(jié)碼寫(xiě)到硬盤(pán)上 
* @param path 保存路徑 
*/
public static void writeProxyClassToHardDisk(String path) {
// 第一種方法
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", true); 
// 第二種方法 
// 獲取代理類(lèi)的字節(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");
}
} 

此時(shí)就會(huì)在指定的C盤(pán)x文件夾下生成代理類(lèi)的.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;
/**
*注意這里是生成代理類(lèi)的構(gòu)造方法,方法參數(shù)為InvocationHandler類(lèi)型,看到這,是不是就有點(diǎn)明白
*super(paramInvocationHandler),是調(diào)用父類(lèi)Proxy的構(gòu)造方法。
*父類(lèi)持有: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)用代理對(duì)象的sayHello方法,直接就調(diào)用了InvocationHandler中的invoke方法,并把m3傳了進(jìn)去。
*this.h.invoke(this, m3, null); this.h就是父類(lèi)Proxy中保存的InvocationHandler實(shí)例變量
*來(lái),再想想,代理對(duì)象持有一個(gè)InvocationHandler對(duì)象,InvocationHandler對(duì)象持有一個(gè)被代理的對(duì)象,
*再聯(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()三個(gè)方法的代碼
// 這3個(gè)方法的內(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());
}
}
}

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

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

相關(guān)文章

  • SpringMVC適配器模式代碼示例

    SpringMVC適配器模式代碼示例

    這篇文章主要介紹了SpringMVC適配器模式代碼示例,涉及模擬springmvc的Java代碼等相關(guān)內(nèi)容,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-11-11
  • IDEA搭建配置Java?Web項(xiàng)目的詳細(xì)步驟

    IDEA搭建配置Java?Web項(xiàng)目的詳細(xì)步驟

    這篇文章詳細(xì)介紹了如何使用IDEA創(chuàng)建和配置JavaWeb項(xiàng)目,包括項(xiàng)目結(jié)構(gòu)設(shè)置、WEB-INF目錄和jsp文件的創(chuàng)建,以及Tomcat的配置,是Java初學(xué)者的實(shí)用指南,需要的朋友可以參考下
    2024-10-10
  • java 獲取日期的幾天前,幾個(gè)月前和幾年前的實(shí)例

    java 獲取日期的幾天前,幾個(gè)月前和幾年前的實(shí)例

    下面小編就為大家?guī)?lái)一篇java 獲取日期的幾天前,幾個(gè)月前和幾年前的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • 使用Logback設(shè)置property參數(shù)方式

    使用Logback設(shè)置property參數(shù)方式

    這篇文章主要介紹了使用Logback設(shè)置property參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • IDEA中maven依賴(lài)報(bào)紅的問(wèn)題解決辦法

    IDEA中maven依賴(lài)報(bào)紅的問(wèn)題解決辦法

    這篇文章主要給大家介紹了關(guān)于IDEA中maven依賴(lài)報(bào)紅的問(wèn)題解決辦法,在使用IDEA過(guò)程中,經(jīng)常會(huì)出現(xiàn)maven依賴(lài)報(bào)紅的問(wèn)題,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Java定義泛型方法實(shí)例分析

    Java定義泛型方法實(shí)例分析

    這篇文章主要介紹了Java定義泛型方法,結(jié)合實(shí)例形式分析了java定義泛型的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-07-07
  • Java 超詳細(xì)講解異常的處理

    Java 超詳細(xì)講解異常的處理

    異常就是不正常,比如當(dāng)我們身體出現(xiàn)了異常我們會(huì)根據(jù)身體情況選擇喝開(kāi)水、吃藥、看病、等 異常處理方法。 java異常處理機(jī)制是我們java語(yǔ)言使用異常處理機(jī)制為程序提供了錯(cuò)誤處理的能力,程序出現(xiàn)的錯(cuò)誤,程序可以安全的退出,以保證程序正常的運(yùn)行等
    2022-04-04
  • java動(dòng)態(tài)方法調(diào)度實(shí)例分析

    java動(dòng)態(tài)方法調(diào)度實(shí)例分析

    這篇文章主要介紹了java動(dòng)態(tài)方法調(diào)度,結(jié)合實(shí)例形式對(duì)比分析了java的動(dòng)態(tài)方法調(diào)度原理、使用方法與注意事項(xiàng),需要的朋友可以參考下
    2016-06-06
  • Springboot?集成spring?cache緩存的解決方案

    Springboot?集成spring?cache緩存的解決方案

    這篇文章主要介紹了Springboot?集成spring?cache緩存,使用緩存最關(guān)鍵的一點(diǎn)就是保證緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性,本文給大家介紹最常用的緩存操作模式,對(duì)Springboot?集成spring?cache緩存操作流程感興趣的朋友一起看看吧
    2022-06-06
  • Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具

    Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單QQ聊天工具,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09

最新評(píng)論

江安县| 奇台县| 叶城县| 奎屯市| 宜章县| 阳东县| 阳江市| 涟水县| 屯留县| 尖扎县| 凤翔县| 连平县| 体育| 德兴市| 栾川县| 苗栗市| 岳阳市| 通许县| 邛崃市| 齐齐哈尔市| 曲沃县| 通辽市| 曲靖市| 紫阳县| 巴林右旗| 老河口市| 临泽县| 凤翔县| 扶绥县| 樟树市| 仙桃市| 垣曲县| 临西县| 孟连| 河源市| 松阳县| 嘉峪关市| 乐业县| 嵊州市| 宜兰市| 青神县|