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

jdk動態(tài)代理和cglib動態(tài)代理詳解

 更新時間:2021年07月02日 16:59:12   作者:飯一碗  
本篇文章主要介紹了深度剖析java中JDK動態(tài)代理機制 ,動態(tài)代理避免了開發(fā)人員編寫各個繁鎖的靜態(tài)代理類,只需簡單地指定一組接口及目標(biāo)類對象就能動態(tài)的獲得代理對象

如上圖,代理模式可分為動態(tài)代理和靜態(tài)代理,我們比較常用的有動態(tài)代理中的jdk動態(tài)代理和Cglib代理,像spring框架、hibernate框架中都采用了JDK動態(tài)代理,下面將結(jié)合代碼闡述兩種代理模式的使用與區(qū)別。

靜態(tài)代理

靜態(tài)代理的代理對象和被代理對象在代理之前就已經(jīng)確定,它們都實現(xiàn)相同的接口或繼承相同的抽象類。靜態(tài)代理模式一般由業(yè)務(wù)實現(xiàn)類和業(yè)務(wù)代理類組成,業(yè)務(wù)實現(xiàn)類里面實現(xiàn)主要的業(yè)務(wù)邏輯,業(yè)務(wù)代理類負責(zé)在業(yè)務(wù)方法調(diào)用的前后作一些你需要的處理,如日志記錄、權(quán)限攔截等功能…實現(xiàn)業(yè)務(wù)邏輯與業(yè)務(wù)方法外的功能解耦,減少了對業(yè)務(wù)方法的入侵。靜態(tài)代理又可細分為:基于繼承的方式和基于聚合的方式實現(xiàn)。

場景:假設(shè)一個預(yù)減庫存的操作,需要在預(yù)減的前后加日志記錄(我這里是springboot項目)

基于繼承的方式實現(xiàn)靜態(tài)代理

/**
 * 業(yè)務(wù)實現(xiàn)類接口
 */
public interface OrderService {
    //減庫存操作
    void reduceStock();
}
/**
 * 業(yè)務(wù)實現(xiàn)類
 */
@Slf4j
public class OrderServiceImpl implements OrderService {
    @Override
    public void reduceStock() {
        try {
            log.info("預(yù)減庫存中……");
            Thread.sleep(1000);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
/**
 * 代理類
 */
@Slf4j
public class OrderServiceLogProxy extends OrderServiceImpl{
    @Override
    public void reduceStock() {
        log.info("預(yù)減庫存開始……");
        super.reduceStock();
        log.info("預(yù)減庫存結(jié)束……");
    }
}
    /**
     * 測試?yán)^承方式實現(xiàn)的靜態(tài)代理
     */
    @Test
    public void testOrderServiceProxy(){
        OrderServiceLogProxy proxy = new OrderServiceLogProxy();
        proxy.reduceStock();
    }

輸出結(jié)果

14:53:53.769 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceLogProxy - 預(yù)減庫存開始……
14:53:53.771 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceImpl - 預(yù)減庫存中……
14:53:54.771 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceLogProxy - 預(yù)減庫存結(jié)束……

可以看到,OrderServiceLogProxy已經(jīng)實現(xiàn)了為OrderServiceImpl的代理,通過代理類的同名方法來增強了業(yè)務(wù)方法前后邏輯。

基于聚合的方式實現(xiàn)靜態(tài)代理

聚合的意思就是把業(yè)務(wù)類引入到了代理類中,接口和業(yè)務(wù)實現(xiàn)類還是之前的OrderService、OrderServiceImpl,代理類改為如下:

/**
 * 聚合方式實現(xiàn)靜態(tài)代理:代理類中引入業(yè)務(wù)類
 */
@Slf4j
public class OrderServiceLogProxy2 implements OrderService {
    private OrderServiceImpl orderService;
    public OrderServiceLogProxy2(OrderServiceImpl orderService) {
        this.orderService = orderService;
    }
    @Override
    public void reduceStock() {
        log.info("預(yù)減庫存開始……");
        orderService.reduceStock();
        log.info("預(yù)減庫存結(jié)束……");
    }
}
    /**
     * 測試聚合方式實現(xiàn)的靜態(tài)代理
     */
    @Test
    public void testOrderServiceProxy2() {
        OrderServiceImpl orderService = new OrderServiceImpl();
        OrderServiceLogProxy2 proxy2 = new OrderServiceLogProxy2(orderService);
        proxy2.reduceStock();
    }

測試輸出結(jié)果和上面的結(jié)果是一致的。

繼承與聚合方式實現(xiàn)的靜態(tài)代理對比

結(jié)合上面的代碼來看,如果此時需要疊加代理功能,我不僅要記錄預(yù)減日志,還要增加權(quán)限攔截功能,這個時候如果采用繼承的方式的話,就得新建一個代理類,里面包含日志和權(quán)限邏輯;那要是再增加一個代理功能,又要新增代理類;如果要改變下代理功能的執(zhí)行順序,還是得增加代理類,結(jié)合上面分析來看,這樣做肯定是不妥的。但是如果使用聚合方式的方式呢?我們稍微改造下上面使用的聚合方式實現(xiàn)的靜態(tài)代理代碼:

首先是日志代理類代碼

/**
 * 聚合方式實現(xiàn)靜態(tài)代理--日志記錄功能疊加改造
 */
@Slf4j
public class OrderServiceLogProxy3 implements OrderService {
    //注意,這里換成了接口
    private OrderService orderService;
    public OrderServiceLogProxy3(OrderService orderService) {
        this.orderService = orderService;
    }
    @Override
    public void reduceStock() {
        log.info("預(yù)減庫存開始……");
        orderService.reduceStock();
        log.info("預(yù)減庫存結(jié)束……");
    }
}

然后是新增的權(quán)限驗證代理類代碼

/**
 * 聚合方式實現(xiàn)靜態(tài)代理--日志記錄功能疊加改造
 */
@Slf4j
public class OrderServicePermissionProxy implements OrderService {
    //注意,這里換成了接口
    private OrderService orderService;
    public OrderServicePermissionProxy(OrderService orderService) {
        this.orderService = orderService;
    }
    @Override
    public void reduceStock() {
        log.info("權(quán)限驗證開始……");
        orderService.reduceStock();
        log.info("權(quán)限驗證結(jié)束……");
    }
}

測試用例

    /**
     * 測試聚合方式實現(xiàn)的靜態(tài)代理-功能疊加
     */
    @Test
    public void testOrderServiceProxy3() {
        OrderServiceImpl orderService = new OrderServiceImpl();
        OrderServiceLogProxy2 logProxy2 = new OrderServiceLogProxy2(orderService);
        OrderServicePermissionProxy permissionProxy = new OrderServicePermissionProxy(logProxy2);
        permissionProxy.reduceStock();
    }

測試結(jié)果

16:00:28.348 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServicePermissionProxy - 權(quán)限驗證開始……
16:00:28.365 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceLogProxy2 - 預(yù)減庫存開始……
16:00:28.365 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceImpl - 預(yù)減庫存中……
16:00:29.365 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceLogProxy2 - 預(yù)減庫存結(jié)束……
16:00:29.365 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServicePermissionProxy - 權(quán)限驗證結(jié)束……

接下來,如果你需要調(diào)換一下代理類邏輯執(zhí)行順序問題,你只需要在使用(像測試一樣)時調(diào)換一下實例化順序即可實現(xiàn)日志功能和權(quán)限驗證的先后執(zhí)行順序了,而不需要像繼承方式一樣去不斷的新建代理類。

動態(tài)代理

看完上面的靜態(tài)代理,我們發(fā)現(xiàn),靜態(tài)代理模式的代理類,只是實現(xiàn)了特定類的代理,比如上面OrderServiceLogProxy實現(xiàn)的OrderServiceimpl的代理,如果我還有個UserService也許要日志記錄、權(quán)限校驗功能,又得寫雙份的UserServiceLogProxy、UserServicePermissionProxy代理類,里面的邏輯很多都是相同的,也就是說你代理類對象的方法越多,你就得寫越多的重復(fù)的代碼,那么有了動態(tài)代理就可以比較好的解決這個問題,動態(tài)代理就可以動態(tài)的生成代理類,實現(xiàn)對不同類下的不同方法的代理。

JDK動態(tài)代理

jdk動態(tài)代理是利用反射機制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用業(yè)務(wù)方法前調(diào)用InvocationHandler處理。代理類必須實現(xiàn)InvocationHandler接口,并且,JDK動態(tài)代理只能代理實現(xiàn)了接口的類,沒有實現(xiàn)接口的類是不能實現(xiàn)JDK動態(tài)代理。結(jié)合下面代碼來看就比較清晰了。

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * JDK動態(tài)代理實現(xiàn),必須實現(xiàn)InvocationHandler接口
 * InvocationHandler可以理解為事務(wù)處理器,所有切面級別的邏輯都在此完成
 */
@Slf4j
public class DynamicLogProxy implements InvocationHandler {
    //需要代理的對象類
    private Object target;

    public DynamicLogProxy(Object target) {
        this.target = target;
    }
    /**
     * @param obj    代理對象
     * @param method 對象方法
     * @param args   方法參數(shù)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        log.info("這里是日志記錄切面,日志開始……");
        //使用方法的反射
        Object invoke = method.invoke(target, args);
        log.info("這里是日志記錄切面,日志結(jié)束……");
        return invoke;
    }
}

使用時代碼

    /**
     * 測試JDK動態(tài)代理實現(xiàn)的日志代理類
     */
    @Test
    public void testDynamicLogProxy() {
        OrderServiceImpl orderService = new OrderServiceImpl();
        Class<?> clazz = orderService.getClass();
        DynamicLogProxy logProxyHandler = new DynamicLogProxy(orderService);
        //通過Proxy.newProxyInstance(類加載器, 接口s, 事務(wù)處理器Handler) 加載動態(tài)代理
        OrderService os = (OrderService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), logProxyHandler);
        os.reduceStock();
    }

輸出結(jié)果

16:35:54.584 [main] INFO com.simons.cn.springbootdemo.proxy.DynamicLogProxy - 這里是日志記錄切面,日志開始……
16:35:54.587 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceImpl - 預(yù)減庫存中……
16:35:55.587 [main] INFO com.simons.cn.springbootdemo.proxy.DynamicLogProxy - 這里是日志記錄切面,日志結(jié)束……

使用JDK動態(tài)代理類基本步驟:

1、編寫需要被代理的類和接口(我這里就是OrderServiceImpl、OrderService);

2、編寫代理類(例如我這里的DynamicLogProxy),需要實現(xiàn)InvocationHandler接口,重寫invoke方法;

3、使用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)動態(tài)創(chuàng)建代理類對象,通過代理類對象調(diào)用業(yè)務(wù)方法。

那么這個時候,如果我需要在代理類中疊加功能,該如何是好?比如不僅要日志,還新增權(quán)限認證,思路還是上面的聚合方式實現(xiàn)靜態(tài)代理里的那樣,貼下代碼,先新增權(quán)限認證代理類

import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 基于JDK動態(tài)代理實現(xiàn)的權(quán)限認證代理類
 */
@Slf4j
public class DynamicPermissionProxy implements InvocationHandler{
    private Object target;
    public DynamicPermissionProxy(Object target) {
        this.target = target;
    }
    /**
     * @param obj    代理對象
     * @param method 對象方法
     * @param args   方法參數(shù)
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
        log.info("這里是權(quán)限認證切面,開始驗證……");
        Object invoke = method.invoke(target,args);
        log.info("這里是權(quán)限認證切面,結(jié)束驗證……");
        return invoke;
    }
}

然后使用時候,需要稍微改動下

    /**
     * 測試JDK動態(tài)代理實現(xiàn)的日志、權(quán)限功能代理類
     */
    @Test
    public void testDynamicLogAndPermissProxy() {
        OrderServiceImpl orderService = new OrderServiceImpl();
        Class<?> clazz = orderService.getClass();
        DynamicLogProxy logProxyHandler = new DynamicLogProxy(orderService);
        OrderService os = (OrderService) Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), logProxyHandler);
        //注:這里把日志代理類實例對象傳入權(quán)限認證代理類中
        DynamicPermissionProxy dynamicPermissionProxy = new DynamicPermissionProxy(os);
        OrderService os2 = (OrderService)Proxy.newProxyInstance(os.getClass().getClassLoader(),os.getClass().getInterfaces(),dynamicPermissionProxy);
        os2.reduceStock();
    }

如上即可,后面還需要疊加功能代理類的話,按照上面的思路依次傳入代理對象實例即可。

如何實現(xiàn)一個HashMap的動態(tài)代理類?

public class HashMapProxyTest {
    public static void main(String[] args) {
        final HashMap<String, Object> hashMap = new HashMap<>();
        Map<String, Object> mapProxy = (Map<String, Object>) Proxy.newProxyInstance(HashMap.class.getClassLoader(), HashMap.class.getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return method.invoke(hashMap, args);
            }
        });
        mapProxy.put("key1", "value1");
        System.out.println(mapProxy);
    }
}

Cglib動態(tài)代理

cglib是針對類來實現(xiàn)代理的,它會對目標(biāo)類產(chǎn)生一個代理子類,通過方法攔截技術(shù)對過濾父類的方法調(diào)用。代理子類需要實現(xiàn)MethodInterceptor接口。另外,如果你是基于Spring配置文件形式開發(fā),那你需要顯示聲明:

<aop:aspectj-autoproxy proxy-target-class="true"/>

如果你是基于SpringBoot開發(fā),則一般在啟動類頭部顯示的添加注解 

@EnableAspectJAutoProxy(proxyTargetClass = true)
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
 * 基于Cglib方式實現(xiàn)動態(tài)代理-日志功能
 * 它是針對類實現(xiàn)代理的,類不用實現(xiàn)接口,CGlib對目標(biāo)類產(chǎn)生一個子類,通過方法攔截技術(shù)攔截所有的方法調(diào)用
 */
@Slf4j
public class DynamicCglibLogProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxyObj(Class clazz) {
        //設(shè)置父類
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        enhancer.setUseCache(false);
        return enhancer.create();
    }
    /**
     * 攔截所有目標(biāo)類的方法調(diào)用
     *
     * @param o           目標(biāo)對象
     * @param method      目標(biāo)方法
     * @param args        方法參數(shù)
     * @param methodProxy 代理類實例
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        log.info("這里是日志記錄切面,日志開始……");
        //代理類對象實例調(diào)用父類方法
        Object result = methodProxy.invokeSuper(o, args);
        log.info("這里是日志記錄切面,日志結(jié)束……");
        return result ;
    }
}

測試用例

    /**
     * 測試Cglib實現(xiàn)的動態(tài)代理-日志功能
     */
    @Test
    public void testGclibDynamicLogProxy(){
        DynamicCglibLogProxy dynamicCglibLogProxy = new DynamicCglibLogProxy();
        OrderServiceImpl orderService = (OrderServiceImpl)dynamicCglibLogProxy.getProxyObj(OrderServiceImpl.class);
        orderService.reduceStock();
    }

輸出結(jié)果

17:41:07.007 [main] INFO com.simons.cn.springbootdemo.proxy.DynamicCglibLogProxy - 這里是日志記錄切面,日志開始……
17:41:07.038 [main] INFO com.simons.cn.springbootdemo.proxy.OrderServiceImpl - 預(yù)減庫存中……
17:41:08.038 [main] INFO com.simons.cn.springbootdemo.proxy.DynamicCglibLogProxy - 這里是日志記錄切面,日志結(jié)束……

JDK與Cglib動態(tài)代理對比?

1、JDK動態(tài)代理只能代理實現(xiàn)了接口的類,沒有實現(xiàn)接口的類不能實現(xiàn)JDK的動態(tài)代理;

2、Cglib動態(tài)代理是針對類實現(xiàn)代理的,運行時動態(tài)生成被代理類的子類攔截父類方法調(diào)用,因此不能代理聲明為final類型的類和方法;

動態(tài)代理和靜態(tài)代理的區(qū)別?

1、靜態(tài)代理在代理前就知道要代理的是哪個對象,而動態(tài)代理是運行時才知道;

2、靜態(tài)代理一般只能代理一個類,而動態(tài)代理能代理實現(xiàn)了接口的多個類;

Spring如何選擇兩種代理模式的?

1、如果目標(biāo)對象實現(xiàn)了接口,則默認采用JDK動態(tài)代理;

2、如果目標(biāo)對象沒有實現(xiàn)接口,則使用Cglib代理;

3、如果目標(biāo)對象實現(xiàn)了接口,但強制使用了Cglib,則使用Cglib進行代理

我們可以結(jié)合源碼來看下,上面的選擇:

補充

Cglib實現(xiàn)的MethodInterceptor接口在spring-core包下,你可能需要要引入

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.1.0.RELEASE</version>
</dependency>

@Slf4j是lombok里提供的,而且如果你在intellij idea開發(fā)工具中使用還需要安裝lombok插件

@Test是Junit包下的,你可能需要引入

<dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>4.11</version>
     <scope>test</scope>
</dependency>

總結(jié)

本篇文章就到這里了,希望可以給你帶來一些幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • SpringBoot定制三種錯誤頁面及錯誤數(shù)據(jù)方法示例

    SpringBoot定制三種錯誤頁面及錯誤數(shù)據(jù)方法示例

    Spring Boot提供的默認異常處理機制通常并不一定適合我們實際的業(yè)務(wù)場景,因此,我們通常會根據(jù)自身的需要對Spring Boot全局異常進行統(tǒng)一定制,例如定制錯誤頁面,定制錯誤數(shù)據(jù)等。本文主要介紹了SpringBoot三種自定義錯誤頁面的實現(xiàn),快來學(xué)習(xí)吧
    2021-12-12
  • 詳解Java面向?qū)ο笾械睦^承

    詳解Java面向?qū)ο笾械睦^承

    這篇文章主要介紹了詳解Java面向?qū)ο笾械睦^承,繼承提高代碼的復(fù)用性:通過繼承,子類可以直接使用父類的屬性和方法,不需要重復(fù)定義,需要的朋友可以參考下
    2023-05-05
  • Spring中的路徑匹配器AntPathMatcher詳解

    Spring中的路徑匹配器AntPathMatcher詳解

    這篇文章主要介紹了Spring中的路徑匹配器AntPathMatcher詳解,Spring的PathMatcher路徑匹配器接口,用于支持帶通配符的資源路徑匹配,本文提供了部分實現(xiàn)代碼,需要的朋友可以參考下
    2023-09-09
  • JDK21新特性Record?Patterns記錄模式詳解(最新推薦)

    JDK21新特性Record?Patterns記錄模式詳解(最新推薦)

    這篇文章主要介紹了JDK21新特性Record?Patterns記錄模式詳解,本JEP建立在Pattern?Matching?for?instanceof(JEP?394)的基礎(chǔ)上,該功能已在JDK?16中發(fā)布,它與Pattern?Matching?for?switch(JEP?441)共同演進,需要的朋友可以參考下
    2023-09-09
  • Spring Cloud引入Eureka組件,完善服務(wù)治理

    Spring Cloud引入Eureka組件,完善服務(wù)治理

    這篇文章主要介紹了Spring Cloud引入Eureka組件,完善服務(wù)治理的過程詳解,幫助大家更好的理解和使用spring cloud,感興趣的朋友可以了解下
    2021-02-02
  • 淺析SpringBoot及環(huán)境搭建過程

    淺析SpringBoot及環(huán)境搭建過程

    Spring Boot是由Pivotal團隊提供的全新框架,其設(shè)計目的是用來簡化新Spring應(yīng)用的初始搭建以及開發(fā)過程.這篇文章主要介紹了SpringBoot介紹及環(huán)境搭建,需要的朋友可以參考下
    2018-01-01
  • java多線程之Phaser的使用詳解

    java多線程之Phaser的使用詳解

    這篇文章主要介紹了java多線程之Phaser的使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 使用SSM+Layui+Bootstrap實現(xiàn)汽車維保系統(tǒng)的示例代碼

    使用SSM+Layui+Bootstrap實現(xiàn)汽車維保系統(tǒng)的示例代碼

    本文主要實現(xiàn)對汽車維修廠的信息化管理功能,。實現(xiàn)的主要功能包含用戶管理、配置管理、汽車管理、故障管理、供應(yīng)商管理、配件管理、維修訂單管理、統(tǒng)計信息、公告管理、個人信息管理,感興趣的可以了解一下
    2021-12-12
  • JAVA操作MongoDB數(shù)據(jù)庫實例教程

    JAVA操作MongoDB數(shù)據(jù)庫實例教程

    MongoDB是一個文檔型數(shù)據(jù)庫,是NOSQL家族中最重要的成員之一,下面這篇文章主要給大家介紹了關(guān)于JAVA操作MongoDB數(shù)據(jù)庫的相關(guān)資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2023-05-05
  • MyBatis-Plus 查詢返回實體對象還是map

    MyBatis-Plus 查詢返回實體對象還是map

    這篇文章主要介紹了MyBatis-Plus 查詢返回實體對象還是map,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論