一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

有點(diǎn)干貨 | JDK、CGLIB動(dòng)態(tài)代理使用以及源碼分析

 小傅哥 2021-12-13

微信公眾號(hào):bugstack蟲(chóng)洞棧
沉淀、分享、成長(zhǎng),專(zhuān)注于原創(chuàng)專(zhuān)題案例,以最易學(xué)習(xí)編程的方式分享知識(shí),讓自己和他人都能有所收獲。目前已完成的專(zhuān)題有;Netty4.x實(shí)戰(zhàn)專(zhuān)題案例、用Java實(shí)現(xiàn)JVM、基于JavaAgent的全鏈路監(jiān)控、手寫(xiě)RPC框架、架構(gòu)設(shè)計(jì)專(zhuān)題案例[Ing]等。歡迎?Star和使用,你用劍🗡、我用刀🔪,好的代碼都很燒😏,望你不吝出招💨!

前言介紹

在Java中動(dòng)態(tài)代理是非常重要也是非常有用的一個(gè)技術(shù)點(diǎn),如果沒(méi)有動(dòng)態(tài)代理技術(shù)幾乎也就不會(huì)有各種優(yōu)秀框架的出現(xiàn),包括Spring。
其實(shí)在動(dòng)態(tài)代理的使用中,除了我們平時(shí)用的Spring還有很多中間件和服務(wù)都用了動(dòng)態(tài)代理,例如;

  1. RPC通信框架Dubbo,在通信的時(shí)候由服務(wù)端提供一個(gè)接口描述信息的Jar,調(diào)用端進(jìn)行引用,之后在調(diào)用端引用后生成了對(duì)應(yīng)的代理類(lèi),當(dāng)執(zhí)行方法調(diào)用的時(shí)候,實(shí)際需要走到代理類(lèi)向服務(wù)提供端發(fā)送請(qǐng)求信息,直至內(nèi)容回傳。
  2. 另外在使用Mybatis時(shí)候可以知道只需要定義一個(gè)接口,不需要實(shí)現(xiàn)具體方法就可以調(diào)用到Mapper中定義的數(shù)據(jù)庫(kù)操作信息了。這樣極大的簡(jiǎn)化了代碼的開(kāi)發(fā),又增強(qiáng)了效率。
  3. 最后不知道你自己是否嘗試過(guò)開(kāi)發(fā)一些基于代理類(lèi)的框架,以此來(lái)優(yōu)化業(yè)務(wù)代碼。也就是將業(yè)務(wù)代碼中非業(yè)務(wù)邏輯又通用性的功能抽離出來(lái),開(kāi)發(fā)為獨(dú)立的組件。推薦個(gè)案例,方便知道代理類(lèi)的應(yīng)用:手寫(xiě)RPC框架第三章《RPC中間件》

代理方式

動(dòng)態(tài)代理可以使用Jdk方式也可以使用CGLB,他們的區(qū)別,如下;

類(lèi)型機(jī)制回調(diào)方式適用場(chǎng)景效率
JDK委托機(jī)制,代理類(lèi)和目標(biāo)類(lèi)都實(shí)現(xiàn)了同樣的接口,InvocationHandler持有目標(biāo)類(lèi),代理類(lèi)委托InvocationHandler去調(diào)用目標(biāo)類(lèi)的原始方法反射目標(biāo)類(lèi)是接口類(lèi)效率瓶頸在反射調(diào)用稍慢
CGLIB繼承機(jī)制,代理類(lèi)繼承了目標(biāo)類(lèi)并重寫(xiě)了目標(biāo)方法,通過(guò)回調(diào)函數(shù)MethodInterceptor調(diào)用父類(lèi)方法執(zhí)行原始邏輯通過(guò)FastClass方法索引調(diào)用非接口類(lèi),非final類(lèi),非final方法第一次調(diào)用因?yàn)橐啥鄠€(gè)Class對(duì)象較JDK方式慢,多次調(diào)用因?yàn)橛蟹椒ㄋ饕^反射方式快,如果方法過(guò)多switch case過(guò)多其效率還需測(cè)試

案例工程

itstack-demo-test
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo
    │           ├── proxy
    │           │└── cglib
    │           │    └── CglibProxy.java
    │           ├── jdk
    │           │├── reflect
    │           ││   ├── JDKInvocationHandler.java
    │           ││   └── JDKProxy.java
    │           │   └── util
    │           │    └── ClassLoaderUtils.java
    │           └── service
    │           ├── IUserService.java
    │           └── UserService.java
    └── test
        └── java
            └── org.itstack.demo.test
                └── ApiTest.java

基礎(chǔ)接口和方法便于驗(yàn)證

service/IUserService.java

public interface IUserService {

    String queryUserNameById(String userId);

}

service/UserService.java

public class UserService implements IUserService {

    public String queryUserNameById(String userId) {
        return "hi user " + userId;
    }

}

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

reflect/JDKInvocationHandler.java & 代理類(lèi)反射調(diào)用

  • 實(shí)現(xiàn)InvocationHandler.invoke,用于方法增強(qiáng){監(jiān)控、執(zhí)行其他業(yè)務(wù)邏輯、遠(yuǎn)程調(diào)用等}
  • 如果有需要額外的參數(shù)可以提供構(gòu)造方法
public class JDKInvocationHandler implements InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName());
        return "我被JDKProxy代理了";
    }

}

reflect/JDKProxy.java & 定義一個(gè)代理類(lèi)獲取的服務(wù)

  • Proxy.newProxyInstance 來(lái)實(shí)際生成代理類(lèi),過(guò)程如下;
    1. Class<?> cl = getProxyClass0(loader, intfs); 查找或生成指定的代理類(lèi)
    2. proxyClassCache.get(loader, interfaces); 代理類(lèi)的緩存中獲取
    3. subKeyFactory.apply(key, parameter) 繼續(xù)下一層
    4. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags); 生成代理類(lèi)的字節(jié)碼
public class JDKProxy {

    public static <T> T getProxy(Class<T> interfaceClass) throws Exception {
        InvocationHandler handler = new JDKInvocationHandler();
        ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();
        T result = (T) Proxy.newProxyInstance(classLoader, new Class[]{interfaceClass}, handler);
        return result;
    }

}

ApiTest.test_proxy_jdk() & 執(zhí)行調(diào)用并輸出反射類(lèi)的字節(jié)碼

  • 代理后調(diào)用方法驗(yàn)證
  • 通過(guò)使用ProxyGenerator.generateProxyClass獲取實(shí)際的字節(jié)碼,查看代理類(lèi)的內(nèi)容
@Test
public void test_proxy_jdk() throws Exception {

IUserService proxy = (IUserService) JDKProxy.getProxy(ClassLoaderUtils.forName("org.itstack.demo.service.IUserService"));
String userName = proxy.queryUserNameById("10001");
System.out.println(userName);

String name = "ProxyUserService";
byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{IUserService.class});

// 輸出類(lèi)字節(jié)碼
FileOutputStream out = null;
try {
out = new FileOutputStream(name + ".class");
System.out.println((new File("")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}

}

輸出結(jié)果

queryUserNameById
我被JDKProxy代理了

將生成的代理類(lèi)進(jìn)行反編譯jd-gui

部分內(nèi)容抽取,可以看到比較核心的方法,也就是我們?cè)谡{(diào)用的時(shí)候走到了這里

public final String queryUserNameById(String paramString)
    throws 
{
try
{
  return (String)this.h.invoke(this, m3, new Object[] { paramString });
}
catch (Error|RuntimeException localError)
{
  throw localError;
}
catch (Throwable localThrowable)
{
  throw new UndeclaredThrowableException(localThrowable);
}
}
  

static
{
try
{
  m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  m3 = Class.forName("org.itstack.demo.service.IUserService").getMethod("queryUserNameById", new Class[] { Class.forName("java.lang.String") });
  return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
  throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
  throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

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

cglib/CglibProxy.java

  • 提供構(gòu)造方法,生成CGLIB的代理類(lèi),回調(diào)this
  • intercept可以進(jìn)行方法的增強(qiáng),處理相關(guān)業(yè)務(wù)邏輯
  • CGLIB是通過(guò)ASM來(lái)操作字節(jié)碼生成類(lèi)
public class CglibProxy implements MethodInterceptor {

    public Object newInstall(Object object) {
        return Enhancer.create(object.getClass(), this);
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("我被CglibProxy代理了");
        return methodProxy.invokeSuper(o, objects);
    }

}

ApiTest.test_proxy_cglib() & 調(diào)用代理類(lèi)

@Test
public void test_proxy_cglib() {
    CglibProxy cglibProxy = new CglibProxy();
    UserService userService = (UserService) cglibProxy.newInstall(new UserService());
    String userName = userService.queryUserNameById("10001");
    System.out.println(userName);
}

輸出結(jié)果

我被CglibProxy代理了
hi user 10001

綜上總結(jié)

  • 在我們實(shí)際使用中兩種方式都用所有使用,也可以依照不同的訴求進(jìn)行選擇
  • 往往動(dòng)態(tài)代理會(huì)和注解共同使用,代理類(lèi)拿到以后獲取方法的注解,并做相應(yīng)的業(yè)務(wù)操作
  • 有時(shí)候你是否會(huì)遇到增加AOP不生效,因?yàn)橛袝r(shí)候有些類(lèi)是被代理操作的,并沒(méi)有執(zhí)行你的自定義注解也就是切面

微信公眾號(hào):bugstack蟲(chóng)洞棧,歡迎關(guān)注&獲取源碼

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    久久久免费精品人妻一区二区三区| 粗暴蹂躏中文一区二区三区| 精品日韩中文字幕视频在线| 久久精品中文字幕人妻中文| 91日韩欧美国产视频| 日本欧美一区二区三区就| 九九热精品视频在线观看| 精品国模一区二区三区欧美| 91福利视频日本免费看看| 91麻豆精品欧美一区| 激情丁香激情五月婷婷| 欧美日韩乱码一区二区三区| 成人国产激情福利久久| 亚洲综合精品天堂夜夜| 又色又爽又无遮挡的视频 | 熟女乱一区二区三区四区| 日韩免费午夜福利视频| 中文文精品字幕一区二区| 熟女乱一区二区三区丝袜| 伊人国产精选免费观看在线视频 | 欧美整片精品日韩综合| 亚洲精品偷拍一区二区三区| 欧美激情区一区二区三区| 欧美美女视频在线免费看| 超碰在线免费公开中国黄片| 久久99青青精品免费观看| 白丝美女被插入视频在线观看| 国产一区二区精品丝袜| 日本欧美一区二区三区就| 久久精品国产熟女精品| 国内真实露脸偷拍视频| 免费一级欧美大片免费看| 91久久精品在这里色伊人| 色婷婷国产精品视频一区二区保健| 又黄又色又爽又免费的视频| 一本色道久久综合狠狠躁| 国产又色又粗又黄又爽| 欧美黄色成人真人视频| 成人日韩视频中文字幕| 午夜福利直播在线视频| 亚洲一区二区三区日韩91|