Java 动态代理机制简单理解
Spring有两个核心的思想,一个是IOC,另一个就是AOP,而这个AOP就是建立在JAVA动态代理基础上的,下面先用一个简单的示例来说明动态代理的用法,然后简单叙述动态代理实现的原理。
一、示例
实现代理有四个步骤
1、创建一个接口
public interface Login { void validate(); void login(); }
2、编写这个接口的实现类,这个类里面只包含业务方法
public class LoginImpl implements Login{ @Override public void validate() { // TODO Auto-generated method stub System.out.println("validate"); } @Override public void login() { // TODO Auto-generated method stub System.out.println("login"); } }
3、创建代理类,实现InvacationHandler,通过这个代理类可以动态创建代理对象,并且完成相关业务。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class LoginProxy implements InvocationHandler { private Object proxyObj; public LoginProxy(Object obj) { this.proxyObj=obj; } public static Object bind(Object obj) { Class clz=obj.getClass(); return Proxy.newProxyInstance(clz.getClassLoader(), clz.getInterfaces(), new LoginProxy(obj)); } @Override public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable { // TODO Auto-generated method stub login(arg1); Object res=arg1.invoke(proxyObj, arg2); logout(arg1); return res; } private void login(Method method) { System.out.println("before "+method.getName()); } private void logout(Method method) { System.out.println("after "+method.getName()); } }
4、创建测试类。
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import sun.misc.ProxyGenerator; public class Main { public static void main(String[] args) { //生成一个实现Login接口的类的字节码数组,测试用 byte[] clazzFile = ProxyGenerator.generateProxyClass("$Proxy11", LoginImpl.class.getInterfaces()); OutputStream os=null; try { //生成的class文件放在bin文件夹里面 os=new FileOutputStream(Main.class.getClassLoader().getResource("").getPath()+"/$Proxy11.class"); os.write(clazzFile); os.flush(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { os.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //测试动态代理,生成一个代理对象,这个代理对象实现了LoginImpl实现的接口 Login login=(Login)LoginProxy.bind(new LoginImpl()); //代理类调用方法 login.validate(); login.login(); } }
输出
before validate
validate
after validate
before login
login
after login
二、原理简述
代理对象怎么获得的呢?这得从Proxy类的newProxyInstance开始,
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 = getProxyClass0(loader, interfaces); // stack walk magic: do not refactor /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; SecurityManager sm = System.getSecurityManager(); if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }
具体的细节可以不去看他,这里只要看红色的部分,显示获取了一个代理类,这个代理类里面有个参数是接口,正是需要代理的对象的所实现的接口,最后返回一个这个类的实例,这个类怎么得到的呢? //弱引用hashmap key是classloader,value是Map,缓存对应类加载器的接口数组 object存放的是生成的类的弱引用
private static Map<ClassLoader, Map<List<String>, Object>> loaderToCache= new WeakHashMap<>(); //存放生成的代理类 private static Map<Class<?>, Void> proxyClasses = Collections.synchronizedMap(new WeakHashMap<Class<?>, Void>());
private static Class<?> getProxyClass0(ClassLoader loader , Class<?>... interfaces) { ....................此处省略一对判断 ....................此处省略得到接口名数组 //对象的接口数组 List<String> key = Arrays.asList(interfaceNames); ....................又是一堆验证 //通过上面的接口数组生成一个类 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); try { proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } // add to set of all generated proxy classes, for isProxyClass proxyClasses.put(proxyClass, null); }
finally { synchronized (cache) { //这里的cache是Map<List<String>,Object>的对象,最上面的那个 if (proxyClass != null) { cache.put(key, new WeakReference<Class<?>>(proxyClass)); } else { cache.remove(key); } cache.notifyAll(); } } //返回类 return proxyClass;
在这函数里面,先获取这个对象的接口数组,再在缓存中去搜索看看有没有这个类,具体过程就是先通过classLoader来找Map,根据这个接口链表来找存放类的弱引用,假如没有的话就重新生成,至于怎么生成这个类的字节码,太复杂了,这里就省略了。
那么为什么这个实现了接口的代理对象调用的是login方法,而结果确实invoke被调用了呢?我们看一下生成的$Proxy1这个类,把它反编译一下就能够看到。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class $Proxy11 extends Proxy implements Login { private static Method m1; private static Method m4; private static Method m3; private static Method m0; private static Method m2; public $Proxy11(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { try { return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void validate() { try { this.h.invoke(this, m4, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final void login() { try { this.h.invoke(this, m3, null); return; } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final int hashCode() { try { return ((Integer)this.h.invoke(this, m0, null)).intValue(); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } public final String toString() { try { return (String)this.h.invoke(this, m2, null); } catch (Error|RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("Login").getMethod("validate", new Class[0]); m3 = Class.forName("Login").getMethod("login", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
而Proxy类里面
protected InvocationHandler h; protected Proxy(InvocationHandler h) { doNewInstanceCheck(); this.h = h; }
到此真正的幕后已经出来了,就是生成了一个类,然后实例化一个对象,并把InvocationHandler接口的实现类的对象作为参数传进去,那么这个对象在调用login或者validate方法时,就会调用这个InvocationHandler接口的实现类的对象的invoke方法。