* added possibility do declare lists and registries as inner beans

git-svn-id: svn+ssh://svn.synyx.de/var/svn/synyx/opensource/hera/trunk@3663 5a64d73e-33d6-4ccc-9058-23f8668ecac9
This commit is contained in:
Oliver Gierke
2008-12-05 15:03:23 +00:00
parent 28b0b5130e
commit 8ee08a56a3
7 changed files with 279 additions and 23 deletions

View File

@@ -0,0 +1,66 @@
package org.synyx.hera.core.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.BeanReference;
/**
* {@link BeanPostProcessor} to handle {@link BeanReference} beans and replacing
* it by its reference target.
*
* @author Oliver Gierke - gierke@synyx.de
*/
public class BeanReferenceBeanPostProcessor implements BeanPostProcessor,
BeanFactoryAware {
private BeanFactory beanFactory;
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
* .springframework.beans.factory.BeanFactory)
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
/*
* (non-Javadoc)
*
* @seeorg.springframework.beans.factory.config.BeanPostProcessor#
* postProcessAfterInitialization(java.lang.Object, java.lang.String)
*/
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// Skip non RuntimeBeanReferences
if (!(bean instanceof BeanReference)) {
return bean;
}
BeanReference reference = (BeanReference) bean;
return beanFactory.getBean(reference.getBeanName());
}
/*
* (non-Javadoc)
*
* @seeorg.springframework.beans.factory.config.BeanPostProcessor#
* postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}

View File

@@ -15,31 +15,38 @@
*/
package org.synyx.hera.core.config;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Bean definition parser to register {@code <list />} elements from the plugin
* namespace.
*
* @author Oliver Gierke - gierke@synyx.de
*/
public class PluginListDefinitionParser extends
AbstractSingleBeanDefinitionParser {
public class PluginListDefinitionParser extends AbstractBeanDefinitionParser {
protected static final String PACKAGE = "org.synyx.hera.core.support.";
/*
* (non-Javadoc)
/**
* Returns the name of the {@link BeanFactoryPostProcessor} to be
* registered.
*
* @see
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
* #getBeanClassName(org.w3c.dom.Element)
* @return
*/
@Override
protected String getBeanClassName(Element element) {
protected String getPostProcessorName() {
return PACKAGE + "BeanListBeanFactoryPostProcessor";
}
@@ -48,18 +55,116 @@ public class PluginListDefinitionParser extends
/*
* (non-Javadoc)
*
* @see
* org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
* #doParse(org.w3c.dom.Element,
* org.springframework.beans.factory.support.BeanDefinitionBuilder)
* @seeorg.springframework.beans.factory.xml.AbstractBeanDefinitionParser#
* parseInternal(org.w3c.dom.Element,
* org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected AbstractBeanDefinition parseInternal(Element element,
ParserContext context) {
if (context.isNested()) {
return parseNested(element, context);
} else {
return parseStandalone(element, context);
}
}
/**
* Parses a nested {@code <list />} or {@code <registry />} element.
* Registers the {@link BeanFactoryPostProcessor} as in standalone mode but
* returns a reference to the list or registry created by instead of the
* {@link BeanFactoryPostProcessor} itself.
*
* @param element
* @param context
* @return
*/
private AbstractBeanDefinition parseNested(Element element,
ParserContext context) {
// Step 1 - Create reference wrapped in a bean definition
BeanDefinitionBuilder builder =
BeanDefinitionBuilder
.genericBeanDefinition(RuntimeBeanNameReference.class
.getName());
// Extract id attribute from element or generate custom one
String idAttribute = element.getAttribute("id");
String listId =
StringUtils.hasText(idAttribute) ? idAttribute
: BeanDefinitionReaderUtils.generateBeanName(builder
.getBeanDefinition(), context.getRegistry(),
true);
// Let reference point to the bean with the calculated id
builder.addConstructorArgValue(listId);
// Step 2 - Register BeanPostProcessor to unwrap the reference
registerReferenceResolver(element, context);
// Step 3 - Set the id to let the BeanFactoryPostProcessor (BFPP)
// register the list as reference target
element.setAttribute("id", listId);
// Step 4 - Register BFPP ourselves
AbstractBeanDefinition definition = parseStandalone(element, context);
String beanFactoryxPostProcessorId =
resolveId(element, definition, context);
BeanDefinitionHolder holder =
new BeanDefinitionHolder(definition,
beanFactoryxPostProcessorId);
registerBeanDefinition(holder, context.getRegistry());
// Step 5 - Return the reference to the list created by the BFPP
return getSourcedBeanDefinition(builder, element, context);
}
/**
* Registers a {@link BeanReferenceBeanPostProcessor} to automatically
* unwrap {@link BeanDefinition}s that contain a reference to other beans.
*
* @param element
* @param context
*/
private void registerReferenceResolver(Element element,
ParserContext context) {
BeanDefinitionBuilder builder =
BeanDefinitionBuilder
.genericBeanDefinition(BeanReferenceBeanPostProcessor.class);
BeanDefinition definition =
getSourcedBeanDefinition(builder, element, context);
registerBeanDefinition(new BeanDefinitionHolder(definition, generateId(
definition, context)), context.getRegistry());
}
/**
* Parses a standalone {@code <list />} or {@code <registry />} element and
* registers a {@link BeanFactoryPostProcessor} to automatically register
* all beans of the type specified in {@code class} property under the given
* id.
*
* @param element
* @param context
* @return
*/
@SuppressWarnings("unchecked")
protected void doParse(Element element, BeanDefinitionBuilder builder) {
private AbstractBeanDefinition parseStandalone(Element element,
ParserContext context) {
ManagedMap map = new ManagedMap();
map.put(element.getAttribute("id"), element.getAttribute("class"));
BeanDefinitionBuilder builder =
BeanDefinitionBuilder
.genericBeanDefinition(getPostProcessorName());
builder.addPropertyValue("lists", map);
String initFactories = element.getAttribute("init-factories");
@@ -67,6 +172,40 @@ public class PluginListDefinitionParser extends
if (StringUtils.hasText(initFactories)) {
builder.addPropertyValue("allowEagerInit", initFactories);
}
return getSourcedBeanDefinition(builder, element, context);
}
/**
* Returns the bean definition prepared by the builder and has connected it
* to the {@code source} object.
*
* @param builder
* @param source
* @param context
* @return
*/
private AbstractBeanDefinition getSourcedBeanDefinition(
BeanDefinitionBuilder builder, Object source, ParserContext context) {
AbstractBeanDefinition definition = builder.getRawBeanDefinition();
definition.setSource(context.extractSource(source));
return definition;
}
/**
* Generates a custom id for the given {@link BeanDefinition}.
*
* @param definition
* @param context
* @return
*/
private String generateId(BeanDefinition definition, ParserContext context) {
return context.getReaderContext().generateBeanName(definition);
}

View File

@@ -15,12 +15,9 @@
*/
package org.synyx.hera.core.config;
import org.w3c.dom.Element;
/**
* Simple extension of {@link PluginListDefinitionParser}. Simply registers a
* {@code PluginListDefinitionParser} instead of the original class.
* {@code PluginRegistryBeanFactoryPostProcessor} instead of the original class.
*
* @author Oliver Gierke - gierke@synyx.de
*/
@@ -34,7 +31,7 @@ public class PluginRegistryDefinitionParser extends PluginListDefinitionParser {
* #getBeanClassName(org.w3c.dom.Element)
*/
@Override
protected String getBeanClassName(Element element) {
protected String getPostProcessorName() {
return PACKAGE + "PluginRegistryBeanFactoryPostProcessor";
}

View File

@@ -0,0 +1,28 @@
package org.synyx.hera.core;
/**
* @author Oliver Gierke - gierke@synyx.de
*/
public class SamplePluginHost {
private PluginRegistry<SamplePlugin, String> registry =
PluginRegistry.create();
/**
* @param registry the registry to set
*/
public void setRegistry(PluginRegistry<SamplePlugin, String> registry) {
this.registry = registry;
}
/**
* @return the registry
*/
public PluginRegistry<SamplePlugin, String> getRegistry() {
return registry;
}
}

View File

@@ -1,14 +1,18 @@
package org.synyx.hera.core.config;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.synyx.hera.core.PluginRegistry;
import org.synyx.hera.core.SamplePlugin;
import org.synyx.hera.core.SamplePluginHost;
/**
@@ -24,11 +28,22 @@ public class PluginConfigurationIntegrationTest {
List<SamplePlugin> samplePlugins;
@Autowired
@Qualifier("bar")
PluginRegistry<SamplePlugin, String> pluginRegistry;
@Autowired
@Qualifier("host")
SamplePluginHost host;
@Autowired
@Qualifier("otherHost")
SamplePluginHost otherHost;
@Test
public void test() throws Exception {
assertSame(pluginRegistry, host.getRegistry());
assertNotSame(pluginRegistry, otherHost.getRegistry());
}
}

View File

@@ -9,6 +9,17 @@
<plugin:registry id="bar" class="org.synyx.hera.core.SamplePlugin" />
<bean class="org.synyx.hera.core.SamplePluginImplementation" />
<bean class="org.synyx.hera.core.SamplePluginImplementation" />
<bean id="host" class="org.synyx.hera.core.SamplePluginHost">
<property name="registry" ref="bar" />
</bean>
<!-- Uncommented till TODO in PluginListDefinitionParser is resolved -->
<bean id="otherHost" class="org.synyx.hera.core.SamplePluginHost">
<property name="registry">
<plugin:registry id="tadaa" class="org.synyx.hera.core.SamplePlugin" />
</property>
</bean>
</beans>

View File

@@ -9,4 +9,4 @@ log4j.rootLogger=WARN, stdout
# Hibernate logging options (INFO only shows startup messages)
log4j.logger.org.springframework=INFO
log4j.logger.org.synyx.plugin=DEBUG
log4j.logger.org.synyx.hera=DEBUG