最近在看項目中關於aop的一些東西,正好想起之前沒畢業的時候去唯品會面試,面試官問我知不知道aop底層實現原理,當時真是一臉懵逼啊。現在工作後對aop怎麼使用稍微瞭解了一點,也知道底層原理其實是動態代理,具體怎麼實現還不是很清楚所以這篇就聊一聊動態代理設計模式。在這裏分享一下之前無意中看到的大神對aop的比喻,感覺很有道理。
aop切面編程就是在常規的執行java類中方法前或執行後加入自定義的方法。比如你本來每天都去打醬油,去,打醬油,回。現在我每天在你打醬油路上等着,你去打醬油的時候我打你一頓,回來的時候給你點糖果吃。你根本不知道爲什麼我會在路上攔住打你。所以在切面中插入你自定義的方法,這個方法的執行和本身要執行的類方法無關係,也就是不是這個類的方法來調用你寫的方法的,你寫的方法什麼時候執行都是要通過在配置指定。我打完你,你該打醬油還是去打醬油,當然我如果是攔住你讓你醬油打少點,你打醬油的時候還是會打那麼多,但是在你打完醬油回來的時候我可以把你的醬油倒些出去,所以嵌入的自定義方法對要調用的類方法本身沒有影響,但是可以操縱這個方法的返結果或者處理結果。
上面這段話我覺得還是很形象的,我現在接觸到的aop的應用一般是日誌以及異常處理,session之類的還不是很瞭解。
按照我現在的理解,我覺得動態代理只要有兩個地方比較重要,一個是InvocationHandler接口,還有一個是Proxy類。
爲什麼要有代理模式?
在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。比如我們買二手房,往往只能通過中介,因爲我們這時候不能直接接觸到客戶,但是中介可以。
代理模式代碼如下:
首先建立buyHouse接口
public interface BuyHouse {
public void buyHouse();
}
建立一個realSubject
public class RealSubject implements BuyHouse {
@Override
public void buyHouse() {
System.out.println("我要在外灘旁邊買一套房");
}
}
定義一個動態代理類,實現InvocationHandler接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object object;
public DynamicProxy(Object object){
this.object = object;
}
@Override
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
//實現真實對象前可以添加自己想實現的業務,實際可能是日誌,權限驗證等等
System.out.println("你必須給我中介費先付5w");
//實現真實對象方法
paramMethod.invoke(object, paramArrayOfObject);
//之後繼續實現一些業務,可以通過回調
System.out.println("中介費尾款給我");
return null;
}
}
最後看一下測試類
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class TestDynamicProxy {
public static void main(String[] args) {
// TODO Auto-generated method stub
BuyHouse buyHouse = new RealSubject();
InvocationHandler handler = new DynamicProxy(buyHouse);
BuyHouse proxyHouse = (BuyHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), buyHouse.getClass().getInterfaces(), handler);
proxyHouse.buyHouse();
}
}
控制檯輸出結果爲
你必須給我中介費先付5w
我要在外灘旁邊買一套房
中介費尾款給我
到此建議動態代理結束,我們看下jdk1.8中InvocationHandler以及Proxy源碼。首先是InvocationHandler
public abstract interface InvocationHandler
{
public abstract Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject)
throws Throwable;
}
InvocationHandler接口中只有一個invoke方法,該方法三個參數分別指 真實對象,調用對象的method方法,最後一個不是很明白作用,待以後補充,或者知道的大神解答下
Proxy
Proxy類在jdk1.8中定義的屬性如圖,這裏我們主要使用newProxyInstance這個方法(我也不知道爲什麼,我一看到Instance這個就想到反射),代碼如下
public static Object newProxyInstance(ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)
throws IllegalArgumentException
{
Objects.requireNonNull(paramInvocationHandler);
Class[] arrayOfClass = (Class[])paramArrayOfClass.clone();
SecurityManager localSecurityManager = System.getSecurityManager();
if (localSecurityManager != null) {
checkProxyAccess(Reflection.getCallerClass(), paramClassLoader, arrayOfClass);
}
Class localClass = getProxyClass0(paramClassLoader, arrayOfClass);
try
{
if (localSecurityManager != null) {
checkNewProxyPermission(Reflection.getCallerClass(), localClass);
}
Constructor localConstructor = localClass.getConstructor(constructorParams);
localObject = paramInvocationHandler;
if (!Modifier.isPublic(localClass.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction()
{
public Void run()
{
this.val$cons.setAccessible(true);
return null;
}
});
}
return localConstructor.newInstance(new Object[] { paramInvocationHandler });
}
catch (IllegalAccessException|InstantiationException localIllegalAccessException)
{
throw new InternalError(localIllegalAccessException.toString(), localIllegalAccessException);
}
catch (InvocationTargetException localInvocationTargetException)
{
Object localObject = localInvocationTargetException.getCause();
if ((localObject instanceof RuntimeException)) {
throw ((RuntimeException)localObject);
}
throw new InternalError(((Throwable)localObject).toString(), (Throwable)localObject);
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new InternalError(localNoSuchMethodException.toString(), localNoSuchMethodException);
}
}
這段代碼我只能看個大體,基本就是利用反射獲取構造函數對象並生成一個代理類,看一下他的三個參數:
ClassLoader paramClassLoader, Class<?>[] paramArrayOfClass, InvocationHandler paramInvocationHandler)
paramClassLoader指類裝載器,paramArrayOfClass 一般表示需要提供給代理對象的需要的接口,最後InvocationHandler不多做介紹了。