4

Spring BeanFactoryPostProcessor の問題

現在の ApplicationContext に Bean を追加する Spring BeanFactoryPostProcessor を作成したいと考えています。

社内に多数の Web サービス定義があり、spring-ws-config.xmlできるだけ削減したいと考えています。

XML 構成

構成は次のようになります。

<bean id="menu"
    class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition"
    lazy-init="true">
    <property name="schemaCollection">
        <bean
            class="org.springframework.xml.xsd.commons.CommonsXsdSchemaCollection">
            <property name="inline" value="true" />
            <property name="xsds">
                <list>
                    <value>classpath:xsd.xsd</value>
                </list>
            </property>
        </bean>
    </property>
    <property name="portTypeName" value="portType" />
    <property name="serviceName" value="serviceName" />
    <property name="locationUri" value="/endpoints" />
</bean>

Java 構成

そこで、次の Bean 定義を使用して @Configuration クラスを作成します。

@Bean
@Lazy
public DefaultWsdl11Definition webService() throws IOException {

    logger.info("Creating Web Service");
    DefaultWsdl11Definition toRet = new DefaultWsdl11Definition();
    toRet.setPortTypeName("portType");
    toRet.setServiceName("serviceName");

    CommonsXsdSchemaCollection collection = new CommonsXsdSchemaCollection();
    collection.setInline(true);
    collection.setXsds(new Resource[] { new ClassPathResource("path1") });
    collection.afterPropertiesSet();

    toRet.setSchemaCollection(collection);
    toRet.setLocationUri("/endpoints");
    return toRet;

}

これでだいぶマシになりました! でももっと減らしたいので @WebServiceDefinition というアノテーションをつけて BeanFactoryPostProcessor をつけて bean を自動生成したいのでこう書きました。

BeanFactoryPostProcessor

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
        throws BeansException {

    Map<String, Object> beans = bf.getBeansWithAnnotation(WebService.class);

    for (Entry<String, Object> entry : beans.entrySet()) {
        Object bean = entry.getValue();
        WebService ws = bean.getClass().getAnnotation(WebService.class);
        String name = getName(entry.getKey());
        DefaultWsdl11Definition newWS = createWebService(name, ws.xsds());

        bf.registerSingleton(name, newWS);
    }
}

しかし、これはうまくいきません!簡単なテストを書きました。ここで見ることができます

IOC が注釈付きのクラスで機能しないことがわかります。これは、メソッド BeanFactory#getBeansWithAnnotation が初期化せず、作成済みとしてマークし、何も注入しないためです。

回避策

名前ですべての Bean を取得し、対応するクラスを取得して、#bf.getBeansOfType(Class) を使用します (このメソッドは初期化しません!)。

私の質問:

  • これは有効な回避策ですか?
  • メソッド #getBeansWithAnnotation() を使用して、Bean を初期化しないようにするにはどうすればよいですか?
4

3 に答える 3

5

問題は、BeanFactoryPostProcessor がインスタンスを操作できず、#getBeansWithAnnotation() がインスタンスを返すため、推奨されません。関連するJavadocは次のとおりです。

A BeanFactoryPostProcessor may interact with and modify bean definitions, but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. If bean instance interaction is required, consider implementing BeanPostProcessor instead.

だから私の解決策はこれです:

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf)
        throws BeansException {

    String[] beans = bf.getBeanDefinitionNames();
    for (String s : beans) {
        Class<?> beanType = bf.getType(s);
        WebService ws = AnnotationUtils.findAnnotation(beanType,
                WebService.class);
        if (ws != null) {
            String name = getName(s);
            DefaultWsdl11Definition newWS = createWebService(name,
                    ws.xsds());

            bf.registerSingleton(name, newWS);
        }
    }

}
于 2013-10-18T20:47:31.377 に答える