Java-JDK动态代理
文章目錄
- 導(dǎo)讀
- 問題
- 概述
- 改造
- 其他相關(guān)接口/類
導(dǎo)讀
Spring-AOP基礎(chǔ)知識
Java-JDK動態(tài)代理
Java-CGLib動態(tài)代理
問題
另一篇博文中的問題
性能監(jiān)視橫切邏輯代碼
概述
Java1.3以后,JAVA提供了動態(tài)代理技術(shù),允許開發(fā)者在運(yùn)行期創(chuàng)建接口的代理實(shí)例。
JDK的動態(tài)代理主要涉及java.lang.reflect包中的兩個類:Proxy和InvocationHandler.
InvocationHandler是一個接口,可以通過實(shí)現(xiàn)該接口定義橫切邏輯并通過反射機(jī)制調(diào)用目標(biāo)類的代碼,動態(tài)的將橫切邏輯和業(yè)務(wù)邏輯編織在一起。
而Proxy利用InvocationHandler動態(tài)的創(chuàng)建一個符合某一接口的實(shí)例,生成目標(biāo)類的代理對象。
改造
代碼已托管到Github—> https://github.com/yangshangwei/SpringMaster
首先我們移除性能監(jiān)視的橫切代碼,如下
package com.xgj.aop.base.jdkproxy;/*** * * @ClassName: ForumServiceImpl* * @Description: ForumService實(shí)現(xiàn)類* * @author: Mr.Yang* * @date: 2017年8月12日 下午4:14:30*/ public class ForumServiceImpl implements ForumService {@Overridepublic void removeTopic(int topicId) {// 模擬業(yè)務(wù)邏輯System.out.println("模擬刪除Topic,topicId=" + topicId);try {Thread.currentThread().sleep((long) (Math.random() * 1000 * 10));} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void removeForum(int forumId) {// 模擬業(yè)務(wù)邏輯System.out.println("模擬刪除forum,forumId=" + forumId);try {Thread.currentThread().sleep((long) (Math.random() * 1000 * 10));} catch (InterruptedException e) {e.printStackTrace();}}}如上只編寫了業(yè)務(wù)代碼,移除的性能監(jiān)視橫切代碼,放哪里呢?
InvocationHandler就是橫切代碼的家,編寫實(shí)現(xiàn)類實(shí)現(xiàn)InvocationHandler 接口,重寫invoke方法
比如:
package com.xgj.aop.base.jdkproxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;public class PerformanceHandler implements InvocationHandler {private Object target;/*** * * @Title:PerformanceHandler* * @Description:構(gòu)造函數(shù), 入?yún)arget為業(yè)務(wù)目標(biāo)類* * @param target*/public PerformanceHandler(Object target) {this.target = target;}// 性能監(jiān)視的橫切代碼 + 業(yè)務(wù)代碼@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 開始監(jiān)控 入?yún)?方法的全限定名稱PerformanceMonitor.begin(target.getClass().getName() + "."+ method.getName());// 通過反射調(diào)用業(yè)務(wù)類的目標(biāo)方法Object object = method.invoke(target, args);// 結(jié)束監(jiān)控PerformanceMonitor.end();return object;} }分析:
invoke方法中PerformanceMonitor相關(guān)的為性能監(jiān)視的橫切代碼,我們發(fā)現(xiàn),橫切代碼只出現(xiàn)一次,而不是像原來那樣每個類中都有。
method.invoke()通過Java反射機(jī)制間接調(diào)用目標(biāo)對象的方法,這樣 InvocationHandler的invoke方法就將橫切邏輯代碼和業(yè)務(wù)類方法的業(yè)務(wù)邏輯代碼編織到一起,所以可以將InvocationHandler看成一個編織器。
具體分析這段代碼:
首先實(shí)現(xiàn)了InvocationHandler接口,重寫invoke方法
public Object invoke(Object proxy, Method method, Object[] args)其中
-
proxy是最終生成的代理實(shí)例,一般不會用到
-
method是被代理目標(biāo)實(shí)例的某個具體方法,通過它可以發(fā)起目標(biāo)實(shí)例方法的反射調(diào)用
-
args是被代理實(shí)例某個方法的入?yún)?#xff0c;在方法反射調(diào)用時使用。
其次,在構(gòu)造函數(shù)里通過target傳入希望被代理的目標(biāo)對象。 在invoke方法中,將目標(biāo)對象傳遞給method.invoke()方法,并調(diào)用目標(biāo)實(shí)例的方法。
測試類,利用Proxy創(chuàng)建ForumService接口的代理實(shí)例:
package com.xgj.aop.base.jdkproxy;import java.lang.reflect.Proxy;public class ForumServiceTest {public static void main(String[] args) {// 希望被代理的目標(biāo)業(yè)務(wù)類ForumService target = new ForumServiceImpl();// 將目標(biāo)類業(yè)務(wù)和橫切代碼編織到一起PerformanceHandler handler = new PerformanceHandler(target);// 根據(jù)編織了目標(biāo)業(yè)務(wù)類邏輯和性能監(jiān)控橫切邏輯的InvocationHandler實(shí)例創(chuàng)建代理實(shí)例ForumService proxy = (ForumService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), handler);// 調(diào)用代理實(shí)例proxy.removeTopic(3);proxy.removeForum(1);} }上述代碼完成了業(yè)務(wù)類代碼和橫切面代碼的編織工作并生成了代理實(shí)例。
PerformanceHandler handler = new PerformanceHandler(target); —>將性能監(jiān)視橫切邏輯編織到ForumService實(shí)例中。
然后通過Proxy的 newProxyInstance()講臺方法為編織了業(yè)務(wù)邏輯和心梗監(jiān)視橫切邏輯的handler創(chuàng)建了一個符合ForumService接口的代理實(shí)例。
-
第一個入?yún)轭惣虞d器
-
第二個入?yún)閯?chuàng)建代理實(shí)例所需要實(shí)現(xiàn)的一組接口
-
第三個參數(shù)是整了業(yè)務(wù)邏輯和橫切邏輯的編織器對象。
按照上述設(shè)置,這個代理實(shí)例就實(shí)現(xiàn)了目標(biāo)業(yè)務(wù)類的所有接口,即ForumServiceImpl的ForumService接口。
這樣就可以按照調(diào)用ForumService接口實(shí)例相同的方式調(diào)用代理實(shí)例了。
代理實(shí)例方法調(diào)用的時序圖:
運(yùn)行結(jié)果:
其他相關(guān)接口/類
ForumService.java
package com.xgj.aop.base.jdkproxy;/*** * * @ClassName: ForumService* * @Description: ForumService接口* * @author: Mr.Yang* * @date: 2017年8月12日 下午4:13:31*/ public interface ForumService {/*** * * @Title: removeTopic* * @Description: 根據(jù)topicId刪除Topic* * @param topicId* * @return: void*/void removeTopic(int topicId);/*** * * @Title: removeForum* * @Description: 根據(jù)forumId刪除Forum* * @param forumId* * @return: void*/void removeForum(int forumId); }PerformanceMonitor.java
package com.xgj.aop.base.jdkproxy;public class PerformanceMonitor {// 通過一個ThreadLocal保存與調(diào)用線程相關(guān)的性能監(jiān)視信息private static ThreadLocal<MethoPerformance> performanceLocal = new ThreadLocal<MethoPerformance>();/*** * * @Title: begin* * @Description: 啟動對某一目標(biāo)方法的性能監(jiān)視* * @param method* * @return: void*/public static void begin(String method) {System.out.println("begin to monitor:" + method);MethoPerformance methoPerformance = new MethoPerformance(method);performanceLocal.set(methoPerformance);}/*** * * @Title: end* * @Description: 輸出性能監(jiān)視結(jié)果* * @param method* * @return: void*/public static void end() {MethoPerformance methoPerformance = performanceLocal.get();// 打印出方法性能監(jiān)視的結(jié)果信息methoPerformance.printPerformance();} }MethoPerformance.java
package com.xgj.aop.base.jdkproxy;public class MethoPerformance {private long beginTime;private long endTime;private String methodName;/*** * * @Title:MethoPerformance* * @Description:構(gòu)造函數(shù)* * @param methodName*/public MethoPerformance(String methodName) {super();this.methodName = methodName;this.beginTime = System.currentTimeMillis();}/*** * * @Title: printPerformance* * @Description: 計(jì)算耗時* * * @return: void*/public void printPerformance() {endTime = System.currentTimeMillis();long cost = endTime - beginTime;System.out.println(methodName + " costs " + cost / 1000 + "秒\n");} }總結(jié)
以上是生活随笔為你收集整理的Java-JDK动态代理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring-AOP基础知识
- 下一篇: Java-CGLib动态代理