1. 代理的分類:
靜態代理:每個代理類只能為一個接口服務
動態代理:可以通過一個代理類完成全部的代理功能(由JVM生成實現一系列接口的代理類,即:生成實現接口的類的代理)
2. 動態代理:
在Java中要想實現動態代理機制,需要 java.lang.reflect.InvocationHandler 接口和 java.lang.reflect.Proxy 類的支持
java.lang.reflect.InvocationHandler 接口的定義如下:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
Object proxy:被代理的對象
Method method:要調用的方法
Object[] args:方法調用時傳遞的參數
注意:並不是對象的所有方法均通過代理來完成,對如繼承自Object的方法,只將hashCode()、equals()、toString()轉交給InvocationHandler來處理。
可以通過Proxy中的getProxyClass()或newProxyInstance()方法獲取代理
其中newProxyInstance()方法定義如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
ClassLoader loader:類加載器
Class<?>[] interfaces:要實現的接口
InvocationHandler h:InvocationHandler子類的實例對象
下面來實現動態代理:
首先定義接口:
1 public interface Sourceable { 2 void function(); 3 }
然后定義實現類:
1 public class Source implements Sourceable { 2 3 @Override 4 public void function() { 5 System.out.println("function"); 6 } 7 }
定義工具類:
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 6 public class SourceableProxyUtil { 7 private Sourceable source; 8 9 public SourceableProxyUtil(Sourceable source) { 10 super(); 11 this.source = source; 12 } 13 14 public Sourceable getProxy() throws Exception { 15 Class<Sourceable> clazzSource = (Class<Sourceable>) Proxy 16 .getProxyClass(source.getClass().getClassLoader(), source 17 .getClass().getInterfaces()); 18 Constructor<Sourceable> constructor = clazzSource 19 .getConstructor(InvocationHandler.class); // 沒有無參構造函數 20 21 return constructor.newInstance(new InvocationHandler() { 22 23 @Override 24 public Object invoke(Object proxy, Method method, Object[] args) 25 throws Throwable { 26 System.out.println("before"); 27 Object retVal = method.invoke(source, args); 28 System.out.println("after"); 29 return retVal; 30 } 31 }); 32 } 33 }
但是,使用getProxyClass()方式過於復雜,改用newProxyInstance():
1 import java.lang.reflect.Constructor; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 6 public class SourceableProxyUtil { 7 private Sourceable source; 8 9 public SourceableProxyUtil(Sourceable source) { 10 super(); 11 this.source = source; 12 } 13 14 public Sourceable getProxy() throws Exception { 15 return (Sourceable)Proxy.newProxyInstance(source.getClass().getClassLoader(), 16 source.getClass().getInterfaces(), new InvocationHandler() { 17 18 @Override 19 public Object invoke(Object proxy, Method method, 20 Object[] args) throws Throwable { 21 System.out.println("before"); 22 Object retVal = method.invoke(source, args); 23 System.out.println("after"); 24 return retVal; 25 } 26 }); 27 } 28 }
測試代碼:
1 public class Main { 2 3 public static void main(String[] args) throws Exception { 4 Sourceable source = new Source(); 5 Sourceable proxy = new SourceableProxyUtil(source).getProxy(); 6 proxy.function(); 7 } 8 }
測試結果:
before
function
after
可以看出,代理類能夠正常使用。
但是上述方式只能用於生成Sourceable接口的代理類,可以進一步將其抽象得到通用的代理類生成工具:
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public final class ProxyUtil { 6 public static Object getProxy(final Object target) throws Exception { 7 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 8 target.getClass().getInterfaces(), new InvocationHandler() { 9 10 @Override 11 public Object invoke(Object proxy, Method method, 12 Object[] args) throws Throwable { 13 System.out.println("before"); 14 Object retVal = method.invoke(target, args); 15 System.out.println("after"); 16 return retVal; 17 } 18 }); 19 } 20 }
這樣,無論任何類型的對象都可以通過此種方法獲得相應的代理對象。此時,測試代碼如下:
1 public class Main { 2 3 public static void main(String[] args) throws Exception { 4 // TODO Auto-generated method stub 5 Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source()); 6 proxy.function(); 7 } 8 }
3. 抽取切面:
將上述代碼中的目標方法執行前后的操作進一步地抽象,將其抽取為切面。將切面代碼進行封裝得到通告。
定義通告接口:
1 public interface Advice { 2 void before(); 3 void after(); 4 }
改寫工具類:
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public final class ProxyUtil { 6 public static Object getProxy(final Object target, final Advice advice) throws Exception { 7 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 8 target.getClass().getInterfaces(), new InvocationHandler() { 9 10 @Override 11 public Object invoke(Object proxy, Method method, 12 Object[] args) throws Throwable { 13 advice.before(); 14 Object retVal = method.invoke(target, args); 15 advice.after(); 16 return retVal; 17 } 18 }); 19 } 20 }
此時的測試代碼為:
1 public class Main { 2 3 public static void main(String[] args) throws Exception { 4 Sourceable proxy = (Sourceable)ProxyUtil.getProxy(new Source(), new Advice() { 5 6 @Override 7 public void before() { 8 // TODO Auto-generated method stub 9 System.out.println("before"); 10 } 11 12 @Override 13 public void after() { 14 // TODO Auto-generated method stub 15 System.out.println("after"); 16 } 17 }); 18 proxy.function(); 19 } 20 }
此種方式更具有普遍適用性,無論對於任意類型都可以產生相對應的代理對象,同時可以自定義所要進行的操作(通過通告)。
在整個過程中需要手動寫的只有通告Advice
4. AOP編程:
AOP(Aspect-Oriented Programming)可以通過代理方式完成諸如:安全控制、事務管理、性能統計、日志記錄、錯誤處理...等功能。
在實際過程中,更多是通過getter/setter方式傳遞target與advice的:
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 5 public class ProxyFactoryBean { 6 7 private Object target; 8 private Advice advice; 9 10 11 public Object getTarget() { 12 return target; 13 } 14 15 16 public void setTarget(Object target) { 17 this.target = target; 18 } 19 20 21 public Advice getAdvice() { 22 return advice; 23 } 24 25 26 public void setAdvice(Advice advice) { 27 this.advice = advice; 28 } 29 30 31 public Object getProxy() { 32 33 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 34 target.getClass().getInterfaces(), new InvocationHandler() { 35 36 @Override 37 public Object invoke(Object proxy, Method method, 38 Object[] args) throws Throwable { 39 advice.before(); 40 Object retVal = method.invoke(target, args); 41 advice.after(); 42 return retVal; 43 } 44 }); 45 }; 46 }
通過AOP的方式可以在運行時動態地將代碼切入到類的指定方法、指定位置上。
Spring框架的核心即是IoC和AOP。