Java動態代理的基本用法

簡介

在開發中,我們可能會遇到一些類的方法不太合適我們實際的業務邏輯需求,而且我們不想或者不能修改該類的源碼的時候我們通常會採用集成重寫的方法去達到目標。
但是也有些情況下使用繼承重寫不是那麼的方便,那麼這個時候我們可以用java的動態代理技術去實現。

案例

本案例用來演示java動態代理可能不太合適,但是主要是爲了演示動態代理的用法,所以不必過於糾結,我們主要看用法!

假設有一個類:貓,實現接口:動物。動物接口有三個方法~分別是:叫,睡,吃。
(之前看到一段代碼,發現原來類名和方法名可以用中文來寫,所以這裏也用一下試試~)
public interface 動物 {
public void 睡覺();
public void 吃();
public void 叫();
}

public class 貓 implements 動物 {

	@Override
	public void 睡覺() {
		System.out.println("呼呼呼地睡覺");
	}

	@Override
	public void 吃() {
		System.out.println("呵呵呵呵地吃");

	}

	@Override
	public void 叫() {
		System.out.println("喵喵喵地叫");
	}

}

public class Main {

	public static void main(String[] args) {
		貓 cat = new 貓();
		cat.叫();
		cat.吃();
		cat.睡覺();

	}

}
運行結果:



貓實現”叫“方法的叫聲筆者不太滿意,因此筆者對此進行改造~我們可以使用動態代理這樣改:

主要用Proxy這個類來實現,首先調用java.lang.reflect.Proxy的newProxyInstance方法~
動物  a=(動物) Proxy.newProxyInstance(貓.class.getClassLoader(), 貓.class.getInterfaces(), new InvocationHandler());
可以看到newProxyInstance接受三個參數,第一個是被代理的類加載器,第二個是被代理的類所實現過的接口數組(因爲一個類可能實現了不止一個接口),第三個我們暫時不知道是什麼來的,我們先new它出來~~我們再說說newProxyInstance的返回值,這個返回值是一個Object,但是我們知道貓實現了動物接口,所以我們可以用動物來接受這個返回值,同時耶因爲返回值是一個object,所以在使用動物接收這個返回值的之前需要經過強轉。

好了,寫完這些之後發覺這句話還有錯,一看發現InvocationHandler是一個接口,接口是不可以直接new的~因此我們再它後面加一對大括號~機智地把它變成一個匿名內部類~

動物  a=(動物) Proxy.newProxyInstance(貓.class.getClassLoader(), 貓.class.getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
				
					return null;
			
				}
				
			}
		});

這裏唯一未實現的方法就是invoke了,需要說一下的是:現在a對象可以用來調用吃,睡,叫這三個方法啦。但是a調用這三個方法的時候最終底層還是會調用剛纔所實現的方法invoke~
爲什麼會這樣?不急我們再看看invoke的參數,傳入三個參數,一個參數是調用該方法的對象,第二個參數是a調用動物藉口的方法~,第三個是a調用動物接口方法時所傳進的參數~

根據這三個參數我們可以判斷出a在外部到底要調用什麼方法,在剛纔實現的invoke方法裏面進行簡單的判斷再做出相應的操作。
再回到我們的案例,我們最終的目的是改造貓的叫聲:

public static void main(String[] args) {
		貓 cat=new 貓();
	
		
		動物  a=(動物) Proxy.newProxyInstance(貓.class.getClassLoader(), 貓.class.getInterfaces(), new InvocationHandler() {
			
			@Override
			public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
				if("叫".equals(arg1.getName())){
					System.out.println("汪汪汪叫");
					return null;
				}else{
					return arg1.invoke(cat, arg2);
				}
				
			}
		});
		a.叫();
		
	}

在invoke方法裏面進行判斷,如果方法名等於“叫”的話,則進行我想要的操作:輸出”汪汪汪叫“。否則的話就調用貓原本的方法~
arg1.invoke(cat, arg2);

最後再說一下這個返回值,這個返回值是a調用實現接口的方法的返回值,如果沒有返回值則直接返回null。

最後運行一下,就發現叫聲已經變成狗叫啦~















發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章