代理模式是23種設計模式的一種,他是指一個對象A通過持有另一個對象B,可以具有B同樣的行為的模式。
代理模式可分為靜態代理和動態代理兩種。而動態代理又有JDK、CGLIB動態代理。
靜態代理與動態代理的區別主要在:
- 靜態代理在編譯時就已經實現,編譯完成後代理類是一個實際的class文件
- 動態代理是在運行時動態生成的,即編譯完成後沒有實際的class文件,而是在運行時動態生成類字節碼,並加載到JVM中
靜態代理
這個較為簡單,我們寫個小例子。
- 被代理接口和實現類
<code>package
com.sxdx.proxy;public
interface
Count
{void
queryCount
()
;void
updateCount
()
; }/<code>
<code>public
class
CountImpl
implements
Count
{public
void
queryCount
()
{ System.out.println("查詢賬戶"
); }public
void
updateCount
()
{ System.out.println("修改賬戶"
); } }/<code>
- 代理類
<code>public
class
CountProxy
implements
Count
{ ("countImpl"
)private
Count countImpl;public
void
queryCount
()
{ countImpl.queryCount(); }public
void
updateCount
()
{ countImpl.updateCount(); } }/<code>
- 測試及結果
<code>public
void
test1
()
throws
Exception { CountProxy countProxy = applicationContext.getBean("countProxy"
,CountProxy.
class
); countProxy.queryCount(); countProxy.updateCount(); }/<code>
<code> 查詢賬戶 修改賬戶 /<code>
靜態代理就是這麼簡單。靜態代理類隱藏了實現類的具體實現,客戶端只需知道代理即可。
靜態代理類只能為特定的接口(Service)服務。如想要為多個接口服務則需要建立很多個代理類。這樣就出現了大量的代碼重複。如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的複雜度。
動態代理才是我們真正需要學習的。
動態代理
動態代理分為
JDK動態代理、CGLIB動態代理 2種。JDK動態代理
JDK動態代理所用到的代理類在程序調用到代理類對象時才由JVM真正創建,JVM根據傳進來的業務實現類對象以及方法名 ,動態地創建了一個代理類的class文件並被字節碼引擎執行,然後通過該代理類對象進行方法調用。
JDK動態代理只能代理接口(不支持抽象類),代理類都需要實現InvocationHandler類,實現invoke方法。該invoke方法就是調用被代理接口的方法時需要調用的,該invoke方法返回的值是被代理接口的一個實現類。
我們在上面代碼的基礎上,新建一個JdkProxy代理類
<code>package com.sxdx.proxy;import
java.lang.reflect.InvocationHandler;import
java.lang.reflect.Method;import
java.lang.reflect.Proxy;public
class
JdkProxyimplements
InvocationHandler {private
Object
target;public
JdkProxy(Object
target) {super
();this
.target = target; }public
Object
getProxy() {return
Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this
); }public
Object
invoke(Object
proxy, Method method,Object
[] args) throws Throwable { System.out.println("==代理方法開始執行"
);Object
invoke = method.invoke(target, args); System.out.println("==代理方法結束執行"
);return
invoke; } }/<code>
測試方法及結果:
<code>public
void
test2
()
throws
Exception { JdkProxy handler =new
JdkProxy(new
CountImpl()); Count proxy = (Count) handler.getProxy(); proxy.queryCount(); }/<code>
<code>==代理方法開始執行 查詢賬戶 ==代理方法結束執行 /<code>
打個斷點
可以看到 handler.getProxy()返回的是一個CountImpl代理對象。這個代理對象包含寫什麼內容呢?我們可以使用ProxyGenerator查看
<code>public
void
test2
()
throws
Exception { JdkProxy handler =new
JdkProxy(new
CountImpl()); Count proxy = (Count) handler.getProxy(); proxy.queryCount(); String name ="$Proxy"
;byte
[] data = ProxyGenerator.generateProxyClass(name,new
Class[]{Count.
class
}); FileOutputStream out =null
;try
{ out =new
FileOutputStream(name+".class"
); System.out.println((new
File("hello"
)).getAbsolutePath()); out.write(data); }catch
(FileNotFoundException e) { e.printStackTrace(); }catch
(IOException e) { e.printStackTrace(); }finally
{if
(null
!=out)try
{ out.close(); }catch
(IOException e) { e.printStackTrace(); } } }/<code>
執行代碼,項目目錄下生成一個ProxyCount.class文件
<code>import
com.sxdx.proxy.Count;import
java.lang.reflect.InvocationHandler;import
java.lang.reflect.Method;import
java.lang.reflect.Proxy;import
java.lang.reflect.UndeclaredThrowableException;public
final
class
$Proxy
extends
Proxy
implements
Count
{private
static
Method m1;private
static
Method m2;private
static
Method m3;private
static
Method m4;private
static
Method m0;public
$Proxy(InvocationHandler var1)throws
{super
(var1); }public
final
boolean
equals
(Object var1)
throws
{try
{return
(Boolean)super
.h.invoke(this
, m1,new
Object[]{var1}); }catch
(RuntimeException | Error var3) {throw
var3; }catch
(Throwable var4) {throw
new
UndeclaredThrowableException(var4); } }public
final
StringtoString
()
throws
{try
{return
(String)super
.h.invoke(this
, m2, (Object[])null
); }catch
(RuntimeException | Error var2) {throw
var2; }catch
(Throwable var3) {throw
new
UndeclaredThrowableException(var3); } }public
final
void
queryCount
()
throws
{try
{super
.h.invoke(this
, m3, (Object[])null
); }catch
(RuntimeException | Error var2) {throw
var2; }catch
(Throwable var3) {throw
new
UndeclaredThrowableException(var3); } }public
final
void
updateCount
()
throws
{try
{super
.h.invoke(this
, m4, (Object[])null
); }catch
(RuntimeException | Error var2) {throw
var2; }catch
(Throwable var3) {throw
new
UndeclaredThrowableException(var3); } }
public
final
int
hashCode
()
throws
{try
{return
(Integer)super
.h.invoke(this
, m0, (Object[])null
); }catch
(RuntimeException | Error var2) {throw
var2; }catch
(Throwable var3) {throw
new
UndeclaredThrowableException(var3); } }static
{try
{ m1 = Class.forName("java.lang.Object"
).getMethod("equals"
, Class.forName("java.lang.Object"
)); m2 = Class.forName("java.lang.Object"
).getMethod("toString"
); m3 = Class.forName("com.sxdx.proxy.Count"
).getMethod("queryCount"
); m4 = Class.forName("com.sxdx.proxy.Count"
).getMethod("updateCount"
); m0 = Class.forName("java.lang.Object"
).getMethod("hashCode"
); }catch
(NoSuchMethodException var2) {throw
new
NoSuchMethodError(var2.getMessage()); }catch
(ClassNotFoundException var3) {throw
new
NoClassDefFoundError(var3.getMessage()); } } }/<code>
這就是最終真正的代理類,它繼承自Proxy並實現了我們的Count接口。
看到這裡 我們就知道了,proxy.queryCount()其實執行的 ProxyCount代理類的queryCount方法
<code>public
final
void
queryCount
()
throws
{try
{super
.h.invoke(this
, m3, (Object[])null
); }catch
(RuntimeException | Error var2) {throw
var2; }catch
(Throwable var3) {throw
new
UndeclaredThrowableException(var3); } }/<code>
Java動態代理為我們提供了非常靈活的代理機制,但Java動態代理是基於接口的,如果對象沒有實現接口我們該如何代理呢?CGLIB登場。
CGLIB動態代理
CGLIB(Code Generation Library)是一個開源項目!是一個強大的,高性能,高質量的Code生成類庫.
CGLIB特點
- JDK的動態代理有一個限制,就是使用動態代理的對象必須實現一個或多個接口。
如果想代理沒有實現接口的類,就可以使用CGLIB實現。 - CGLIB是一個強大的高性能的代碼生成包,它可以在運行期擴展Java類與實現Java接口。
它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。 - CGLIB包的底層是通過使用一個小而快的字節碼處理框架ASM,來轉換字節碼並生成新的類。
不鼓勵直接使用ASM,因為它需要你對JVM內部結構包括class文件的格式和指令集都很熟悉。
CGLIB是針對類來實現代理的,原理是對指定的業務類生成一個子類,並覆蓋其中業務方法實現代理。因為採用的是繼承,所以不能對final修飾的類進行代理。 在使用的時候需要引入cglib和asm(字節碼處理框架)的jar包,並實現MethodInterceptor接口。
<code>compilegroup
:'asm'
, name:'asm'
, version:'3.3.1'
compilegroup
:'cglib'
, name:'cglib'
, version:'2.2.2'
/<code>
代理類:
<code>package
com.sxdx.proxy;import
net.sf.cglib.proxy.Enhancer;import
net.sf.cglib.proxy.MethodInterceptor;import
net.sf.cglib.proxy.MethodProxy;import
java.lang.reflect.Method;public
class
CglibProxy
implements
MethodInterceptor
{private
Enhancer enhancer =new
Enhancer();public
ObjectgetInstance
(Class clazz)
{ enhancer.setSuperclass(clazz); enhancer.setCallback(this
);return
enhancer.create(); }public
Objectintercept
(Object o, Method method, Object[] objects, MethodProxy methodProxy)
throws
Throwable { System.out.println("Cglib代理方法開始執行"
); Object result = methodProxy.invokeSuper(o, objects); System.out.println("Cglib代理方法結束執行"
);return
result; } }/<code>
測試類
<code>public
void
test3
()
throws
Exception { CountImpl count = (CountImpl)new
CglibProxy().getInstance(CountImpl.
class
); count.queryCount(); }/<code>
<code>Cglib代理方法開始執行 查詢賬戶 Cglib代理方法結束執行 /<code>
OK ,代理模式就先介紹到這裡。