Spring进阶 - Spring AOP实现原理(三)JDK代理实现
上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。
本文是AOP实现原理的最后一篇。
什么是 JDK 代理?
JDK动态代理是有JDK提供的工具类Proxy实现的,动态代理类是在运行时生成指定接口的代理类,每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke方法上。
简单来说,对目标对象 UserService 接口(有一个实现类UserServiceImpl)使用 JDK 动态代理,那么会自动生成一个代理类对象,这个代理对象实现了 InvocationHandler。调用UserService 方法,业务逻辑走的是 InvocationHandler 的 invoke 方法,因为代理对象关联上目标对象了。
实战演示就懂了
JDK 实战
创建一个maven 项目,什么依赖都不用。
- User 类
▼java复制代码public class User { private String name; private Integer age; public User() { } public User(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
- 目标对象和实现类
▼java复制代码// 目标接口 public interface UserService { void addUser(); List<User> findUserList(); } // 目标对象实现 public class UserServiceImpl implements UserService { @Override public void addUser() { // do something } @Override public List<User> findUserList() { return Collections.singletonList(new User("fency", 18)); } }
- 创建一个代理,getLoggingProxy方法有一个 InvocationHandler 实现,代理的秘密都在 InvocationHandler 中实现。
▼java复制代码public class UserLogProxy { // 目标对象 /** * proxy target */ private UserServiceImpl target; /** * init. * * @param target target */ public UserLogProxy(UserServiceImpl target) { super(); this.target = target; } public UserService getLoggingProxy() { UserService proxy; ClassLoader loader = target.getClass().getClassLoader(); Class[] interfaces = new Class[]{UserService.class}; InvocationHandler h = new InvocationHandler() { /** * proxy: 代理对象。 一般不使用该对象 method: 正在被调用的方法 args: 调用方法传入的参数 */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); // log - before method System.out.println("[before] execute method: " + methodName); // call method Object result = null; try { // 前置通知 result = method.invoke(target, args); // 返回通知, 可以访问到方法的返回值 } catch (NullPointerException e) { e.printStackTrace(); // 异常通知, 可以访问到方法出现的异常 } // 后置通知. 因为方法可以能会出异常, 所以访问不到方法的返回值 // log - after method System.out.println("[after] execute method: " + methodName + ", return value: " + result); return result; } }; /** * loader: 代理对象使用的类加载器. * interfaces: 指定代理对象的类型. 即代理代理对象中可以有哪些方法. * h: 当具体调用代理对象的方法时, 应该如何进行响应, 实际上就是调用 InvocationHandler 的 invoke 方法 */ proxy = (UserService) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } }
- 调用
▼java复制代码public class ProxyDemo { /** * main interface. * * @param args args */ public static void main(String[] args) { // proxy UserService userService = new UserLogProxy(new UserServiceImpl()).getLoggingProxy(); // call methods userService.findUserList(); userService.addUser(); } }
测试结果

JDK代理的流程
JDK代理自动生成的class是由sun.misc.ProxyGenerator来生成的。
sun.misc.ProxyGenerator是什么?刚刚的代码没看懂这个类啊?
**sun.misc.ProxyGenerator** 确实存在JDK中,但它不属于 Java 的公开 API。仅java 内部的 API 可以调用这个类,这是为了为了防止开发者误用它们。
我们来解读 ProxyGenerator 源码**。**
ProxyGenerator生成代码
我们看下sun.misc.ProxyGenerator生成代码的逻辑:
▼java复制代码/** * Generate a proxy class given a name and a list of proxy interfaces. * * @param name the class name of the proxy class * @param interfaces proxy interfaces * @param accessFlags access flags of the proxy class */ public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) { ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags); final byte[] classFile = gen.generateClassFile(); ... }
generateClassFile方法如下。
代码很长,无非就是三步(把大象放冰箱分几步?):
- 第一步:(把冰箱门打开)准备工作,将所有方法包装成ProxyMethod对象,包括Object类中hashCode、equals、toString方法,以及被代理的接口中的方法
- 第二步:(把大象装进去)为代理类组装字段,构造函数,方法,static初始化块等
- 第三步:(把冰箱门带上)写入class文件
▼java复制代码/** * Generate a class file for the proxy class. This method drives the * class file generation process. */ private byte[] generateClassFile() { /* 第一步:将所有方法包装成ProxyMethod对象 */ // 将Object类中hashCode、equals、toString方法包装成ProxyMethod对象 addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); // 将代理类接口方法包装成ProxyMethod对象 for (Class<?> intf : interfaces) { for (Method m : intf.getMethods()) { addProxyMethod(m, intf); } } // 校验返回类型 for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* 第二步:为代理类组装字段,构造函数,方法,static初始化块等 */ try { // 添加构造函数,参数是InvocationHandler 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)); // 上述ProxyMethod中的方法 methods.add(pm.generateMethod()); } } // static初始化块 methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception", e); } if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } /* 第三步:写入class文件 */ /* * Make sure that constant pool indexes are reserved for the * following items before starting to write the final class file. */ cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (Class<?> intf: interfaces) { cp.getClass(dotToSlash(intf.getName())); } /* * Disallow new constant pool additions beyond this point, since * we are about to write the final constant pool table. */ cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { /* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */ // 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(accessFlags); // 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 (Class<?> intf : interfaces) { dout.writeShort(cp.getClass( dotToSlash(intf.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", e); } return bout.toByteArray(); }
从生成的Proxy代码看执行流程
从上述sun.misc.ProxyGenerator类中可以看到,这个类里面有一个配置参数sun.misc.ProxyGenerator.saveGeneratedFiles,可以通过这个参数将生成的Proxy类保存在本地,比如设置为true 执行后,生成的文件如下:
我们看下生成后的代码:
▼java复制代码// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package com.sun.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import java.util.List; import com.fency.springframework.service.IUserService; // 所有类和方法都是final类型的 public final class $Proxy0 extends Proxy implements UserService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; private static Method m4; // 构造函数注入 InvocationHandler public $Proxy0(InvocationHandler var1) throws { super(var1); } public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final List findUserList() throws { try { return (List)super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final void addUser() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } static { try { // 初始化 methods, 2个UserService接口中的方法,3个Object中的接口 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("com.fency.springframework.service.UserService").getMethod("findUserList"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m4 = Class.forName("com.fency.springframework.service.UserService").getMethod("addUser"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
这个代理类没干任何实事,它的唯一使命就是 **“转发”,**我来描述 App 调用 proxy.findUserList() 方法时,整体的工作流程,让你理解这个 proxy0 的职责。
- 你调用
**proxy.findUserList()** **$Proxy0**的**findUserList()**方法被触发。- 它什么也不做,直接把调用请求(我是谁?调了啥方法?参数是啥?)打包,扔给它的“大脑”——
**InvocationHandler**的**invoke**方法。 - 你的
**InvocationHandler**的**invoke**方法开始执行。你可以在这里做任何事:打印日志、检查权限、开启事务、调用真实对象的方法等等。 - 你的
**invoke**方法执行完毕后,返回一个结果。 **$Proxy0**的**findUserList()**方法接收到这个结果,把它**return**出去,最终回到你的手里。
很清晰的看见 $Proxy0 这个代理类就是个中转站**,**把 app 调用消息给 **InvocationHandler** ,**InvocationHandler** 结果给回 App.
至此,我们明白了 JDK 代理实现原理,循序渐进,来看 Spring AOP 中 JDK 代理的实现原理。
SpringAOP中JDK代理的实现
SpringAOP扮演的是JDK代理的创建和调用两个角色,我们通过这两个方向来看下SpringAOP的代码(JdkDynamicAopProxy类)
SpringAOP Jdk代理的创建
代理的创建比较简单,调用getProxy方法,然后直接调用JDK中Proxy.newProxyInstance()方法将classloader和被代理的接口方法传入即可。这就是Spring AOP 框架的代理创建入口,这里进去就接上了本文前面提到的 ”JDK 代理的流程“。
这一个部分的源码我们在 “AOP实现原理(二)“文章中 ”创建代理的入口方法“ 扒过一次源码的。
▼java复制代码@Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this); }
这里就生成一个 $Proxy0 的代理对象,然后就要调用 invoke 执行代理。
SpringAOP Jdk代理的执行
在spring aop 框架中我们不用手写一个invoke,框架内部有一个 invoke 给$Proxy0 的代理对象调用,invoke 最终逻辑是开发者编写的切面编程的通知逻辑。
至此,SpringAOP 和 JDK 动态代理的执行流程打通了!
▼java复制代码/** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Object target = null; try { // 执行的是equal方法 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } // 执行的是hashcode方法 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } // 如果是包装类,则dispatch to proxy config else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } // 用反射方式来执行切点 else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // 获取拦截链 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } // Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
小结
本文因为有一个代理对象和invoke方法,所有调用链比前面的文章理解起来稍微有点困难,这东西其实不用深入理解,作为开发者,仅需要知道 Spring AOP 的注解和几个术语就可以开发切面编程。稍微了解切面编程底层是 JDK 动态代理和Cglib 代理的技术原理即可。

