日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

吃透Java中的动态代理

發(fā)布時(shí)間:2025/6/15 java 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 吃透Java中的动态代理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

動(dòng)態(tài)代理在Java中是很重要的一部分,在很多框架中都會(huì)用到,如Spring中的AOP、Hadoop中的RPC等。為此在這把我對(duì)Java中的動(dòng)態(tài)代理的理解分享給大家,同時(shí)寫(xiě)了一個(gè)模擬AOP編程的實(shí)例。(Demo實(shí)例提供下載)

引入場(chǎng)景

如果要對(duì)第三方提供的JAR包中的某個(gè)類(lèi)中的某個(gè)方法的前后加上自己的邏輯,比如打LOG,注意此時(shí)我們只有第三方提供的CLASS文件,因此根本不可能去修改別人的源代碼,那該怎么辦?

有兩種方法可以實(shí)現(xiàn),一種是利用繼承,另一種是利用聚合。舉例說(shuō)明:

假設(shè)第三方中提供一個(gè)Run接口,里面只一個(gè)run方法,以及它的實(shí)現(xiàn)類(lèi)Person。

Run.java

package com.cloud.proxy.example;public interface Run {public void run(); }

?Person.java

package com.cloud.proxy.example;public class Person implements Run {@Overridepublic void run() {System.out.println("person running...");}}

?第一種利用繼承實(shí)現(xiàn)

SuperMan1.java

package com.cloud.proxy.example;public class SuperMan1 extends Person { @Override public void run() { //方法開(kāi)始前打LOG LOG.info("super man1 before run..."); super.run(); //方法結(jié)束后打LOG LOG.info("super man1 after run..."); }}

?第二種利用聚合實(shí)現(xiàn)

SuperMan2.java

package com.cloud.proxy.example;public class SuperMan2 implements Run { private Run person; public SuperMan2(Run person) { this.person = person; } @Override public void run() { //方法開(kāi)始前打LOG LOG.info("super man2 before run..."); person.run(); //方法結(jié)束后打LOG LOG.info("super man2 after run..."); }}

?這兩種實(shí)現(xiàn)方式,哪一種更好呢?

顯然是第二種利用聚合實(shí)現(xiàn)方法好,因?yàn)檫@種方式很靈活,同時(shí)又不會(huì)有多層的父子類(lèi)關(guān)系。而繼承最不好的地方就是不靈活,同時(shí)會(huì)很容易形成臃腫的父子類(lèi)關(guān)系,不利于后期的維護(hù)。

點(diǎn)題

其實(shí)SuperMan1類(lèi)和SuperMan2類(lèi)都是Person類(lèi)的代理類(lèi),對(duì)Person類(lèi)中的方法進(jìn)行加強(qiáng),只不過(guò)這種代理是靜態(tài)代理,很受限制。因?yàn)镾uperMan1和SuperMan2只能代理Run類(lèi)型的類(lèi),其它類(lèi)型沒(méi)法代理。為了解決這個(gè)問(wèn)題,Java中引入動(dòng)態(tài)代理。

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

看了上面的分析,動(dòng)態(tài)代理的意思就是一個(gè)類(lèi)的(比如Person)的代理類(lèi)(比如SuperMan2)是動(dòng)態(tài)生成的,也就是說(shuō)這個(gè)代理類(lèi)不是提前寫(xiě)好的,是在程序運(yùn)行時(shí)動(dòng)態(tài)的生成的。而且能夠代理實(shí)現(xiàn)了某個(gè)接口的任何類(lèi)型的類(lèi)。我們暫且把動(dòng)態(tài)代理這個(gè)過(guò)程當(dāng)作一個(gè)黑箱子,然后看它的輸入和輸出。對(duì)于輸入,就是要被代理的類(lèi)和它實(shí)現(xiàn)的接口,對(duì)于輸出就是代理類(lèi),如圖所示:



?分析動(dòng)態(tài)代理過(guò)程

根據(jù)上圖,我們大致可以得知?jiǎng)討B(tài)代理會(huì)有這樣的過(guò)程:

1.根據(jù)輸入的接口,利用反射機(jī)制,肯定可以拿到有哪些方法;

2.根據(jù)輸入的被代理類(lèi),同樣利用反射機(jī)制,肯定去調(diào)用其實(shí)現(xiàn)的方法。

到了這里,好像少了一點(diǎn)東西,就是少了對(duì)某個(gè)方法的前后的加強(qiáng)的邏輯。那么該如何解決這個(gè)問(wèn)題呢?為此,我們必須對(duì)輸入進(jìn)行改造,如圖:



?看圖我們可以發(fā)現(xiàn),被代理類(lèi)不是直接給黑箱子了,而是先給Handler這樣的一個(gè)類(lèi),再給黑箱子,那么在Handler類(lèi)中我們就是添加相應(yīng)的加強(qiáng)的邏輯了。

Java中的動(dòng)態(tài)代理

其實(shí)上面對(duì)動(dòng)態(tài)代理的分析過(guò)程,也就是Java中動(dòng)態(tài)代理的過(guò)程。我們來(lái)一一對(duì)應(yīng)一下:

黑箱子:就是Java中的Proxy類(lèi);

Handler:就是Java中的InvocationHandler的子類(lèi)。

利用Proxy類(lèi)中的newProxyInstance靜態(tài)方法,就可以動(dòng)態(tài)生成一個(gè)代理類(lèi)。這個(gè)方法有三個(gè)參數(shù):

第一個(gè)是要一個(gè)類(lèi)加載器,它的作用是將動(dòng)態(tài)生成的代理類(lèi)的字節(jié)碼文件加載到JVM虛擬機(jī)中,一般我們可以用被代理類(lèi)的加載器;

第二個(gè)是被代理類(lèi)實(shí)現(xiàn)的接口的Class類(lèi);

第三個(gè)是InvocationHandler的子類(lèi),在這個(gè)類(lèi)中的invoke方法中,對(duì)某個(gè)方法的前后加入加強(qiáng)的邏輯。

實(shí)例

這個(gè)實(shí)例是簡(jiǎn)單的模擬Spring是的AOP機(jī)制,即只要我們?cè)谂渲梦募蜷_(kāi)了事務(wù)機(jī)制,那么在調(diào)用方法時(shí)就會(huì)開(kāi)啟事務(wù),同樣我們?cè)谂渲梦募P(guān)閉了事務(wù)機(jī)制,那么在調(diào)用方法時(shí)就不會(huì)開(kāi)啟事務(wù)了。

AOP.java

package com.cloud.proxy;public interface AOP {public void show(String str);public String say(String str); }

?AOPImpl,java

package com.cloud.proxy;public class AOPImpl implements AOP { @Override public void show(String str) { System.out.println("show: " + str); } @Override public String say(String str) { return "say: " + str; }}

?AOPHandler.java

package com.cloud.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map;public class AOPHandler implements InvocationHandler { private Object obj; private boolean flag; public AOPHandler(Object obj) { this.obj = obj; } public void setFlag(Map<String, String> config) { if (null == config) { flag = false; } else { if (config.containsKey("transaction") && "true".equalsIgnoreCase(config.get("transaction"))) { flag = true; } else { flag = false; } } } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (flag) { doBefore(); } Object result = method.invoke(obj, args); if (flag) { doAfter(); } return result; } private void doBefore() { System.out.println("Transaction start..."); } private void doAfter() { System.out.println("Transaction commit..."); } }

?Client.java

package com.cloud.proxy;import java.lang.reflect.Proxy;import com.cloud.proxy.util.JVMCache;public class Client { public static void main(String[] args) throws Exception { AOPImpl impl = new AOPImpl(); AOPHandler handler = new AOPHandler(impl); handler.setFlag(JVMCache.getConfig()); AOP aop = (AOP) Proxy.newProxyInstance(AOPImpl.class.getClassLoader(), new Class<?>[] {AOP.class}, handler); aop.show("cloud"); String result = aop.say("芝加哥09"); System.out.println(result); } }

?JVMCache.java

package com.cloud.proxy.util;import java.util.Map;public class JVMCache { private static Map<String, String> config; public synchronized static Map<String, String> getConfig() throws Exception { if (null == config) { config = XMLUtil.parseXML(); } return config; } }

?XMLUtil.java

package com.cloud.proxy.util;import java.io.InputStream; import java.util.HashMap; import java.util.Map;import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory;import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList;public class XMLUtil { public static Map<String, String> parseXML() throws Exception { Map<String, String> result = new HashMap<String, String>(); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputStream in = XMLUtil.class.getClassLoader().getResourceAsStream("config.xml"); Document document = db.parse(in); Element root = document.getDocumentElement(); NodeList xmlNodes = root.getChildNodes(); for (int i = 0; i < xmlNodes.getLength(); i++) { Node config = xmlNodes.item(i); if (null != config && config.getNodeType() == Node.ELEMENT_NODE) { String nodeName = config.getNodeName(); if ("transaction".equals(nodeName)) { String textContent = config.getTextContent(); result.put("transaction", textContent); } } } return result; } }

?config.xml

<?xml version="1.0" encoding="UTF-8"?> <config><transaction>true</transaction> </config>

運(yùn)行結(jié)果

當(dāng)config.xml文件中配置為true時(shí),運(yùn)行結(jié)果:

Transaction start... show: cloud Transaction commit... Transaction start... Transaction commit... say: 芝加哥09

?我們可以發(fā)現(xiàn),在執(zhí)行每個(gè)方法的前會(huì)開(kāi)啟事務(wù),每個(gè)方法后提交事務(wù)。

當(dāng)config.xml文件中配置為false時(shí),運(yùn)行結(jié)果:

show: cloud say: 芝加哥09

?我們可以發(fā)現(xiàn)這樣就可以將事務(wù)機(jī)制關(guān)掉。

這樣我們模擬了一個(gè)AOP編程。

總結(jié)

以上是生活随笔為你收集整理的吃透Java中的动态代理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。