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へのプロキシーを作るなりしてくださいな♪


べっ、別に、暇だからこんなことして遊んでいた訳じゃないんだからねΣ(゚Д゚;)!