4

Spring BeanFactoryPostProcessor 问题

我想创建一个将 bean 添加到当前 ApplicationContext 的 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,所以我写了这个:

BeanFactory后处理器

@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);
    }
}

但是,这不起作用!,我写了一个简单的测试,你可以看到它here

我看到 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 回答
1

上面的模式是我一直使用的。然而,Spring 4 现在有了 ListableBeanFactory::getBeanNamesForAnnotation 方法,它似乎提供了相同的功能。

从javadoc:

查找 {@code Class} 具有提供的 {@link Annotation} 类型的所有 bean 名称,而无需创建任何 bean 实例。

更新:不幸的是,这种方法似乎也实例化了某些(工厂)bean,在我的情况下,这在我处理的 bean 中导致了@Resource 注入问题。

于 2017-03-03T08:41:07.227 回答
0

我通过以下方式取得了一些成功:

@Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
    return bf -> {
        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) bf;
        IntStream.range(0, 10).forEach(i ->
                beanFactory.registerBeanDefinition(String.format("bean-name-%d", i),
                        createCustomBeanDefinition()));
    };
}

private BeanDefinition createCustomBeanDefinition() {
    return BeanDefinitionBuilder.genericBeanDefinition(MyBeanClass.class)
            .setFactoryMethodOnBean("create", "myFactory")
            .getBeanDefinition();
}

特别是关于工厂的部分对我很有帮助,因为 bean 实例化并不那么简单,所以我可以创建一个工厂,初始化它,然后用它来创建实例。

另一个需要注意的重点是 usingbeanFactory.registerSingleton(name, newWS);意味着 Spring 不会调用它的 init/destroy 方法,不会自动装配等。而对于 bean 定义,它应该做它通常所做的一切。

还有一件事情。application.properties我在从挂钩到 BeanFactoryPostProcessor中获取属性方面有点挣扎。事实证明,您可以org.springframework.core.env.Environment在上下文加载和调用的那个阶段准备好自动装配:

Integer numberOfBeans = environment.getRequiredProperty("myApp.numberOfBeans", 
                           Integer.class);
于 2017-04-28T16:27:20.340 回答