1. 程式人生 > >Java動態代理的基本用法

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。 最後執行一下,就發現叫聲已經變成狗叫啦~