spring中的設計模式——動態代理(一)
阿新 • • 發佈:2018-12-08
前言 |
今天一定要把這一篇部落格補上,前段時間看了看動態代理的jdk方式如何實現,用起來簡單,但是jdk中的實現就不那麼容易了~~
代理模式的特點 |
- 有執行者、被代理者兩種角色
- 這件事一定要做,自己不想做,或者沒有時間做
- 執行者需要獲取被代理人的資料
例子:取快遞、中介、媒人等
代理模式的應用小例子——Celine取快遞 |
celine的快遞到了,但是她去公司上班了,所以她爸幫她取得快遞。從這個例子中,我們抽象出了被代理人(celine),代理人(她爸爸),介面(取快遞)
(1) 介面
public interface Receive {
public void Receive();
}
(2)celine
/**
* 被代理類
*/
public class Celine implements Receive {
@Override
public void Receive() {
System.out.println("Celine在上班,沒有辦法取快遞");
}
}
(3)代理類
/**
* 代理類需要實現InvocationHandler介面,並且重寫invoke方法,其中的method.invoke(target, args)就是celine具體實現介面的方法
*/
public class CelineFather implements InvocationHandler {
private Object target;
public CelineFather(Object target) {
super();
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(target, args);
System.out.println("celine她爸取得快遞" );
return null;
}
}
(4)測試類
public class TestJdkDynamicProxy {
public static void main(String[] args){
Celine celine=new Celine();
Class celineClass=celine.getClass();
//通過newProxyInstance生成代理物件
Receive Receiver = (Receive)Proxy.newProxyInstance(celineClass.getClassLoader(), celineClass.getInterfaces(),
new CelineFather(celine));
//實際呼叫的是代理類中的invoke方法
Receiver.Receive();
}
}
JDK實現原理 |
- 拿到被代理物件的引用,然後獲取他的介面
- jdk代理重新生成一個類,同時實現代理物件所實現的介面(所以jdk的動態代理必須有介面)
- 拿到物件的引用
- 重新動態生成一個class位元組碼
- 重新編譯
jdk原始碼中幾個重要的方法 |
首先提出幾個疑問:
1.為什麼最後真正實現的是代理類的invoke方法?
2.為什麼newProxyInstance返回值要強轉成介面的例項?
3.newProxyInstance沒有轉成介面類之前是什麼?
我們從newProxyInstance這個方法入手,首先這個方法傳入了三個引數,分別是Celine的類載入器,Celine實現的介面陣列,CelineFather的物件
這個方法為我們做了三件比較重要的事情:
1、生成代理類
2、獲得代理類的建構函式
3、根據建構函式例項化代理類(生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法 )
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 生成代理類
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//獲得代理類的建構函式
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
getProxyClass0(loader, intfs)這個方法是最為重要的,他會生成一個$Proxy0的類,經過反編譯,我們看看這個類是什麼樣子的(下邊程式碼來源於網路,我沒有下載反編譯工具,所以就在網上找了一個)
這個類他會實現我們的被代理類中所有的方法,最為重要的是其中的super.h.invoke(this, m3, null); 方法,這也就是為什麼呼叫invoke方法實現的確實代理類的invoke方法,而且invoke方法會呼叫Celine的receive方法,第二個引數m3就是被代理類物件的receive的方法,這也解決了我們的三個疑問
import dynamic.proxy.UserService;
import java.lang.reflect.*;
public final class $Proxy11 extends Proxy
implements UserService
{
// 構造方法,引數就是剛才傳過來的MyInvocationHandler類的例項
public $Proxy11(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
/**
* 這個方法是關鍵部分
*/
public final void add()
{
try
{
// 實際上就是呼叫MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二個問題就解決了
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
// 在靜態程式碼塊中獲取了4個方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}