Spring的靜态代理和動态代理
文章目錄
一、前言
二、分類
2.1、靜态代理
2.2、動态代理
2.2.1、分類
2.2.2、對比
三、實作
3.1 靜态代理
3.1 動态代理
3.1.1基于jdk的動态代理
3.1.2基于cglib的動态代理
四、結語
開始接觸代理是在設計模式動态代理中了解的,大概是這樣的:張三喜歡一個女孩,但是她不敢表白怎麼辦,很簡單,他找李四幫他去表白。這裡李四就是代理對象,代替張三幹活,可以看下面的畫面:
慢慢積累之後,發現很多經典的架構背後都使用了代理的模式,例如Spring的AOP實作原理便是動态代理;常用的ORM模型中mybatis架構實作原理也是動态代理;事務的實作原理反射+動态代理;dubbo的實作中也有動态代理…很多很多,我們可以得出的結論是熟悉代理以及熟悉動态代理的重要性。代理模式是:通過代理控制對目标對象的通路,下面是代理類圖,我們先對代理有個整體認識。
代理分為靜态代理和動态代理,他們的差別是靜态代理有真實的代理類存在,就是我們會代碼中建立一個代理類,并在代理類的方法中調用目标對象的方法,以此來完成代理的工作。動态代理的代理類沒有在代碼中建立一個代理類,而是在運作時在JVM裡面建立代理對象。
我們知道靜态代理是有實實在在的代理類存在,并且和目标類實作相同的接口。他的特點是效率高,因為所有的類都是已經編寫完成的,用戶端隻需要取得代理對象并且執行即可,同時他可以實作對目标對象中指定的方法進行增強。但是他也有如下缺點:
與目标類實作相同的接口代碼備援
如果接口發生改變,代理類中的方法也要修改
代理類服務于一種類型的對象,如果要服務多類型的對象,那麼勢必要為每種類型的對象都生成代理類
與靜态代理的寫死方式相比,動态代理支援運作時動态生成代理對象這種方式。動态代理能用很少的代碼對一個類的所有方法實作一樣的增強效果,但是不能增強其中的某一個方法,他的好處很明顯,在編碼時代理邏輯與業務邏輯互相獨立,各不影響,減少侵入,降低耦合。
動态代理可以分為兩種,一種是基于JDk的動态代理一種是基于CGLIB的動态代理。在spring中使用他們的差別是:當目标對象有實作接口時,預設使用jdk動态代理的方式,當目标類沒有實作接口時使用cglib的動态代理方式,并且代理對象以目标對象為父類。
jdk cglib
依賴 基于spring,不需要引用 需要引用第三方類庫
方式 有實作的接口,常見是mybatis的mapper檔案是代理 沒有,原理目标對象作為父類
對targetObject要求 實作接口 不能用final修飾
生成對象的技術 反射,使用了動态生成位元組碼技術 使用位元組碼技術,使用了動态生成位元組碼技術
沒有代碼的講理論等于耍流氓,下面分享一下我在實踐中的code,同時進行适當的補充。
有代理類,實實在在的存在代理類,是動态代理的理論基礎,這裡有一個work接口,接口中有一個唱歌的方法,我們想通過通路經紀人(代理對象)來達到讓明星(目标對象)唱歌的方法。
//接口
public interface Work {
void sing();
}
//目标對象
public class TargetObj implements Work {
@Override
public void sing() {
System.out.println("明星在唱歌");;
}
//代理對象
public class ProxyObj implements Work {
Work obj=null;
public ProxyObj() {
obj=new TargetObj();
}
@Override
public void sing() {
System.out.println("先到經濟人");
obj.sing();
}
void sing();
@Override
public void sing() {
System.out.println("明星在唱歌");;
}
public class MainTest {
//JDK方式發的動态代理
public static void main(String[] args) {
//1.建立真實的對象
Work targetObj=new TargetObj();
//2.建立代理對象
Work proxyObj = (Work) Proxy.newProxyInstance(
targetObj.getClass().getClassLoader(),//真實類使用什麼類加載器
targetObj.getClass().getInterfaces(),//真實實作的接口
new InvocationHandler() { //調用處理器
/**
* 處理器的方法
* @param proxy 參數一:代理對象,一般不會使用
* @param method 參數二:外面的代理對象調用的方法引用,代理對象.sing(),method便是sing這個方法的引用
* @param args 參數三:外面的代理對象調用的方法裡面的參數。代理對象.sing(“參數"),args就是“參數”
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//反射調用
return method.invoke(targetObj,args);//方法的傳回參數
}
}
);
//3讓代理工作
proxyObj.sing("北冰洋");
}
從代碼中我們可以get到兩點
基于jdk的動态代理需要有實作的接口
動态代理的核心:是建立了處理器 InvocationHandler執行個體
在調用目标對象時,會調用代理對象,代理對象中請求目标對象,invoke方法便是調用目标對象的方法生成代理對象的過程,同時增強工作也是在invoke方法中進行的。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
public class TargetObj2 {
public String sing(String args) {
System.out.println("明星在唱歌");
return args;
}
public void dance() {
System.out.println("明星在跳舞");
}
public static void main(String[] args) {
//1.真實對象
TargetObj2 targetObj2=new TargetObj2();
//2.建立代理對象
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(targetObj2.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//增強功能
System.out.println("123");
return method.invoke(targetObj2,objects);
}
});
//建立代理
TargetObj2 proxyObj = (TargetObj2) enhancer.create();
//讓代理工作
String ret= proxyObj.sing("丁香花");
System.out.println(ret);
}
從cglib中我們可以get到兩點
基于cglib的動态代理是沒有實作接口的,他是以目标類作為代理類的父類
cglib動态代理的關鍵是基于enhancer.setCallback方法,和其中的MethodInterceptor() 方法
在MethodInterceptor執行個體中重寫intercept方法中調用invoke方法設定代理對象,最後通過enhancer.create()建立代理對象。
理論知識會比較空洞和抽象,但是實踐一下我們會有不一樣的收獲。
感謝您寶貴的閱讀時間。
作者:Viola_tt
來源:CSDN
原文:
https://blog.csdn.net/SweetyoYY/article/details/90743578版權聲明:本文為部落客原創文章,轉載請附上博文連結!