Java CGLIB

Java Cglib

1.CGLIB包的介绍

     代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,当它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB

     CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOPdynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMockjMock是通过使用模仿(moke)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(moke)对象。

     CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如 GroovyBeanShell,也是使用ASM来生成java的字节码。当不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

Figure 1: CGLIB Library and ASM Bytecode Framework

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

2.CGLIB 代理的APIS

     CGLIB包的基本代码很少,当学起来有一定的困难,主要是缺少文档,这也是开源软件的一个不足之处。目前CGLIB的版本是(2.1.2,主要由一下部分组成:

net.sf.cglib.core

底层字节码处理类,他们大部分与ASM有关系。

? net.sf.cglib.transform

编译期或运行期类和类文件的转换

? net.sf.cglib.proxy

实现创建代理和方法拦截器的类

? net.sf.cglib.reflect

实现快速反射和C#风格代理的类

? net.sf.cglib.util

集合排序工具类

? net.sf.cglib.beans

JavaBean相关的工具类

·  大多时候,仅仅为了动态地创建代理,你仅需要使用到代理包中很少的一些API

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

·  Figure 2: CGLIB library APIs commonly used for proxying classes

2 为我们演示了创建一个具体类的代理时,通常要用到的CGLIB包的APIsnet.sf.cglib.proxy.Callback接口在CGLIB包中是一个很关键的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。 net.sf.cglib.proxy.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.Proxydrop-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);

}

}

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

java.lang.reflect.Method对象。

Use a CallbackFilter

net.sf.cglib.proxy.CallbackFilter允许我们在方法层设置回调(callback)。假如你有一个PersistenceServiceImpl类,它有两个方法:saveload,其中方法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/下载设置回调类型和接口的完整代码。

cglib is a powerful, high performance and quality Code Generation Library, It is used to extend JAVA classes and implements interfaces at runtime.
cglib
Code Generation Library的缩写。
cglib
依赖于ASM库。
Hibernate
主要是利用cglib生成pojo的子类并override get方法来实现lazy loading机制,Spring则是利用cglib来实现动态代理。
JDK的动态代理机制要求有接口才行,这样就强制我们的pojo实现某个接口。

这里还是提供一个cglib的入门级的示例:
MyClass.java:

Java代码

  1. public class MyClass {   
  2.   
  3.   public void print() {   
  4.      System.out.println("I'm in MyClass.print!");   
  5.    }   
  6.   
  7. }  

public class MyClass {

 

  public void print() {

    System.out.println("I'm in MyClass.print!");

  }

 

}


Main.java:

Java代码

  1. import java.lang.reflect.Method;   
  2. import net.sf.cglib.proxy.Enhancer;   
  3. import net.sf.cglib.proxy.MethodInterceptor;   
  4. import net.sf.cglib.proxy.MethodProxy;   
  5.   
  6. public class Main {   
  7.   
  8.   public static void main(String[] args) {   
  9.   
  10.      Enhancer enhancer = new Enhancer();   
  11.      enhancer.setSuperclass(MyClass.class);   
  12.      enhancer.setCallback(new MethodInterceptorImpl());   
  13.      MyClass my = (MyClass) enhancer.create();   
  14.      my.print();   
  15.    }   
  16.   
  17.   private static class MethodInterceptorImpl implements MethodInterceptor {   
  18.     public Object intercept(Object obj, Method method, Object[] args,   
  19.          MethodProxy proxy) throws Throwable {   
  20.       // log something   
  21.        System.out.println(method + " intercepted!");   
  22.   
  23.        proxy.invokeSuper(obj, args);   
  24.       return null;   
  25.      }   
  26.    }   
  27. }  

转自:http://hi.baidu.com/zymsuper/blog/item/0d931c8233c2909bf703a630.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值