Java 動態代理作用是什麼 ==AOP?
阿新 • • 發佈:2019-02-06
之前雖然會用JDK的動態代理,但是有些問題卻一直沒有搞明白。比如說:InvocationHandler的invoke方法是由誰來呼叫的,代理物件是怎麼生成的,直到前幾個星期才把這些問題全部搞明白了。
廢話不多說了,先來看一下JDK的動態是怎麼用的。
Java程式碼
執行結果如下:
------------------before------------------
--------------------add---------------
-------------------after------------------
用起來是很簡單吧,其實這裡基本上就是AOP的一個簡單實現了,在目標物件的方法執行之前和執行之後進行了增強。Spring的AOP實現其實也是用了Proxy和InvocationHandler這兩個東西的。
用起來是比較簡單,但是如果能知道它背後做了些什麼手腳,那就更好不過了。首先來看一下JDK是怎樣生成代理物件的。既然生成代理物件是用的Proxy類的靜態方newProxyInstance,那麼我們就去它的原始碼裡看一下它到底都做了些什麼?
Java程式碼
我們再進去getProxyClass方法看一下
Java程式碼
廢話不多說了,先來看一下JDK的動態是怎麼用的。
Java程式碼
- package dynamic.proxy;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
-
/**
- * 實現自己的InvocationHandler
- * @author zyb
- * @since 2012-8-9
- *
- */
- public class MyInvocationHandler implements InvocationHandler {
- // 目標物件
- private Object target;
- /**
- * 構造方法
- * @param target 目標物件
- */
-
public MyInvocationHandler(Object target) {
- super();
- this.target = target;
- }
- /**
- * 執行目標物件的方法
- */
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- // 在目標物件的方法執行之前簡單的列印一下
-
System.out.println("------------------before------------------");
- // 執行目標物件的方法
- Object result = method.invoke(target, args);
- // 在目標物件的方法執行之後簡單的列印一下
- System.out.println("-------------------after------------------");
- return result;
- }
- /**
- * 獲取目標物件的代理物件
- * @return 代理物件
- */
- public Object getProxy() {
- return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
- target.getClass().getInterfaces(), this);
- }
- }
- package dynamic.proxy;
- /**
- * 目標物件實現的介面,用JDK來生成代理物件一定要實現一個介面
- * @author zyb
- * @since 2012-8-9
- *
- */
- public interface UserService {
- /**
- * 目標方法
- */
- public abstract void add();
- }
- package dynamic.proxy;
- /**
- * 目標物件
- * @author zyb
- * @since 2012-8-9
- *
- */
- public class UserServiceImpl implements UserService {
- /* (non-Javadoc)
- * @see dynamic.proxy.UserService#add()
- */
- public void add() {
- System.out.println("--------------------add---------------");
- }
- }
- package dynamic.proxy;
- import org.junit.Test;
- /**
- * 動態代理測試類
- * @author zyb
- * @since 2012-8-9
- *
- */
- public class ProxyTest {
- @Test
- public void testProxy() throws Throwable {
- // 例項化目標物件
- UserService userService = new UserServiceImpl();
- // 例項化InvocationHandler
- MyInvocationHandler invocationHandler = new MyInvocationHandler(userService);
- // 根據目標物件生成代理物件
- UserService proxy = (UserService) invocationHandler.getProxy();
- // 呼叫代理物件的方法
- proxy.add();
- }
- }
執行結果如下:
------------------before------------------
--------------------add---------------
-------------------after------------------
用起來是很簡單吧,其實這裡基本上就是AOP的一個簡單實現了,在目標物件的方法執行之前和執行之後進行了增強。Spring的AOP實現其實也是用了Proxy和InvocationHandler這兩個東西的。
用起來是比較簡單,但是如果能知道它背後做了些什麼手腳,那就更好不過了。首先來看一下JDK是怎樣生成代理物件的。既然生成代理物件是用的Proxy類的靜態方newProxyInstance,那麼我們就去它的原始碼裡看一下它到底都做了些什麼?
Java程式碼
- /**
- * loader:類載入器
- * interfaces:目標物件實現的介面
- * h:InvocationHandler的實現類
- */
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- if (h == null) {
- throw new NullPointerException();
- }
- /*
- * Look up or generate the designated proxy class.
- */
- Class cl = getProxyClass(loader, interfaces);
- /*
- * Invoke its constructor with the designated invocation handler.
- */
- try {
- // 呼叫代理物件的構造方法(也就是$Proxy0(InvocationHandler h))
- Constructor cons = cl.getConstructor(constructorParams);
- // 生成代理類的例項並把MyInvocationHandler的例項傳給它的構造方法
- return (Object) cons.newInstance(new Object[] { h });
- } catch (NoSuchMethodException e) {
- throw new InternalError(e.toString());
- } catch (IllegalAccessException e) {
- throw new InternalError(e.toString());
- } catch (InstantiationException e) {
- throw new InternalError(e.toString());
- } catch (InvocationTargetException e) {
- throw new InternalError(e.toString());
- }
- }
我們再進去getProxyClass方法看一下
Java程式碼
- public static Class<?> getProxyClass(ClassLoader loader,
- Class<?>... interfaces)
- throws IllegalArgumentException
- {
- // 如果目標類實現的介面數大於65535個則丟擲異常(我XX,誰會寫這麼NB的程式碼啊?)
- if (interfaces.length > 65535) {
- throw new IllegalArgumentException("interface limit exceeded");
- }
- // 宣告代理物件所代表的Class物件(有點拗口)
- Class proxyClass = null;
- String[] interfaceNames = new String[interfaces.length];
- Set interfaceSet = new HashSet(); // for detecting duplicates
- // 遍歷目標類所實現的介面
- for (int i = 0; i < interfaces.length; i++) {
- // 拿到目標類實現的介面的名稱
- String interfaceName = interfaces[i].getName();
- Class interfaceClass = null;
- try {
- // 載入目標類實現的介面到記憶體中
- interfaceClass = Class.forName(interfaceName, false, loader);
- } catch (ClassNotFoundException e) {
- }
- if (interfaceClass != interfaces[i]) {
- throw new IllegalArgumentException(
- interfaces[i] + " is not visible from class loader");
- }
- // 中間省略了一些無關緊要的程式碼 .......
- // 把目標類實現的介面代表的Class物件放到Set中
- interfaceSet.add(interfaceClass);
- interfaceNames[i] = interfaceName;
- }
- // 把目標類實現的介面名稱作為快取(Map)中的key
- Object key = Arrays.asList(interfaceNames);
- Map cache;
- synchronized (loaderToCache) {
- // 從快取中獲取cache
- cache = (Map) loaderToCache.get(loader);
- if (cache == null) {
- // 如果獲取不到,則新建地個HashMap例項
- cache = new HashMap();
- // 把HashMap例項和當前載入器放到快取中
- loaderToCache.put(loader, cache);
- }
- }
- synchronized (cache) {
- do {
- // 根據介面的名稱從快取中獲取物件
- Object value = cache.get(key);
- if (value instanceof Reference) {
- proxyClass = (Class) ((Reference) value).get();
- }
- if (proxyClass != null) {
- // 如果代理物件的Class例項已經存在,則直接返回 <