我想创建一个Spring BeanFactoryPostProcessor,它将bean添加到当前的ApplicationContext。
我的spring-ws-config.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>
因此,我创建了一个带有以下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,所以我写了这个:
@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),(此方法不要初始化它!)。
答案 0 :(得分: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);
}
}
}
答案 1 :(得分:1)
以上模式是我一直使用的模式。但是,Spring 4现在有了ListableBeanFactory :: getBeanNamesForAnnotation方法,它似乎提供了相同的功能。
来自javadoc:
查找{@code Class}具有提供的{@link Annotation}类型的bean的所有名称,而不创建任何bean实例。
更新:遗憾的是,这种方法似乎也会安装某些(工厂)bean,在我的情况下会导致我的已处理bean中的@Resource注入问题。
答案 2 :(得分:0)
我通过以下方式取得了一些成功:
char (*shm)[10] = shmat(shmid, NULL, 0);
特别是关于工厂的部分帮助了我,因为bean实例化并不那么简单,所以我可以创建一个工厂,初始化它然后用它来创建实例。
需要注意的另一个要点是,使用@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();
}
意味着Spring不会调用它的init / destroy方法,也不会自动装配等等。而对于bean定义,它应该通常执行所有操作。
还有一件事。我在将beanFactory.registerSingleton(name, newWS);
的属性连接到BeanFactoryPostProcessor时遇到了一些麻烦。事实证明,您可以在上下文加载阶段准备好application.properties
并致电:
org.springframework.core.env.Environment