您好,登錄后才能下訂單哦!
這篇文章主要介紹“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í)吧!
引用網(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é)選擇自己喜歡的車,然后付錢就可以了。用圖表示如下:
代理對(duì)象:增強(qiáng)后的對(duì)象 目標(biāo)對(duì)象:被增強(qiáng)的對(duì)象
先來(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(); }
目標(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,那么這里就有多種組合方式。
那么有不同的需求就要有不同的實(shí)現(xiàn)方式,每一種都要編寫不同的代理實(shí)現(xiàn)邏輯。就會(huì)寫大量的java代理類。我們回頭看一下繼承的方式來(lái)實(shí)現(xiàn)的話,還更加麻煩,代理類會(huì)更多。
繼承:代理類過多,比較復(fù)雜 聚合:也會(huì)有大量的代理類,不過相比繼承要好很多。
所以,在不確定的情況下,就不要去使用靜態(tài)代理,因?yàn)闀?huì)編寫大量的代理類來(lái)滿足不同的需求。比較麻煩。這里模擬的還僅僅是一個(gè)UserDao,若有多個(gè)目標(biāo)代理對(duì)象,那么通過硬編碼的方式,就真的很不理智了。
我們可以通過動(dòng)態(tài)代理來(lái)避免編寫大量的代理對(duì)象的困擾。
我們先來(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)的文件
查看生成的代理類代碼
控制臺(tái)打印的日志,實(shí)現(xiàn)了對(duì)方法加上日志打印
我們要實(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ì)訂單接口的代理
通過動(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í)用的文章!
免責(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)容。