内容
java.lang.reflect.Proxy
を利用して、AOPによるTraceログ機能を実装します。
※補足
AOP=アスペクト指向プログラミング(Aspect Oriented Programming)
プログラムに共通する横断的要素などを「アスペクト」として
モジュール化することで。変更・保守を用意にする手法。
既存コードに手を加えずに共通機能を実現出来る。
掲題のロギングが代表的な実用例。
構成
┗─aop Hoge.java:ログ出力対象のクラス HogeIntf.java:ログ出力対象のクラスのインターフェース SampleProxy.java:mainメソッドからの実行用クラス TraceIntercepter.java:トレースログ出力インターセプター TraceLoggable.java:トレースログ出力機能を各クラスに提供するクラス
TraceIntercepter.javaとTraceLoggable.javaだけあれば
好きなクラスにトレースログ出力機能を実現可能です。
ログの出力内容を変えたければTraceIntercepter.javaをいじればいい。
※実際に業務に適用するならTraceIntercepter.java内部のログ出力内容も部品化して
別クラスにユーティリティ化すると思いますけど。
サンプル
HogeIntf.java
package aop; public interface HogeIntf { String getHoge(); void setHoge(String hoge); }
package aop; import java.util.concurrent.TimeUnit; public class Hoge implements HogeIntf { private String hoge; public Hoge(String hoge) { super(); this.hoge = hoge; } public static HogeIntf getProxyInstance(HogeIntf hoge) { return TraceLoggable.getProxyInstance(hoge); } @Override public String getHoge() { waitRandom(); return hoge; } @Override public void setHoge(String hoge) { waitRandom(); this.hoge = hoge; } private void waitRandom() { try { Double rand = Math.random() * 10; TimeUnit.MILLISECONDS.sleep(rand.longValue()); } catch (InterruptedException e) { // sampleなので気にしない } } }
TraceIntercepter.java
package aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class TraceIntercepter implements InvocationHandler { Object target; public TraceIntercepter(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); outputStarTrace(method, args, start); // 実際のコードを呼び出し Object ret = method.invoke(target, args); long end = System.currentTimeMillis(); long total = end - start; outputEndTrace(method, ret, end, total); return ret; } private void outputStarTrace(Method method, Object[] args, long start) { System.out.println("Trace Start|" + start + "msec|" + target.getClass() + "|" + method.getName() + "|---msec"); if (args != null && args.length != 0) { for (int i = 0; i < args.length; i++) { outputArg(args[i]); } } } private void outputEndTrace(Method method, Object ret, long end, long total) { System.out.println("Trace End|" + end + "msec|" + target.getClass() + " : " + method.getName() + "|" + total + "msec"); if (ret != null) { outputReturn(ret); } } private void outputArg(Object value) { outputTypeValue(value, "引数の値|"); } private void outputReturn(Object value) { outputTypeValue(value, "戻り値の値|"); } private void outputTypeValue(Object value, String preMessage) { System.out.println(preMessage + value.getClass().getSimpleName() + "=" + value.toString()); } }
TraceLoggable.java
package aop; import java.lang.reflect.Proxy; public class TraceLoggable { @SuppressWarnings({ "rawtypes", "unchecked" }) public static <T> T getProxyInstance(T instance) { Class<? extends Object> clazz = instance.getClass(); // 対象クラスが実装するインターフェースのリスト Class[] classes = clazz.getInterfaces(); TraceIntercepter intercepter = new TraceIntercepter(instance); T proxyInstance = (T) Proxy.newProxyInstance(clazz.getClassLoader(), classes, intercepter); return proxyInstance; } }
SampleProxy.java
package aop; public class SampleProxy { public static void main(String[] args) { HogeIntf proxyHoge = Hoge.getProxyInstance(new Hoge("hoge")); proxyHoge.setHoge("newHoge"); proxyHoge.getHoge(); } }
出力
Trace Start|1353867111031msec|class aop.Hoge|setHoge|---msec 引数の値|String=newHoge Trace End|1353867111032msec|class aop.Hoge : setHoge|1msec Trace Start|1353867111032msec|class aop.Hoge|getHoge|---msec Trace End|1353867111040msec|class aop.Hoge : getHoge|8msec 戻り値の値|String=newHoge