JDK的动态代理 动态代理,Spring框架经常使用到的一个技术,Spring有使用两种动态代理技术,这次介绍的是JDK提供的动态代理。
以下基于JDK1.8进行说明。
使用 JDK动态代理是基于接口实现的,使用的核心围绕着java.lang.reflect.Proxy
类来使用的。
java.lang.reflect.Proxy#newProxyInstance
方法可以创建出代理对象,示例代码如下:
Main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Main { public static void main (String[] args) { IHumanity iHumanity = (IHumanity)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class []{IHumanity.class}, new InvocationHandler () { private final Student student = new Student (); @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始人类行为:" + method.getName()); return method.invoke(student, args); } }); iHumanity.say(); iHumanity.eat(); } }
IHumanity
1 2 3 4 public interface IHumanity { void say () ; void eat () ; }
Student
1 2 3 4 5 6 7 8 9 10 11 public class Student implements IHumanity { @Override public void say () { System.out.println("我是学生" ); } @Override public void eat () { System.out.println("正在进食" ); } }
探索 java.lang.reflect.Proxy
类不提供构造方法,但有protected
修饰的构造方法可以提供继承,下面看看它提供的静态类方法。
newProxyInstance newProxyInstance方法有三个参数,接下来分别介绍参数的意义。
ClassLoader loader 要求提供一个类加载器,该加载器要能够加载到被代理接口。下面可以给出一个反例:
1 2 3 4 5 6 7 Proxy.newProxyInstance(ZipInfo.class.getClassLoader(), new Class []{IHumanity.class}, new InvocationHandler () { private final Student student = new Student (); @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(student, args); } });
com.sun.nio.zipfs.ZipInfo
是位于JDK扩展目录下的jar包,由扩展类加载器加载的,一般情况下它是不会加载到我们编写的代码类,所以上面所示的反例将会抛出异常Exception in thread "main" java.lang.IllegalArgumentException: interface com.gugu.service.IHumanity is not visible from class loader
。
Class<?>[] interfaces 要求提供被代理的接口类数组,这个参数与第三个参数关联,下面可以给出一个反例:
1 2 3 4 5 6 7 8 IMale iMale = (IMale)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class []{IMale.class}, new InvocationHandler () { private final Student student = new Student (); @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(student, args); } }); iMale.readme();
Student类是没有实现IMale接口的,执行iMale.readme();
将会抛出异常Exception in thread "main" java.lang.IllegalArgumentException: object is not an instance of declaring class
。
InvocationHandler h 要求提供实现代理逻辑的接口,这个参数将在调用代理对象方法时调用。这个接口有三个参数可以看看:
Object proxy 表示代理对象,这个参数的意义要看场景,下面举一个例子说明:
IProfession
1 2 3 public interface IProfession { IProfession action () ; }
Student
1 2 3 4 5 6 7 public class Student implements IProfession { @Override public IProfession action () { System.out.println("作为学生开始学习" ); return this ; } }
Main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Main { public static void main (String[] args) { IProfession iProfession = (IProfession)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class []{IProfession.class}, new InvocationHandler () { private final Student student = new Student (); @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("行动前先摸下鱼" ); method.invoke(student, args); return proxy; } }); IProfession iProfession1 = iProfession.action(); iProfession1.action(); } }
如果注释Main的第8、9行代码打开第10行代码注释的话,执行到14行代码时将绕过代理对象,学生就不会在行动前摸鱼了。
Method method 方法实例对象,表示要执行的方法。
Object[] args 方法参数,表示要执行的方法所需参数。
getProxyClass 这个方法相比于newProxyInstance缺少了InvocationHandler参数,也缺少了一些验证相关的逻辑,缺少实例化步骤,提供了可自定义替换的步骤,缺少的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 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); }
isProxyClass 该方法可以判断是否是代理类,示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Main { public static void main (String[] args) { Object newProxyInstance = Proxy.newProxyInstance(Main.class.getClassLoader(), new Class []{IHumanity.class, IProfession.class}, new InvocationHandler () { private final Student student = new Student (); @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理:" + method.getName()); return method.invoke(student, args); } }); IHumanity newProxyInstance1 = (IHumanity) newProxyInstance; newProxyInstance1.say(); newProxyInstance1.eat(); IProfession newProxyInstance2 = (IProfession) newProxyInstance; newProxyInstance2.action(); System.out.println("(Proxy.isProxyClass(newProxyInstance2.getClass())) = " + (Proxy.isProxyClass(newProxyInstance2.getClass()))); } }
getInvocationHandler 看名字可以知道是获取代理对象的InvocationHandler,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class Main { private static class MyInvocationHandler implements InvocationHandler { private final Student student; public MyInvocationHandler (Student student) { this .student = student; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始代理:" + method.getName()); return method.invoke(student, args); } } public static void main (String[] args) { Student student = new Student (); MyInvocationHandler myInvocationHandler = new MyInvocationHandler (student); Object proxyInstance = Proxy.newProxyInstance(Main.class.getClassLoader(), new Class []{IHumanity.class, IProfession.class}, myInvocationHandler); System.out.println("(Proxy.getInvocationHandler(proxyInstance) == myInvocationHandler) = " + (Proxy.getInvocationHandler(proxyInstance) == myInvocationHandler)); } }