jdk动态代理实现原理详解编程语言
动态代理的作用
我们都知道,spring的面向切面编程默认由jdk动态代理和cglib动态代理实现,使用动态代理我们可以无侵入的实现切面编程,比如日志管理、权限管理、事务管理等。jdk动态代理是面向接口的,cglib是面向普通类。弄明白了这两种动态代理实现原理也就懂了spring的aop编程。
jdk动态代理说明
jdk动态代理是面向接口的,只能对接口生成代理类。我曾经以为这个接口必须有实现类,其实这是错误的。接口实现是否存在是根据业务需求而来的。
jdk动态代理实现方法
1、目标接口、目标接口实现类
2、自定义的InvocationHandler
public interface UserService { public void methodA(); }
public class UserServiceImpl implements UserService { public void methodA() { System.out.println("UserServiceImpl methodA()..."); }
public class MyInvocationHandler implements InvocationHandler { public MyInvocationHandler() { super(); public MyInvocationHandler(Object obj) { super(); this.obj = obj; private Object obj; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("执行前...."); Object value = method.invoke(obj, args); System.out.println("执行后"); return value;
import java.lang.reflect.Proxy; import com.interceptor.MyInvocationHandler; import com.jun.UserService; import com.jun.impl.UserServiceImpl; public class TestJDKDynamicProxy { public static void main(String[] args) { MyInvocationHandler h = new MyInvocationHandler(new UserServiceImpl()); Class ? [] interfaces ={UserService.class}; UserService service = (UserService) Proxy.newProxyInstance(MyInvocationHandler.class.getClassLoader(), interfaces, h); service.methodA(); System.out.println(service.getClass()); }
输出结果
执行前.... UserServiceImpl methodA()... class com.sun.proxy.$Proxy0
jdk动态代理原理
切面编程的效果出来了,在执行方法前和执行方法后,执行了逻辑操作,这里,我们可以做事务开启、关闭,日志的记录等。但背后的原理是什么?
通过对目标接口生成代理类,代理类会继承Proxy并实现目标接口,代理类的所有方法都会调用定制的invocationhandler的invoke方法,在定制InvocationHandler中调用目标类方法, 从而实现切面编程。
我们注意到打印出来的UserService类是com.sun.Proxy.$Proxy0,而不是UserService。通过追踪Proxy.newProxyInstance方法,我们发现有个getProxyClass0方法
Class ? cl = getProxyClass0(loader, interfaces);
这个方法生成了代理类信息,进入方法发现,是这一行代码生成字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces);
由于jdk是不开源的,所以我们需要下载openjdk查看源代码
/** * Generate a proxy class given a name and a list of proxy interfaces. public static byte[] generateProxyClass(final String name, Class[] interfaces) ProxyGenerator gen = new ProxyGenerator(name, interfaces); final byte[] classFile = gen.generateClassFile(); 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; }
我们发现saveGeneratedFiles控制是否把代理类保存到磁盘上
private final static boolean saveGeneratedFiles = java.security.AccessController.doPrivileged( new GetBooleanAction( "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();
现在已经确定是ProxyGenerator.generateProxyClass生成代理类,既然已经找到 真凶 ,我们就生成代理类看看吧。
import sun.misc.ProxyGenerator; public class TestJDKDynamicProxy { public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); Class ? [] interfaces ={UserService.class}; ProxyGenerator.generateProxyClass( "C://Users//DELL//Desktop//$Proxy1", interfaces); }
注意,传入的属性值是字符串 true ,而不是布尔值true,传入布尔值true不会保存到磁盘
通过java反编译工具jad(java decomplier)反编译$Proxy1生成jad文件,打开后如下所示
import java.lang.reflect.*; public final class C_3A__5C_Users_5C_DELL_5C_Desktop_5C_$Proxy1 extends Proxy implements UserService static try m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m4 = Class.forName("com.jun.UserService").getMethod("methodB", new Class[0]); m3 = Class.forName("com.jun.UserService").getMethod("methodA", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); catch(NoSuchMethodException nosuchmethodexception) throw new NoSuchMethodError(nosuchmethodexception.getMessage()); catch(ClassNotFoundException classnotfoundexception) throw new NoClassDefFoundError(classnotfoundexception.getMessage()); private static Method m3; public C_3A__5C_Users_5C_DELL_5C_Desktop_5C_$Proxy1(InvocationHandler invocationhandler) super(invocationhandler);我们可以看到代理类$Proxy1,实现了指定接口UserService并继承了Proxy,执行methodA时,会调用父类的invocationHandler,而这个InvocationHandler就是在创建代理类对象时,传入的MyInvocationHandler
Class ? cl = getProxyClass(loader, interfaces); * Invoke its constructor with the designated invocation handler. try { Constructor cons = cl.getConstructor(constructorParams); return cons.newInstance(new Object[] { strong span h /span /strong我们再回过头来回顾一下事情的经过,首先执行代理的methodA方法,然后调用MyInvocationHandler.invoke方法,首先进行方法前逻辑处理,然后执行UserServiceImpl的methodA方法,然后执行方法后逻辑处理。
注意:
1、目标接口不是必须有实现类的,比如Mybatis的mapper,mapper都是接口,但是没有实现类。Mybatis的InvocationHandler就是MapperProxy,在invoke方法中方法里执行sql。
20276.html
cjava
相关文章
- maven对应jdk版本_maven安装配置教程
- JDK下载安装及环境变量配置的图文教程(详解)「建议收藏」
- JDK 下载与安装
- JDK的安装和配置
- mysql下载与安装教程_jdk下载与安装教程
- Spring 初识Aop JDK动态代理实现 原理初显
- cglib动态代理实现原理_jdk cglib 动态代理 区别
- 爱说JDK集合-List源码剖析
- 实战:常见的延时队列解决方案及代码实现,真的很全:MQ、Redis、JDK队列、Netty时间轮~
- 【Java 虚拟机原理】JDK 体系结构 | Java 源码运行原理 | Java 虚拟机内存
- ——Linux系统快速安装JDK教程(linux系统安装jdk)
- Linux下安装JDK指南(linux中安装jdk)
- 卸载Linux环境下的JDK(jdk卸载linux)
- Linux环境搭建JDK简易指南(linux搭建jdk)
- Linux系统下JDK安全删除方法.(jdklinux删除)
- Linux查看JDK安装路径的操作步骤(linux查看jdk路径)
- 安装Oracle JDK安装指南(oracle的jdk)
- Linux下修改JDK内存配置的指南(linux修改jdk内存)
- 在Linux系统中安装JDK:从官方下载Linux版本JDK(linux版本jdk下载)
- Linux安装JDK的新版本(linux版本的jdk)
- Linux查询JDK版本:简单快捷(linux版本查询jdk)
- 如何在Linux上设置JDK的环境变量?(jdk环境变量linux)
- Oracle JDK优质的开发工具(jdk属于oracle)
- Oracle推出Java SE JDK 7新世界拓宽技术视野(jdk 7 oracle)
- Oracle下的JDK安装实现你的梦想(oracle下jdk下载)
- Oracle JDK查询利用工具更高效完成操作(oracle jdk查询)
- 别于Oracle JDK其他开发者Java选择(oracle jdk以外)
- win2003jsp运行环境架设心得(jdk+tomcat)
- jdk中密钥和证书管理工具keytool常用命令详解