首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Cglib引见

2012-11-15 
Cglib介绍1.CGLIB包的介绍??? 代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接

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;

(原文请参照http://www.ociweb.com/jnb/jnbNov2005.html)

?

?

???? 当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是一个好的选择。

热点排行