`
vefan
  • 浏览: 85571 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

使用CGLIB包创建动态代理

阅读更多

1.CGLIB包的介绍 代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包
CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(moke)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。
CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

Figure 1: CGLIB Library and ASM Bytecode Framework图一显示了和CGLIB包和一些框架和语言的关系图。需要注意的是一些框架例如Spring AOP和Hibernate,它们为了满足需要经常同时使用JDK的动态代理和CGLIB包。Hiberater使用JDK的动态代理实现一个专门为WebShere应用服务器的事务管理适配器;Spring AOP,如果不强制使用CGLIB包,默认情况是使用JDK的动态代理来代理接口。

2.CGLIB 代理的APIS
CGLIB包的基本代码很少,当学起来有一定的困难,主要是缺少文档,这也是开源软件的一个不足之处。目前CGLIB的版本是(2.1.2),主要由一下部分组成:
net.sf.cglib.core
Low-level bytecode manipulation classes; Most of them are related to ASM.
net.sf.cglib.transform
Classes for class file transformations at runtime or build time
net.sf.cglib.proxy
Classes for proxy creation and method interceptions
net.sf.cglib.reflect
Classes for a faster reflection and C#-style delegates
net.sf.cglib.util
Collection sorting utilities
net.sf.cglib.beans
JavaBean related utilities
大多时候,仅仅为了动态地创建代理,你仅需要使用到代理包中很少的一些API。

正如我们先前所讨论的,CGLIB包是在ASM之上的一个高级别的层。对代理那些没有实现接口的类非常有用。本质上,它是通过动态的生成一个子类去覆盖所要代理类的不是final的方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors),这比JDK动态代理方法快多了。

Figure 2: CGLIB library APIs commonly used for proxying classes
图2为我们演示了创建一个具体类的代理时,通常要用到的CGLIB包的APIs。net.sf.cglib.proxy.Callback接口在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。net.sf.cglib.pr用oxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用来实现拦截(intercept)方法的调用。这个接口只定义了一个方法
public Object intercept(Object object, java.lang.reflect.Method method,
Object[] args, MethodProxy proxy) throws Throwable;



当net.sf.cglib.proxy.MethodInterceptor做为所有代理方法的回调(callback)时,当对基于代理的方法调用时,在调用原对象的方法的之前会调用这个方法,如图3所示。第一个参数是代理对像,第二和第三个参数分别是拦截的方法和方法的参数。原来的方法可能通过使用java.lang.reflect.Method对象的一般反射调用,或者使用net.sf.cglib.proxy.MethodProxy对象调用。net.sf.cglib.proxy.MethodProxy通常被首选使用,因为它更快。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。



Figure 3: CGLIB MethodInterceptor

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一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。
如图3所示,代理类的所以方法经常会用到回调(callback),当是你也可以使用net.sf.cglib.proxy.CallbackFilter 有选择的对一些方法使用回调(callback),这种考虑周详的控制特性在JDK的动态代理中是没有的。在JDK代理中,对java.lang.reflect.InvocationHandler方法的调用对代理类的所以方法都有效。

除了代理类外,CGLIB通过提供一个对java.lang.reflect.Proxy的drop-in替代来实现对对接口的代理。因为这种代理能力的替代很少被用到,因此相应的APIs也很少提到。

CGLIB的代理包也对net.sf.cglib.proxy.Mixin提供支持。基本上,它允许多个对象被绑定到一个单个的大对象。在代理中对方法的调用委托到下面相应的对象中。

接下来我们看看如何使用CGLIB代理APIs创建代理。

创建一个简单的代理CGLIB代理最核心的是net.sf.cglib.proxy.Enhancer类,为了创建一个代理,最起码你要用到这个类。首先,让我们使用NoOp回调创建一个代理:
/**
* Create a proxy using NoOp callback. The target class
* must have a default zero-argument constructor.
*
* @param targetClass the super class of the proxy
* @return a new proxy for a target class instance
*/
public Object createProxy(Class targetClass) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetClass);
enhancer.setCallback(NoOp.INSTANCE);
return enhancer.create();
}
返回值是target类一个实例的代理。在这个例子中,我们为net.sf.cglib.proxy.Enhancer 配置了一个单一的回调(callback)。我们可以看到很少直接创建一个简单的代理,而是创建一个net.sf.cglib.proxy.Enhancer的实例,在net.sf.cglib.proxy.Enhancer类中你可使用静态帮助方法创建一个简单的代理。一般推荐使用上面例子的方法创建代理,因为它允许你通过配置net.sf.cglib.proxy.Enhancer实例很好的控制代理的创建。

要注意的是,target类是作为产生的代理的父类传进来的。不同于JDK的动态代理,它不能在创建代理时传target对象,target对象必须被CGLIB包来创建。在这个例子中,默认的无参数构造器时用来创建target实例的。如果你想用CGLIB来创建有参数的实例,用net.sf.cglib.proxy.Enhancer.create(Class[], Object[])方法替代net.sf.cglib.proxy.Enhancer.create()就可以了。方法中第一个参数定义了参数的类型,第二个是参数的值。在参数中,基本类型应被转化成类的类型。

------------

Use a MethodInterceptor为了更好的使用代理,我们可以使用自己定义的MethodInterceptor类型回调(callback)来代替net.sf.cglib.proxy.NoOp回调。当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常AuthorizationException。其中AuthorizationService.java接口的代码如下:

package com.lizjason.cglibproxy;

import java.lang.reflect.Method;

/**
* A simple authorization service for illustration purpose.
*
* @author Jason Zhicheng Li (jason@lizjason.com)
*/
public interface AuthorizationService {
/**
* Authorization check for a method call. An AuthorizationException
* will be thrown if the check fails.
*/
void authorize(Method method);
}对net.sf.cglib.proxy.MethodInterceptor接口的实现的类AuthorizationInterceptor.java代码如下:

package com.lizjason.cglibproxy.impl;


import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

import com.lizjason.cglibproxy.AuthorizationService;


/**

* A simple MethodInterceptor implementation to

* apply authorization checks for proxy method calls.

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*

*/

public class AuthorizationInterceptor implements MethodInterceptor {

private AuthorizationService authorizationService;


/**

* Create a AuthorizationInterceptor with the given

* AuthorizationService

*/

public AuthorizationInterceptor (AuthorizationService authorizationService) {

this.authorizationService = authorizationService;

}


/**

* Intercept the proxy method invocations to inject authorization check.

* The original method is invoked through MethodProxy.

* @param object the proxy object

* @param method intercepted Method

* @param args arguments of the method

* @param proxy the proxy used to invoke the original method

* @throws Throwable any exception may be thrown; if so, super method will not be invoked

* @return any value compatible with the signature of the proxied method.

*/

public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy ) throws Throwable {

if (authorizationService != null) {

//may throw an AuthorizationException if authorization failed

authorizationService.authorize(method);

}

return methodProxy.invokeSuper(object, args);

}

}


我们可以看到在拦截方法中,首先进行权限的检查,如果通过权限的检查,拦截方法再调用目标对象的原始方法。由于性能的原因,对原始方法的调用我们使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用
java.lang.reflect.Method对象。


Use a CallbackFilter

net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:save和load,其中方法save需要权限检查,而方法load不需要权限检查。



package com.lizjason.cglibproxy.impl;


import com.lizjason.cglibproxy.PersistenceService;


/**

* A simple implementation of PersistenceService interface

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceImpl implements PersistenceService {


public void save(long id, String data) {

System.out.println(data + " has been saved successfully.");

}


public String load(long id) {

return "Jason Zhicheng Li";

}

}

注意到PersistenceServiceImpl类实现了PersistenceService 接口,因此没有要求要使用CGLIB创建代理。
net.sf.cglib.proxy.CallbackFilter 接口的实现如下:

package com.lizjason.cglibproxy.impl;


import java.lang.reflect.Method;

import net.sf.cglib.proxy.CallbackFilter;


/**

* An implementation of CallbackFilter for PersistenceServiceImpl

*

* @author Jason Zhicheng Li (jason@lizjason.com)

*/

public class PersistenceServiceCallbackFilter implements CallbackFilter {


//callback index for save method

private static final int SAVE = 0;


//callback index for load method

private static final int LOAD = 1;


/**

* Specify which callback to use for the method being invoked.

* @method the method being invoked.

* @return the callback index in the callback array for this method

*/

public int accept(Method method) {

String name = method.getName();

if ("save".equals(name)) {

return SAVE;

}

// for other methods, including the load method, use the

// second callback

return LOAD;

}

}


accept方法中对代理方法和回调进行了匹配,返回的值是某方法在回调数组中的索引。下面是PersistenceServiceImpl类代理的实现。


...

Enhancer enhancer = new Enhancer();

enhancer.setSuperclass(PersistenceServiceImpl.class);


CallbackFilter callbackFilter = new PersistenceServiceCallbackFilter();

enhancer.setCallbackFilter(callbackFilter);


AuthorizationService authorizationService = ...

Callback saveCallback = new AuthorizationInterceptor(authorizationService);

Callback loadCallback = NoOp.INSTANCE;

Callback[] callbacks = new Callback[]{saveCallback, loadCallback };

enhancer.setCallbacks(callbacks);

...

return (PersistenceServiceImpl)enhancer.create();

在这个例子中save方法使用了AuthorizationInterceptor实例,load方法使用了NoOp实例。此外,你也可以通过
net.sf.cglib.proxy.Enhancer.setInterfaces(Class[])方法指定代理对象所实现的接口。

除了为net.sf.cglib.proxy.Enhancer指定回调数组,你还可以通过net.sf.cglib.proxy.Enhancer.setCallbackTypes(Class[]) 方法指定回调类型数组。当创建代理时,如果你没有回调实例的数组,就可以使用回调类型。象使用回调一样,你必须使用,net.sf.cglib.proxy.CallbackFilter为每一个方法指定一个回调类型索引。你可以从http://www.lizjason.com/downloads/下载设置回调类型和接口的完整代码。

小结 GLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。 它底层使用字节码处理框架ASM。其原理是,生产一个要代理类的子类,子类覆盖要代理的类的所有不是final的方法。 它比使用java反射的JDK动态代理要快。通常情况下,你可以使用JDK的动态代理创建代理,当你要代理的类没有实现接口 或者为了更好的性能,CGLIB是一个好的选择。

(原文请参照http://www.ociweb.com/jnb/jnbNov2005.html)
  • 大小: 44.1 KB
  • 大小: 34.5 KB
  • 大小: 78.8 KB
分享到:
评论

相关推荐

    使用cglib演示动态代理时用到的jar包 aop 动态代理

    JDK动态代理基于接口,如果目标类实现了至少一个接口,就可以使用JDK的java.lang.reflect.Proxy类来创建代理对象。然而,如果目标类没有实现任何接口,那么我们就需要依赖于字节码生成技术,这就是CGLIB派上用场的...

    cglib的动态代理需要的包

    这时,CGlib就派上用场了,它可以通过继承目标类来创建代理对象,即使目标类没有实现接口。 在使用CGlib时,我们需要引入CGlib的相关jar包,比如这里提到的`cglib-2.2.2.jar`。这个版本的CGlib包含了所有必要的类和...

    CGlib.jar动态代理资源包

    4. 通过Enhancer.create()方法创建代理对象。 5. 通过代理对象调用原对象的方法,此时会触发拦截器的intercept()方法。 需要注意的是,虽然CGLib的性能通常优于JDK动态代理,但它对目标类的修改可能带来一些问题,...

    cglib动态代理jar包

    当目标类不支持接口时,CGlib可以创建目标类的子类,并在子类中拦截方法调用,从而实现动态代理。具体步骤如下: 1. 创建Enhancer对象:这是CGlib的核心类,它可以配置代理的创建过程,包括设置回调函数、代理类名...

    cglib代理模式要使用的相关jar包

    在Java编程中,代理模式是一种设计模式,它允许我们在...然而,由于CGLIB是基于继承的,所以如果目标类为final或者其方法为final,那么CGLIB将无法创建代理。在这种情况下,可以考虑使用JDK动态代理或者其他代理方式。

    cglib及其依赖包

    在Spring框架中,CGLib被用作AOP代理的一个重要组件,特别是在没有实现接口的类上创建代理对象时,Spring会默认使用CGLib。 CGLib的工作原理是基于ASM库,ASM是一个字节码操作和分析框架,它可以用来动态生成类或...

    cglib动态代理相关包

    1. Enhancer:主要用来创建代理对象,可以设置拦截器、父类等信息。 2. Callback:接口,提供了拦截方法调用的抽象,CGlib中的MethodInterceptor是它的实现。 3. MethodInterceptor:用于拦截和处理方法调用,实现...

    CGlib动态代理类的jar包

    在Java编程中,动态代理是一种在运行时创建代理类和对象的技术,可以用于拦截方法调用,实现AOP(面向切面编程)或者为已有接口或类提供额外功能。CGlib是Java动态代理的一种实现方式,它不是依赖于Java的反射机制,...

    cglib动态代理用到的两个包

    1. **cglib** 包:这是CGLib的主要实现包,包含了用于创建代理对象的所有类和接口。当无法或者不想使用Java标准的JDK动态代理(只能代理接口实现类)时,CGLib可以生成目标类的子类来实现对目标对象的代理。这个过程...

    动态代理cglibjar包和源码

    可以通过Enhancer.setSuperclass()设置需要代理的类,通过Enhancer.create()创建代理对象。 - 为了实现方法的拦截,我们需要提供一个Callback接口的实现,如MethodInterceptor。在intercept()方法中,我们可以添加...

    CGLIB动态代理jar包

    1. **Enhancer**:这是CGLIB中最主要的类,用于创建代理对象。你可以向Enhancer传递一个类或类名作为父类,然后通过回调机制(Callback)来定义代理对象的行为。 2. **MethodInterceptor**:这是CGLIB中的一个接口...

    cglib动态代理介绍

    了解CGlib的工作原理和使用方式,有助于我们在实际开发中更好地利用动态代理实现复杂的功能,提高代码的灵活性和可维护性。在阅读源码时,我们可以深入理解CGlib如何生成子类以及如何进行方法拦截,这对于提升我们的...

    cglib_2.2.2 动态代理jar包(包含asm_3.3.1)

    对于大型对象,创建代理对象可能比JDK动态代理慢。 5. **适用场景**: - AOP框架:Spring框架中就使用了CGLIB作为默认的代理方式,实现事务管理、日志记录等切面逻辑。 - 性能优化:在性能敏感的场景下,如果不能...

    CGLib代理模式用到的jar包

    1. Enhancer:这是CGLib的核心类,用于创建代理对象。通过调用它的`create()`方法并传入目标对象,即可得到一个代理对象。这个代理对象在运行时实际上是目标类的子类,它继承了目标类,并且在目标类的方法上添加了...

    CGlib动态代理类所需的jar包

    CGlib是实现动态代理的一种方式,尤其是在Spring框架中,它被用于AOP(面向切面编程)来创建代理对象。 CGLib库主要包含以下几个核心组件: 1. **cglib-nodep**: 这个jar包是CGlib的主要库,它包含了生成和操作...

    CGLib代理jar包

    4. 创建代理对象:最后,通过Enhancer的create()方法,你可以创建出代理对象。这个代理对象在调用目标方法时会自动触发之前设置的回调函数。 CGLib代理的应用场景包括但不限于: - AOP:在不修改源代码的情况下,...

    cglib实现动态代理依赖jar包

    这个“cglib实现动态代理依赖jar包”包含了两个核心的JAR文件:asm-5.1.jar和cglib-3.2.4.jar。 **ASM库**: ASM是一个字节码操控和分析框架,它可以直接生成和修改Java类和Android Dalvik字节码。ASM库主要用在...

    cglib包及依赖汉cglib3.1和asm4.2

    JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该...

    cglib代理jar包

    在Spring AOP中,当目标对象没有实现接口时,Spring会自动选择使用CGlib来创建代理。Spring AOP会将MethodInterceptor实现的`invoke()`方法作为回调函数,这样我们就可以在`invoke()`方法中添加切面逻辑,比如日志...

    cglib所有jar包

    在Spring中,当目标类没有实现任何接口时,Spring会选择使用CGlib来创建代理,实现事务管理、日志记录等功能。此外,CGlib也被ORM框架如Hibernate用于动态生成实体类的代理,以便实现懒加载和其他优化策略。 总的来...

Global site tag (gtag.js) - Google Analytics