什麼是代理
代理通俗來說就是代替某人去做一件事情,可以舉個例子,假設有個老師今天生病了不能來上課,那麼是不是可以請一個代理老師來幫上課,實際意義上來說這兩個老師完成的東西是一樣的,都是給學生傳道受業解惑,但是,代理老師是不是能在原來上課的基礎上拓展更多“功能”,比如說代理老師去上課之前,先到隔壁班看了一眼中意的女老師有沒有來,上完課之後又去吃了自己最愛喫的螺螄粉~看到這兒有沒有感覺到跟一個東西很像,所以SpringAOP面向切面變成的原理就是這樣,當然實際實現方面會更加複雜。
代理分爲三個角色
- 接口
- 委託類
- 代理類
靜態代理
我們先說一下靜態代理,就用我們前面舉的例子,直接上代碼。
接口:
package com.staticproxy;
//我和代理老師都是老師,共同實現了老師接口
public interface Teacher {
void teach();
}
委託類:
package com.staticproxy;
//這個就是生病的老師
public class MathTeacher implements Teacher {
@Override
public void teach() {
System.out.println("傳道受業解惑40分鐘");
}
}
代理類:
package com.staticproxy;
//這個是代理老師
public class ProxyTeacher implements Teacher {
//通過構造器注入被代理對象(注入一個生病老師的對象)
private Teacher targetTeacher;
public ProxyTeacher(Teacher teacher){
this.targetTeacher=teacher;
}
//代理老師實現了接口也有teach方法
@Override
public void teach() {
System.out.println("上課之前先去隔壁班看美女老師");
//調用生病老師的teach方法
targetTeacher.teach();
System.out.println("上完課了,去喫個螺螄粉壓壓驚!");
}
}
Client的main方法:
package com.staticproxy;
public class Client {
public static void main(String[] args) {
Teacher teacher=new MathTeacher();
Teacher proxyteacher=new ProxyTeacher(teacher);
proxyteacher.teach();
}
}
結果:
靜態代理跟着走一遍應該很快就能理清,非常簡單。
下面我們繼續動態代理,動態代理需要用到反射機制。
JDK動態代理
接口:
package com.jdkproxy;
public interface Teacher {
void teach();
}
沒什麼好說的跟前面一樣的接口
委託類:
package com.jdkproxy;
public class MathTeacher implements Teacher {
@Override
public void teach() {
System.out.println("傳道受業解惑40分鐘");
}
}
重點來了
package com.jdkproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 注意動態代理這裏不需要再實現教師接口了
* 這樣才能體現動態的概念,往往在實際應用之中
* 我們是不會提前知道我們需要代理什麼對象的
*/
public class ProxyTeacher {
private Object targetteacher;
public ProxyTeacher(Teacher teacher){
this.targetteacher=teacher;
}
/**
* 這裏調用了Proxy的newProxyInstance方法獲得代理對象
* 參數1表示被代理對象的類加載器
* 參數2是被代理對象實現的接口,可以是數組(表示實現多個接口)
* 參數3是用匿名內部類實例化一個實現了InvocationHandler接口的對象
*/
public Object getProxyInstance(){
return Proxy.newProxyInstance(targetteacher.getClass().getClassLoader(), targetteacher.getClass().getInterfaces(),
new InvocationHandler() {
/**
* @param proxy 調用這個方法的代理實例
* @param method 要調用的方法
* @param args 方法調用時所需要的參數
* @return 方法調用的結果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("上課之前先去隔壁班看美女老師");
method.invoke(targetteacher,args);
System.out.println("上完課了,去喫個螺螄粉壓壓驚");
return null;
}
});
}
}
Client:
package com.jdkproxy;
public class Client {
public static void main(String[] args) {
Teacher teacher=new MathTeacher();
Teacher proxyTeacher=(Teacher)new ProxyTeacher(teacher).getProxyInstance();
proxyTeacher.teach();
}
}
結果:
總結:
當然SpringAop用的動態代理更爲複雜,還有cglib動態代理等其他技術這裏不做延伸,我們只需要知道,動態代理可以在完成原來對象實現的功能的基礎之上,繼續延伸和拓展,這也是SpringAop面向切面編程的一個重要思想,廣泛應用在比如日誌,異常處理,監控,事務管理等功能上。這就是面向切面的大致原理,可以這麼想,上課本來就是老師必做的流程,在該流程上橫向切入了看美女和喫東西兩個功能,而絲毫不影響功能的實現。