Java代理模式
java靜态代理模式
首先關于java的代理模式我的了解是:分為四個部分
1 針對于用戶端的部分
2 有一個公共的接口
3 有個被代理的類
4 有個代理類
而代理模式帶來的優點是:我們可以對被代理類增加更多的處理,在Spring的AOP中就是使用的代理模式可以使得針對于切面進行程式設計。
靜态代理的例子
一個公共的接口Person.class
public interface Person{}public void giveMoney();
一個被代理的類
public class Student implements Person{private String name; public Student(String name){ this.name = name; } public void giveMoney(){ System.out.println(name+"給了50塊錢");
一個代理類
public class Proxy implements Person{
private Person person;
public Proxy(Person person){
this.person = person;
public void giveMoney(){
System.out.println("作業完成的很好");//這個就是在我們被代理的對象前面添加的方法。
person.giveMoney();
System.out.println("老師再見");//這個使我們在被代理的對象後面添加的方法類似于Spring AOP中的切面前置通知和前面後置通知
一個用戶端的類
public class Client{Student stu = new Student("jhc"); Proxy proxy = new Proxy(stu); proxy.giveMoney();
結果可以看到在我們交錢的時候增加了新的方法。
但是靜态代理會有一個缺點就是:我們可能不存在隻有一個代理類的情況,也許我們會有很多代理類,而添加而過每個代理類都通過對代理類增加方法的話那樣操作起來會很麻煩。是以還有一個方式是動态代理。
動态代理模式
通過JDK的動态代理模式實作代理。
在java.lang.reflect包下面有一個Proxy類和InvocationHandler接口,通過這兩個類去實作java的動态代理。
建立一個動态代理對象的步驟
- 建立一個實作了InvocationHandler接口的對象
InvocationHandler stuHandler = new MyInvocationHandler(stu);
- 使用Proxy類的靜态方法得到一個動态代理的類
Class<?> stuProxyClass = Proxy.getProxyClass(Person.class.getClassLoader(),new Class<?>[]{Person.class});
- 通過類然後由反射獲得一個具有InvocationHandler類的構造函數
Constructor<?> cons = stuProxyClass.getConstructor(InvocationHandler.class);
- 通過構造器constructor來建立一個動态執行個體stuProxy
Person person = (Person)cons.newInstance();
上面四個步驟可以簡化成為兩個步驟
Person proxy = (Person)Proxy.newInstance(Person.class.getClassLoader(),new Class[]{Person.class},stuHandler);
關于動态代理的執行個體模式其實一樣
- 首先是一個公共的接口Person
public interface Person {public void giveMoney();
- 其次是被代理類的實作Student
private String name;
- 第三步是比較中的實作InvocationHandler的接口
public class StuInvocationHandler<T> implements InvocationHandler<T>{ private T target; public StuInvocationHandler(T target){ this.target = target; } public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{ System.out.println("在這插入我們想要的方法"); Object result = method.invoke(target,args); System.out.println("這裡也可以插入我們想要的方法"); return result; } }
- 第四步是在用戶端使用動态代理并測試
public class ProxyTest{ public static void main(String[] args){ Person jhc = new Student("jhc"); InvocationHandler stuHandler = new StuInvocationHandler<Person>(jhc); Person stuProxy = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[?]{Person.class},stuHandler); stuProxy.giveMoney(); } }
結果如圖
下面是Proxy.newInstance()的源碼的内容。
/*首先解釋一下三個參數,第一個參數是為了能夠加載出來Person即公共接口這個類,第二個參數是找出實作的全部的接口,第三個參數是為了動态類能夠調用方法
*/
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);
}
/*
*這裡産生了代理類.
*/
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;
}
});
}
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);
}
}
下面是大神關于怎麼産生的代理類的分析,具體的做法我也不知道怎麼回事,隻知道他是生成了動态的代理類的檔案并且對該檔案進行了反編譯。得到的内容是
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
String path = "G:/javacode/javase/Test/bin/proxy/StuProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理類class檔案寫入成功");
} catch (Exception e) {
System.out.println("寫檔案錯誤");
}```
這是把檔案寫入到硬碟上,利用javap可以把檔案進行反編譯
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
*注意這裡是生成代理類的構造方法,方法參數為InvocationHandler類型,看到這,是不是就有點明白
*為何代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
*被代理對象的執行個體,不禁會想難道是....? 沒錯,就是你想的那樣。
*
*super(paramInvocationHandler),是調用父類Proxy的構造方法。
*父類持有:protected InvocationHandler h;
*Proxy構造方法:
- protected Proxy(InvocationHandler h) {
- Objects.requireNonNull(h);
- this.h = h;
- *
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
super(paramInvocationHandler);
//這個靜态塊本來是在最後的,我把它拿到前面來,友善描述
static
{
try
{
//看看這兒靜态塊兒裡面有什麼,是不是找到了giveMoney方法。請記住giveMoney通過反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
- *這裡調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,并把m3傳了進去。
*this.h.invoke(this, m3, null);這裡簡單,明了。
*來,再想想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象,
*再聯系到InvacationHandler中的invoke方法。嗯,就是這樣。
*/
public final void giveMoney()
throws
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
//注意,這裡為了節省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一樣。
這裡大神寫的很清楚,産生的動态代理類繼承自Proxy類,并且構造參數自動的擁有了InvocationHandler的構造參數,并且實作了接口以及接口的方法m3,這裡值得注意的是其實縱觀全局來看的話,動态代理類隻是一個中介類,他并沒有實作被代理類的任何方法都是通過InvocationHandler裡面具體的Target實作的,這樣動态代理的優點在于便于修改動态代理類
###總結
---
這是動态代理的JDK實作,缺點在于實作公共接口的類才能實作動态代理,沒有辦法實作對于繼承來的類的處理。
主要參考的是大神的部落格
https://www.cnblogs.com/gonjan-blog/p/6685611.html