欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java代理的幾種實(shí)現(xiàn)方式總結(jié)

 更新時(shí)間:2023年12月29日 10:45:46   作者:鏗然架構(gòu)  
本文將通過(guò)例子說(shuō)明java代理的幾種實(shí)現(xiàn)方式,并比較它們之間的差異,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的參考價(jià)值,需要的朋友可以參考下

1. 簡(jiǎn)介

本文將通過(guò)例子說(shuō)明java代理的幾種實(shí)現(xiàn)方式,并比較它們之間的差異。

1.1 例子應(yīng)用場(chǎng)景

代理對(duì)象給被代理對(duì)象(目標(biāo)對(duì)象),增加一個(gè)目標(biāo)對(duì)象方法被調(diào)用的日志,用于后續(xù)通過(guò)日志采集統(tǒng)計(jì)方法被調(diào)用次數(shù)。

1.2 被代理對(duì)象代碼

后面所有例子均使用這些相同代碼。

● 被代理對(duì)象接口

public interface Animal {
    void say();
}

● 被代理對(duì)象Dog類

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Dog implements Animal {
    @Override
    public void say() {
        log.info("汪汪");
    }
}

2. 靜態(tài)代理

簡(jiǎn)單點(diǎn)說(shuō),靜態(tài)代理就是在編碼階段已經(jīng)確定了代理對(duì)象。

這里簡(jiǎn)單回顧下代理模式的基本類結(jié)構(gòu):

描述
Subject被代理目標(biāo)類的接口。
RealSubject被代理目標(biāo)類。
ProxySubject代理類,需要實(shí)現(xiàn)被代理目標(biāo)類接口,同時(shí)持有被代理目標(biāo)類對(duì)象實(shí)例,實(shí)際調(diào)用時(shí),通過(guò)委托的方式調(diào)用被代理目標(biāo)類。
SubjectFactory被代理目標(biāo)類的工廠類,通過(guò)工廠類屏蔽創(chuàng)建Subject的細(xì)節(jié),客戶端看到的是Subject,至于具體是哪個(gè)實(shí)現(xiàn)類不需要關(guān)心。

2.1 代碼示例

2.1.1 DogStaticProxy

Dog代理類:

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
// 代理類,實(shí)現(xiàn)Subject接口
public class DogStaticProxy implements Animal {
    // 持有RealSubject對(duì)象實(shí)例
    private Dog dog;

    @Override
    public void say() {
        // 增強(qiáng)的能力,打印方法調(diào)用日志
        log.info("Dog.say() called.");
        // 通過(guò)委托的方式調(diào)用RealSubject對(duì)象實(shí)例方法
        dog.say();
    }
}

2.1.2 DuckStaticProxy

Duck代理類:

import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@AllArgsConstructor
public class DuckStaticProxy implements Animal {
    private Duck duck;

    @Override
    public void say() {
        log.info("Dog.say() called.");
        duck.say();
    }
}

2.1.3 StaticProxyFactory

代理工廠類:

public class StaticProxyFactory {
    
    // 屏蔽創(chuàng)建Subject實(shí)例的細(xì)節(jié),使用者看到的只有Subject接口,即Animal
    public static Animal createDog() {
        return new DogStaticProxy(new Dog());
    }

    public static Animal createDuck() {
        return new DuckStaticProxy(new Duck());
    }
}

2.1.4 StaticProxyFactoryTest

測(cè)試代碼:

import org.junit.jupiter.api.Test;

class StaticProxyFactoryTest {
    @Test
    public void exec() {
        Animal dog = StaticProxyFactory.createDog();
        Animal duck = StaticProxyFactory.createDuck();

        dog.say();
        duck.say();
    }
}

2.1.5 輸出日志

2023-12-28 12:00:35 INFO  DogStaticProxy:16 - Dog.say() called.
2023-12-28 12:00:35 INFO  Dog:10 - 汪汪
2023-12-28 12:00:35 INFO  DuckStaticProxy:13 - Dog.say() called.
2023-12-28 12:00:35 INFO  Duck:9 - 嘎嘎

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

動(dòng)態(tài)代理就是在編碼階段還沒(méi)有確定代理對(duì)象,在運(yùn)行期才動(dòng)態(tài)確定。

如下是JDK動(dòng)態(tài)代理的類結(jié)構(gòu):

描述
Subject被代理目標(biāo)類的接口。
RealSubject被代理目標(biāo)類。
InvocationHandler可以理解為一個(gè)攔截器,被代理對(duì)象方法調(diào)用時(shí)都會(huì)先調(diào)用此接口的invoke方法,在invoke方法內(nèi)部去調(diào)用被代理對(duì)象方法。
InvocationHandler實(shí)現(xiàn)類InvocationHandler的實(shí)現(xiàn)類
ProxyJDK代理工具類,用于生成代理對(duì)象,生成代理對(duì)象時(shí)會(huì)用到RealSubject和InvocationHandler實(shí)現(xiàn)類。

3.1 代碼示例

3.1.1 AnimalInvocationHandler

InvocationHandler的實(shí)現(xiàn)類:

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

@Slf4j
public class AnimalInvocationHandler implements InvocationHandler {
    private Animal animal;

    // 通過(guò)構(gòu)造器傳入目標(biāo)類實(shí)例對(duì)象
    public AnimalInvocationHandler(Animal animal) {
        this.animal = animal;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 增強(qiáng)能力,打印方法調(diào)用日志,采集后統(tǒng)計(jì)調(diào)用次數(shù)
        log.info(getCalledMethodInfo(method));
        // 調(diào)用目標(biāo)類方法,參數(shù)是目標(biāo)類實(shí)例和對(duì)應(yīng)的方法參數(shù)
        return method.invoke(animal, args);
    }

    // 輔助方法,返回被調(diào)用方法信息
    private String getCalledMethodInfo(Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(animal.getClass().getSimpleName())
                .append(".")
                .append(method.getName())
                .append("() called.");
        return builder.toString();
    }
}

3.1.2 JDKProxyFactory

代理工廠類:

import java.lang.reflect.Proxy;

public class JDKProxyFactory {

    public static Animal createDog() {
        return getProxy(new Dog());
    }

    public static Animal createDuck() {
        return getProxy(new Duck());
    }

    private static Animal getProxy(Animal implInstance) {
        AnimalInvocationHandler handler = new AnimalInvocationHandler(implInstance);
        // 創(chuàng)建代理對(duì)象
        Object proxy = Proxy.newProxyInstance(implInstance.getClass().getClassLoader(),
                implInstance.getClass().getInterfaces(),
                handler);
        return (Animal) proxy;
    }
}

3.1.3 JDKProxyFactoryTest

測(cè)試類:

import org.junit.jupiter.api.Test;

class JDKProxyFactoryTest {

    @Test
    public void exec() {
        Animal dog = JDKProxyFactory.createDog();
        Animal duck = JDKProxyFactory.createDuck();

        dog.say();
        duck.say();
    }
}

3.1.4 輸出日志

2023-12-28 12:27:37 INFO  AnimalInvocationHandler:19 - Dog.say() called.
2023-12-28 12:27:37 INFO  Dog:10 - 汪汪
2023-12-28 12:27:37 INFO  AnimalInvocationHandler:19 - Duck.say() called.
2023-12-28 12:27:37 INFO  Duck:9 - 嘎嘎

3.2 通用能力重構(gòu)

打印方法調(diào)用日志不僅僅適用于Animal,也適用于其他所有有此需求的類,可以優(yōu)化一下。

注:此優(yōu)化非JDK動(dòng)態(tài)代理的能力,存粹就是順便處理一下,重構(gòu)無(wú)處不在,只要你想做。

3.2.1 代碼

3.2.1.1 MethodPrinterInvocationHandler

AnimalInvocationHandler只能適用于Animal,重構(gòu)通過(guò)泛型實(shí)現(xiàn):

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

@Slf4j
// 使用泛型,只要是有接口的實(shí)現(xiàn)類都可以使用
public class MethodPrinterInvocationHandler<T> implements InvocationHandler {
    private T t;
    public MethodPrinterInvocationHandler(T t) {
        this.t = t;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info(getCalledMethod(method));
        return method.invoke(t, args);
    }

    private String getCalledMethod(Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(t.getClass().getSimpleName())
                .append(".")
                .append(method.getName())
                .append("() called.");
        return builder.toString();
    }
}

3.2.1.2 MethodPrinterJDKProxyFactory

代理工廠類同樣通過(guò)泛型實(shí)現(xiàn):

import java.lang.reflect.Proxy;

public class MethodPrinterJDKProxyFactory {

    // 使用泛型,除了Animal接口和其子類,其他接口和子類也可以使用
    public static <T, R> R getProxy(T implInstance ) {
        MethodPrinterInvocationHandler<T> handler = new MethodPrinterInvocationHandler(implInstance);
        return (R) Proxy.newProxyInstance(implInstance.getClass().getClassLoader(),
                implInstance.getClass().getInterfaces(),
                handler);
    }
}

3.2.1.3 AnimalJDKProxyFactory

public class AnimalJDKProxyFactory {
    public static Animal createDuck() {
        return MethodPrinterJDKProxyFactory.getProxy(new Duck());
    }

    public static Animal createDog() {
        return MethodPrinterJDKProxyFactory.getProxy(new Dog());
    }
}

3.2.1.4 MethodPrinterJDKProxyFactoryTest

import org.junit.jupiter.api.Test;

class MethodPrinterJDKProxyFactoryTest {

    @Test
    public void exec() {
        Animal dog = AnimalJDKProxyFactory.createDog();
        Animal duck = AnimalJDKProxyFactory.createDuck();
        dog.say();
        duck.say();
    }
}

3.2.1.5 打印日志

2023-12-28 12:54:00 INFO  MethodPrinterInvocationHandler:18 - Dog.say() called.
2023-12-28 12:54:00 INFO  Dog:10 - 汪汪
2023-12-28 12:54:00 INFO  MethodPrinterInvocationHandler:18 - Duck.say() called.
2023-12-28 12:54:00 INFO  Duck:9 - 嘎嘎

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

JDK動(dòng)態(tài)代理只能代理接口,無(wú)法直接代理類,下面看個(gè)例子。

3.3.1 代碼

3.3.1.1 Cat

新增一個(gè)類,沒(méi)有實(shí)現(xiàn)Animal接口,用于證明沒(méi)有接口無(wú)法使用JDK動(dòng)態(tài)代理:

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Cat {

    public void say() {
        log.info("喵喵");
    }
}

3.3.1.2 CatInvocationHandler

攔截器也要調(diào)整,將Cat作為構(gòu)造器參數(shù),不能再使用Animal:

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

@Slf4j
public class CatInvocationHandler implements InvocationHandler {
    private Cat cat;
    
    public CatInvocationHandler(Cat cat) {
        this.cat = cat;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        log.info(getCalledMethod(method));
        return method.invoke(cat, args);
    }

    private String getCalledMethod(Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(cat.getClass().getSimpleName())
                .append(".")
                .append(method.getName())
                .append("() called.");
        return builder.toString();
    }
}

3.3.1.3 ErrorJDKProxyFactory

代理工廠類:

import java.lang.reflect.Proxy;

public class ErrorJDKProxyFactory {

    public static Cat createCat() {
        return getProxy(new Cat());
    }

    private static Cat getProxy(Cat cat) {
        CatInvocationHandler handler = new CatInvocationHandler(cat);
        // 這里的寫法仍然不變,和之前一樣,只是類型變化
        return (Cat) Proxy.newProxyInstance(cat.getClass().getClassLoader(), cat.getClass().getInterfaces(), handler);
    }
}

3.3.1.4 ErrorJDKProxyFactoryTest

測(cè)試類:

import org.junit.jupiter.api.Test;

class ErrorJDKProxyFactoryTest {

    @Test
    void createCat() {
        Cat cat = ErrorJDKProxyFactory.createCat();
        cat.say();
    }
}

3.3.1.5 輸出日志

java.lang.ClassCastException: com.sun.proxy.$Proxy8 cannot be cast to com.kengcoder.javaawsome.proxy.jdkproxy.Cat

	at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.getProxy(ErrorJDKProxyFactory.java:13)
	at com.kengcoder.javaawsome.proxy.jdkproxy.ErrorJDKProxyFactory.createCat(ErrorJDKProxyFactory.java:8)

3.3.1.6 原因分析

● 通過(guò)類代理

兩個(gè)對(duì)象類型不一致,沒(méi)法做強(qiáng)轉(zhuǎn)。

● 通過(guò)接口代理

代理對(duì)象類型為Animal,可以強(qiáng)轉(zhuǎn)為Animal接口。

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

CGLIB動(dòng)態(tài)代理是三方庫(kù)實(shí)現(xiàn),和JDK動(dòng)態(tài)代理的區(qū)別是:既可以代理接口,也可以代理類。

CGLIB動(dòng)態(tài)代理實(shí)現(xiàn)的類結(jié)構(gòu)如下:

描述
Subject被代理目標(biāo)類的接口。
RealSubject被代理目標(biāo)類。
MethodInterceptor可以理解為一個(gè)攔截器,被代理對(duì)象方法調(diào)用時(shí)都會(huì)先調(diào)用此接口的intercept方法,在intercept方法內(nèi)部去調(diào)用被代理對(duì)象方法。
MethodInterceptor實(shí)現(xiàn)類MethodInterceptor的實(shí)現(xiàn)類
Enhancer代理工具類,用于生成代理對(duì)象,生成代理對(duì)象時(shí)會(huì)用到RealSubject和MethodInterceptor實(shí)現(xiàn)類。

4.1 代碼

4.1.1 CglibInterceptor

MethodInterceptor的實(shí)現(xiàn)類:

import lombok.extern.slf4j.Slf4j;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

@Slf4j
public class CglibInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        log.info(getCalledMethod(method));
        return methodProxy.invokeSuper(target, args);
    }

    private String getCalledMethod(Method method) {
        StringBuilder builder = new StringBuilder();
        builder.append(method.getDeclaringClass().getSimpleName())
                .append(".")
                .append(method.getName())
                .append("() called.");
        return builder.toString();
    }
}

4.1.2 CglibProxyFactory

CGLIB代理工廠類,抽象出通用方法:

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {

    public static <T> T getProxy(Class<T> clazz) {
        Enhancer enhancer = new Enhancer();
        // 設(shè)置超類,字面看就是代理對(duì)象是目標(biāo)類的子類,通過(guò)繼承方式實(shí)現(xiàn),而不是組合方式實(shí)現(xiàn)。
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new CglibInterceptor());
        return (T)enhancer.create();
    }
}

4.1.3 AnimalCglibProxyFactory

Animal工廠類:

public class AnimalCglibProxyFactory {
    public static Animal createDog() {
        return CglibProxyFactory.getProxy(Dog.class);
    }

    public static Animal createDuck() {
        return CglibProxyFactory.getProxy(Duck.class);
    }
}

4.1.4 CglibProxyFactoryTest

import org.junit.jupiter.api.Test;

class CglibProxyFactoryTest {
    @Test
    public void exec() {
        Animal dog = AnimalCglibProxyFactory.createDog();
        Animal duck = AnimalCglibProxyFactory.createDuck();

        dog.say();
        duck.say();
    }
}

4.1.5 輸出日志

2023-12-28 13:23:56 INFO  CglibInterceptor:13 - Dog.say() called.
2023-12-28 13:23:56 INFO  Dog:10 - 汪汪
2023-12-28 13:23:56 INFO  CglibInterceptor:13 - Duck.say() called.
2023-12-28 13:23:56 INFO  Duck:9 - 嘎嘎

5. 總結(jié)

本文介紹了幾種java代理的實(shí)現(xiàn)方式,在過(guò)程中通過(guò)重構(gòu)展示了如何通過(guò)java泛型實(shí)現(xiàn)通用能力,以及通過(guò)工廠模式屏蔽類實(shí)例創(chuàng)建細(xì)節(jié),提升擴(kuò)展性。

幾種java代理的差別如下:

比較項(xiàng)靜態(tài)代理JDK動(dòng)態(tài)代理CGLIB動(dòng)態(tài)代理
代理對(duì)象接口和類接口接口和類
實(shí)現(xiàn)接口方法需手動(dòng)一一實(shí)現(xiàn)自動(dòng)實(shí)現(xiàn)自動(dòng)實(shí)現(xiàn)
攔截方法每個(gè)方法獨(dú)立實(shí)現(xiàn),完全隔離開(kāi)。都在一處實(shí)現(xiàn),當(dāng)指定方法攔截時(shí)要增加判斷邏輯。同靜態(tài)代理。

6. 最后

以上就是Java代理的幾種實(shí)現(xiàn)方式總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于Java代理實(shí)現(xiàn)方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Jmeter內(nèi)置變量vars和props的使用詳解

    Jmeter內(nèi)置變量vars和props的使用詳解

    JMeter是一個(gè)功能強(qiáng)大的負(fù)載測(cè)試工具,它提供了許多有用的內(nèi)置變量來(lái)支持測(cè)試過(guò)程,其中最常用的變量是 vars 和 props,本文通過(guò)代碼示例詳細(xì)給大家介紹了Jmeter內(nèi)置變量vars和props的使用,需要的朋友可以參考下
    2024-08-08
  • Java執(zhí)行shell命令的實(shí)現(xiàn)

    Java執(zhí)行shell命令的實(shí)現(xiàn)

    本文主要介紹了Java執(zhí)行shell命令的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • 基于RabbitMQ幾種Exchange 模式詳解

    基于RabbitMQ幾種Exchange 模式詳解

    下面小編就為大家?guī)?lái)一篇基于RabbitMQ幾種Exchange 模式詳解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-11-11
  • spring Security的自定義用戶認(rèn)證過(guò)程詳解

    spring Security的自定義用戶認(rèn)證過(guò)程詳解

    這篇文章主要介紹了spring Security的自定義用戶認(rèn)證過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • java線程并發(fā)控制同步工具CountDownLatch

    java線程并發(fā)控制同步工具CountDownLatch

    這篇文章主要為大家介紹了java線程并發(fā)控制同步工具CountDownLatch使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • Java設(shè)計(jì)模式之觀察者模式

    Java設(shè)計(jì)模式之觀察者模式

    本文詳細(xì)講解了Java設(shè)計(jì)模式之觀察者模式,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • Java二叉樹(shù)查詢?cè)砩钊敕治鲋v解

    Java二叉樹(shù)查詢?cè)砩钊敕治鲋v解

    這篇文章主要介紹了Java二叉樹(shù)查詢?cè)恚娌檎覙?shù),又稱二叉排序樹(shù),亦稱二叉搜索樹(shù),是數(shù)據(jù)結(jié)構(gòu)中的一類。在一般情況下,查找效率比鏈表結(jié)構(gòu)要高
    2022-11-11
  • mybatis-plus處理blob字段的完整示例代碼

    mybatis-plus處理blob字段的完整示例代碼

    在Spring Boot項(xiàng)目中使用MyBatis-Plus處理longblob字段時(shí),我們可以按照本文的步驟進(jìn)行操作,假設(shè) longblob 存儲(chǔ)的是字符串?dāng)?shù)據(jù),本文給大家提供完整示例代碼,感興趣的朋友參考下
    2023-12-12
  • SpringBoot?Validation快速實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例代碼

    SpringBoot?Validation快速實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例代碼

    在實(shí)際開(kāi)發(fā)中,肯定會(huì)經(jīng)常遇到對(duì)參數(shù)字段進(jìn)行校驗(yàn)的場(chǎng)景,通常我們只能寫大量的if else來(lái)完成校驗(yàn)工作,而如果使用SpringBoot Validation則可以輕松的通過(guò)注解來(lái)完成,接下來(lái)小編給大家介紹下利用SpringBoot?Validation快速實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的示例代碼,需要的朋友參考下吧
    2022-06-06
  • 如何通過(guò)ServletInputStream讀取http請(qǐng)求傳入的數(shù)據(jù)

    如何通過(guò)ServletInputStream讀取http請(qǐng)求傳入的數(shù)據(jù)

    這篇文章主要介紹了如何通過(guò)ServletInputStream讀取http請(qǐng)求傳入的數(shù)據(jù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10

最新評(píng)論