其他更多java基础文章:java基础学习(目录)
经过上一节我们讲了Class对象和反射机制,这节就来讲一下反射机制在java中的主要应用——动态代理。在讲动态代理之前,会先讲一下代理模式和静态代理。
代理模式是常用的java设计模式,他的特征是代理类与委托类有 同样的接口
,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途,比如说Spring的AOP。代理模式结构图(图片来自《大话设计模式》):
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
根据上面代理模式的类图,来写一个简单的静态代理的例子。 首先定义一个商品接口
public interface ProductService { void addProduct(String productName); } 复制代码
然后定义一个商品的实现
public class ProductServiceImpl implements ProductService{ public void addProduct(String productName) { System.out.println("正在添加"+productName); } } 复制代码
此时我们定义一个商品销售员工,员工在添加商品的时候,会先检查产品是否合格,然后添加完后会报告一声。
public class ProductEmployee implements ProductService{ ProductServiceImpl productService; public ProductEmployee(ProductServiceImpl productService) { this.productService = productService; } @Override public void addProduct(String productName) { System.out.println("检查产品"+productName); productService.addProduct(productName); System.out.println("添加完成"); } public static void main(String[] args) { ProductServiceImpl impl = new ProductServiceImpl(); ProductEmployee productEmployee = new ProductEmployee (impl); productEmployee .addProduct("book"); } } /*output 检查产品book 正在添加book 添加完成 复制代码
代理模式最主要的就是有一个公共接口(ProductService ),一个具体的类(ProductServiceImpl ),一个代理类(ProductEmployee ),代理类持有具体类的实例,代为执行具体类实例方法。上面说到,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。最直白的就是在Spring中的面向切面编程(AOP),我们能在一个切点之前执行一些操作,在一个切点之后执行一些操作,这个切点就是一个个方法。这些方法所在类肯定就是被代理了,在代理过程中切入了一些其他操作。
静态代理有不少缺点,每需要一个代理类就要新增一个类,当代理类越来越多时,就会显得非常的庞大和臃肿。而且当需要修改代理类中的方法时,就需要每个代理类都修改方法代码,代理类越多,越繁琐。此时,就需要使用我们的 动态代理
。代理类在程序运行时创建的代理方式被成为动态代理。
我们上面静态代理的例子中,代理类(ProductEmployee)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
我们需要定义一个InvocationHandler的实现类
public class ProductInvocationHandler implements InvocationHandler { // 目标对象 private Object target; /** * 构造方法 * @param target 目标对象 */ public ProductInvocationHandler(Object target) { this.target = target; } /** * 执行目标对象的方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在目标对象的方法执行之前简单的打印一下 System.out.println("检查产品"); // 执行目标对象的方法 Object result = method.invoke(target, args); // 在目标对象的方法执行之后简单的打印一下 System.out.println("添加完成"); return result; } /** * 获取目标对象的代理对象 * @return 代理对象 */ public Object getProxy() { return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this); } } 复制代码
然后调用这个类生成代理对象,运行方法
public static void main(String[] args) { ProductService impl = new ProductServiceImpl(); ProductInvocationHandler productInvocationHandler = new ProductInvocationHandler(impl); ProductService proxy = (ProductService) productInvocationHandler.getProxy(); proxy.addProduct("book"); } /* output 检查产品 正在添加book 添加完成 复制代码
通过上面例子,我们可以看到动态代理机制中有两个重要的类和接口 InvocationHandler(接口)
和 Proxy(类)
,这两个类Proxy和接口InvocationHandler是我们实现动态代理的核心;
InvocationHandler
InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个代理的实例都会有一个关联的调用处理程序(InvocationHandler)。对待代理实例进行调用时,将对方法的调用进行编码并指派到它的调用处理器(InvocationHandler)的invoke方法。所以对代理对象实例方法的调用都是通过InvocationHandler中的invoke方法来完成的,而invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。
可能现在看起来会觉得有点绕,有点难懂,在学习完下面Proxy代理类的生成之后,再回来看会更好理解。
Proxy
Proxy类是用来创建一个代理对象的类。通过上面的简单例子,我们可以看到创建获取代理对象是通过Proxy的newProxyInstance方法得到的。那我们就以此为入口,深入了解一下Proxy类。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { //如果h为空将抛出异常 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); /* * 使用指定的调用处理程序(invocation handler)获取代理类的构造函数对象 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } //获取代理类的构造函数 final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; //假如代理类的构造函数是private的,就使用反射来set accessible 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); } } 复制代码
文中的注释已经很清楚的标明了这个方法的执行逻辑,其中有三行代码是这个方法的重点。
Class<?> cl = getProxyClass0(loader, intfs); final Constructor<?> cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[]{h});
其中的重中之重就是getProxy0这个方法了,因为后面两个就是反射的常用方法。
getProxyClass0(loader, intfs)
我们跟进去看一下这个方法的代码:
/** * 生成一个代理类,但是在调用本方法之前必须进行权限检查 */ private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //如果接口数量大于65535,抛出非法参数错误 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // 如果在缓存中有对应的代理类,那么直接返回 // 否则代理类将有 ProxyClassFactory 来创建 return proxyClassCache.get(loader, interfaces); } 复制代码
这里说的ProxyClassFactory是在Proxy类下的一个静态类
/** * 一个工厂函数,用于生成、定义和返回给定ClassLoader和接口数组的代理类。 * */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // 代理类的名字的前缀统一为“$Proxy” private static final String proxyClassNamePrefix = "$Proxy"; // 每个代理类前缀后面都会跟着一个唯一的编号,如$Proxy0、$Proxy1、$Proxy2 private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * 验证类加载器加载接口得到对象是否与由apply函数参数传入的对象相同 */ 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对象是不是接口 */ 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; // 声明代理类所在的package int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 记录一个非公共代理接口的包,以便在同一个包中定义代理类。同时验证所有非公共 * 代理接口都在同一个包中 */ 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) { // 如果全是公共代理接口,那么生成的代理类就在com.sun.proxy package下 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * 为代理类生成一个name package name + 前缀+唯一编号 * 如 com.sun.proxy.$Proxy0.class */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * 生成指定代理类的字节码文件 */ 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()); } } } 复制代码
从注释中可以得知,ProxyClassFactory是通过ProxyGenerator.generateProxyClass方法生成的代理类的字节码文件。由于这个jar包不公开,所以我们可以在百度Google上搜一下,或者通过OpenJDK来查看,两者的这部分代码基本一致。 OpenJDK ProxyGenerator.java
public static byte[] generateProxyClass(final String name, Class[] interfaces) { ProxyGenerator gen = new ProxyGenerator(name, interfaces); //生成获取字节码 final byte[] classFile = gen.generateClassFile(); //是否将二进制保存到本地文件中,saveGeneratedFiles由参数sun.misc.ProxyGenerator.saveGeneratedFiles决定。 if (saveGeneratedFiles) { java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() { public Void run() { try { FileOutputStream file = new FileOutputStream(dotToSlash(name) + ".class"); file.write(classFile); file.close(); return null; } catch (IOException e) { throw new InternalError( "I/O exception saving generated file: " + e); } } }); } return classFile; } 复制代码
我们继续跟进生成字节码的gen.generateClassFile()方法
private byte[] generateClassFile() { /* ============================================================ * Step 1: 为所有方法组装ProxyMethod对象,以为其生成代理调度代码 */ //生成proxy代理类的hashcode,equals,toString方法 addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); //添加各个接口的方法 //这就是为什么我们能够通过代理调用接口方法实现的原因 for (int i = 0; i < interfaces.length; i++) { Method[] methods = interfaces[i].getMethods(); for (int j = 0; j < methods.length; j++) { addProxyMethod(methods[j], interfaces[i]); } } //检查返回类型 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* ============================================================ * Step 2: 为我们生成的类中的所有字段和方法组装FieldInfo和MethodInfo结构。 */ //编译成class的相关内容 try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } /* ============================================================ * Step 3: 将组织好的class文件写入到文件中 */ //在开始编写最终类文件之前,请确保为以下项保留了常量池索引 cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (int i = 0; i < interfaces.length; i++) { cp.getClass(dotToSlash(interfaces[i].getName())); } //设置为只读模式,在此之后不允许添加新的常量池,因为我们将要编写一个final常量池表。 cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { //以下是class文件的结构,想深入了解的话可以看深入Java虚拟机 // u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count; dout.writeShort(interfaces.length); // u2 interfaces[interfaces_count]; for (int i = 0; i < interfaces.length; i++) { dout.writeShort(cp.getClass(dotToSlash(interfaces[i].getName()))); } // u2 fields_count; dout.writeShort(fields.size()); // field_info fields[fields_count]; for (FieldInfo f : fields) { f.write(dout); } // u2 methods_count; dout.writeShort(methods.size()); // method_info methods[methods_count]; for (MethodInfo m : methods) { m.write(dout); } // u2 attributes_count; dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } return bout.toByteArray(); } 复制代码
查看生成的Proxy代理类
到这里,我们就基本解析完了Java Proxy在动态代理中的作用了。不过大家可能会对动态代理生成出来的代理类具体结构有疑问。为了更好地了解Proxy代理类的结构(顺便加深印象),我们不妨通过FileOutputStream 来将generateProxyClass创建的二进制生成相应的class文件
public static void main(String[] args) { String name = "ProductServiceProxy"; byte[] data = sun.misc.ProxyGenerator.generateProxyClass(name, new Class[] { ProductService.class }); try { FileOutputStream out = new FileOutputStream(name + ".class"); out.write(data); out.close(); } catch (Exception e) { e.printStackTrace(); } } 复制代码
运行后在项目根目录会找到一个ProductServiceProxy.class文件,我们通过反编译打开
import com.example.javabase.proxyDemo.ProductService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class ProductServiceProxy extends Proxy implements ProductService { private static Method m1; private static Method m2; private static Method m3; private static Method m0; public ProductServiceProxy(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 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 addProduct(String paramString) { try { this.h.invoke(this, m3, 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 { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m3 = Class.forName("com.example.javabase.proxyDemo.ProductService").getMethod("addProduct", 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()); } } } 复制代码
从上面我们可以很清楚的看到,代理类的方法调用最后还是通过InvocationHandler.invoke反射调用到了被代理类的方法。代理的大概结构包括4部分:
同时, 因为所有代理类都继承了Proxy类 ,所以JDK动态代理只能对接口进行代理,Java的继承机制注定了这些动态代理类们无法实现对class的动态代理。