当前位置: 首页 > 知识库问答 >
问题:

使用BeanFactoryPostProcessor创建bean

秦鸿羽
2023-03-14

我想创建一个Spring Bean Factory后处理器,将Bean添加到当前的ApplicationContext中。

我的spring-ws-config.xml中有很多Web-Services定义,我想尽可能地减少。

配置如下所示:

<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>

因此,我用以下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;

}

这好多了!,但是我想把它减少得更多,所以我想创建一个名为@WebService定义的注释,并添加一个BeanFactoryPostProcess来自动创建bean,所以我写了这个:

@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(类),(此方法不会初始化它!)。

  • 这是有效的解决方法吗
  • 如何使用方法#getBeansWithAnnotation()而不初始化bean

共有3个答案

芮叶秋
2023-03-14

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

@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的实例化并不那么简单,所以我可以创建一个工厂,初始化它,然后使用它来创建实例。

另一个需要注意的要点是使用beanFactory.registerSingleton(name, newWS);意味着Spring不会调用它的init/破坏方法,不会自动装配等。而使用bean定义,它应该做它通常做的所有事情。

还有一件事。我在将application.properties中的属性连接到BeanFactoryPostProcess上时有点困难。事实证明,您可以自动装配org.springframework.core.env.环境,它在上下文加载和调用的那个阶段就准备好了:

Integer numberOfBeans = environment.getRequiredProperty("myApp.numberOfBeans", 
                           Integer.class);
景靖琪
2023-03-14

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

来自javadoc:

查找{@code Class}具有提供的{@link注释}类型的bean的所有名称,但尚未创建任何bean实例。

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

傅泉
2023-03-14

问题是BeanFactoryPostProcencer无法使用实例,而#getBeansSusAnnotion()返回实例,因此不建议使用,这里是相关的Javadoc:

BeanFactoryPostProcessor可以与bean定义进行交互和修改,但不能与bean实例进行交互。这样做可能会导致过早实例化bean,违反容器,并导致意外的副作用。如果需要bean实例交互,请考虑实现BeanPostProcessor

所以我的解决方案是:

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

}

 类似资料:
  • 我正在用Spring Data JPA做一个项目。 我已经设法用BeanFactoryPostProcessor动态创建了datasource,并在使用AbstracTroutingDataSource登录时切换到所需的datasource。 现在我想在运行时做的是: 使用BeanFactoryPostProcessor获取动态数据源映射 创建新的数据源 将最近创建的数据源与其他数据源一起放在映射

  • 主要内容:1.BeanFactoryPostProcessor接口,2.BeanPostProcessor接口,3.实例,4.分析,5.Spring 闭环操作,6.总结BeanFactoryPostProcessor和BeanPostProcessor,这两个接口,都是Spring初始化bean时对外暴露的扩展点。两个接口名称看起来很相似,但作用及使用场景却不同. BeanFactoryPostProcessor和BeanPostProcessor都是后置增强器。还有一个是BeanDefinit

  • 主要内容:1.BeanFactoryPostProcessor接口,2.BeanPostProcessor接口,3.实例,4.分析,5.Spring 闭环操作,6.总结BeanFactoryPostProcessor和BeanPostProcessor,这两个接口,都是Spring初始化bean时对外暴露的扩展点。两个接口名称看起来很相似,但作用及使用场景却不同. BeanFactoryPostProcessor和BeanPostProcessor都是后置增强器。还有一个是BeanDefinit

  • 主要内容:1.使用案例,2.ConfigurationClassPostProcessor和 两个都是实现了, 隐含地也实现了接口. 用于spring 应用启动过程中 类的处理 : 发现和处理所有的配置类,注册其中的bean定义。 用于spring应用启动过程中 类的处理, 发现带有注解的接口, 用于 框架的使用 1.使用案例 A05Application.java Config.java Bean01.java Bean02,java 2.ConfigurationClassPost

  • 问题内容: 我决定使用Java重新创建Snake,但是我有些困惑。目前,我有一个正方形,用户可以使用箭头键在屏幕上移动。当您按一次LEFT时,方型会开始使用计时器向左移动。当您按任何其他已设置的键(向右,向上,向下)时,它会改变方向。我的目标是使用ArrayList容纳组成蛇的正方形。目前,我已经创建了一个ArrayList,其中仅包含一个Snake对象,如果我将第二个Snake对象添加到列表中并

  • 问题内容: 我有在Objective-C中创建和NSAlert的代码,但是现在我想在Swift中创建它。 该警报是为了确认用户要删除文档。 我希望“删除”按钮可以运行删除功能,而“取消”按钮只是为了消除警报。 如何在Swift中编写此代码? 问题答案: 在OS X 10.10 Yosemite中已弃用。 迅捷2 返回或根据用户的选择。 表示添加到对话框的第一个按钮,此处为“确定”。 迅捷3 斯威夫