JDK动态代理、CGLib动态代理
JDK动态代理源码
一、public static Object newProxyInstance ——> 调用下面这个方法
二、Class<?> cl = getProxyClass0(loader, intfs); ——> 这个方法从下面这个缓存对象中返回代理类Class对象
三、return proxyClassCache.get(loader, interfaces); ——> 这个缓存对象是一个成员变量
四、proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); ——> 打开ProxyClassFactory这个类
五、private static final class ProxyClassFactory ——> 这是Proxy的一个内部类
六、public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { ——> 该内部类有一个apply方法,查看apply方法
1、确定代理类的名称
(2)确定代理类包名:得到传入接口的全限定名,截取接口包名作为代理类的包名;如果接口没有包名,把包名设置为com.sun.proxy
(3)确定代理类类名:类名前缀为§Proxy;后面加一个递增的数字,从0开始,如第一个代理类的名称为§Proxy0
(4)把代理类的包名和类名拼接起来
2、生成代理类字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); ——> 打开generateProxyClass()方法,查看generateClassFile()方法
七、generateClassFile() ——>
1、生成hashCode、equals、toString方法
2、生成构造方法,this.methods.add(this.generateConstructor());
3、生成接口里的方法private ProxyGenerator.MethodInfo generateMethod() throws IOException {
JDK动态代理分析
JDK动态代理在运行期创建接口的代理类并返回代理对象。
InvocationHandler可以看成是一个编织器。
Invocationhandler的invoke(Object proxy, Method method, Object[] args)方法,第一个参数是代理对象,一般不会用到;第二个参数method是JDK通过调用——我们调用Proxy的newProxyInstance方法时传入的第二个接口类型参数——的getMethods得来的;通过反射调用method的invoke方法来调用目标类的代码,将横切逻辑和业务逻辑编织在一起。
InvocationHandler还有一个成员变量,即目标对象,声明为Object类型即可,调用method方法时的invoke方法时要把这个Object传入进去。因为在调用Proxy的静态方法时已经传入了接口,所以JDK会把Object类型的成员变量向下转型为接口类型。
JDK动态代理实现
直接运行下面的代码,在D盘就可以看到生成的源码类$GameProxy.class
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class ViewProxyClass {
public static void main(String[] args) throws Exception {
Moveable huiXiong = new Tank("灰熊坦克");
ClassLoader classLoader = huiXiong.getClass().getClassLoader();
Class<?>[] interfaces = huiXiong.getClass().getInterfaces();
Moveable proxy = (Moveable) Proxy.newProxyInstance(classLoader, interfaces, new TimeInvocationHandler(huiXiong));
System.out.println(proxy.getClass());
proxy.move();
proxy.stop(); byte[] bts = ProxyGenerator.generateProxyClass("$GameProxy", interfaces);
FileOutputStream fos = new FileOutputStream(new File("D:/$GameProxy.class"));
fos.write(bts);
fos.flush();
fos.close();
}
} interface Moveable {
public void move();
public void stop();
public String getName();
} class Tank implements Moveable {
private String name;
public Tank(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void stop() {
System.out.println(this.name + " stop ...");
}
@Override
public void move() {
System.out.println(this.name + " run ...");
}
} class TimeInvocationHandler implements InvocationHandler {
private Object target;
public TimeInvocationHandler(Moveable target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("begin");
method.invoke(target, args);
System.out.println("end");
return proxy;
}
}
生成的代理类
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException; public final class $GameProxy extends Proxy implements Moveable {
private static Method m3;
private static Method m1;
private static Method m4;
private static Method m5;
private static Method m0;
private static Method m2; public $GameProxy(InvocationHandler var1) throws {
// // 代理类调用父类的构造方法,父类Proxy把InvocationHandler赋值给一个成员变量
super(var1);
} public final String getName() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} 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 void stop() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
} public final void move() throws {
try {
super.h.invoke(this, m5, (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 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);
}
} static {
try {
m3 = Class.forName("Moveable").getMethod("getName");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("Moveable").getMethod("stop");
m5 = Class.forName("Moveable").getMethod("move");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m2 = Class.forName("java.lang.Object").getMethod("toString");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
CGLib动态代理
CGLib不需要目标类实现接口,而是在运行期首先获得目标类的字节码文件,然后采用字节码技术为目标类成生一个子类,并返回父类引用的子类对象,利用多态织入横切逻辑。
CGLib不能对目标类的private、final修饰的方法进行代理,因为父类的final方法不允许子类重写,父类的private方法不允许子类访问。
CGLib动态代理实现
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class ViewProxyClass {
public static void main(String[] args) throws Exception {
MyCGlib myCGlib = new MyCGlib();
Tank tank = (Tank)myCGlib.getProxy(Tank.class);
tank.move();
tank.stop();
}
} class Tank {
public Tank() {};
public void stop() {
System.out.println(" stop ...");
}
public void move() {
System.out.println(" run ...");
}
} class MyCGlib implements MethodInterceptor {
Enhancer enhancer = new Enhancer();
// 参数clazz是目标类的Class对象,此方法返回代理对象
public Object getProxy(Class clazz) throws Exception {
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
// 通过字节码规范动态创建代理类对象
return enhancer.create();
}
@Override
// 参数o是目标类实例,method是目标类方法的反射对象,objects是方法的入参,methodProxy是代理类方法
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("begin");
// 调用目标类也就是父类的方法
Object proxy = methodProxy.invokeSuper(o, objects);
System.out.println("end");
return proxy;
}
}
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
pom.xml
动态代理
JDK和CGLib都是动态代理。
在程序运行时,当代码执行到Proxy.newProxyInstance()时,使用反射机制——调用传入的接口Class对象的getMethods(),在内存中动态生成代理类。
在程序运行时,当代码执行到xxxxx.getProxy(xxx.class)时,使用字节码技术——先获得目标类的字节码文件,再根据这个字节码文件为其生成子类。
CGLib创建的代理对象的性能比GDK创建的代理对象的性能高10倍,GDK生成的代理对象的方法调用也要用到反射,比如invoke方法的第二个参数就是Method对象,生成Method,调用Method的方法都是反射调用,效率低。
但CGLib创建代理对象花费的时间比JDK要多8倍,使用字节码技术生成代理类不如直接调用反射的API生成代理类效率高。
单例代理或者具有实例池的代理适合于CGLib,反之适于GDK。
JDK动态代理、CGLib动态代理的更多相关文章
- Atitit 代理CGLIB 动态代理 AspectJ静态代理区别
Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...
- 【Java入门提高篇】Day12 Java代理——Cglib动态代理
今天来介绍另一种更为强大的代理——Cglib动态代理. 什么是Cglib动态代理? 我们先回顾一下上一篇的jdk动态代理,jdk动态代理是通过接口来在运行时动态创建委托类的代理对象,但是跟静态代理一样 ...
- Java的三种代理模式:静态代理/JDK动态代理/Cglib动态代理
1.静态代理:需要定义接口或者父类,目标对象与代理对象均实现同一接口或继承同一父类. 2.JDK动态代理:需要目标对象实现一个接口,通过动态反射的机制,生成代理对象,实现同一个接口 3.Cglib动态 ...
- java 静态代理 JDK动态代理 Cglib动态代理
下面以一个简单的银行账户为例讲述讲述动态代理. 设计一个银行账户类,包含用户的账户余额,实现查询和更新余额功能 这个系统用了一段时间,有客户要求对账说账户余额给弄错了?因为上面没有存取款记录,最后银行 ...
- Java之代理(jdk静态代理,jdk动态代理,cglib动态代理,aop,aspectj)
一.概念 代理是什么呢?举个例子,一个公司是卖摄像头的,但公司不直接跟用户打交道,而是通过代理商跟用户打交道.如果:公司接口中有一个卖产品的方法,那么公司需要实现这个方法,而代理商也必须实现这个方法. ...
- JDK和CGLIB动态代理原理区别
JDK和CGLIB动态代理原理区别 https://blog.csdn.net/yhl_jxy/article/details/80635012 2018年06月09日 18:34:17 阅读数:65 ...
- 静态代理,动态代理和CGLIB代理模式
代理模式 一.概述 代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象.如此便于在目标实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目 ...
- Java深入浅出系列(四)——深入剖析动态代理--从静态代理到动态代理的演化
静态代理 如上图,在程序执行之前.程序猿就要编写Proxy.然后进行编译,即在程序执行之前,代理类的字节码文件就已经生成了(Proxy类的class文件已经存在了). 静态代理尽管在增强现有的接口业务 ...
- java 代理模式-静态代理与动态代理
最近在研究SpringAOP,当然要学习AOP就要知道这么健硕.强大的功能的背后究竟隐藏着怎样不可告人的“秘密”?? 接下来就是查阅了许多资料详细的研究了一下Java的代理模式,感觉还是非常非常重要的 ...
- Java静态代理与动态代理 理解与应用场景
角色 抽象角色:接口类 实现角色: 实现类 代理角色:代理实现的类,最终使用的对象 静态代理 1. 接口 /** * description * * @author 70KG * @date 2018 ...
随机推荐
- 课程一(Neural Networks and Deep Learning)总结——2、Deep Neural Networks
Deep L-layer neural network 1 - General methodology As usual you will follow the Deep Learning metho ...
- (转) CentOS7.4 + MySQL8.0 + Git + Gogs搭建
原文:https://blog.csdn.net/qq_16075483/article/details/80295793 1.装系统,这个不会的下面就不用看了2.CentOS7.X安装MySQL8. ...
- (转)python通过paramiko实现,ssh功能
python通过paramiko实现,ssh功能 1 import paramiko 2 3 ssh =paramiko.SSHClient()#创建一个SSH连接对象 4 ssh.set_missi ...
- vue2打包时内存溢出解决方案
vue项目完成时,若项目过大,就会出现内存溢出的问题,导致vue打包不成功 错误截图 解决方案 在依赖package.json中修改build为 "build":"nod ...
- ASP.NET Core 1.0 中使用 Log 日志配置
https://github.com/aspnet/Logging https://docs.asp.net/en/latest/fundamentals/logging.html ASP.NET C ...
- Google Chrome Native Messaging开发实录(一)背景介绍
最近接手了一个针对Google Chrome的需求,最终是使用Native Messaging来实现的.通过这个连载,将把本次开发从方案选定到编码的全部过程进行完整的回顾,并记录开发过程中踩过的各种坑 ...
- Android在代码中获取应用签名
平时都是用AS敲命令获取签名信息...还没有在代码中获取过签名~ 也算是老编程了,没做过这个稍微有点尴尬...本着有好轮子就用的原则,网上找了几篇博客,这块内容已经很完善了,我也没什么可以优化的... ...
- 全网最详细的zkfc启动以后,几秒钟以后自动关闭问题的解决办法(图文详解)
不多说,直接上干货! 问题详情 情况描述如题所示,zkfc启动以后,几秒钟以后自动关闭. 解决办法: 1.检查下每台机器的时间是否同步: 2.检查下每台机器的防火墙是否关闭: 3.查看zkfc的日志路 ...
- 二:理解ASP.NET的运行机制(例:基于HttpHandler的URL重写)
url重写就是把一些类似article.aspx?id=28的路径重写成 article/28/这样的路径 当用户访问article/28/的时候我们通过asp.net把这个请求重定向到article ...
- Java总结:语法基础
更新时间:2018-1-7 10:34:05 更多请查看在线文集:http://android.52fhy.com/java/index.html Hello World 文件名:HelloWorld ...