技术标签: JDK动态代理 JAVA设计模式 Cglib动态代理
先来谈谈JDK动态代理
需求背景:
一电商项目的程序猿辛辛苦苦写完支付接口并测试成功且上线。这个时候项目经理跑过来提了一个需求,需要在客户支付之前记录客户支付日志,方便日后进行统计支付数据。
为了不影响到之前写好的支付接口,不破坏接口的开闭原则。我们通过PayProxy绑定原先的支付接口,来解决这问题。
pay.java
public interface Pay {
public void doPay();
}
AliPay.java
public class AliPay implements Pay{
@Override
public void doPay() {
System.out.println("do AliPay");
}
}
PayProxy.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class PayProxy implements InvocationHandler {
private Object target;
public Object bind(Object target){
this.target = target;
return Proxy.
newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public void before() {
System.out.println("do something before pay");
}
public void after() {
System.out.println("do something after pay");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
}
PayCenter.java
public class PayCenter {
public static void main(String[] args) {
Pay aliPay = (Pay)new PayProxy().bind(new AliPay());
aliPay.doPay();
}
}
输出
do something before pay
do AliPay
do something after pay
思考:
为什么现在的Pay接口调用的doPay()会打印before和after方法?现在Pay还是原先的Pay接口吗?
aliPay = {
$Proxy0@528} "com.zjqx.proxy.jdk.AliPay@6979e8cb"
通过调试可以发现,现在的Pay已经不是我们原先创建的Pay接口了;而是 P r o x y 0 @ 528 这 个 对 象 ; 为 了 更 好 的 理 解 J D K 动 态 代 理 的 原 理 , 我 们 可 以 打 印 出 Proxy0@528这个对象;为了更好的理解JDK动态代理的原理,我们可以打印出 Proxy0@528这个对象;为了更好的理解JDK动态代理的原理,我们可以打印出Proxy0@528对象的反编译代码;
$Proxy0.class反编译后的文件
import com.sun.proxy.$Proxy0;
import java.lang.reflect.*;
public final class $Proxy extends Proxy
implements $Proxy0
{
public $Proxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final InvocationHandler getInvocationHandler(Object obj)
throws IllegalArgumentException
{
try
{
return (InvocationHandler)super.h.invoke(this, m5, new Object[] {
obj
});
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Class getProxyClass(ClassLoader classloader, Class aclass[])
throws IllegalArgumentException
{
try
{
return (Class)super.h.invoke(this, m6, new Object[] {
classloader, aclass
});
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Class getClass()
{
try
{
return (Class)super.h.invoke(this, m11, null);
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void notifyAll()
{
try
{
super.h.invoke(this, m13, null);
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait()
throws InterruptedException
{
try
{
super.h.invoke(this, m8, null);
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void doPay()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void notify()
{
try
{
super.h.invoke(this, m12, null);
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final Object newProxyInstance(ClassLoader classloader, Class aclass[], InvocationHandler invocationhandler)
throws IllegalArgumentException
{
try
{
return (Object)super.h.invoke(this, m7, new Object[] {
classloader, aclass, invocationhandler
});
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait(long l)
throws InterruptedException
{
try
{
super.h.invoke(this, m10, new Object[] {
Long.valueOf(l)
});
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final boolean isProxyClass(Class class1)
{
try
{
return ((Boolean)super.h.invoke(this, m4, new Object[] {
class1
})).booleanValue();
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void wait(long l, int i)
throws InterruptedException
{
try
{
super.h.invoke(this, m9, new Object[] {
Long.valueOf(l), Integer.valueOf(i)
});
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m5;
private static Method m2;
private static Method m6;
private static Method m11;
private static Method m13;
private static Method m0;
private static Method m8;
private static Method m3;
private static Method m12;
private static Method m7;
private static Method m10;
private static Method m4;
private static Method m9;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", new Class[] {
Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;")
});
m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass", new Class[0]);
m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[0]);
m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("doPay", new Class[0]);
m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify", new Class[0]);
m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", new Class[] {
Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler")
});
m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] {
Long.TYPE
});
m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", new Class[] {
Class.forName("java.lang.Class")
});
m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", new Class[] {
Long.TYPE, Integer.TYPE
});
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
提取关键代码
public $Proxy(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final void doPay()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("doPay", new Class[0]);
通过上述的代码可以看出,$Proxy对象是JVM自动生成的对象,实现了Pay接口,并重写了接口里的所有方法;
我们提取dopay方法可以看出,实际调用的是invocationhandler.invoke方法,这正是我们之前PayProxy类中的invoke方法,通过JDK的反射技术调用实际Object target即AliPay的dopay方法。
最后给出JDK动态代理简单的时序图(有点丑,见谅,哈哈哈)
JDK动态代理的简单总结:
CGLib动态代理
区别于JDK动态代理,CGLib动态代理的被代理类则不需要实现固定的接口;CGLib的动态代理会自动生成被代理类的子类,并重写父类的方法,从而实现代理;
实现代码
CglibProxy.java
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 CglibProxy implements MethodInterceptor {
public Object getInstance(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
//回调intercept方法
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
public void before(){
System.out.println("Before");
}
public void after(){
System.out.println("After");
}
}
AliPay.java
public class AliPay {
public void doPay(){
System.out.println("alipay");
}
}
CglibTest.java
import net.sf.cglib.core.DebuggingClassWriter;
public class CglibTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G://cglib_folder");
AliPay alipay = (AliPay)new CglibProxy().getInstance(new AliPay());
alipay.doPay();
}
}
输出
before
doPay
after
我们通过以下代码输出CgLib的class文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G://cglib_folder");
进入G://cglib_folder会发现有三个类
通过调试我们发现:
alipay = {
AliPay$$EnhancerByCGLIB$$e2467acf@671} "com.tomi.proxy.cglib.AliPay$$EnhancerByCGLIB$$e2467acf@3c0ecd4b"
AliPayEnhancerByCGLIBe2467acf.class文件正是Cglib动态获取的代理类,反编译该class文件;
public class AliPay$$EnhancerByCGLIB$$e2467acf extends AliPay
implements Factory
{
final void CGLIB$doPay$0()
{
super.doPay();
}
public final void doPay()
{
CGLIB$CALLBACK_0;
if(CGLIB$CALLBACK_0 != null) goto _L2; else goto _L1
_L1:
JVM INSTR pop ;
CGLIB$BIND_CALLBACKS(this);
CGLIB$CALLBACK_0;
_L2:
JVM INSTR dup ;
JVM INSTR ifnull 37;
goto _L3 _L4
_L3:
break MISSING_BLOCK_LABEL_21;
_L4:
break MISSING_BLOCK_LABEL_37;
this;
CGLIB$doPay$0$Method;
CGLIB$emptyArgs;
CGLIB$doPay$0$Proxy;
intercept();
return;
super.doPay();
return;
}
}
CgLib代理类继承了AliPay类,并重写了父类的所有方法,调用doPay方法时会调用CGLIB$doPay$0方法,该方法正是父类的doPay方法;接着调用了intercept()方法。
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
进入MethodProxy类的invokeSuper方法。
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
查看MethodProxy.FastClassInfo可以看到
private static class FastClassInfo {
FastClass f1;
FastClass f2;
int i1;
int i2;
private FastClassInfo() {
}
}
这里的f1和f2便是上述G://cglib_folder目录下的
- AliPay__EnhancerByCGLIB__e2467acf__FastClassByCGLIB__9697d901.class(代理类的FastClass)
- AliPay__FastClassByCGLIB__bf557141.class(被代理类的FastClass)
总结
- CgLib动态代理类之所以执行效率比JDK动态代理类快,是因为其FastClass机制,直接通过生成的FastClass类执行对应的方法;
- CgLib动态代理需要生成三个class类,所以比JDK动态代理生成时效率要慢;
最后附上一份手写JDK动态代理的git地址
https://github.com/TomiCat/DynamicProxyForMyself
此文章为本人平时学习整理巩固所写,如有错误,欢迎大家纠正~谢谢!
文章浏览阅读1.3k次。记录一次故障处理:RMAN-06025 no backup of archive log …… found to restore_rman-06025
文章浏览阅读955次。您可以在AJAX的帮助下完成此操作…下面是一个调用python函数的示例,该函数在不重定向或刷新页面的情况下打印hello.在app.py下面的代码段.//rendering the HTML page which has the [email protected]('/json')def json():return render_template('json.html')//background ..._flask 在click时javascript调用python函数
文章浏览阅读5.7k次。一、org.springframework.web.client.ResourceAccessException: I/O error: Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect问题原因:sguap部署时,默认数据库为Or_sg-uap : warn jobloglistener:144 - taskdispatch: 【jobloglistener】没有定义
文章浏览阅读1.2k次。坦诚地说每周想去更新博客,但是工作、生活、家庭各方面因素被搁置了,撰写前两个博客专栏“FPGA基础知识”和“FPGA 20个例程”时也酝酿准备了很久,把市面很多教程和视频都研究了一遍,也查阅了很多经典FPGA设计书籍,最后的最后更结合了实际项目经验,CSDN也收到了很多留言和私信等。这几个月因为忙于单位的新项目所以很久没有更新博客了,面对新的需求和新的挑战也需要不断学习新的知识,甚至推翻老的惯性思维,这时想到一个老同事说过:研发如果不能迎难而上,那基本不太会有什么提高了。
文章浏览阅读3.8k次,点赞2次,收藏7次。最近重新学习了一下JavaWeb的一些基础知识,今天在学习JavaBean时遇到了一个中文的乱码问题。当JavaBean获取表单中的数据有中文时就会出现乱码的现象。原因大概是因为MyEclipse对Java文件的默认编码是iso8859-1,而我们在jsp中的编码设置一般为utf-8,所以在获取中文时必然会出现乱码的现象。其实解决的方法很简单,只需要我们在Getter方法中用getBytes(_javabean get中文字符串
文章浏览阅读163次。Bo Schwarzstein http://jedimaster.cnblogs.com/ 6/21/2008 写在狗屎一样的中国高等教育之前 摘要GNU Scientific Library(以下简称GSL)是来自开源社区的重磅礼物,历史悠久功能强大,集成了最基础的同时也是最重要的科学计算算法如BLAS、Monte Carlo积分等。本文讲解了其基本使用方法..._c++ ordinary differential equation
文章浏览阅读3.2k次,点赞2次,收藏12次。作者提出了_towards accurate multi-person pose estimation in the wild.
文章浏览阅读124次。题目描述小蓝正在学习一门神奇的语言,这门语言中的单词都是由小写英文字母组 成,有些单词很长,远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词,他准备不再完全记忆这些单词,而是根据单词中哪个字母出现得最多来分辨单词。现在,请你帮助小蓝,给了一个单词后,帮助他找到出现最多的字母和这 个字母出现的次数。输入描述输入一行包含一个单词,单词只由小写英文字母组成。对于所有的评测用例,输入的单词长度不超过 1000。输出描述输出两行,第一行包含一个英文字母,表示单词中出现得最多的字
文章浏览阅读132次。本题要求实现一个函数,计算下列简单交错幂级数的部分和:f(x,n)=x−x2+x3−x4+⋯+(−1)n−1xnf(x,n)=x− x_{2} +x_3 −x_4 +⋯+(−1)^{n−1} x_nf(x,n)=x−x2+x3−x4+⋯+(−1)n−1xn函数接口定义:double fn( double x, int n )..._double fn( double x, int n );
文章浏览阅读2.3k次。1、建立模型 将black box模块拖入新建MDL文件中,出现要添加的硬件描述文件的对话框把生成的m文件改为dout_port.setType('Fix_26_13');意思是输出带13位小数的26位定点数,并添加下行代码调用如下图搭建完模型运行后发现并不知道要怎么改。。挖个坑放在这里——————————————————————————————发现是# ** Warning: There ..._system generator blackbox模块
文章浏览阅读1.4k次。参考: https://paulyang.cn/2016/11/28/rsa-padding.html 作者: 杨洋https://infohunter.github.io/2016/11/28/rsa-padding.htmlopenssl 更新工具包, 使用pkeyutl取代rsautl之后才开始支持使用PSS padding scheme进行数字..._rsa_padding_mode:pss
文章浏览阅读1.5w次。Matplotlib可以无缝的处理LaTex字体,在图中加入数学公式from matplotlib.patches import Polygonimport matplotlib.pyplot as pltimport numpy as np# 定义一个求积分的函数def func(x): return 0.3* (x**2) + (0.1*x) + 1# 定义积分区间a, b =_matplotlib latex