Introduction to cglib – cglib简介

最后修改: 2017年 2月 27日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we will be looking at the cglib (Code Generation Library) library. It is a byte instrumentation library used in many Java frameworks such as Hibernate or Spring. The bytecode instrumentation allows manipulating or creating classes after the compilation phase of a program.

在这篇文章中,我们将关注cglib(代码生成库)库。它是许多Java框架(如HibernateSpring)中使用的字节码工具化库。字节码工具化允许在程序的编译阶段后操作或创建类。

2. Maven Dependency

2.Maven的依赖性

To use cglib in your project, just add a Maven dependency (latest version can be found here):

要在你的项目中使用cglib,只需添加一个Maven依赖项(最新版本可在这里找到)。

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.4</version>
</dependency>

3. Cglib

3.Cglib

Classes in Java are loaded dynamically at runtime. Cglib is using this feature of Java language to make it possible to add new classes to an already running Java program.

Java中的类是在运行时动态加载的。Cglib就是利用Java语言的这一特点,使其有可能向已经运行的Java程序中添加新的类。

Hibernate uses cglib for generation of dynamic proxies. For example, it will not return full object stored in a database but it will return an instrumented version of stored class that lazily loads values from the database on demand.

Hibernate使用cglib来生成动态代理。例如,它不会返回存储在数据库中的完整对象,但它会返回存储类的工具化版本,根据需要从数据库中懒散地加载值。

Popular mocking frameworks, like Mockito, use cglib for mocking methods. The mock is an instrumented class where methods are replaced by empty implementations.

流行的嘲讽框架,如Mockito,使用cglib来嘲讽方法。嘲讽是一个工具化的类,其中的方法被空的实现所取代。

We will be looking at the most useful constructs from cglib.

我们将关注cglib.中最有用的结构。

4. Implementing Proxy Using cglib

4.使用cglib实现代理

Let’s say that we have a PersonService class that has two methods:

假设我们有一个PersonService类,它有两个方法。

public class PersonService {
    public String sayHello(String name) {
        return "Hello " + name;
    }

    public Integer lengthOfName(String name) {
        return name.length();
    }
}

Notice that first method returns String and the second one Integer.

注意第一个方法返回字符串,第二个方法返回整数

4.1. Returning the Same Value

4.1.返回相同的值

We want to create a simple proxy class that will intercept a call to a sayHello() method. The Enhancer class allows us to create a proxy by dynamically extending a PersonService class by using a setSuperclass() method from the Enhancer class:

我们想创建一个简单的代理类,它将拦截对sayHello()方法的调用。Enhancer类允许我们通过使用Enhancer类中的setSuperclass()方法动态地扩展PersonService类来创建一个代理。

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((FixedValue) () -> "Hello Tom!");
PersonService proxy = (PersonService) enhancer.create();

String res = proxy.sayHello(null);

assertEquals("Hello Tom!", res);

The FixedValue is a callback interface that simply returns the value from the proxied method. Executing sayHello() method on a proxy returned a value specified in a proxy method.

FixedValue是一个回调接口,它只是返回被代理方法的值。在代理上执行sayHello()方法,返回代理方法中指定的值。

4.2. Returning Value Depending on a Method Signature

4.2.取决于方法签名的返回值

The first version of our proxy has some drawbacks because we are not able to decide which method a proxy should intercept, and which method should be invoked from a superclass. We can use a MethodInterceptor interface to intercept all calls to the proxy and decide if want to make a specific call or execute a method from a superclass:

我们第一个版本的代理有一些缺点,因为我们无法决定代理应该拦截哪个方法,以及哪个方法应该从超类中调用。我们可以使用MethodInterceptor接口来拦截对代理的所有调用,并决定是否要进行特定的调用或从超类中执行方法。

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    if (method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
        return "Hello Tom!";
    } else {
        return proxy.invokeSuper(obj, args);
    }
});

PersonService proxy = (PersonService) enhancer.create();

assertEquals("Hello Tom!", proxy.sayHello(null));
int lengthOfName = proxy.lengthOfName("Mary");
 
assertEquals(4, lengthOfName);

In this example, we are intercepting all calls when method signature is not from the Object class, meaning that i.e. toString() or hashCode() methods will not be intercepted. Besides that, we are intercepting only methods from a PersonService that returns a String. Call to a lengthOfName() method will not be intercepted because its return type is an Integer.

在这个例子中,我们将拦截所有方法签名不是来自Object 类的调用,也就是说,toString() hashCode() 方法将不会被拦截。除此之外,我们只拦截来自PersonService的返回String的方法。对lengthOfName()方法的调用将不会被拦截,因为它的返回类型是Integer

5. Bean Creator

5.Bean创造者

Another useful construct from the cglib is a BeanGenerator class. It allows us to dynamically create beans and to add fields together with setter and getter methods. It can be used by code generation tools to generate simple POJO objects:

来自cglib的另一个有用的结构是BeanGenerator类。它允许我们动态地创建Bean,并将字段与setter和getter方法加在一起。它可以被代码生成工具用来生成简单的POJO对象。

BeanGenerator beanGenerator = new BeanGenerator();

beanGenerator.addProperty("name", String.class);
Object myBean = beanGenerator.create();
Method setter = myBean.getClass().getMethod("setName", String.class);
setter.invoke(myBean, "some string value set by a cglib");

Method getter = myBean.getClass().getMethod("getName");
assertEquals("some string value set by a cglib", getter.invoke(myBean));

6. Creating Mixin

6.创建混合器

A mixin is a construct that allows combining multiple objects into one. We can include a behavior of a couple of classes and expose that behavior as a single class or interface. The cglib Mixins allow the combination of several objects into a single object. However, in order to do so all objects that are included within a mixin must be backed by interfaces.

mixin是一种允许将多个对象合并为一个的结构。我们可以包含几个类的行为,并将该行为作为一个单一的类或接口公开。cglib混合器允许将几个对象组合成一个对象。然而,为了做到这一点,所有被包含在混合器中的对象都必须有接口的支持。

Let’s say that we want to create a mixin of two interfaces. We need to define both interfaces and their implementations:

假设我们想创建一个两个接口的混合体。我们需要定义这两个接口和它们的实现。

public interface Interface1 {
    String first();
}

public interface Interface2 {
    String second();
}

public class Class1 implements Interface1 {
    @Override
    public String first() {
        return "first behaviour";
    }
}

public class Class2 implements Interface2 {
    @Override
    public String second() {
        return "second behaviour";
    }
}

To compose implementations of Interface1 and Interface2 we need to create an interface that extends both of them:

为了组成Interface1Interface2的实现,我们需要创建一个扩展了它们的接口。

public interface MixinInterface extends Interface1, Interface2 { }

By using a create() method from the Mixin class we can include behaviors of Class1 and Class2 into a MixinInterface:

通过使用Mixin类的create()方法,我们可以将Class1Class2的行为纳入一个MixinInterface:

Mixin mixin = Mixin.create(
  new Class[]{ Interface1.class, Interface2.class, MixinInterface.class },
  new Object[]{ new Class1(), new Class2() }
);
MixinInterface mixinDelegate = (MixinInterface) mixin;

assertEquals("first behaviour", mixinDelegate.first());
assertEquals("second behaviour", mixinDelegate.second());

Calling methods on the mixinDelegate will invoke implementations from Class1 and Class2.

调用mixinDelegate上的方法将调用Class1Class2的实现。

7. Conclusion

7.结论

In this article, we were looking at the cglib and its most useful constructs. We created a proxy using an Enhancer class. We used a BeanCreator and finally, we created a Mixin that included behaviors of other classes.

在这篇文章中,我们正在研究cglib和它最有用的结构。我们使用Enhancer类创建了一个代理。我们使用了一个BeanCreator,最后,我们创建了一个Mixin,其中包括了其他类的行为。

Cglib is used extensively by the Spring framework. One example of using a cglib proxy by Spring is adding security constraints to method calls. Instead of calling a method directly, Spring security will first check (via proxy) if a specified security check passes and delegate to the actual method only if this verification was successful. In this article, we saw how to create such proxy for our own purpose.

Cglib被Spring框架广泛使用。Spring使用cglib代理的一个例子是为方法调用添加安全约束。与其直接调用一个方法,Spring安全将首先检查(通过代理)指定的安全检查是否通过,并且只有在验证成功后才委托给实际的方法。在这篇文章中,我们看到了如何为我们自己的目的创建这种代理。

The implementation of all these examples and code snippets can be found in the GitHub project – this is a Maven project, so it should be easy to import and run as it is.

所有这些例子和代码片段的实现都可以在GitHub项目中找到–这是一个Maven项目,所以应该很容易导入并按原样运行。