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

Java中的動態(tài)代理和靜態(tài)代理詳細(xì)解析

 更新時間:2023年11月24日 09:29:30   作者:怪 咖@  
這篇文章主要介紹了Java中的動態(tài)代理和靜態(tài)代理詳細(xì)解析,Java中的代理可以幫助被代理者完成一些前期的準(zhǔn)備工作和后期的善后工作,但是核心的業(yè)務(wù)邏輯仍然是由被代理者完成,需要的朋友可以參考下

1.什么是代理?

生活中的代理是很常見的,比如代購、律師、中介等,他們都有一個共性就是幫助被代理人處理一些前前后后的事情。而被代理人只需要專注做自己要做的那部分事情就可以了。當(dāng)然這里的被代理人不可能是只有一個,假如只有一個那根本養(yǎng)不活這群代理。

Java中的代理也是類似的,代理可以幫助被代理者完成一些前期的準(zhǔn)備工作和后期的善后工作,但是核心的業(yè)務(wù)邏輯仍然是由被代理者完成。就好比中介找房,你想要什么房中介都可以幫你找,但是錢還是得你出,房還是得你親自看。

2.為什么要使用代理?

從生活的角度上來說,租房為什么需要中介呢?我們直接找房東不好嗎,其實中介他起到的作用是匯集房源,假如我們直接找房東,先不說房東好不好找,房子肯定是有數(shù)的,一個房東頂多也就幾套房,而一個中介可能頂上好幾個房東的房源了。說直白點,租房是我們的目的,在不改變自己的目的情況下,快速的找到房源,那就是通過中介!

從代碼角度上來說,在不修改源碼的基礎(chǔ)上對方法進(jìn)行加強(qiáng)。當(dāng)然不是所有代碼涉及到增強(qiáng)就需要使用代理,而是很多方法都涉及到了增強(qiáng),才會統(tǒng)一使用一個代理,舉例:我們要給所有的接口添加操作日志,這時候不可能說在每個接口上添加操作日志,這樣會導(dǎo)致重復(fù)代碼一大堆,所以這時候我們從中間抽出來一個代理,被代理類只需要專注于自己的核心代碼即可,日志記錄交給代理類就可以了,優(yōu)點:使得代碼更加簡潔,分工明確。

3靜態(tài)代理

代理模式分為動態(tài)代理和靜態(tài)代理。兩者的差別還是很大的,不過思想都是一樣的,起到一個服務(wù)中介的作用。

所謂靜態(tài)代理也就是在程序運行前就已經(jīng)存在代理類的字節(jié)碼文件,而動態(tài)代理是通過某種方式來生成的代理字節(jié)碼文件。

3.1.靜態(tài)代理的示例

以下是通過一個買車示例來演示靜態(tài)代理的思想,實際開發(fā)當(dāng)中我們可能不會這么寫,但是更多的是需要理解他的思想!

(1)定義接口,接口當(dāng)中有一個抽象方法

/**
 * 買車接口
 * @author guo
 *
 */
public interface StaticMy {
	void lawsuit();
}

(2)定義被代理類,實現(xiàn)StaticMy接口

package com.gzl.static1;

/**
 * 被代理方 我
 * @author guo
 *
 */
public class StaticMyImpl implements StaticMy{

	public void lawsuit() {
		System.out.println("我要買車");
	}

}

(3)代理類同樣也需要實現(xiàn)StaticMy接口,將被代理類通過構(gòu)造器的方式傳入代理類,由代理類對被代理類進(jìn)行加強(qiáng)

package com.gzl.static1;

public class Shop implements StaticMy{
	private StaticMyImpl staticMy;

	public Shop(StaticMyImpl staticMy) {
		super();
		this.staticMy = staticMy;
	}


	public void lawsuit() {
		System.out.println("廠子進(jìn)車");
		this.staticMy.lawsuit();
		System.out.println("交車");
	}

}

(4)測試

package com.gzl.static1;

public class Test {
	public static void main(String[] args) {
	    // 創(chuàng)建被代理類
		StaticMyImpl shop = new StaticMyImpl();
		// 將被代理類傳入代理類當(dāng)中
		Shop shop2 = new Shop(shop);
		// 由代理類來執(zhí)行
		shop2.lawsuit();
	}
}

執(zhí)行結(jié)果

在這里插入圖片描述

4.動態(tài)代理

動態(tài)代理就是,在程序運行期,創(chuàng)建目標(biāo)對象的代理對象,并對目標(biāo)對象中的方法進(jìn)行功能性增強(qiáng)的一種技術(shù)。在生成代理對象的過程中,目標(biāo)對象不變,代理對象中的方法是目標(biāo)對象方法的增強(qiáng)方法??梢岳斫鉃檫\行期間,對象中方法的動態(tài)攔截,在攔截方法的前后執(zhí)行功能操作。

靜態(tài)代理是直接在代碼中聲明好的代理對象,而動態(tài)代理中的代理對象,并不是事先在Java代碼中定義好的。而是在運行期間,根據(jù)我們在動態(tài)代理對象中的“指示”,動態(tài)生成的。也就是說,你想獲取哪個對象的代理,動態(tài)代理就會為你動態(tài)的生成這個對象的代理對象。動態(tài)代理一般有兩種實現(xiàn)方式,cglib和jdk。

4.1.cglib和jdk動態(tài)代理的區(qū)別

  • JDK代理使用的是反射機(jī)制生成一個實現(xiàn)代理接口的匿名類,在調(diào)用具體方法前調(diào)用InvokeHandler來處理。
  • CGLIB代理使用字節(jié)碼處理框架ASM,對代理對象類的class文件加載進(jìn)來,通過修改字節(jié)碼生成子類。
  • JDK創(chuàng)建代理對象效率較高,執(zhí)行效率較低;
  • CGLIB創(chuàng)建代理對象效率較低,執(zhí)行效率高。
  • JDK動態(tài)代理機(jī)制是委托機(jī)制,只能對實現(xiàn)接口的類生成代理,通過反射動態(tài)實現(xiàn)接口類;
  • CGLIB則使用的繼承機(jī)制,針對類實現(xiàn)代理,被代理類和代理類是繼承關(guān)系,所以代理類是可以賦值給被代理類的,因為是繼承機(jī)制,不能代理final修飾的類。

JDK代理是不需要依賴第三方的庫,只要JDK環(huán)境就可以進(jìn)行代理,需要滿足以下要求:

  • 實現(xiàn)InvocationHandler接口,重寫invoke()
  • 使用Proxy.newProxyInstance()產(chǎn)生代理對象
  • 被代理的對象必須要實現(xiàn)接口

CGLib 必須依賴于CGLib的類庫,需要滿足以下要求:

  • 實現(xiàn)MethodInterceptor接口,重寫intercept()
  • 使用Enhancer對象.create()產(chǎn)生代理對象

4.2.cglib動態(tài)代理示例

jdk動態(tài)代理只能為接口創(chuàng)建代理,使用上有局限性。實際的場景中我們的類不一定有接口,此時如果我們想為普通的類也實現(xiàn)代理功能,我們就需要用到cglib來實現(xiàn)了。

cglib是一個強(qiáng)大、高性能的字節(jié)碼生成庫,它用于在運行時擴(kuò)展Java類和實現(xiàn)接口;本質(zhì)上它是通過動態(tài)的生成一個子類去覆蓋所要代理的類(非final修飾的類和方法)。Enhancer可能是CGLIB中最常用的一個類,和jdk中的Proxy不同的是,Enhancer既能夠代理普通的class,也能夠代理接口。Enhancer創(chuàng)建一個被代理對象的子類并且攔截所有的方法調(diào)用(包括從Object中繼承的toString和hashCode方法)。Enhancer不能夠攔截final方法,例如Object.getClass()方法,這是由于Java final方法語義決定的?;谕瑯拥牡览恚珽nhancer也不能對final類進(jìn)行代理操作。

CGLIB底層使用了ASM(一個短小精悍的字節(jié)碼操作框架)來操作字節(jié)碼生成新的類。除了CGLIB庫外,腳本語言(如Groovy和BeanShell)也使用ASM生成字節(jié)碼。ASM使用類似SAX的解析器來實現(xiàn)高性能。

(1)cglib并不是java當(dāng)中自帶的,所以使用的話需要引入jar包

<dependencies>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>2.1_3</version>
    </dependency>
</dependencies>

(2)創(chuàng)建一個類,寫入兩個方法

package com.itheima.cglib;

/**
 * 一個生產(chǎn)者
 */
public class Producer {

    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("銷售產(chǎn)品,并拿到錢:"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服務(wù),并拿到錢:"+money);
    }
}

(3)動態(tài)代理類

在源碼當(dāng)中看到MethodInterceptor就是使用的cglib動態(tài)代理

package com.gzl.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 模擬一個消費者
 */
public class Client {

    public static void main(String[] args) {
        final Producer producer = new Producer();

        /**
         * 動態(tài)代理:
         *  特點:字節(jié)碼隨用隨創(chuàng)建,隨用隨加載
         *  作用:不修改源碼的基礎(chǔ)上對方法增強(qiáng)
         *  分類:
         *      基于接口的動態(tài)代理
         *      基于子類的動態(tài)代理
         *  基于子類的動態(tài)代理:
         *      涉及的類:Enhancer
         *      提供者:第三方cglib庫
         *  如何創(chuàng)建代理對象:
         *      使用Enhancer類中的create方法
         *  創(chuàng)建代理對象的要求:
         *      被代理類不能是最終類
         *  create方法的參數(shù):
         *      Class:字節(jié)碼
         *          它是用于指定被代理對象的字節(jié)碼。
         *
         *      Callback:用于提供增強(qiáng)的代碼
         *          它是讓我們寫如何代理。我們一般都是些一個該接口的實現(xiàn)類,通常情況下都是匿名內(nèi)部類,但不是必須的。
         *          此接口的實現(xiàn)類都是誰用誰寫。
         *          我們一般寫的都是該接口的子接口實現(xiàn)類:MethodInterceptor
         */
        Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
            /**
             * 執(zhí)行producer的任何方法都會經(jīng)過該方法
             * @param proxy
             * @param method
             * @param args
             *    以上三個參數(shù)和基于接口的動態(tài)代理中invoke方法的參數(shù)是一樣的
             * @param methodProxy :當(dāng)前執(zhí)行方法的代理對象
             * @return
             * @throws Throwable
             */
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                //提供增強(qiáng)的代碼
                Object returnValue = null;

                //1.獲取方法執(zhí)行的參數(shù)
                Float money = (Float)args[0];
                //2.判斷當(dāng)前方法是不是銷售
                if("saleProduct".equals(method.getName())) {
                    returnValue = method.invoke(producer, money*0.8f);
                }
                return returnValue;
            }
        });
        cglibProducer.saleProduct(12000f);
    }
}

執(zhí)行結(jié)果

在這里插入圖片描述

4.3.JDK動態(tài)代理示例

(1)創(chuàng)建一個接口

package com.gzl.proxy;

/**
 * 對生產(chǎn)廠家要求的接口
 */
public interface IProducer {

    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money);

    /**
     * 售后
     * @param money
     */
    public void afterService(float money);
}

(2)實現(xiàn)類

package com.gzl.proxy;

/**
 * 一個生產(chǎn)者
 */
public class Producer implements IProducer{

    /**
     * 銷售
     * @param money
     */
    public void saleProduct(float money){
        System.out.println("銷售產(chǎn)品,并拿到錢:"+money);
    }

    /**
     * 售后
     * @param money
     */
    public void afterService(float money){
        System.out.println("提供售后服務(wù),并拿到錢:"+money);
    }
}

(3)代理類

package com.gzl.proxy;

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

/**
 * 模擬一個消費者
 */
public class Client {

    public static void main(String[] args) {
        final Producer producer = new Producer();

        /**
         * 動態(tài)代理:
         *  特點:字節(jié)碼隨用隨創(chuàng)建,隨用隨加載
         *  作用:不修改源碼的基礎(chǔ)上對方法增強(qiáng)
         *  分類:
         *      基于接口的動態(tài)代理
         *      基于子類的動態(tài)代理
         *  基于接口的動態(tài)代理:
         *      涉及的類:Proxy
         *      提供者:JDK官方
         *  如何創(chuàng)建代理對象:
         *      使用Proxy類中的newProxyInstance方法
         *  創(chuàng)建代理對象的要求:
         *      被代理類最少實現(xiàn)一個接口,如果沒有則不能使用
         *  newProxyInstance方法的參數(shù):
         *      ClassLoader:類加載器
         *          它是用于加載代理對象字節(jié)碼的。和被代理對象使用相同的類加載器。固定寫法。
         *      Class[]:字節(jié)碼數(shù)組
         *          它是用于讓代理對象和被代理對象有相同方法。固定寫法。
         *      InvocationHandler:用于提供增強(qiáng)的代碼
         *          它是讓我們寫如何代理。我們一般都是些一個該接口的實現(xiàn)類,通常情況下都是匿名內(nèi)部類,但不是必須的。
         *          此接口的實現(xiàn)類都是誰用誰寫。
         */
       IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
                producer.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用:執(zhí)行被代理對象的任何接口方法都會經(jīng)過該方法
                     * 方法參數(shù)的含義
                     * @param proxy   代理對象的引用
                     * @param method  當(dāng)前執(zhí)行的方法
                     * @param args    當(dāng)前執(zhí)行方法所需的參數(shù)
                     * @return        和被代理對象方法有相同的返回值
                     * @throws Throwable
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //提供增強(qiáng)的代碼
                        Object returnValue = null;

                        //1.獲取方法執(zhí)行的參數(shù)
                        Float money = (Float)args[0];
                        //2.判斷當(dāng)前方法是不是銷售
                        if("saleProduct".equals(method.getName())) {
                            returnValue = method.invoke(producer, money*0.8f);
                        }
                        return returnValue;
                    }
                });
        proxyProducer.saleProduct(12000f);
    }
}

執(zhí)行結(jié)果

在這里插入圖片描述

到此這篇關(guān)于Java中的動態(tài)代理和靜態(tài)代理詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java動態(tài)代理和靜態(tài)代理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java使用Sftp和Ftp實現(xiàn)對文件的上傳和下載

    Java使用Sftp和Ftp實現(xiàn)對文件的上傳和下載

    這篇文章主要介紹了Java使用Sftp和Ftp實現(xiàn)對文件的上傳和下載,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • 簡單捋捋@RequestParam 和 @RequestBody的使用

    簡單捋捋@RequestParam 和 @RequestBody的使用

    這篇文章主要介紹了簡單捋捋@RequestParam 和 @RequestBody的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 在SpringBoot下讀取自定義properties配置文件的方法

    在SpringBoot下讀取自定義properties配置文件的方法

    這篇文章主要介紹了在SpringBoot下讀取自定義properties配置文件的方法,文中涉及到了Spring-boot中讀取config配置文件的兩種方式,需要的朋友可以參考下
    2017-12-12
  • Java 數(shù)組元素倒序的三種方式(小結(jié))

    Java 數(shù)組元素倒序的三種方式(小結(jié))

    這篇文章主要介紹了Java 數(shù)組元素倒序的三種方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • ShardingSphere-Proxy5搭建使用過程分析

    ShardingSphere-Proxy5搭建使用過程分析

    ShardingSphere-Proxy是跨語言的數(shù)據(jù)庫代理服務(wù)端,主要用來處理:分表、分庫、讀寫分離 等,這篇文章主要介紹了ShardingSphere-Proxy5搭建使用過程,需要的朋友可以參考下
    2022-10-10
  • SpringCloud Ribbon負(fù)載均衡代碼實例

    SpringCloud Ribbon負(fù)載均衡代碼實例

    這篇文章主要介紹了SpringCloud Ribbon負(fù)載均衡代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03
  • Java虛擬機(jī)類加載器之雙親委派機(jī)制模型案例

    Java虛擬機(jī)類加載器之雙親委派機(jī)制模型案例

    這篇文章主要介紹了Java虛擬機(jī)類加載器之雙親委派機(jī)制模型案例,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • SpringBoot中的五種對靜態(tài)資源的映射規(guī)則的實現(xiàn)

    SpringBoot中的五種對靜態(tài)資源的映射規(guī)則的實現(xiàn)

    這篇文章主要介紹了SpringBoot中的五種對靜態(tài)資源的映射規(guī)則的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • 圖解Spring Security 中用戶是如何實現(xiàn)登錄的

    圖解Spring Security 中用戶是如何實現(xiàn)登錄的

    這篇文章主要介紹了圖解Spring Security 中用戶是如何實現(xiàn)登錄的,文中通過示例代碼和圖片介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • SpringBoot多模塊如何統(tǒng)一管理

    SpringBoot多模塊如何統(tǒng)一管理

    本文詳細(xì)介紹了SpringBoot多模塊項目的統(tǒng)一管理方法,包括核心思想、Maven和Gradle的統(tǒng)一管理實踐以及最佳實踐和技巧,通過集中化配置、約定優(yōu)于配置、減少重復(fù)性工作,可以提高多模塊項目的可維護(hù)性、一致性和可擴(kuò)展性
    2025-03-03

最新評論