JDK、CGLIB、Spring三种实现代理的区别(一)JDK Proxy 解析基于Java 8
2017-12-20 14:22
881 查看
Java中从1.3中引入Proxy,实现接口的动态代理。JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。本文从简单例子入手,通过分析源码看看其内部实现原理,使用的是JDK 1.8。
通过动态代理实现对接口中方法调用前后进行拦截处理
创建要代理的接口,以及一个实现类。
简单实现类EnglishService
定义自己的InvocationHandler
编写测试类
运行结果:
成功实现对接口方法的前后拦截。JDK内部究竟怎么实现代理的?什么时候调用MyInvocationHandler 的invoke方法?下面分析原理:
看代理类获取时,关键代码为Proxy.newProxyInstance,源码:
查看getProxyClass0方法:
这里只是用WeakCache做了proxy classes缓存:
通过KeyFactory和ProxyClassFactory作为WeakCache的键和值,它们都是Proxy的内部静态类,都实现了BiFunction接口。继续查看WeakCache的get方法可看到,proxy class的生成最终调用了ProxyClassFactory的apply方法。关于WeakCache实现缓存的代码这里不多做描述。下面重点看ProxyClassFactory的定义和appy方法的实现:
调用ProxyGenerator.generateProxyClass生成proxy class, ProxyGenerator在sun.misc包中,未提供源码,我们通过反编译可以看到:
下面是generateClassFile方法:
看到这,我们可以很清晰的知道Proxy class是在什么时候生成的,以及这个class的大概面目(有Object的基本hashCode、equals、toString方法;有构造方法,有接口的实现方法)。我们回到generateProxyClass方法,看到生成后处理(上面有代码,不做重复):对saveGeneratedFiles进行判断,为真的话进行AccessController.doPrivileged(new 1(arg, arg3)),查看资料是将生成的proxy class文件存在硬盘,未测试,不做说明。其中saveGeneratedFiles是一个系统变量,可以通过 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);进行设置,默认false。
知道调用原理,我们可以使用ProxyGenerator.generateProxyClass生成文件看看具体内容,编写测试方法:
最后在指定路径下生成$Proxy11.class文件,反编译查看内容:
java.lang.reflect中Proxy有以下代码:
现在再看生成的$Proxy11.class反编译的java代码中,调用sayHello时会this.h.invoke(this, m3, null);调用Proxy类中InvocationHandler的invoke方法,所以,文章开头我们的小例子中:
proxyService.sayHello()的结果是走的MyInvocationHandler的invoke方法,也就一目了然。
环境变量使得proxy class生成文件存在disk中,我未测试通过,对这一块,持保留意见
参考
特别感谢以下网友博客提供的帮助:
http://rejoy.iteye.com/blog/1627405
http://blog.csdn.net/mhmyqn/article/details/48474815
简单运用
使用动态代理主要涉及接口InvocationHandler,以及Proxy类。通过动态代理实现对接口中方法调用前后进行拦截处理
创建要代理的接口,以及一个实现类。
package com.proxy; /** * * 要代理的接口<br/> * * @version */ public interface PeopleService { public void sayHello(); public void printName(String name); }
简单实现类EnglishService
package com.proxy; /** * * 注释内容<br/> * * * @version */ public class EnglishService implements PeopleService { @Override public void sayHello() { System.out.println("Hi~"); } @Override public void printName(String name) { System.err.println("Your name:" + name); } }
定义自己的InvocationHandler
package com.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * 注释内容<br/> * * @version */ public class MyInvocationHandler implements InvocationHandler { //目标对象 private Object target; public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //调用前打印 System.err.println("------begin----------"); method.invoke(target, args); System.err.println("------end----------"); return null; } }
编写测试类
package com.test; import java.lang.reflect.Proxy; import org.testng.annotations.Test; import com.proxy.EnglishService; import com.proxy.MyInvocationHandler; import com.proxy.PeopleService; public class MyTest { @Test public void proxyTest() { MyInvocationHandler handler = new MyInvocationHandler(new EnglishService()); PeopleService proxyService = (PeopleService) Proxy.newProxyInstance(MyTest.class.getClassLoader(), new Class[] { PeopleService.class }, handler); proxyService.sayHello(); } }
运行结果:
成功实现对接口方法的前后拦截。JDK内部究竟怎么实现代理的?什么时候调用MyInvocationHandler 的invoke方法?下面分析原理:
看代理类获取时,关键代码为Proxy.newProxyInstance,源码:
@CallerSensitive 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); /* * 通过传入的InvocationHander调用其构造器获取接口实例 */ 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; } }); } 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方法:
//生成一个代理类,在调用该方法前,必须调用checkProxyAccess校验权限 private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //代理接口数限制 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } //如果由给定的加载器实现给定的接口定义的代理类存在,返回缓存;否则,它将通过ProxyClassFactory创建代理类 return proxyClassCache.get(loader, interfaces); }
这里只是用WeakCache做了proxy classes缓存:
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
通过KeyFactory和ProxyClassFactory作为WeakCache的键和值,它们都是Proxy的内部静态类,都实现了BiFunction接口。继续查看WeakCache的get方法可看到,proxy class的生成最终调用了ProxyClassFactory的apply方法。关于WeakCache实现缓存的代码这里不多做描述。下面重点看ProxyClassFactory的定义和appy方法的实现:
//通过ClassLoader和接口列表,生成和定义一个proxy class private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 所有代理类的命名前缀 private static final String proxyClassNamePrefix = "$Proxy"; // 一个唯一的number作为proxy class的名称标识 private static final AtomicLong nextUniqueNumber = new AtomicLong(); //proxy class生成方法 @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { //确认类加载器解析了这个名字的接口到相同的Class对象。 Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } //确认代理的Class是接口,从这可看出JDK动态代理的劣势 if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } //接口重复校验 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // 定义proxy class 所在的包 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; //记录所有non-public的 proxy interfaces都在同一个package中 for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // 如果没有non-public的interfaces,默认包为com.sun.proxy proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); //最终大概名字为:com.sun.proxy.$Proxy1 String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 生成特殊的proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }
调用ProxyGenerator.generateProxyClass生成proxy class, ProxyGenerator在sun.misc包中,未提供源码,我们通过反编译可以看到:
public static byte[] generateProxyClass(String arg, Class<?>[] arg0, int arg1) { ProxyGenerator arg2 = new ProxyGenerator(arg, arg0, arg1); byte[] arg3 = arg2.generateClassFile(); if(saveGeneratedFiles) { AccessController.doPrivileged(new 1(arg, arg3)); } return arg3; } //私有构造器 private ProxyGenerator(String arg0, Class<?>[] arg1, int arg2) { this.className = arg0; this.interfaces = arg1; this.accessFlags = arg2; }
下面是generateClassFile方法:
private byte[] generateClassFile() { //添加从 Object基类中继承的方法 this.addProxyMethod(hashCodeMethod, Object.class); this.addProxyMethod(equalsMethod, Object.class); this.addProxyMethod(toStringMethod, Object.class); //添加接口中的方法实现 Class[] arg0 = this.interfaces; int arg1 = arg0.length; int arg2; Class arg3; for (arg2 = 0; arg2 < arg1; ++arg2) { arg3 = arg0[arg2]; Method[] arg4 = arg3.getMethods(); int arg5 = arg4.length; for (int arg6 = 0; arg6 < arg5; ++arg6) { Method arg7 = arg4[arg6]; this.addProxyMethod(arg7, arg3); } } Iterator arg10 = this.proxyMethods.values().iterator(); List arg11; while (arg10.hasNext()) { arg11 = (List) arg10.next(); checkReturnTypes(arg11); } Iterator arg14; try { //构造方法 this.methods.add(this.generateConstructor()); arg10 = this.proxyMethods.values().iterator(); while (arg10.hasNext()) { arg11 = (List) arg10.next(); arg14 = arg11.iterator(); while (arg14.hasNext()) { ProxyMethod arg15 = (ProxyMethod) arg14.next(); this.fields.add(new FieldInfo(this, arg15.methodFieldName, "Ljava/lang/reflect/Method;", 10)); this.methods.add(ProxyMethod.access$100(arg15)); } } this.methods.add(this.generateStaticInitializer()); } catch (IOException arg9) { throw new InternalError("unexpected I/O Exception", arg9); } if (this.methods.size() > '') { throw new IllegalArgumentException("method limit exceeded"); } else if (this.fields.size() > '') { throw new IllegalArgumentException("field limit exceeded"); } else { this.cp.getClass(dotToSlash(this.className)); this.cp.getClass("java/lang/reflect/Proxy"); arg0 = this.interfaces; arg1 = arg0.length; for (arg2 = 0; arg2 < arg1; ++arg2) { arg3 = arg0[arg2]; this.cp.getClass(dotToSlash(arg3.getName())); } this.cp.setReadOnly(); //生成文件 ByteArrayOutputStream arg12 = new ByteArrayOutputStream(); DataOutputStream arg13 = new DataOutputStream(arg12); try { arg13.writeInt(-889275714); arg13.writeShort(0); arg13.writeShort(49); this.cp.write(arg13); arg13.writeShort(this.accessFlags); arg13.writeShort(this.cp.getClass(dotToSlash(this.className))); arg13.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); arg13.writeShort(this.interfaces.length); Class[] arg16 = this.interfaces; int arg17 = arg16.length; for (int arg18 = 0; arg18 < arg17; ++arg18) { Class arg21 = arg16[arg18]; arg13.writeShort(this.cp.getClass(dotToSlash(arg21.getName()))); } arg13.writeShort(this.fields.size()); arg14 = this.fields.iterator(); while (arg14.hasNext()) { FieldInfo arg19 = (FieldInfo) arg14.next(); arg19.write(arg13); } arg13.writeShort(this.methods.size()); arg14 = this.methods.iterator(); while (arg14.hasNext()) { MethodInfo arg20 = (MethodInfo) arg14.next(); arg20.write(arg13); } arg13.writeShort(0); //返回字节流 return arg12.toByteArray(); } catch (IOException arg8) { throw new InternalError("unexpected I/O Exception", arg8); } } }
看到这,我们可以很清晰的知道Proxy class是在什么时候生成的,以及这个class的大概面目(有Object的基本hashCode、equals、toString方法;有构造方法,有接口的实现方法)。我们回到generateProxyClass方法,看到生成后处理(上面有代码,不做重复):对saveGeneratedFiles进行判断,为真的话进行AccessController.doPrivileged(new 1(arg, arg3)),查看资料是将生成的proxy class文件存在硬盘,未测试,不做说明。其中saveGeneratedFiles是一个系统变量,可以通过 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);进行设置,默认false。
private static final boolean saveGeneratedFiles = ((Boolean) AccessController .doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
知道调用原理,我们可以使用ProxyGenerator.generateProxyClass生成文件看看具体内容,编写测试方法:
public static void main(String[] args) { String fileName = "$Proxy11"; // 获取代理类的字节码 byte[] classFile = ProxyGenerator.generateProxyClass(fileName, EnglishService.class.getInterfaces()); FileOutputStream out = null; String outFilePath = "yourpath" + fileName + ".class"; try { out = new FileOutputStream(outFilePath); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } }
最后在指定路径下生成$Proxy11.class文件,反编译查看内容:
import com.proxy.api.PeopleService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; /** * 生成的类继承了Proxy,实现了要代理的接口PeopleService * */ public final class $Proxy11 extends Proxy implements PeopleService { private static Method m1; private static Method m3; private static Method m2; private static Method m4; private static Method m0; public $proxy11(InvocationHandler paramInvocationHandler) { //调用基类Proxy的构造器 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 sayHello() { try { this.h.invoke(this, m3, null); return; } 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); } } public final void printName(String paramString) { try { this.h.invoke(this, m4, new Object[] { paramString }); 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); } } static { try { //利用反射生成5个方法,包括Object中的equals、toString、hashCode以及PeopleService中的sayHello和printName m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.proxy.api.PeopleService").getMethod("sayHello", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m4 = Class.forName("com.proxy.api.PeopleService").getMethod("printName", new Class[] { Class.forName("java.lang.String") }); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); return; } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
java.lang.reflect中Proxy有以下代码:
/** * the invocation handler for this proxy instance. * @serial */ //直接子类可使用 protected InvocationHandler h; /** * Constructs a new {@code Proxy} instance from a subclass * (typically, a dynamic proxy class) with the specified value * for its invocation handler. * * @param h the invocation handler for this proxy instance * * @throws NullPointerException if the given invocation handler, {@code h}, * is {@code null}. */ //直接子类可调用 protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; }
现在再看生成的$Proxy11.class反编译的java代码中,调用sayHello时会this.h.invoke(this, m3, null);调用Proxy类中InvocationHandler的invoke方法,所以,文章开头我们的小例子中:
PeopleService proxyService = (PeopleService) Proxy.newProxyInstance(MyTest.class.getClassLoader(), new Class[] { PeopleService.class }, handler); proxyService.sayHello();
proxyService.sayHello()的结果是走的MyInvocationHandler的invoke方法,也就一目了然。
总结
因为生成的proxy class中,继承了Proxy类,实现了需要代理的接口,而Java中是单继承,多实现的处理方式,也就解释了JDK动态代理中只能代理接口的原因了。注意
关于通过设置 System.setProperty(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);环境变量使得proxy class生成文件存在disk中,我未测试通过,对这一块,持保留意见
参考
特别感谢以下网友博客提供的帮助:
http://rejoy.iteye.com/blog/1627405
http://blog.csdn.net/mhmyqn/article/details/48474815
相关文章推荐
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务
- Spring(十)通过动态代理(JDK的Proxy)和cglib实现AOP技术
- 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务
- JDK动态代理和CGLIB动态代理,实现Spring注解管理事务区别。
- 三种实现动态代理方式(jdk、cglib、javaassist)
- Java动态代理模式jdk和cglib的2种实现以及二者的区别(AOP面向切面的前奏)
- 【java】 浅谈动态数据源切换和Spring两种代理JDK和CGLIB的区别