jdk中的动态代理通过反射类ProxyInvocationHandler回调接口实现,要求委托类必须实现一个接口,只能对该类接口中定义的方法实现代理,这在实际编程中有一定的局限性。

其原理完全基于反射。

cglib实现(不仅仅是反射,其主要是Java 字节码生成技术)

使用cglib[Code Generation Library]实现动态代理,并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码,下面通过一个例子看看使用CGLib如何实现动态代理。

1、定义业务逻辑

public class UserServiceImpl {  public void add() { System.out.println("This is add service"); } public void delete(int id) { System.out.println("This is delete service:delete " + id ); } }
 

2、实现MethodInterceptor接口,定义方法的拦截器

public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { System.out.println("Before:" + method); Object object = proxy.invokeSuper(obj, arg); System.out.println("After:" + method); return object; } }
 

3、利用Enhancer类生成代理类;

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MyMethodInterceptor()); UserServiceImpl userService = (UserServiceImpl)enhancer.create();
userService.add(); 

4、userService.add()的执行结果:

Before: add
This is add service
After: add

代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。

cglib字节码生成

Enhancer是CGLib的字节码增强器,可以方便的对类进行扩展,内部调用GeneratorStrategy.generate方法生成代理类的字节码,通过以下方式可以生成class文件。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\\\Code\\\\whywhy\\\\target\\\\classes\\\\zzzzzz")

使用 反编译工具 procyon 查看代理类实现

java -jar procyon-decompiler-0.5.30.jar UserService$$EnhancerByCGLIB$$394dddeb;

反编译之后的代理类add方法实现如下:

import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;//
// Decompiled by Procyon v0.5.30
// public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory
{private boolean CGLIB$BOUND;private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$add$0$Method; private static final MethodProxy CGLIB$add$0$Proxy; private static final Object[] CGLIB$emptyArgs; static void CGLIB$STATICHOOK2() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb"); final Class<?> forName3; CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0]; CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0"); } final void CGLIB$add$0() { super.add(); } public final void add() { MethodInterceptor cglib$CALLBACK_2; MethodInterceptor cglib$CALLBACK_0; if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) { CGLIB$BIND_CALLBACKS(this); cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0); } if (cglib$CALLBACK_0 != null) { cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy); return; } super.add(); } static { CGLIB$STATICHOOK2(); } }

通过cglib生成的字节码相比jdk实现来说显得更加复杂。
1、代理类UserService$$EnhancerByCGLIB$$394dddeb继承了委托类UserSevice,且委托类的final方法不能被代理;
2、代理类为每个委托方法都生成两个方法,以add方法为例,一个是重写的add方法,一个是CGLIB$add$0方法,该方法直接调用委托类的add方法;
3、当执行代理对象的add方法时,会先判断是否存在实现了MethodInterceptor接口的对象cglib$CALLBACK_0,如果存在,则调用MethodInterceptor对象的intercept方法:

public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) {System.out.println("Before:" + method); Object object = proxy.invokeSuper(obj, arg); System.out.println("After:" + method); return object; }

参数分别为:1、代理对象;2、委托类方法;3、方法参数;4、代理方法的MethodProxy对象(方法代理对象)。

4、每个被代理的方法都对应一个MethodProxy对象,methodProxy.invokeSuper方法最终调用委托类的add方法,实现如下:

public Object invokeSuper(Object obj, Object[] args) throws Throwable { try { init(); FastClassInfo fci = fastClassInfo; return fci.f2.invoke(fci.i2, obj, args); } catch (InvocationTargetException e) { throw e.getTargetException(); } }

单看invokeSuper方法的实现,似乎看不出委托类add方法调用,在MethodProxy实现中,通过FastClassInfo维护了委托类和代理类的FastClass。

private static class FastClassInfo {FastClass f1;FastClass f2;int i1; int i2; }

以add方法的methodProxy为例,f1指向委托类对象,f2指向代理类对象,i1和i2分别是方法add和CGLIB$add$0在对象中索引位置。

FastClass实现机制

FastClass其实就是对Class对象进行特殊处理,提出下标概念index,通过索引保存方法的引用信息,将原先的反射调用,转化为方法的直接调用,从而体现所谓的fast,下面通过一个例子了解一下FastClass的实现机制。
1、定义原类

class Test {public void f(){ System.out.println("f method"); } public void g(){ System.out.println("g method"); } }

2、定义Fast类

class FastTest {public int getIndex(String signature){ switch(signature.hashCode()){ case 3078479: return 1; case 3108270: return 2; } return -1; } public Object invoke(int index, Object o, Object[] ol){ Test t = (Test) o; switch(index){ case 1: t.f(); return null; case 2: t.g(); return null; } return null; } }

在FastTest中有两个方法,getIndex中对Test类的每个方法根据hash建立索引,invoke根据指定的索引,直接调用目标方法,避免了反射调用。所以当调用methodProxy.invokeSuper方法时,实际上是调用代理类的CGLIB$add$0方法,CGLIB$add$0直接调用了委托类的add方法。

jdk和cglib动态代理实现的区别

1、jdk动态代理生成的代理类和委托类实现了相同的接口;
2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

说说 Spring AOP 原理相关推荐

  1. modelandview使用过程_面试问烂的 Spring AOP 原理、Spring MVC 过程

    点击上方 Java后端,选择 设为星标 优质文章,及时送达 作者:莫那一鲁道链接:www.jianshu.com/p/e18fd44964eb Spring AOP ,SpringMVC ,这两个应该 ...

  2. 面试问烂的 Spring AOP 原理、SpringMVC 过程(求求你别问了)

    Spring AOP ,SpringMVC ,这两个应该是国内面试必问题,网上有很多答案,其实背背就可以.但今天笔者带大家一起深入浅出源码,看看他的原理.以期让印象更加深刻,面试的时候游刃有余. Sp ...

  3. spring aop原理_Spring知识点总结!已整理成142页离线文档(源码笔记+思维导图)...

    写在前面 由于Spring家族的东西很多,一次性写完也不太现实.所以这一次先更新Spring[最核心]的知识点:AOP和IOC 无论是入门还是面试,理解AOP和IOC都是非常重要的.在面试的时候,我没 ...

  4. Spring AOP原理浅析及入门实例

    上篇问题及Spring AOP实现原理浅析 上篇说了一个AOP编程问题,那是一个错误的AOP案例.它的错误在A类中,再次粘贴A类代码: @Componentpublic class AImpl imp ...

  5. 史上最烂 spring aop 原理分析

    盗引·中篇·spring aop spring aop: jdk 动态代理和 cglib 动态代理的特点.区别.使用方式.原理及各自对反射的优化.二者在 spring 中的统一.通知顺序.从 @Asp ...

  6. Spring AOP原理及使用,面试必备

    a) 什么是AOP? AOP(Aspect Oriented Programming),也就是面向切面编程,是对面向对象编程OOP的一种补充:通过"横切"技术剖解开封装的对象内部, ...

  7. 动态代理jdk和cglib区别、注意事项(private,static,final)、spring aop原理

    文章目录 一.代理简聊 二.静态代理 三.动态代理 1.jdk动态代理 2.cglib动态代理 四.动态代理总结.注意事项 1.jdk动态代理与cglib的区别. 2.注意事项及分析. 五.aop原理 ...

  8. Spring原理学习系列之三:Spring AOP原理(从源码层面分析)-------上部

    引言 本文是Spring原理分析的第三篇博文,主要阐述Spring AOP相关概念,同时从源码层面分析AOP实现原理.对于AOP原理的理解有利于加深对Spring框架的深入理解.同时我也希望可以探究S ...

  9. Spring AOP 原理

    总结 基于AspectJ注解的  @Before,@After....,基于原生的,基于org.aopalliance.intercept.MethodInterceptor的,三种方式 最终的在sp ...

  10. 面试题--对于Spring Aop原理的理解

    这段时间对于Spring中的Aop原理做了一些了解,整理了一下大概分为以下几点,aop是什么以及aop实现原理,aop中常用的术语以及代理方式等等: 1.什么是 aop? aop是面向切面编程,不同于 ...

最新文章

  1. 阿里云oss云存储图片上传在wangEditor富文本编辑器上的集成
  2. [模仿微软Live.cn]JavaScript输入邮箱自动提示
  3. 关于Postfix邮件网关无法重启问题
  4. linux/centos/集群安装ntp时间同步,自身实验成功
  5. linux下网络排错与查看
  6. 音视频技术开发周刊 | 151
  7. C#连接池的详细分析(转)
  8. 在 Yii框架中使用session 的笔记:
  9. 将Maven项目发布到Nexus私服
  10. IOT(5)---物联网系统框架介绍
  11. 【mysql union all limit的使用】
  12. Vultr 修改 Root 密码
  13. 《智能优化算法及其MATLAB实现》书籍出版啦
  14. AssionShop开源B2C电子商务系统-概述(转载)
  15. 潘多拉PandoraBox官网下载地址
  16. Google Earth Engine(GEE)基于哨兵数据计算植被覆盖度—以宁夏为例
  17. dpbs和pbs的区别_简单问题:PBS缓冲液到底是什么?
  18. 大海为什么是蓝色的?
  19. Linux: sd 0:0:1:0: timing out command, waited 1080s, 访问磁盘错误
  20. 目前vivo手机最新android版本,vivo-智能手机官方网站

热门文章

  1. 固态+机械(uefi类型的bios),用easybcd安装win10+ubuntu16.04双系统
  2. ValueError: You are trying to load a weight file containing 12 layers into a model with 2 layers.
  3. ValueError: No engine for filetype: 'csv'解决与书本7-2代码改写
  4. 图像处理-自适应中值滤波
  5. 2.2 矩阵基本运算
  6. 优酷html5视频没有弹幕,优酷弹幕怎么设置 优酷PC端怎么屏蔽底下弹幕?
  7. spring 全局异常处理
  8. 10 大方法,让您的客服中心提供更出色的服务
  9. 1027. 打印沙漏(20)
  10. 【转】Nginx服务器的反向代理proxy_pass配置方法讲解