Spring源碼入門到放棄(七):Spring中的代理模式

代理模式是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

JdkProxy

implements

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>

打個斷點

Spring源碼入門到放棄(七):Spring中的代理模式

可以看到 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

String

toString

()

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>compile 

group

:

'asm'

, name:

'asm'

, version:

'3.3.1'

compile

group

:

'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

Object

getInstance

(Class clazz)

{ enhancer.setSuperclass(clazz); enhancer.setCallback(

this

);

return

enhancer.create(); }

public

Object

intercept

(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 ,代理模式就先介紹到這裡。


分享到:


相關文章: