一、定義
二、分類
(1)靜態代理:
(2)動態代理:
三、目的
(1)安全原因:屏蔽第三方直接與真是對象進行通信。
(2)RMI中:需要使用代理類處理遠程方法調用的技術細節。
(3)提升系統性能,延遲加載。
四、四個角色
(1)主題接口:定義代理類和真實主題的公共接口
(2)真實主題:真正實現業務邏輯的類
(3)代理類:用來代理和封裝真實主題
(4)Main:客戶端,第三方,與代理類進行直接通信,完成一些工作。
五、實現
(1)靜態代理:
/*
* 靜態代理
* 每一個代理類只能爲一個接口服務,這樣一來程序開發中必然會產生過多的代理
*/
public class LearnProxy0 {
public static void main(String[] args) {
IDBQuery dbQuery=new DBQueryProxy();
dbQuery.select();
}
}
interface IDBQuery{
public void select();
}
class DBQuery implements IDBQuery{
public DBQuery() {
try {
//可能包含數據庫連接等耗時操作,這裏僅僅是模擬
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void select() {
// TODO Auto-generated method stub
System.out.println("進行數據庫查詢操作...");
}
}
class DBQueryProxy implements IDBQuery{
//初始值設置爲空,即在軟件啓動時,不進行初始化操作
private IDBQuery dbQuery=null;
@Override
public void select() {
// TODO Auto-generated method stub
//只有在真正需要的時候,才創建真正的對象
if (dbQuery==null) {
dbQuery=new DBQuery();
}
System.out.println("查詢操作前...");
dbQuery.select();
System.out.println("查詢操作後...");
}
}
(2)JDK動態代理:
/*
* JDK的動態代理
*/
public class LearnProxy1 {
public static void main(String[] args) {
DynamicProxy proxy=new DynamicProxy();
Sport sportProxy=(Sport)proxy.bind(new Basketball());
sportProxy.play();
}
}
interface Sport{
public void play();
}
class Basketball implements Sport{
public void play() {
System.out.println("i am playing basketball");
}
}
class DynamicProxy implements InvocationHandler{
//要代理的接口,因此動態代理可以代理多個不同接口
private Object target;
public Object bind(Object target){ //設置目標代理對象,並且返回代理
this.target=target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), //注意第二個參數是接口的列表,代理實例實現了所有接口
target.getClass().getInterfaces(), this); //因此,我們可以像使用接口的方式一樣使用代理來調用方法
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法執行前");
method.invoke(target, args); //通過反射機制調用真實主題的目標方法
System.out.println("方法執行後");
return null;
}
}
(3)cglib動態代理:
/*
* JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了
*
* cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因爲採用的
* 是繼承,所以不能對final修飾的類進行代理
*/
public class LearnProxy2 implements MethodInterceptor{
//
private Object target;
//設置被代理的對象,並且返回一個動態代理
public Object bind(Object target){
this.target=target;
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback((Callback) this);
return enhancer.create();
}
@Override
public Object intercept(Object target, Method method, Object[] arg2,
MethodProxy proxy) throws Throwable {
// TODO Auto-generated method stub
System.out.println("方法執行前...");
proxy.invokeSuper(target, arg2);
System.out.println("方法執行後");
return null;
}
public static void main(String[] args) {
LearnProxy2 proxy=new LearnProxy2();
Actor actorProxy=(Actor)proxy.bind(new Actor());
actorProxy.play();
}
}
class Actor{
public void play() {
System.out.println("表演....");
}
}
使用JDK創建代理有一個限制,即他只能爲接口創建代理實例。但是現實情況是,有的類並沒有實現接口(現實中存在一些並沒有使用接口的項目),但是它也需要動態代理,那怎麼辦呢?這個時候就需要使用CGLIB啦。
CGLIB採用非常底層的字節碼技術,可以爲一個類創建子類,並在子類中採用方法攔截的技術攔截所有父類方法的調用,並且順勢織入橫切邏輯。
另外,CGLIB創建的動態代理的性能要比JDK創建的代理的性能好很多。但是,CGLIB創建代理對象花費的時間要比JDK多。因此,我們建議,對於singleton的代理對象,因爲不需要頻繁創建,最好使用CGLIB技術創建代理,反之,適合使用JDK代理技術。還要注意的是,由於CGLIB採用動態創建子類的方式生成代理對象,所以不能對目標類中的final和private方法進行代理。
下面篇,我會講解一下動態代理在AOP中的應用。