interfaceをConfigurationとして利用する(3)
Springを使った実装例(・∀・)
まずは、コンポーネント情報を登録するHogeRegisterの主要部分。
public class HogeRegister implements BeanFactoryAware, BeanFactoryPostProcessor { ... public void register(Class<?> clazz) { RootBeanDefinition bd = new RootBeanDefinition( HogeFactoryBean.class ); MutablePropertyValues proxyValues = new MutablePropertyValues(); proxyValues.addPropertyValue( "target", clazz ); proxyValues.addPropertyValue( "interceptorNames", interceptorNames ); bd.setPropertyValues( proxyValues ); String beanName = getBeanNameGenerator.generateBeanName( bd, getRegistory() ); getBeanFactory().registerBeanDefinition( beanName, bd ); } }
対象となるinterfaceのClass clazzを見つけたら、RootBeanDefinitionを作成してBeanFactoryに登録します。
なお、RootBeanDefinitionで指定するのは対象interfaceではなくHogeFactoryBeanになります。
また、HogeFactoryBeanに情報を渡すために、MutablePropertyValuesを使用して対象となるinterfaceと、織り込むinterceptorの名称を指定します。
で、この仕組みを動かすために、HogeRegister、HogeInterceptorはapplicationContext.xmlでコンポーネント登録しておきます。
また、HogeRegisterのinterceptorNamesにはHogeInterceptorのidを指定します。
次に、動的なインスタンスを生成するHogeFactoryBeanについて。
public class HogeFactoryBean implements FactoryBean, BeanFactoryAware { private transient BeanFactory beanFactory; private Class<?> target; private String[] interceptorNames; ... public Object getObject() throws Exception { // インスタンス作成 Object obj; if ( Modifier.isAbstract( target.getModifiers() ) ) { // 拡張クラス作成 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass( target ); enhancer.setCallback( new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { Object ret = null; if( !Modifier.isAbstract( method.getModifiers() ) ) { ret = proxy.invokeSuper( obj, args ); } return ret; } }); obj = enhancer.create(); } else { obj = ClassUtil.newInstance( target ); } // プロキシー作成 ProxyFactory factory = new ProxyFactory( obj ); factory.setProxyTargetClass( true ); factory.setExposeProxy( true ); // Weave for( String name : interceptorNames ) { factory.addAdvice( (Advice)beanFactory.getBean( name ) ); } return factory.getProxy(); } public Class getObjectType() { return target; } public boolean isSingleton() { return true; } }
ちと微妙な実装…(´д`;)
getObject()内の処理は2つのフェーズに分かれていて。
まずは簡単な後半のプロキシー作成部分ですが、ProxyFactoryのインスタンスを作って、HogeInterceptorを織り込んだプロキシを返しています。
そしてProxyFactoryに渡すターゲットのインスタンスを作成する前半部分。
まず、元々のターゲットが具象クラスの場合、そのインスタンスを作成しています(ClassUtil.newInstance()の部分)。
元々のターゲットがinterfaceもしくはabstractなクラスの場合、CGLIBのEnhancerを使って元々のターゲットを継承したクラスを構築し、そのインスタンスを作成しています。
微妙だと思うのは、ProxyFactoryを使ったaopallianceなinterceptorの指定と、CGLIBなinterceptorの指定の2つがあるところ(・ω・)
例えはCGLIBで全部やっちゃうことも出来るわけですが、ポイントカットの指定とかはProxyFactoryを使った方が楽なので、CGLIBはダミーの継承クラスを作るだけに利用しています。
で、最後は、メソッド呼び出しに割り込んで処理を外部に委譲するHogeInterceptorの実装。
public class HogeInterceptor implements MethodInterceptor { public Object invoke(MethodInvocation invoke) throws Throwable { HogeMetaData hmd = getHogeMetaDataFactory().getHogeMetaData( invoke.getThis().getClass() ); HogeExecute hogeExecute = hmd.getExecute( invoke.getMethod() ); return hogeExecute.execute( invoke.getArguments() ); } }
invoke()内では本来のメソッドは呼び出さず、対象クラス/メソッドからメタ情報と処理の移譲先を取得して、移譲先の処理を呼び出してその結果を返却します。
なお、getHogeMetaData()内においては、動的に作成されたクラスではなく、元々の対象interfaceを取得するためにgetSuperclass()やgetInterfaces()をする必要があるかもしれません(・ω・)
っというわけで、interfaceをConfigurationとして利用し、interfaceから実装クラス/メソッドは動的に生成して、実際の処理はInterceptorで外部に委譲する方法を、Sprint/CGLIBを使って実現できました(・∀・)
あとはHogeMetaDataの先を作り込んで、SQLを発行するS2Daoモドキを作るなり、Web APIへのプロキシーを作るなりしてくださいな♪
べっ、別に、暇だからこんなことして遊んでいた訳じゃないんだからねΣ(゚Д゚;)!