0 概述

CGLIB基于ASM实现。提供比反射更为强大的动态特性。使用CGLIB可以非常方便的实现的动态代理。

0.1 CGLIB包结构

  • net.sf.cglib.core    底层字节码处理类。
  • net.sf.cglib.transform    该包中的类用于class文件运行时转换或编译时转换。
  • net.sf.cglib.proxy    该包中的类用于创建代理和方法拦截。
  • net.sf.cglib.reflect    该包中的类用于快速反射,并提供了C#风格的委托。
  • net.sf.cglib.util    集合排序工具类。
  • net.sf.cglib.beans    JavaBean工具类。

1 使用CGLIB实现动态代理

1.1 CGLIB代理相关的类

  • net.sf.cglib.proxy.Enhancer    主要的增强类。
  • net.sf.cglib.proxy.MethodInterceptor    主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
  • net.sf.cglib.proxy.MethodProxy    JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

cglib是通过动态的生成一个子类去覆盖所要代理类的非final方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors)。

CGLIB代理相关的常用API如下图所示:

net.sf.cglib.proxy.Callback接口在CGLIB包中是一个重要的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。

net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要。对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型:

  • net.sf.cglib.proxy.FixedValue 为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
  • net.sf.cglib.proxy.NoOp NoOp回调把对方法调用直接委派到这个方法在父类中的实现。
  • net.sf.cglib.proxy.LazyLoader 当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
  • net.sf.cglib.proxy.Dispatcher Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
  • net.sf.cglib.proxy.ProxyRefDispatcher ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

1.2 CGLIB动态代理的基本原理

CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数,如图

在intercept()函数里,除执行代理类的原因方法,在原有方法前后加入其他需要实现的过程,改变原有方法的参数值,即可以实现对原有类的代理了。这似于AOP中的around advice。

1.3 使用MethodInterceptor接口实现方法回调

当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常。

net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用 来实现拦截(intercept)方法的调用。

MethodInterceptor接口只定义了一个方法:

public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

参数Object object是被代理对象,不会出现死循环的问题。

参数java.lang.reflect.Method method是java.lang.reflect.Method类型的被拦截方法。

参数Object[] args是被被拦截方法的参数。

参数MethodProxy proxy是CGLIB提供的MethodProxy 类型的被拦截方法。

注意:

1、若原方法的参数存在基本类型,则对于第三个参数Object[] args会被转化成类的类型。如原方法的存在一个参数为int,则在intercept方法中,对应的会存在一个Integer类型的参数。

2、若原方法为final方法,则MethodInterceptor接口无法拦截该方法。

1.3.1 实现MethodInterceptor接口

class MethodInterceptorImpl implements MethodInterceptor {

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println("Before invoke " + method);

Object result = proxy.invokeSuper(obj, args);

System.out.println("After invoke" + method);

return result;

}

}

Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。

由于性能的原因,对原始方法的调用使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象。

1.4 使用CGLIB代理最核心类Enhancer生成代理对象

net.sf.cglib.proxy.Enhancer中有几个常用的方法:

  • void setSuperclass(java.lang.Class superclass) 设置产生的代理对象的父类。
  • void setCallback(Callback callback) 设置CallBack接口的实例。
  • void setCallbacks(Callback[] callbacks) 设置多个CallBack接口的实例。
  • void setCallbackFilter(CallbackFilter filter) 设置方法回调过滤器。
  • Object create() 使用默认无参数的构造函数创建目标对象。
  • Object create(Class[], Object[]) 使用有参数的构造函数创建目标对象。参数Class[] 定义了参数的类型,第二个Object[]是参数的值。

注意:在参数中,基本类型应被转化成类的类型。

基本代码:

public Object createProxy(Class targetClass) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(targetClass);

enhancer.setCallback(new MethodInterceptorImpl ());

return enhancer.create();

}

createProxy方法返回值是targetClass的一个实例的代理。

1.5 使用CGLIB继进行动态代理示例

例1:使用CGLIB生成代理的基本使用。

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class TestMain {

public static void main(String[] args) {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(Cglib.class);

enhancer.setCallback(new HelloProxy());

Cglib cglibProxy = (Cglib)enhancer.create();

cglibProxy.cglib();

}

}

class Cglib{

public void cglib(){

System.out.println("CGLIB");

}

}

class HelloProxy implements MethodInterceptor{

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println("Hello");

Object object = proxy.invokeSuper(obj, args);

System.out.println("Powerful!");

return object;

}

}

输出内容:

Hello

CGLIB

Powerful!

例2:使用CGLIB创建一个Dao工厂,并展示一些基本特性。

public interface Dao {

void add(Object o);

void add(int i);

void add(String s);

}

public class DaoImpl implements Dao {

@Override

public void add(Object o) {

System.out.println("add(Object o)");

}

@Override

public void add(int i) {

System.out.println("add(int i)");

}

public final void add(String s) {

System.out.println("add(String s)");

}

}

public class Proxy implements MethodInterceptor {

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println("拦截前...");

// 输出参数类型

for (Object arg : args) {

System.out.print(arg.getClass() + ";");

}

Object result = proxy.invokeSuper(obj, args);

System.out.println("拦截后...");

return result;

}

}

public class DaoFactory {

public static Dao create() {

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(DaoImpl.class);

enhancer.setCallback(new Proxy());

Dao dao = (Dao) enhancer.create();

return dao;

}

}

public class TestMain {

public static void main(String[] args) {

Dao dao = DaoFactory.create();

dao.add(new Object());

dao.add(1);

dao.add("1");

}

}

输出内容:

拦截前...

class java.lang.Object;add(Object o)

拦截后...

拦截前...

class java.lang.Integer;add(int i)

拦截后...

add(String s)

2 回调过滤器CallbackFilter

net.sf.cglib.proxy.CallbackFilter有选择的对一些方法使用回调。

CallbackFilter可以实现不同的方法使用不同的回调方法。所以CallbackFilter称为"回调选择器"更合适一些。

CallbackFilter中的accept方法,根据不同的method返回不同的值i,这个值是在callbacks中callback对象的序号,就是调用了callbacks[i]。

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Callback;

import net.sf.cglib.proxy.CallbackFilter;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import net.sf.cglib.proxy.NoOp;

public class CallbackFilterDemo {

public static void main(String[] args) {

// 回调实例数组

Callback[] callbacks = new Callback[] { new MethodInterceptorImpl(), NoOp.INSTANCE };

// 使用enhancer,设置相关参数。

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(User.class);

enhancer.setCallbacks(callbacks);

enhancer.setCallbackFilter(new CallbackFilterImpl());

// 产生代理对象

User proxyUser = (User) enhancer.create();

proxyUser.pay(); // 买

proxyUser.eat(); // 吃

}

/**

* 回调过滤器类。

*/

private static class CallbackFilterImpl implements CallbackFilter {

@Override

public int accept(Method method) {

String methodName = method.getName();

if ("eat".equals(methodName)) {

return 1; // eat()方法使用callbacks[1]对象拦截。

} else if ("pay".equals(methodName)) {

return 0; // pay()方法使用callbacks[0]对象拦截。

}

return 0;

}

}

/**

* 自定义回调类。

*/

private static class MethodInterceptorImpl implements MethodInterceptor {

@Override

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println("Before invoke " + method);

Object result = proxy.invokeSuper(obj, args); // 原方法调用。

System.out.println("After invoke" + method);

return result;

}

}

}

class User {

public void pay() {

System.out.println("买东西");

}

public void eat() {

System.out.println("吃东西");

}

}

输出结果:

Before invoke public void sjq.cglib.filter.User.pay()

pay()

After invokepublic void sjq.cglib.filter.User.pay()

eat()

3 CGLIB对Mixin的支持

CGLIB的代理包net.sf.cglib.proxy.Mixin类提供对Minix编程的支持。Minix允许多个对象绑定到一个单个的大对象上。在代理中对方法的调用委托到下面相应的对象中。 这是一种将多个接口混合在一起的方式, 实现了多个接口。

Minix是一种多继承的替代方案, 很大程度上解决了多继承的很多问题, 实现和理解起来都比较容易。

import net.sf.cglib.proxy.Mixin;

public class MixinDemo {

public static void main(String[] args) {

//接口数组

Class<?>[] interfaces = new Class[] { MyInterfaceA.class, MyInterfaceB.class };

//实例对象数组

Object[] delegates = new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };

//Minix组合为o对象。

Object o = Mixin.create(interfaces, delegates);

MyInterfaceA a = (MyInterfaceA) o;

a.methodA();

MyInterfaceB b = (MyInterfaceB) o;

b.methodB();

System.out.println("\r\n 输出Mixin对象的结构...");

Class clazz = o.getClass();

Method[] methods = clazz.getDeclaredMethods();

for (int i = 0; i < methods.length; i++) {

System.out.println(methods[i].getName());

}

System.out.println(clazz);

}

}

interface MyInterfaceA {

public void methodA();

}

interface MyInterfaceB {

public void methodB();

}

class MyInterfaceAImpl implements MyInterfaceA {

@Override

public void methodA() {

System.out.println("MyInterfaceAImpl.methodA()");

}

}

class MyInterfaceBImpl implements MyInterfaceB {

@Override

public void methodB() {

System.out.println("MyInterfaceBImpl.methodB()");

}

}

输出结果:

MyInterfaceAImpl.methodA()

MyInterfaceBImpl.methodB()

输出Mixin对象的结构...

methodA

methodB

newInstance

class sjq.cglib.mixin.MyInterfaceA$$MixinByCGLIB$$d1f6261a

4 CGLIB用来对象之间拷贝属性

package sjq.cglib.bean.copy;

import net.sf.cglib.beans.BeanCopier;

public class PropertyCopyDemo {

public static void main(String[] args) {

//两个对象

Other other = new Other("test", "1234");

Myth myth = new Myth();

System.out.println(other);

System.out.println(myth);

//构建BeanCopier,并copy对象的属性值。

BeanCopier copier = BeanCopier.create(Other.class, Myth.class, false);

copier.copy(other, myth, null);

System.out.println(other);

System.out.println(myth);

}

}

class Other {

private String username;

private String password;

private int age;

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

public Other(String username, String password) {

super();

this.username = username;

this.password = password;

}

@Override

public String toString() {

return "Other: " + username + ", " + password + ", " + age;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

class Myth {

private String username;

private String password;

private String remark;

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

@Override

public String toString() {

return "Myth: " + username + ", " + password + ", " + remark;

}

public void setRemark(String remark) {

this.remark = remark;

}

public String getRemark() {

return remark;

}

}

运行结果如下:

Other: test, 1234, 0

Myth: null, null, null

Other: test, 1234, 0

Myth: test, 1234, null

5 使用CGLIB动态生成Bean

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

import net.sf.cglib.beans.BeanGenerator;

import net.sf.cglib.beans.BeanMap;

/**

*动态实体bean

*/

public class CglibBean {

/**

* 实体Object

*/

public Object object = null;

/**

* 属性map

*/

public BeanMap beanMap = null;

public CglibBean() {

super();

}

@SuppressWarnings("unchecked")

public CglibBean(Map<String, Class> propertyMap) {

this.object = generateBean(propertyMap);

this.beanMap = BeanMap.create(this.object);

}

/**

* 给bean属性赋值

* @param property属性名

* @param value值

*/

public void setValue(String property, Object value) {

beanMap.put(property, value);

}

/**

* 通过属性名得到属性值

* @param property属性名

*/

public Object getValue(String property) {

return beanMap.get(property);

}

/**

* 得到该实体bean对象。

*/

public Object getObject() {

return this.object;

}

/**

* 生成Bean

* @param propertyMap

* @return

*/

@SuppressWarnings("unchecked")

private Object generateBean(Map<String, Class> propertyMap) {

BeanGenerator generator = new BeanGenerator();

Set keySet = propertyMap.keySet();

for (Iterator i = keySet.iterator(); i.hasNext();) {

String key = (String) i.next();

generator.addProperty(key, (Class) propertyMap.get(key));

}

return generator.create();

}

}

测试并使用动态Bean

import java.lang.reflect.Method;

import java.util.HashMap;

/**

* Cglib测试类

*/

public class CglibTest {

@SuppressWarnings("unchecked")

public static void main(String[] args) throws ClassNotFoundException {

// 设置类成员属性

HashMap<String, Class> propertyMap = new HashMap<String, Class>();

propertyMap.put("id", Class.forName("java.lang.Integer"));

propertyMap.put("name", Class.forName("java.lang.String"));

propertyMap.put("address", Class.forName("java.lang.String"));

// 生成动态Bean

CglibBean bean = new CglibBean(propertyMap);

// 给Bean设置值

bean.setValue("id", new Integer(123));

bean.setValue("name", "454");

bean.setValue("address", "789");

// 从Bean中获取值,当然了获得值的类型是Object

System.out.println(">>id=" + bean.getValue("id"));

System.out.println(">>name=" + bean.getValue("name"));

System.out.println(">>address=" + bean.getValue("address"));// 获得bean的实体

Object object = bean.getObject();

// 通过反射查看所有方法名

Class clazz = object.getClass();

Method[] methods = clazz.getDeclaredMethods();

for (int i = 0; i < methods.length; i++) {

System.out.println(methods[i].getName());

}

}

}

输出:

>>id=123

>>name=454

>>address=789

setId

getAddress

getName

getId

setName

setAddress

class net.sf.cglib.empty.Object$$BeanGeneratorByCGLIB$$1d39cfaa

本篇学习文档参考于:

http://jnb.ociweb.com/jnb/jnbNov2005.html

http://wenku.baidu.com/view/3f92297c27284b73f24250b9.html

http://www.cnblogs.com/icejoywoo/

http://www.blogjava.net/calvin/archive/2005/11/28/21741.html

http://m635674608.iteye.com/blog/1435221
等其他高手博客提供的资料。

CGLIB学习笔记的更多相关文章

  1. [原创]java WEB学习笔记104:Spring学习---AOP 前奏,通过一个问题引入动态代理

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. [原创]java WEB学习笔记88:Hibernate学习之路-- -Hibernate检索策略(立即检索,延迟检索,迫切左外连接检索)

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  3. [原创]java WEB学习笔记77:Hibernate学习之路---Hibernate 版本 helloword 与 解析,.环境搭建,hibernate.cfg.xml文件及参数说明,持久化类,对象-关系映射文件.hbm.xml,Hibernate API (Configuration 类,SessionFactory 接口,Session 接口,Transaction(事务))

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  4. 不错的Spring学习笔记(转)

    Spring学习笔记(1)----简单的实例 ---------------------------------   首先需要准备Spring包,可从官方网站上下载.   下载解压后,必须的两个包是s ...

  5. mybatis学习笔记(五):mybatis 逆向工程

    mybatis学习笔记(五):mybatis 逆向工程 在日常开发中,如果数据库中存在多张表,自己手动创建 多个pojo 类和编写 SQL 语法配置文件,未免太过繁琐,mybatis 也提供了一键式生 ...

  6. mybatis 学习笔记(四):mybatis 和 spring 的整合

    mybatis 学习笔记(四):mybatis 和 spring 的整合 尝试一下整合 mybatis 和 spring. 思路 spring通过单例方式管理SqlSessionFactory. sp ...

  7. mybatis 学习笔记(一):mybatis 初认识

    mybatis 学习笔记(一):mybatis 初认识 简介 MyBatis是一个Java持久层框架,它通过XML描述符或注解把对象与存储过程或SQL语句关联起来.mybatis 可以将 prepar ...

  8. Spring学习笔记之aop动态代理(3)

    Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. Person ...

  9. 开源规则引擎 Drools 学习笔记 之 -- 1 cannot be cast to org.drools.compiler.kie.builder.impl.InternalKieModule

    直接进入正题 我们在使用开源规则引擎 Drools 的时候, 启动的时候可能会抛出如下异常: Caused by: java.lang.ClassCastException: cn.com.cheng ...

随机推荐

  1. HTML Imports

    为什么需要导入? 先想想你在 web 上是如何加载不同类型的资源.对于 JS,我们有 <script src>.<link rel="stylesheet"> ...

  2. 多线程操作中为什么使用while而不是if来做判断状态是否就绪

    在多线程操作中,我们常常会遇到需要先判断信号量状态是否就绪,然后执行后续操作的场景.这里对状态的判断使用的是while而不是单线程下常用的if. 以下示例展示了一个简单的生产者-消费者模型:当队列满的 ...

  3. Keil C51调试程序时, 对ROM的查看以及RAM 查看或修改

    在Keil 里使用 DeBug 模式时,如要 查看外部 RAM 的数据 或查看 ACC 的内容可以进行以下操作; sysGetTxMode: LCALL Com0185(C:2B95) ,sysGet ...

  4. 移植qt5.3.1到arm

    最近刚入手一款开发板(比较低端,刚毕业工作还没一个月,穷屌丝一个).在学校以前都是用的是Friendly Arm的6410,使用的qt版本是使用的最多的4.7.0版本,入手的板子是4.7.4.qt5出 ...

  5. mysql常见字符串处理函数结束

    一.简明总结 ASCII(char) 返回字符的ASCII码值 BIT_LENGTH(str) 返回字符串的比特长度 CONCAT(s1,s2…,sn) 将s1,s2…,sn连接成字符串 CONCAT ...

  6. 14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器

    14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器 这个章节描述技术关于移动或者复制一些或者所 ...

  7. 深入浅出Node.js (9) - 玩转进程

    9.1 服务模型的变迁 9.1.1 石器时代:同步 9.1.2 青铜时代:复制进程 9.1.3 白银时代:多线程 9.1.4 黄金时代:事件驱动 9.2 多进程架构 9.2.1 创建子进程 9.2.2 ...

  8. 【转】SendMessage及WPRAME、LPARAME

    原文网址:http://www.cnblogs.com/renyuan/archive/2012/11/26/2789103.html SendMessage及WPRAME.LPARAME typed ...

  9. 贪心(哈夫曼树):HDU 5884 sort

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA2QAAAKACAIAAAB8KCy/AAAgAElEQVR4nOy9a5Adx3UmWL+kHxuekU ...

  10. HDU 5920 Ugly Problem 【模拟】 (2016中国大学生程序设计竞赛(长春))

    Ugly Problem Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Tota ...