JDK动态代理

        简单的说,代理模式是在目标对象和访问对象之间增加了一层代理对象,所有的访问对象通过代理对象来实现对目标对象的调用。

        代理对象和目标对象实现同一个接口,由代理对象来调用目标对象,对于外部来说,代理对象就可以替代目标对象被调用。通过这种方式,代理对象中可以在正常的访问中增加额外的逻辑,比如缓存、权限控制、日志记录等。

        但是这种静态代理的模式需要增加额外的代理类的实现,Java 5开始引入了动态代理机制,实现了在运行时动态地创建出代理对象,这其实是一种方法调用的拦截,AOP就是利用了这种模式。


动态代理的使用例子 

package com.dynamic.jdk;

/**
* 目标类的接口定义
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public interface MyInterface {
   void doSomething();
}
package com.dynamic.jdk;

/**
* 目标类的具体实现
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public class MyInterfaceImpl implements MyInterface {

   @Override
   public void doSomething() {
       System.out.println("here is my real operation!");
   }
}
package com.dynamic.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
* 自定义的InvocationHandler
* 封装具体的调用过程
* <p/>
* Created by Vincent Tse on 12/2/15.
*/
public class MyInvocationHandler implements InvocationHandler {
   //target是真正执行方法的目标对象
   private Object target;

   public MyInvocationHandler(Object target) {
       super();
       this.target = target;
   }

   /**
    * 代理对象调用的方法
    * @param proxy
    * @param method
    * @param args
    * @return
    * @throws Throwable
    */
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("before target's operation!");
       Object result = method.invoke(target, args);
       System.out.println("after target's operation");
       return result;
   }
}
package com.dynamic.jdk;

import java.lang.reflect.Proxy;

/**
* 测试类
* <p/>
* Created by Vincent on 12/2/15.
*/
public class DynamicTest {

   public static void main(String[] args) {

       //生成目标对象
       MyInterface myInterface = new MyInterfaceImpl();

       //实例化invocationHandler对象,传入目标对象作为target
       MyInvocationHandler invocationHandler = new MyInvocationHandler(myInterface);

       //调用Proxy的方法生成代理对象
       MyInterface proxy = (MyInterface)Proxy.newProxyInstance(myInterface.getClass().getClassLoader(),
               new Class[]{MyInterface.class}, invocationHandler);

       //调用代理对象的方法
       proxy.doSomething();
   }
}

输出结果:

before target's operation!

here is my real operation!

after target's operation! 


使用起来很简单,接下来看源码分析其中的原理

从Proxy的newProxyInstance()方法开始,这个方法就是用来生成代理对象的,需要传入类加载器、实现的接口以及一个InvocationHandler对象。 

public static Object newProxyInstance(ClassLoader loader,
                                     Class<?>[] interfaces,
                                     InvocationHandler h)
   throws IllegalArgumentException
{
   Objects.requireNonNull(h);

   final Class<?>[] intfs = interfaces.clone();
   final SecurityManager sm = System.getSecurityManager();
   if (sm != null) {
       checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
   }

   //通过传入的类加载器和实现接口生成代理类
   Class<?> cl = getProxyClass0(loader, intfs);

   /*
    * Invoke its constructor with the designated invocation handler.
    */
   try {
       if (sm != null) {
           checkNewProxyPermission(Reflection.getCallerClass(), cl);
       }
                 //反射获取代理类的构造函数
       final Constructor<?> cons = cl.getConstructor(constructorParams);
       final InvocationHandler ih = h;
       if (!Modifier.isPublic(cl.getModifiers())) {
           AccessController.doPrivileged(new PrivilegedAction<Void>() {
               public Void run() {
                   cons.setAccessible(true);
                   return null;
               }
           });
       }
       //反射生成实例,将invocationHandler传入,之后调用invoke方法就靠它了
       return cons.newInstance(new Object[]{h});
   } catch (IllegalAccessException|InstantiationException e) {
       throw new InternalError(e.toString(), e);
   } catch (InvocationTargetException e) {
       Throwable t = e.getCause();
       if (t instanceof RuntimeException) {
           throw (RuntimeException) t;
       } else {
           throw new InternalError(t.toString(), t);
       }
   } catch (NoSuchMethodException e) {
       throw new InternalError(e.toString(), e);
   }
}



getProxyClass0()方法就是用来生成代理类的,首先检查实现接口数量,大于65535就抛异常,感觉应该不会有实现那么多接口的类吧。

private static Class<?> getProxyClass0(ClassLoader loader,
                                      Class<?>... interfaces) {
   if (interfaces.length > 65535) {
       throw new IllegalArgumentException("interface limit exceeded");
   }

      //从一个Cache中获取代理类,如果没有就重新生成
   return proxyClassCache.get(loader, interfaces);
}


proxyClassCache是一个静态的WeakCache,定义在Proxy类里。

WeakCache里有两个factory,一个是subKeyFactory, 是一个映射函数(key, parameter)->sub-key, 另一个是valueFactory,是一个映射函数(key, parameter)->value。WeakCache的get方法需要两个参数,一个是key,另一个是parameter。

具体WeakCache的用法与原理这里不再赘述,请参考源码。

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
   proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

这里传入的KeyFactory和ProxyClassFactory是Proxy类中定义的静态类,分别对应了WeakCache中的subKeyFactory和valueFactory,都实现了BiFunction接口,并实现了apply()方法。ProxyClassFactory的apply()方法里完成的就是生成代理类的逻辑,最关键的是

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
   proxyName, interfaces, accessFlags);

上面的方法用来生成代理类的字节码,这个代理类里会有接口的实现方法,在实现的方法中会调用InvocationHandler的invoke()方法,有兴趣的可以将生成的字节码写到本地,用反编译工具打开看一下。


那么ProxyClassFactory的apply()方法是在什么时候被调用的呢,回到WeakCache的get()方法

//生成subkey来获取对应的supplier
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
//一直循环,直到从supplier中获取到value为止
//如果没有supplier就生成factory并加入到缓存中(源码说这是一个install的过程)
while (true) {
   if (supplier != null) {
       V value = supplier.get();
       if (value != null) {
           return value;
       }
   }
   
   if (factory == null) {
       //Factory是内部类,实现了Supplier接口,实现了get()方法
       //实际上在get()方法中调用了valueFactory(也就是ProxyClassFactory)的apply()方法
       //可以debug进去看
       factory = new Factory(key, parameter, subKey, valuesMap);
   }

   if (supplier == null) {
       supplier = valuesMap.putIfAbsent(subKey, factory);
       if (supplier == null) {
           supplier = factory;
       }
   } else {
       if (valuesMap.replace(subKey, supplier, factory)) {
           supplier = factory;
       } else {
           supplier = valuesMap.get(subKey);
       }
   }
}

好辛苦,终于生成代理类了,回到newProxyInstance方法,有了代理类的Class对象,就可以通过反射生成实例(调用的是带InvocationHandler参数的构造函数),生成实例之后就可以用代理对象了。

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