cglibの紹介
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プロジェクトなので、そのままインポートして実行するのは簡単なはずです。