原文同步发表至个人博客【夜月归途】

原文链接:http://www.guitu18.com/se/java/2018-06-29/18.html

作者:夜月归途
出处:http://www.guitu18.com/
本博客中未标明转载的文章归作者夜月归途和博客园所有。
欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本博客关于Java动态代理相关内容直达链接:

  1. JDK动态代理浅析
  2. Cglib动态代理浅析
  3. JDK动态代理深入理解分析并手写简易JDK动态代理(上)
  4. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

Java中的动态代理设计模式是非常经典且非常重要的设计模式之一,在感叹设计者的天才设计至于,我们想去探究一下这个设计模式是如何来实现的;

著名的spring框架的AOP的原理就是Java的动态代理机制;

在Spring中动态代理是实现有两种:JDK动态代理和Cglib动态代理,本篇分析的是Cglib动态代理的实现;

JDK动态代理之前已经做过分析了[ JDK动态代理浅析 ],通过案例我们知道了,Java动态代理的强大之处;但是不难看出来使用JDK动态代理也有着它的局限性,JDK动态代理是在JVM内部动态的生成class字节码对象,但是JDK动态代理只能针对接口进行操作,也就是只能对接口的实现类去进行代理;

现在分析一下另一种常用的动态代理技术:Cglib动态代理;

CGLIB(Code Generation Library)是一个开源项目;
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口;
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类;
Cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;
所以Cglib可以为无接口的类直接做代理,当然有接口的类也是可以的并无影响。

因为采用的是继承,所以不能对final修饰的类进行代理;Cglib的使用限制无疑要比JDK动态代理要宽松很多;

废话不多说,先上案例;

这里说明一下,如果是Spring框架那么无所谓,Spring框架的spring-core.jar包中已经集成了cglib与asm;如果你要单独使用CGLIB,那么需要导入cglib的jar包和asm相关jar包;

Programmer类:

public class Programmer {
public void work(String name) {
System.out.println(name + " 正在工作...");
}
}

CglibProxyFactory代理类:

public class CglibProxyFactory implements MethodInterceptor {
private Object target;
public CglibProxyFactory(Object target) {
this.target = target;
}
public Object getProxyInstance() {
// 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置目标对象的Class
enhancer.setSuperclass(target.getClass());
// 设置回调操作,相当于InvocationHandler
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("开始事务...");
// 两种方式执行方法,第二行注释掉的,和当前行代码效果相同,下面会分析;
Object invoke = method.invoke(target, args);
// Object invoke = methodProxy.invokeSuper(proxy, args);
System.out.println("提交/回滚事务...");
return invoke;
}
}

Cglib创建代理的方式非常简单,是通过Enhancer对象,需要给它set两个参数:

设置目标对象的Class; 设置回调操作,相当于InvocationHandler,需要实现MethodInterceptor 接口; 之后执行create()方法即可返回目标对象的代理对象;

这里为了方便,直接让CglibProxyFactory实现了MethodInterceptor 接口,需要实现重写MethodInterceptor.intercept()方法;

在intercept()方法中,其实跟InvocationHandler.invoke中做的事是一样的,我们需要调用method.invoke()去执行方法;

在案例中,还有注释掉的一行:Object invoke = methodProxy.invokeSuper(proxy, args);

这个和上面的 Object invoke = method.invoke(target, args); 是一样的效果,都是代理执行方法,下面进行说明;

从代码中看出,intercept()的参数比JDK的invoke()多了一个,前三个参数跟JDK的invoke()方法一样不多说,第四个参数MethodProxy methodProxy是当前代理对象执行的方法的一个代理,也就是MethodProxy methodProxy是第二个参数Method method的代理;

前面我们说过,Cglib动态代理的机制是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理;也就是参数一Object proxy实际上是我们的目标对象Object target的一个子类;

我们直接使用 method.invoke(target, args) 执行方法,和使用methodProxy.invokeSuper(proxy, args) 执行是一个什么关系呢;

第一个被代理类target直接调用method执行没问题,第二个其实是target的子类proxy去掉用父的方法,我第一次看这里也是有点绕哈,但是其实就是这么个关系,这两个方法执行的效果是一样的,但是通常我们只使用第一种方式;

测试执行,贴测试代码:

public class CglibProxyFactoryTest {
public static void main(String[] args) {
Programmer programmer = new Programmer();
CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(programmer);
Programmer instance = (Programmer) cglibProxyFactory.getProxyInstance();
instance.work("夜月归途");
}
}

控制台输出:

开始事务...
夜月归途 正在工作...
提交/回滚事务...

看到这里和JDK的动态代理是一样的,Cglib的的动态代理没有接口的限制,有接口的类和无接口的类都可以被代理,只要不是final的;

在这里,同样代码可以精简一下:

public class CglibProxyFactory {
public static Object getProxyInstance(Object target) {
// 创建 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置目标对象的Class
enhancer.setSuperclass(target.getClass());
// 设置回调操作,相当于InvocationHandler
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable {
System.out.println("开始事务...");
// Object invoke1 = method.invoke(target, args);
Object invoke2 = methodProxy.invokeSuper(proxy, args);
System.out.println("提交/回滚事务...");
return invoke2;
}
});
return enhancer.create();
}
}

以匿名内部类形式创建代理对象,这样子代码清爽许多;

尾巴:

  1. 静态代理是通过在代码中显式定义一个业务实现类一个代理,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
  2. JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
  3. CGlib动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
  4. 如果目标对象有接口,优先使用jdk动态代理,如果目标对象无接口,使用cglib动态代理,如:Spring框架的代理选择机制就是这样的;

Cglib动态代理浅析的更多相关文章

  1. JDK动态代理浅析

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html 作者:夜月归途 出处:http://www.guitu ...

  2. CGLib动态代理原理及实现

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...

  3. Spring中的cglib动态代理

    Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...

  4. 【Java EE 学习 51】【Spring学习第三天】【cglib动态代理】【AOP和动态代理】【切入点表达式】

    一.cglib动态代理 1.简介 (1)CGlib是一个强大的,高性能,高质量的Code生成类库.它可以在运行期扩展Java类与实现Java接口. (2) 用CGlib生成代理类是目标类的子类. (3 ...

  5. JDK动态代理与CGLib动态代理

    1.JDK动态代理 JDK1.3以后java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及到java.lang.reflect ...

  6. Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 /** * 业务接 ...

  7. [z]Java代理(jdk静态代理、动态代理和cglib动态代理)

    一.代理是Java常用的设计模式,代理类通过调用被代理类的相关方法,并对相关方法进行增强.加入一些非业务性代码,比如事务.日志.报警发邮件等操作. 二.jdk静态代理 1.业务接口 1 2 3 4 5 ...

  8. Atitit 代理CGLIB 动态代理 AspectJ静态代理区别

    Atitit 代理CGLIB 动态代理 AspectJ静态代理区别 1.1. AOP 代理主要分为静态代理和动态代理两大类,静态代理以 AspectJ 为代表:而动态代理则以 spring AOP 为 ...

  9. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

随机推荐

  1. Angular 2项目的环境配置和项目搭建

    AngularJS2 发布于2016年9月份,它是基于ES6来开发的. AngularJS2 是一款开源JavaScript库,由Google维护,用来协助单一页面应用程序运行.AngularJS2 ...

  2. vue动态添加路由addRoutes之不能将动态路由存入缓存

    在我不知道vue的路由还可以通过addRoutes动态添加时,我只知道vue的路由都是写死在路由表中的,每当跳转时再去加载相应的路由.直到在一个新公司接到需要根据用户的权限显示不同的菜单的需求时才知道 ...

  3. cadence钻孔文件及光绘文件的生成

    完成PCB布线之后,需要生成钻孔文件和光绘文件交给PCB厂家制作PCB板,下面总结详细方法!

  4. c++ 读取不了hdf5文件中的字符串

    问题描述: 在拿到一个hdf5文件,想用c++去读取文件中的字符串,但是会报错:read failed ps: c++读取hdf5的字符串方法见:https://support.hdfgroup.or ...

  5. react-native 常用的一些插件

    react-native 常用的一些插件 最近在做react-native的app,用到的一些好用的插件,在这儿记录一下 由于返回的后台内容是富文本编辑器Quill,返回的的是Delta对象,使用了q ...

  6. 约瑟夫环问题 --链表 C语言

    总共有m个人在圆桌上,依次报名,数到第n个数的人退出圆桌,下一个由退出人下一个开始继续报名,循环直到最后一个停止将编号输出 #include <stdio.h>#include <s ...

  7. SQL 查询当前时间

    Mysql: select date_format(now(),'%Y-%m-%d'); Oracle: Oracle中如何获取系统当前时间进行语句的筛选是SQL语句的常见功能 获取系统当前时间dat ...

  8. 【javascript】函数中的this的四种绑定形式 — 大家准备好瓜子,我要讲故事啦~~

       javascript中的this和函数息息相关,所以今天,我就给大家详细地讲述一番:javascript函数中的this   一谈到this,很多让人晕晕乎乎的抽象概念就跑出来了,这里我就只说最 ...

  9. dattime和timestamp的异同

    相同点: 1)都可以用来表示YYYY-MM-DD HH:MM:SS[.FRACTION]的时间; 不同点: 1)两者的存储方式不一样 a)timestamp他把客户端插入的时间从当前时区转化为UTC( ...

  10. 基于Java实现简化版本的布隆过滤器

    一.布隆过滤器: 布隆过滤器(Bloom Filter)是1970年由布隆提出的.它实际上是一个很长的二进制向量和一系列随机映射函数.布隆过滤器可以用于检索一个元素是否在一个集合中.它的优点是空间效率 ...