- 追加された行はこの色です。
- 削除された行はこの色です。
[[JAVAの記事一覧]]
&topicpath;
*目次 [#bca26b28]
#contents
*ASMとは [#m1a87732]
ASM はJavaのバイトコードを操作したり、解析するためのフレームワークです。
既存のクラスを変更したり、動的にクラスを生成することができるフレームワークです。
CGLIBがこのASMをつかっています。
**ASMのページ [#p3d1db5d]
http://asm.ow2.org/
**日本語の使用例の記述 [#fc30ec3f]
簡易AOPフレームワークの作成
http://d.hatena.ne.jp/iad_otomamay/20100418/1271560826
*CGLIBとは? [#fa0ae660]
CGLIBとは、Javaコード生成ライブラリです。クラスファイルを実行時に読み込んで編集することが可能です(Javassistと同じジャンル)。CGLIBは、Hibernate、iBatisなどのDBアクセスツールや、Spring、SeasorなどのAOPコンテナなどで利用されています。
**CGLIBのWebページ [#xf7e883a]
http://cglib.sourceforge.net/
*趣旨 [#x0113801]
jparsecみてたら、動的ソースコード生成のライブラリ使っており興味が湧いたのでまとめてみる
SpringやらHibernateなんかに内部的に使われているライブラリーらしい。
*目次 [#bca26b28]
#contents
*インタフェース定義するだけでPOJO的なインスタンスを生成するサンプル [#q3790e79]
http://cglib.sourceforge.net/xref/samples/KeySample.html
*実行トレーサを作るサンプル [#p0328206]
http://cglib.sourceforge.net/xref/samples/Trace.html
*JDKのProxyの代わりに使える [#a225fc5e]
なんのことかというと、あたかも元々のクラスのようにつかうことができるサンプルです。
**JDKのProxyの説明はこちら [#sccd2b29]
http://ffy.afy-system.jp/tips/t_004.html
**サンプルコード [#ffb5798e]
http://cglib.sourceforge.net/xref/samples/JdkCompatibleProxy.html
*java cglibでmixin [#cbef2da9]
**リンク [#q40a8740]
Java with CGLIB でMixinを使う
http://d.hatena.ne.jp/iad_otomamay/20080512/p1
**APIドキュメント [#j2a36f06]
http://www.opendocs.net/javadoc/cglib/2.2/net/sf/cglib/proxy/Mixin.Generator.html
**その他ネタ元はこちら [#r59a6a77]
プログラマメモ2
http://programamemo2.blogspot.com/2007/07/java-cglibmixin-mixin.html
**既に生成されたオブジェクトにたいして、あらたにインターフェイスを付加する [#n1e32423]
***サンプルコード [#v588dc98]
package mixin;
import net.sf.cglib.proxy.Mixin;
public class Test {
public static void main(String[] args) {
new Test().testMixin();
}
void testMixin() {
C c = mixin(new A() {
@Override
public void a() {
System.out.println("o_o i am A!!");
}
});
/* もとの型を保持しているかチェック */
if (c instanceof A) {
((A) c).a();
}
c.c();
}
/*
* クラス配列とクラス配列を連結します。
*/
static Class[] concat(Class[] a, Class[] b) {
Class[] arr = new Class[a.length + b.length];
System.arraycopy(a, 0, arr, 0, a.length);
System.arraycopy(b, 0, arr, a.length, b.length);
return arr;
}
interface A {
public void a();
}
interface C {
public void c();
}
/*
* もとのオブジェクトにたいして、Cインターフェイスを付け加えます。
*/
public C mixin(Object o) {
Class[] interfaces = concat(new Class[] { C.class }, o.getClass()
.getInterfaces());
Object[] delegates = new Object[] { new C() {
@Override
public void c() {
System.out.println("i am C o_o!");
}
}, o };
Object obj = Mixin.create(interfaces, delegates);
C c = (C) obj;
return c;
}
}
**考察 [#i819e59c]
元にするクラスのインタフェースを用意しとかなきゃならんのかな?
あとで下記のドキュメントでもみてみるか。。。
[Java]動的なインターフェイスの追加(擬似的な方法)
http://d.hatena.ne.jp/daisuke-m/20081212/1229083116
*メソッド実行の前後に処理をフックをかけるというか差し込む [#fb50384b]
元ネタサイトはこちら
**CGLIBにさわる [#k59f343f]
http://muimi.com/j/aop/cglib/
メソッド実行の前後に処理をフックをかけるというか差し込む技がつかわれています。
元ネタの元ネタサイトは
本家
http://cglib.sourceforge.net/
Sampleの
Beansサンプルと思われる。
***掲載されていたサンプル [#h5c59501]
Foo.java
package hoge;
public class Foo {
public void doSomething(){
System.out.println("doSomethig");
}
}
Sample1.java
package hoge;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class Sample1 {
public static void main(String[] args) throws Exception{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Foo.class);
enhancer.setCallback(new MyMethodIntercepter());
Foo foo = (Foo)enhancer.create();
foo.doSomething();
}
}
class MyMethodIntercepter implements MethodInterceptor{
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("before");
return proxy.invokeSuper(obj, args);
}
}
実行結果
before
doSomethig
Enhancerというクラスを利用してサブクラスを作り、setCallback()で差込みを行うようです。
*CGLIB で実行時にクラスにメソッドを追加する [#r95d066f]
URLはこちら
http://d.hatena.ne.jp/akishin999/20100604/1275611622
**サンプルコード [#k24425d2]
以下のサンプルでは、Date 型の setCreatedAt しか持たない JavaBean に対して、文字列を引数として同名のメソッドを呼び出せるようにしています。
ポイントは InterfaceMaker を使ってインターフェースの定義をして、Enhancer で作成したインターフェースを元のクラスに追加してやるところになります。
package example;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import net.sf.cglib.asm.Type;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InterfaceMaker;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CGLibExample {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// 新規インターフェースを定義する
InterfaceMaker im = new InterfaceMaker();
// 文字列を引数とした setCreatedAt を定義
im.add(new Signature("setCreatedAt", Type.VOID_TYPE, new Type[] { Type
.getType(String.class) }), null);
// インターフェースを生成
Class myInterface = im.create();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ExampleBean.class);
// 生成したインターフェースを追加する
enhancer.setInterfaces(new Class[] { myInterface });
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
ExampleBean bean = (ExampleBean) obj;
// 文字列を引数とした setCreatedAt が呼ばれた場合に Date 型に変換し本来の Setter を呼び出す。
if (method.getName().startsWith("setCreatedAt")
&& args[0] != null && args[0] instanceof String) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
Date date = null;
try {
date = sdf.parse((String) args[0]);
} catch (final Exception e) { /* nop */ }
bean.setCreatedAt(date);
return null;
}
return proxy.invokeSuper(obj, args);
}
});
// Bean を生成
ExampleBean bean = (ExampleBean) enhancer.create();
bean.setId(999);
// 実行時に型を追加しているため、呼び出しはリフレクション経由
try {
// 追加したメソッドはあくまで CGLIB によって作成された型にしか存在しないため、
// ExampleBean.class ではなく、bean.getClass() のようにして指定する必要がある。
Method method = bean.getClass().getMethod("setCreatedAt", new Class[] {String.class});
method.invoke(bean, new Object[]{"20100531"});
} catch (final Exception e) {
e.printStackTrace();
}
System.out.printf("id : [%d] createdAt : [%s]\n", bean.getId(), bean.getCreatedAt());
}
}
/**
* サンプル用の JavaBeans
*/
class ExampleBean implements Serializable {
private static final long serialVersionUID = -8121418052209958014L;
private int id;
private Date createdAt;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getCreatedAt() {
return createdAt;
}
public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}
}
***実行結果 [#v266c17a]
id : [999] createdAt : [Mon May 31 00:00:00 JST 2010]
*考察 [#b850091a]
インタフェースだけ定義しておいて、テスト用にモックオブジェクトを返すような開発ができるため、
分業することができる。
**jmock [#q536b48c]
http://www.jmock.org/mocking-classes.html