溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理

發(fā)布時(shí)間:2021-06-23 14:53:37 來(lái)源:億速云 閱讀:140 作者:chen 欄目:大數(shù)據(jù)

這篇文章主要介紹“java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理”,在日常操作中,相信很多人在java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

1.什么是代理

引用網(wǎng)上的一段話 代理模式給某一個(gè)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)原對(duì)象的引用。通俗的來(lái)講代理模式就是我們生活中常見的中介。

舉個(gè)例子來(lái)說(shuō)明:假如說(shuō)我現(xiàn)在想買一輛二手車,雖然我可以自己去找車源,做質(zhì)量檢測(cè)等一系列的車輛過戶流程,但是這確實(shí)太浪費(fèi)我得時(shí)間和精力了。我只是想買一輛車而已為什么我還要額外做這么多事呢?于是我就通過中介公司來(lái)買車,他們來(lái)給我找車源,幫我辦理車輛過戶流程,我只是負(fù)責(zé)選擇自己喜歡的車,然后付錢就可以了。用圖表示如下: java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理

2.java中如何實(shí)現(xiàn)代理

①. 代理的名詞

代理對(duì)象:增強(qiáng)后的對(duì)象 目標(biāo)對(duì)象:被增強(qiáng)的對(duì)象

②. 靜態(tài)代理

先來(lái)假設(shè)一個(gè)場(chǎng)景,查詢用戶,在查詢之前要寫日志記錄。

public class UserDaoImpl {
    public void query(){
        System.out.println("查詢用戶信息");
    }
}

上面代碼我們要實(shí)現(xiàn)對(duì)其功能的增強(qiáng),可以通過修改代碼來(lái)實(shí)現(xiàn)。

public class UserDaoImpl {
    public void query(){
        System.out.println("log");
        System.out.println("查詢用戶信息");
    }
}

這樣實(shí)現(xiàn)可以達(dá)到效果,但是破壞了面向?qū)ο蟮拈_閉原則。況且有的時(shí)候,我們是不能拿到該Dao的源碼,也沒有辦法對(duì)其修改。所以可以考慮代理模式來(lái)實(shí)現(xiàn)。

繼承

編寫UserDaoImpl的一個(gè)子類,復(fù)寫query方法.

public class ProxyUserDao extends UserDaoImpl{
    @Override
    public void query() {
        System.out.println("log");
        super.query();
    }
}

編寫測(cè)試,執(zhí)行測(cè)試。

    public static void main(String[] args) {
        ProxyUserDao proxyUserDao = new ProxyUserDao();
        proxyUserDao.query();
    }

java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理

聚合

目標(biāo)對(duì)象和代理對(duì)象都要實(shí)現(xiàn)同一接口。 編寫UserDao接口

public interface UserDao {
    public void query();
}

UserDaoImpl實(shí)現(xiàn)

public class UserDaoImpl implements UserDao{
    public void query(){
        System.out.println("查詢用戶信息");
    }
}

編寫代理類

public class ProxyUserDao implements UserDao{
    //目標(biāo)對(duì)象
    UserDao userDao;
    //通過構(gòu)造函數(shù)傳入目標(biāo)對(duì)象
    public ProxyUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    @Override
    public void query() {
        System.out.println("log");
        userDao.query();
    }
}

改寫測(cè)試

ProxyUserDao proxyUserDao = new ProxyUserDao(new UserDaoImpl());
proxyUserDao.query();

執(zhí)行后,也可以完成代理。上面實(shí)現(xiàn)的都是先打印log,在執(zhí)行查詢,那么要實(shí)現(xiàn)先執(zhí)行查詢,再打印log,我們就要再寫一個(gè)代理類,豈不蛋疼。

現(xiàn)在我們來(lái)假設(shè)要實(shí)現(xiàn)多個(gè)功能的增強(qiáng),以前是日志記錄,log,我們還要家人時(shí)間記錄time,那么這里就有多種組合方式。 java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理
那么有不同的需求就要有不同的實(shí)現(xiàn)方式,每一種都要編寫不同的代理實(shí)現(xiàn)邏輯。就會(huì)寫大量的java代理類。我們回頭看一下繼承的方式來(lái)實(shí)現(xiàn)的話,還更加麻煩,代理類會(huì)更多。

兩種方式的缺點(diǎn)與總結(jié)

繼承:代理類過多,比較復(fù)雜 聚合:也會(huì)有大量的代理類,不過相比繼承要好很多。

所以,在不確定的情況下,就不要去使用靜態(tài)代理,因?yàn)闀?huì)編寫大量的代理類來(lái)滿足不同的需求。比較麻煩。這里模擬的還僅僅是一個(gè)UserDao,若有多個(gè)目標(biāo)代理對(duì)象,那么通過硬編碼的方式,就真的很不理智了。

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

我們可以通過動(dòng)態(tài)代理來(lái)避免編寫大量的代理對(duì)象的困擾。

手動(dòng)實(shí)現(xiàn)動(dòng)態(tài)代理

我們先來(lái)分析一下,生成一個(gè)對(duì)象的途徑。 第一步:編寫.java文件
第二步:編譯.java文件,獲得.class文件 第三步:類記載機(jī)制加載.class文件,new出對(duì)象。

先來(lái)分析第一步:我們編寫java文件以外,我們就是要為了不編寫大量的類代碼,所以這種方式無(wú)效,此外還可以通過編寫程序?qū)崿F(xiàn)輸出一個(gè)java文件。 下面直接貼源碼:

public class ProxyUtil {

    /**
     *  content --->string
     *  .java  io
     * .class
     * .new   反射----》class
     * @return
     */
    public static Object newInstance(Object target){
        Object proxy=null;
        Class targetInf = target.getClass().getInterfaces()[0];
        Method methods[] =targetInf.getDeclaredMethods();
        String line="\n";
        String tab ="\t";
        String infName = targetInf.getSimpleName();
        String content ="";
        //進(jìn)行拼裝代理類代碼
        String packageContent = "package com.google;"+line;
        String importContent = "import "+targetInf.getName()+";"+line;
        String clazzFirstLineContent = "public class $Proxy implements "+infName+"{"+line;
        String filedContent  =tab+"private "+infName+" target;"+line;
        String constructorContent =tab+"public $Proxy ("+infName+" target){" +line
                                  +tab+tab+"this.target =target;"
                                  +line+tab+"}"+line;
        String methodContent = "";
        for (Method method : methods) {
            String returnTypeName = method.getReturnType().getSimpleName();
            String methodName =method.getName();
            // Sting.class String.class
            Class args[] = method.getParameterTypes();
            String argsContent = "";
            String paramsContent="";
            int flag =0;
            for (Class arg : args) {
                String temp = arg.getSimpleName();
                //String
                //String p0,Sting p1,
                argsContent+=temp+" p"+flag+",";
                paramsContent+="p"+flag+",";
                flag++;
            }
            if (argsContent.length()>0){
                argsContent=argsContent.substring(0,argsContent.lastIndexOf(",")-1);
                paramsContent=paramsContent.substring(0,paramsContent.lastIndexOf(",")-1);
            }
            //代理打印log日志功能(這里是寫死的)
            methodContent+=tab+"public "+returnTypeName+" "+methodName+"("+argsContent+") {"+line
                          +tab+tab+"System.out.println(\"log\");"+line
                          +tab+tab+"target."+methodName+"("+paramsContent+");"+line
                          +tab+"}"+line;

        }

        //類中的代碼字符串
        content=packageContent+importContent+clazzFirstLineContent+filedContent+constructorContent+methodContent+"}";
        File file =new File("//$Proxy.java");
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            //將content寫入java文件
            FileWriter fw = new FileWriter(file);
            fw.write(content);
            fw.flush();
            fw.close();
            //進(jìn)行java文件編譯  java--->class
            JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

            StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
            Iterable units = fileMgr.getJavaFileObjects(file);

            JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
            t.call();
            fileMgr.close();
            // 類加載
            URL[] urls = new URL[]{new URL("file:D:\\\\")};
            URLClassLoader urlClassLoader = new URLClassLoader(urls);
            Class clazz = urlClassLoader.loadClass("com.google.$Proxy");
            //獲取構(gòu)造函數(shù)
            Constructor constructor = clazz.getConstructor(targetInf);
            /反射創(chuàng)建實(shí)例
            proxy = constructor.newInstance(target);
        }catch (Exception e){
            e.printStackTrace();
        }
        return proxy;
    }
}

上面是編寫好的代理工具類,會(huì)實(shí)現(xiàn)java代理文件的生成,java的編譯,加載,反射創(chuàng)建實(shí)例。調(diào)用newInstance就返回了代理對(duì)象。不過這里將代理功能寫死的。

編寫測(cè)試類。并執(zhí)行

UserDao o = (UserDao) ProxyUtil.newInstance(new UserDaoImpl());
o.query();

目錄下已經(jīng)生成了對(duì)應(yīng)的文件
java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理
查看生成的代理類代碼
java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理
控制臺(tái)打印的日志,實(shí)現(xiàn)了對(duì)方法加上日志打印 java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理
我們要實(shí)現(xiàn)對(duì)其他目標(biāo)對(duì)象的代理,只需要調(diào)用newInstance傳入目標(biāo)對(duì)象即可。

如:我們要實(shí)現(xiàn)對(duì)訂單OrderDao查詢訂單增加日志打印

//訂單Dao接口
public interface OrderDao {
    public void query();
}
//訂單Dao接口實(shí)現(xiàn)   目標(biāo)對(duì)象
public class OrderDaoImpl implements OrderDao {
    public void query(){
        System.out.println("查詢訂單");
    }
}

//測(cè)試函數(shù)
    public static void main(String[] args) {
        OrderDao o = (OrderDao) ProxyUtil.newInstance(new OrderDaoImpl());
        o.query();
    }

只需測(cè)試,查詢?nèi)罩?,達(dá)到了對(duì)訂單接口的代理
java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理
通過動(dòng)態(tài)代理我們可以在不手動(dòng)編寫代理對(duì)象的方式,實(shí)現(xiàn)對(duì)不同目標(biāo)對(duì)象的代理。增強(qiáng)了代碼的可擴(kuò)展。ps:java底層也有動(dòng)態(tài)代理的工具類proxy。他的實(shí)現(xiàn)原理也是相同的。

到此,關(guān)于“java怎么實(shí)現(xiàn)從靜態(tài)代理到動(dòng)態(tài)代理”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI