上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。@pdai
引入
上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。
什么是JDK代理?
JDK动态代理是有JDK提供的工具类Proxy实现的,动态代理类是在运行时生成指定接口的代理类,每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke方法上。
JDK代理的案例
这里我们写一个使用jdk代理的简单例子。@pdai
不需要maven依赖
jdk代理不需要任何依赖。
tech-pdai-spring-demos tech.pdai 1.0-SNAPSHOT 4.0.0 006-spring-framework-demo-aop-proxy-jdk 8 8 定义实体
User
package tech.pdai.springframework.entity; /** * @author pdai */ public class User { /** * user's name. */ private String name; /** * user's age. */ private int age; /** * init. * * @param name name * @param age age */ public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }被代理的类和接口
接口如下
package tech.pdai.springframework.service; import tech.pdai.springframework.entity.User; import java.util.List; /** * @author pdai */ public interface IUserService { /** * find user list. * * @return user list */ ListfindUserList(); /** * add user */ void addUser(); } 实现类如下:
package tech.pdai.springframework.service; import tech.pdai.springframework.entity.User; import java.util.Collections; import java.util.List; /** * @author pdai */ public class UserServiceImpl implements IUserService { /** * find user list. * * @return user list */ @Override public ListfindUserList() { return Collections.singletonList(new User("pdai", 18)); } /** * add user */ @Override public void addUser() { // do something } } JDK代理类
代理类如下:
package tech.pdai.springframework.proxy; import tech.pdai.springframework.service.IUserService; import tech.pdai.springframework.service.UserServiceImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; /** * This class is for proxy demo. * * @author pdai */ public class UserLogProxy { /** * proxy target */ private IUserService target; /** * init. * * @param target target */ public UserLogProxy(UserServiceImpl target) { super(); this.target = target; } /** * get proxy. * * @return proxy target */ public IUserService getLoggingProxy() { IUserService proxy; ClassLoader loader = target.getClass().getClassLoader(); Class[] interfaces = new Class[]{IUserService.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 = (IUserService) Proxy.newProxyInstance(loader, interfaces, h); return proxy; } }使用代理
启动类中指定代理目标并执行。
package tech.pdai.springframework; import tech.pdai.springframework.proxy.UserLogProxy; import tech.pdai.springframework.service.IUserService; import tech.pdai.springframework.service.UserServiceImpl; /** * Jdk proxy demo. * * @author pdai */ public class ProxyDemo { /** * main interface. * * @param args args */ public static void main(String[] args) { // proxy IUserService userService = new UserLogProxy(new UserServiceImpl()).getLoggingProxy(); // call methods userService.findUserList(); userService.addUser(); } }简单测试
我们启动上述类main 函数,执行的结果如下:
[before] execute method: findUserList [after] execute method: findUserList, return value: [User{name='pdai', age=18}] [before] execute method: addUser [after] execute method: addUser, return value: nullJDK代理的流程
JDK代理自动生成的class是由sun.misc.ProxyGenerator来生成的。
ProxyGenerator生成代码
我们看下sun.misc.ProxyGenerator生成代码的逻辑:
/** * 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方法如下:
/** * 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 (Listsigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* 第二步:为代理类组装字段,构造函数,方法,static初始化块等 */ try { // 添加构造函数,参数是InvocationHandler methods.add(generateConstructor()); // 代理方法 for (List 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(); } 一共三个步骤(把大象装进冰箱分几步?):
第一步:(把冰箱门打开)准备工作,将所有方法包装成ProxyMethod对象,包括Object类中hashCode、equals、toString方法,以及被代理的接口中的方法第二步:(把大象装进去)为代理类组装字段,构造函数,方法,static初始化块等第三步:(把冰箱门带上)写入class文件从生成的Proxy代码看执行流程
从上述sun.misc.ProxyGenerator类中可以看到,这个类里面有一个配置参数sun.misc.ProxyGenerator.saveGeneratedFiles,可以通过这个参数将生成的Proxy类保存在本地,比如设置为true 执行后,生成的文件如下:
我们看下生成后的代码:
// // 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 tech.pdai.springframework.service.IUserService; // 所有类和方法都是final类型的 public final class $Proxy0 extends Proxy implements IUserService { 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个IUserService接口中的方法,3个Object中的接口 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("findUserList"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); m4 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("addUser"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }上述代码是比较容易理解的,我就不画图了。
主要流程是:
ProxyGenerator创建Proxy的具体类$Proxy0由static初始化块初始化接口方法:2个IUserService接口中的方法,3个Object中的接口方法由构造函数注入InvocationHandler执行的时候,通过ProxyGenerator创建的Proxy,调用InvocationHandler的invoke方法,执行我们自定义的invoke方法SpringAOP中JDK代理的实现
SpringAOP扮演的是JDK代理的创建和调用两个角色,我们通过这两个方向来看下SpringAOP的代码(JdkDynamicAopProxy类)
SpringAOP Jdk代理的创建
代理的创建比较简单,调用getProxy方法,然后直接调用JDK中Proxy.newProxyInstance()方法将classloader和被代理的接口方法传入即可。
@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); }SpringAOP Jdk代理的执行
执行的方法如下:
/** * Implementation of {@code InvocationHandler.invoke}. *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
示例源码
https://github.com/realpdai/tech-pdai-spring-demos
更多文章
首先, 从Spring框架的整体架构和组成对整体框架有个认知。
Spring基础 - Spring和Spring框架组成 Spring是什么?它是怎么诞生的?有哪些主要的组件和核心功能呢? 本文通过这几个问题帮助你构筑Spring和Spring Framework的整体认知。其次,通过案例引出Spring的核心(IoC和AOP),同时对IoC和AOP进行案例使用分析。
Spring基础 - Spring简单例子引入Spring的核心 上文中我们简单介绍了Spring和Spring Framework的组件,那么这些Spring Framework组件是如何配合工作的呢?本文主要承接上文,向你展示Spring Framework组件的典型应用场景和基于这个场景设计出的简单案例,并以此引出Spring的核心要点,比如IOC和AOP等;在此基础上还引入了不同的配置方式, 如XML,Java配置和注解方式的差异。Spring基础 - Spring核心之控制反转(IOC) 在Spring基础 - Spring简单例子引入Spring的核心中向你展示了IoC的基础含义,同时以此发散了一些IoC相关知识点; 本节将在此基础上进一步解读IOC的含义以及IOC的使用方式Spring基础 - Spring核心之面向切面编程(AOP) 在Spring基础 - Spring简单例子引入Spring的核心中向你展示了AOP的基础含义,同时以此发散了一些AOP相关知识点; 本节将在此基础上进一步解读AOP的含义以及AOP的使用方式。基于Spring框架和IOC,AOP的基础,为构建上层web应用,需要进一步学习SpringMVC。
Spring基础 - SpringMVC请求流程和案例 前文我们介绍了Spring框架和Spring框架中最为重要的两个技术点(IOC和AOP),那我们如何更好的构建上层的应用呢(比如web 应用),这便是SpringMVC;Spring MVC是Spring在Spring Container Core和AOP等技术基础上,遵循上述Web MVC的规范推出的web开发框架,目的是为了简化Java栈的web开发。 本文主要介绍SpringMVC的请求流程和基础案例的编写和运行。Spring进阶 - IoC,AOP以及SpringMVC的源码分析
Spring进阶 - Spring IOC实现原理详解之IOC体系结构设计 在对IoC有了初步的认知后,我们开始对IOC的实现原理进行深入理解。本文将帮助你站在设计者的角度去看IOC最顶层的结构设计Spring进阶 - Spring IOC实现原理详解之IOC初始化流程 上文,我们看了IOC设计要点和设计结构;紧接着这篇,我们可以看下源码的实现了:Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的Spring进阶 - Spring IOC实现原理详解之Bean实例化(生命周期,循环依赖等) 上文,我们看了IOC设计要点和设计结构;以及Spring如何实现将资源配置(以xml配置为例)通过加载,解析,生成BeanDefination并注册到IoC容器中的;容器中存放的是Bean的定义即BeanDefinition放到beanDefinitionMap中,本质上是一个ConcurrentHashMap;并且BeanDefinition接口中包含了这个类的Class信息以及是否是单例等。那么如何从BeanDefinition中实例化Bean对象呢,这是本文主要研究的内容?Spring进阶 - Spring AOP实现原理详解之切面实现 前文,我们分析了Spring IOC的初始化过程和Bean的生命周期等,而Spring AOP也是基于IOC的Bean加载来实现的。本文主要介绍Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor,为后续交给代理增强实现做准备的过程)。Spring进阶 - Spring AOP实现原理详解之AOP代理 上文我们介绍了Spring AOP原理解析的切面实现过程(将切面类的所有切面方法根据使用的注解生成对应Advice,并将Advice连同切入点匹配器和切面类等信息一并封装到Advisor)。本文在此基础上继续介绍,代理(cglib代理和JDK代理)的实现过程。Spring进阶 - Spring AOP实现原理详解之Cglib代理实现 我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。Spring进阶 - Spring AOP实现原理详解之JDK代理实现 上文我们学习了SpringAOP Cglib动态代理的实现,本文主要是SpringAOP JDK动态代理的案例和实现部分。Spring进阶 - SpringMVC实现原理之DispatcherServlet初始化的过程 前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第一篇:DispatcherServlet的初始化过程的源码解析。Spring进阶 - SpringMVC实现原理之DispatcherServlet处理请求的过程 前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第二篇:DispatcherServlet处理请求的过程的源码解析。 更多文章请参考:Java 全栈知识体系
版权声明:我们致力于保护作者版权,注重分享,被刊用文章【第二类代理问题(Spring框架系列)】因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!;
工作时间:8:00-18:00
客服电话
电子邮件
beimuxi@protonmail.com
扫码二维码
获取最新动态
