[[JAVAの記事一覧]] &topicpath; *趣旨 [#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] ネタ元はこちら プログラマメモ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; } } *メソッド実行の前後に処理をフックをかけるというか差し込む [#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