第二篇:從Java基礎學習Spring AOP——JDK動態代理(重點內容)
在Spring中,嚴格的來說不單單使用JDK的動態代理,還會使用CGLIB動態代理,但是它們大同小異,我們這里講JDK的動態代理,有了它的理解CGLIB也不難掌握,讀者可以閱讀其他的文檔。
這篇文章是揭示Spring AOP原理的基礎,是所有章節中的重點內容之一,初學者需要認真學習和掌握它,否則后面講解會很難理解的,一定多多敲代碼體會它,你的進步就會很大
首先,讓我們先了解什么是動態代理,假設讀者是一個軟件工程師,而你的公司是家軟件公司,我是一個客戶,我需要你公司提供軟件的專業服務。顯然我是找到你們公司的商務,而不是你去討論我的要求。那么商務就等同于你的一個代理,我也會認為商務等于你們公司,而不會去管你這個軟件工程師如何工作的。
代理的實際就是在真實服務對象(軟件工程師)之前加多一個占位或者叫做代理對象(商務),這個占位(商務)可以根據調用者(客戶)的要求去控制真實服務對象的訪問(軟件工程師)。有了這個比喻是不是好理解很多??
那么代理模式的好處在于什么?首先占位(商務)可以在真實服務對象之前之后做一些服務,同時根據需要選擇是否需要啟用真實服務對象,也會加入一些規則,比如商務也可以根據客戶和公司的規則來提供額外的服務,這時可能就連真實服務對象(軟件工程師)都不會啟用。
好,上面的東西貌似很神奇,我們用一張圖來表達代理的含義。
好,這里的代理對象就是我們之前談到的占位,它代理了我們看到的真實對象,可以在真實對象之前之后,甚至是代替真實對象提供服務。
動態代理有好幾種,Spring使用了CGLIG和JDK動態代理。在JDK動態代理中,要求必須提供接口,而CGLIB是不需要的,我們這里只談論JDK動態代理,在大部分的情況下,筆者建議你使用JDK動態代理,因為JDK動態代理的速度要比CGLIB要快,在Spring中一個有切面的Bean如果有接口聲明,Spring就會用JDK動態代理代理它,否者啟用CGLIB。
好了論述講了一大截,我們開始講JDK動態代理,首先我們需要提供一個簡單的接口:
package com.learn.chapter1.proxy;
/**
*
* @author ykzhen2015
*/
public interface HelloService {
public void sayHello(String name);
}
跟著是實現類:
package com.learn.chapter1.proxy;
/**
*
* @author ykzhen2015
*/
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.err.println("hello " + name);
}
}
好都很簡單,不需要筆者做任何解釋。我們跟著就是要生成代理對象(proxy),分成兩步:
- 生成代理對象要建立代理對象(proxy)和真實對象(HelloServiceImpl)的代理關系。
- 實現代理方法
在JDK動態代理中需要實現接口:java.lang.reflect.InvocationHandler。讓我們先來看看它:
package com.learn.chapter1.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
*
* @author ykzhen2015.
*/
public class HelloProxy implements InvocationHandler {
private Object target;
/**
* 生成代理對象,并和真實服務對象綁定.
* @param target 真實服務對線下
* @return 代理對象
*/
public Object bind(Object target) {
this.target = target;
//生成代理對象,并綁定.
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //類的加載器
target.getClass().getInterfaces(), //對象的接口,明確代理對象掛在哪些接口下
this);//指明代理類,this代表用當前類對象,那么就要求其實現InvocationHandler接口
return proxy;
}
/**
* 當生成代理對象時,第三個指定使用HelloProxy進行代理時,代理對象調用的方法就會進入這個方法。
* @param proxy ——代理對象
* @param method -- 被調用的方法
* @param args -- 方法參數
* @return 代理方法返回。
* @throws Throwable -- 異常處理
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("反射真實對象方法前");
Object obj = method.invoke(target, args);//相當于sayHello方法調用.
System.err.println("反射真實對象方法后");
return obj;
}
}
好了,有了上面的代碼我們論述一下(實際注釋也很清晰),
- 首先聲明了一個類的屬性target,它的作用是保存真實服務對象(軟件工程師)。
- 然后用bind方法綁定代理對象(proxy 商務)和真實對象(軟件工程師),是通過這樣去綁定的:
Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), //類的加載器
target.getClass().getInterfaces(), //對象的接口,明確代理對象掛在哪些接口下
this);//指明代理類,this代表用當前類對象,那么就要求其實現InvocationHandler接口的invoke方法,而代理邏輯就放入invoke方法里。
用之前的比喻就是proxy就是商務,它代理了target這個軟件工程師,而商務代理的邏輯方法放在this這個對象的invoke方法里,只是this這個對象需要實現InvocationHandler接口而已。
- 這樣聲明就會進入當前類invoke方法,它實現的是代理邏輯(按比喻就是商務邏輯)它有三個參數:
Object proxy ——當前代理對象(商務)
Method method —— 當前調度的方法
Object[] args -- 方法參數
然后我們通過反射調度真實對象的方法,不懂的可以看到第一篇的論述(鏈接描述)
Object obj = method.invoke(target, args);//相當于sayHello方法調用.
讓我們測試一下這段代碼:
package com.learn.chapter1.main;
import com.learn.chapter1.pojo.Role;
import com.learn.chapter1.proxy.HelloProxy;
import com.learn.chapter1.proxy.HelloService;
import com.learn.chapter1.proxy.HelloServiceImpl;
import com.learn.chapter1.utils.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;
/**
*
* @author ykzhen2015
*/
public class Chapter1Main {
public static void main(String[] args) {
HelloProxy helloProxy = new HelloProxy();
//因為使用了接口HelloService綁定了代理對象,所以可以用HelloService作為代理對象的聲明.
HelloService proxy = (HelloService) helloProxy.bind(new HelloServiceImpl());
proxy.sayHello("張三");//此時使用代理對象運行方法進入HelloProxy的invoke方法里
}
}
這和時候,我們運行一下可以得到下面的打印:
反射真實對象方法前
hello 張三
反射真實對象方法后
好了,我們看到的HelloService實際已經是一個代理對象了,而不是我們普通人看到的HelloServiceImpl這個真實對象,在Spring中不要被它迷糊了哦。
這篇很重要,是我們以后的基礎,所以筆者也論述得比較詳細,希望大家好好學習,一定要多敲代碼,好好體會哦。
本文原創發布于慕課網 ,轉載請注明出處,謝謝合作
共同學習,寫下你的評論
評論加載中...
相關文章推薦