1概要

この記事では、


cglib


(コード生成ライブラリ)ライブラリーを見ていきます。これは、

Hibernate



Spring

などの多くのJavaフレームワークで使用されているバイト計測ライブラリです。バイトコードインスツルメンテーションでは、プログラムのコンパイル段階の後にクラスを操作または作成することができます。


2 Mavenの依存関係

プロジェクトで

cglib

を使用するには、Mavenの依存関係を追加するだけです(最新バージョンはhttps://search.maven.org/classic/#search%7C1av77g1%7Cg%3A%22cglib%22%20AND%20a%3Aにあります)。 %22cglib%22[ここ]):

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


3 Cglib

Javaのクラスは実行時に動的にロードされます。

Cglib

は、既に実行中のJavaプログラムに新しいクラスを追加することを可能にするために、このJava言語の機能を使用しています。


Hibernate

は動的プロキシの生成にcglibを使用します。たとえば、データベースに格納されているオブジェクト全体を返すことはありませんが、要求に応じてデータベースから値を遅延的にロードするストアドクラスのインストルメント化バージョンを返します。


Mockitoなどの一般的なモックフレームワークは、モックメソッドに

cglib__を使用します。モックは、メソッドが空の実装に置き換えられるインストルメンテーションクラスです。


cglib.

の最も有用な構成体を見ていきます。


4

cglib


を使用したプロキシの実装

2つのメソッドを持つ

PersonService

クラスがあるとしましょう。

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

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

最初のメソッドは

String

を返し、2番目のメソッドは

Integer.

を返します。


4.1. 同じ値を返す


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);



FixedValue

は、プロキシメソッドから値を単純に返すコールバックインタフェースです。プロキシで

sayHello()__メソッドを実行すると、プロキシメソッドで指定された値が返されました。


4.2. メソッドシグネチャに応じた戻り値

プロキシの最初のバージョンには、プロキシがどのメソッドをインターセプトするのか、そしてどのメソッドをスーパークラスから呼び出すのかを決定できないため、いくつかの欠点があります。


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);

この例では、メソッドシグネチャが

Object

クラスからのものではない場合、すべての呼び出しをインターセプトしています。つまり、

toString()

メソッドまたは

hashCode()

メソッドはインターセプトされません。それ以外に、

String

を返す

PersonService

からのメソッドのみをインターセプトしています。

lengthOfName()

メソッドの呼び出しは、戻り型が

Integer.

であるため、インターセプトされません。


5ビーンクリエイター


cglib

からのもう1つの便利な構成要素はhttp://cglib.sourceforge.net/apidocs/net/sf/cglib/beans/BeanGenerator.html[

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. Mixinを作成する


mixin

は、複数のオブジェクトを1つにまとめることを可能にする構文です。いくつかのクラスの動作を含めることができ、その動作を単一のクラスまたはインタフェースとして公開できます。

cglib

Mixinsは複数のオブジェクトを単一のオブジェクトに結合することを可能にします。ただし、そうするためにはミックスイン内に含まれるすべてのオブジェクトはインターフェースによって裏付けられなければなりません。

2つのインターフェースをミックスインしたいとしましょう。インターフェイスとその実装の両方を定義する必要があります。

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";
    }
}


Interface1



Interface2

の実装を構成するには、両方を拡張するインターフェースを作成する必要があります。

public interface MixinInterface extends Interface1, Interface2 { }



Mixin


クラスの

create()

メソッドを使用することで、

Class1



Class2

の動作を__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());


mixinDelegate

でメソッドを呼び出すと、

Class1

および__Class2から実装が呼び出されます。


7. 結論

この記事では、

cglib

とその最も有用な構成体を調べました。

Enhancer

クラスを使用してプロキシを作成しました。私たちは

BeanCreator

を使い、最後に他のクラスの振る舞いを含む

Mixin

を作りました。

CglibはSpringフレームワークで広く使われています。 Springがcglibプロキシを使用する一例は、メソッド呼び出しにセキュリティ制約を追加することです。

メソッドを直接呼び出すのではなく、指定されたセキュリティチェックに合格したかどうかを最初に確認し(プロキシ経由で)、この検証が成功した場合にのみ実際のメソッドに委任します。この記事では、このようなプロキシを自分の目的のために作成する方法を見ました。

これらすべての例とコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/libraries/src/test/java/com/baeldung/cglib/proxy[GitHubプロジェクト]にあります。これはMavenプロジェクトなので、そのままインポートして実行するのは簡単なはずです。