From a5eedad48fdef3ecf816c249046ce2f18f1dd3e6 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sat, 18 Jan 2014 12:35:21 +0100 Subject: [PATCH] DATACMNS-420 - Introduced RepositoryConfigurationDelegate. RepositoryConfigurationDelegate allows to easily trigger repository BeanDefinitions to be registered independently of the client API (XML or annotation configuration support). Refactored the existing configuration base classes to use the newly introduced class. Removed defaulting of infrastructure components needed for Spring 3.1 compatibility. --- .../RepositoryBeanDefinitionParser.java | 57 +-------- ...ositoryBeanDefinitionRegistrarSupport.java | 79 +----------- .../RepositoryConfigurationDelegate.java | 113 ++++++++++++++++++ ...anDefinitionRegistrarSupportUnitTests.java | 7 +- 4 files changed, 130 insertions(+), 126 deletions(-) create mode 100644 src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java index 1ccb349cb..acda849ba 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionParser.java @@ -17,13 +17,9 @@ package org.springframework.data.repository.config; import static org.springframework.beans.factory.support.BeanDefinitionReaderUtils.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.parsing.BeanComponentDefinition; import org.springframework.beans.factory.parsing.ReaderContext; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; @@ -41,8 +37,6 @@ import org.w3c.dom.Element; */ public class RepositoryBeanDefinitionParser implements BeanDefinitionParser { - private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryBeanDefinitionParser.class); - private final RepositoryConfigurationExtension extension; /** @@ -65,15 +59,15 @@ public class RepositoryBeanDefinitionParser implements BeanDefinitionParser { try { Environment environment = parser.getDelegate().getEnvironment(); + ResourceLoader resourceLoader = parser.getReaderContext().getResourceLoader(); + XmlRepositoryConfigurationSource configSource = new XmlRepositoryConfigurationSource(element, parser, environment); + RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configSource, resourceLoader); - for (RepositoryConfiguration config : extension.getRepositoryConfigurations( - configSource, parser.getReaderContext().getResourceLoader())) { - registerGenericRepositoryFactoryBean(config, parser); + for (BeanComponentDefinition definition : delegate.registerRepositoriesIn(parser.getRegistry(), extension)) { + parser.registerBeanComponent(definition); } - extension.registerBeansForRoot(parser.getRegistry(), configSource); - } catch (RuntimeException e) { handleError(e, element, parser.getReaderContext()); } @@ -85,47 +79,6 @@ public class RepositoryBeanDefinitionParser implements BeanDefinitionParser { reader.error(e.getMessage(), reader.extractSource(source), e); } - /** - * Registers a generic repository factory bean for a bean with the given name and the provided configuration context. - * - * @param parser - * @param name - * @param context - */ - private void registerGenericRepositoryFactoryBean( - RepositoryConfiguration configuration, ParserContext parser) { - - ResourceLoader resourceLoader = parser.getReaderContext().getResourceLoader(); - BeanDefinitionRegistry registry = parser.getRegistry(); - - RepositoryBeanDefinitionBuilder definitionBuilder = new RepositoryBeanDefinitionBuilder(registry, extension, - resourceLoader); - - try { - - BeanDefinitionBuilder builder = definitionBuilder.build(configuration); - extension.postProcess(builder, configuration.getConfigurationSource()); - - AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); - beanDefinition.setSource(configuration.getSource()); - - RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); - generator.setBeanClassLoader(parser.getReaderContext().getBeanClassLoader()); - - String beanName = generator.generateBeanName(beanDefinition, registry); - - if (LOGGER.isDebugEnabled()) { - LOGGER.debug("Registering repository: " + beanName + " - Interface: " + configuration.getRepositoryInterface() - + " - Factory: " + extension.getRepositoryFactoryClassName()); - } - - BeanComponentDefinition definition = new BeanComponentDefinition(beanDefinition, beanName); - parser.registerBeanComponent(definition); - } catch (RuntimeException e) { - handleError(e, configuration.getConfigurationSource().getElement(), parser.getReaderContext()); - } - } - /** * Returns whether the given {@link BeanDefinitionRegistry} already contains a bean of the given type assuming the * bean name has been autogenerated. diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java index d49e65ea8..390eb64ca 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupport.java @@ -17,20 +17,15 @@ package org.springframework.data.repository.config; import java.lang.annotation.Annotation; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.Environment; -import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** * Base class to implement {@link ImportBeanDefinitionRegistrar}s to enable repository @@ -38,21 +33,11 @@ import org.springframework.util.ClassUtils; * @author Oliver Gierke */ public abstract class RepositoryBeanDefinitionRegistrarSupport implements ImportBeanDefinitionRegistrar, - BeanClassLoaderAware, ResourceLoaderAware, EnvironmentAware { + ResourceLoaderAware, EnvironmentAware { private ResourceLoader resourceLoader; - private ClassLoader beanClassLoader; private Environment environment; - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.BeanClassLoaderAware#setBeanClassLoader(java.lang.ClassLoader) - */ - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.beanClassLoader = classLoader; - } - /* * (non-Javadoc) * @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader) @@ -77,72 +62,20 @@ public abstract class RepositoryBeanDefinitionRegistrarSupport implements Import */ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { - Assert.notNull(annotationMetadata); - Assert.notNull(registry); + Assert.notNull(resourceLoader, "ResourceLoader must not be null!"); + Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!"); + Assert.notNull(registry, "BeanDefinitionRegistry must not be null!"); // Guard against calls for sub-classes if (annotationMetadata.getAnnotationAttributes(getAnnotation().getName()) == null) { return; } - defaultExternalResources(registry); - AnnotationRepositoryConfigurationSource configurationSource = new AnnotationRepositoryConfigurationSource( annotationMetadata, getAnnotation(), environment); - RepositoryConfigurationExtension extension = getExtension(); - extension.registerBeansForRoot(registry, configurationSource); - - RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); - generator.setBeanClassLoader(beanClassLoader); - - RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader); - - for (RepositoryConfiguration configuration : extension - .getRepositoryConfigurations(configurationSource, resourceLoader)) { - - BeanDefinitionBuilder definitionBuilder = builder.build(configuration); - extension.postProcess(definitionBuilder, configurationSource); - - String beanName = generator.generateBeanName(definitionBuilder.getBeanDefinition(), registry); - registry.registerBeanDefinition(beanName, definitionBuilder.getBeanDefinition()); - } - } - - /** - * Workaround the lack of injectability of external resources into {@link ImportBeanDefinitionRegistrar}s in the - * Spring 3.1 timeline. We populate {@link #beanClassLoader} and default the {@link #resourceLoader} in case they - * haven't been set until a call to this method. - * - * @param registry must not be {@literal null}. - * @see SPR-9568 - */ - private void defaultExternalResources(BeanDefinitionRegistry registry) { - - if (beanClassLoader == null) { - this.beanClassLoader = getBeanClassLoader(registry); - } - - if (resourceLoader == null) { - this.resourceLoader = new DefaultResourceLoader(this.beanClassLoader); - } - } - - /** - * Returns the bean class loader contained in the given registry if it is a {@link ConfigurableBeanFactory}. Falls - * back to the {@link ResourceLoader}'s {@link ClassLoader} or the global default one if that one is {@literal null} - * in turn. - * - * @param registry must not be {@literal null}. - * @return - */ - private ClassLoader getBeanClassLoader(BeanDefinitionRegistry registry) { - - if (registry instanceof ConfigurableBeanFactory) { - return ((ConfigurableBeanFactory) registry).getBeanClassLoader(); - } - - return resourceLoader == null ? ClassUtils.getDefaultClassLoader() : resourceLoader.getClassLoader(); + RepositoryConfigurationDelegate delegate = new RepositoryConfigurationDelegate(configurationSource, resourceLoader); + delegate.registerRepositoriesIn(registry, getExtension()); } /** diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java new file mode 100644 index 000000000..6097a91da --- /dev/null +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -0,0 +1,113 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.repository.config; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanNameGenerator; +import org.springframework.core.io.ResourceLoader; +import org.springframework.util.Assert; + +/** + * Delegate for configuration integration to reuse the general way of detecting repositories. Customization is done by + * providing a configuration format specific {@link RepositoryConfigurationSource} (currently either XML or annotations + * are supported). The actual registration can then be triggered for different {@link RepositoryConfigurationExtension} + * s. + * + * @author Oliver Gierke + */ +public class RepositoryConfigurationDelegate { + + private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryConfigurationDelegate.class); + + private final RepositoryConfigurationSource configurationSource; + private final ResourceLoader resourceLoader; + private final BeanNameGenerator generator; + private final boolean isXml; + + /** + * Creates a new {@link RepositoryConfigurationDelegate} for the given {@link RepositoryConfigurationSource} and + * {@link ResourceLoader}. + * + * @param configurationSource must not be {@literal null}. + * @param resourceLoader must not be {@literal null}. + */ + public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurationSource, + ResourceLoader resourceLoader) { + + this.isXml = configurationSource instanceof XmlRepositoryConfigurationSource; + boolean isAnnotation = configurationSource instanceof AnnotationRepositoryConfigurationSource; + + Assert.isTrue(isXml || isAnnotation, + "Configuration source must either be an Xml- or an AnnotationBasedConfigurationSource!"); + Assert.notNull(resourceLoader); + + RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator(); + generator.setBeanClassLoader(resourceLoader.getClassLoader()); + + this.generator = generator; + this.configurationSource = configurationSource; + this.resourceLoader = resourceLoader; + } + + /** + * Registers the found repositories in the given {@link BeanDefinitionRegistry}. + * + * @param registry + * @param extension + * @return {@link BeanComponentDefinition}s for all repository bean definitions found. + */ + public List registerRepositoriesIn(BeanDefinitionRegistry registry, + RepositoryConfigurationExtension extension) { + + extension.registerBeansForRoot(registry, configurationSource); + + RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader); + List definitions = new ArrayList(); + + for (RepositoryConfiguration configuration : extension + .getRepositoryConfigurations(configurationSource, resourceLoader)) { + + BeanDefinitionBuilder definitionBuilder = builder.build(configuration); + + if (isXml) { + extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource); + } else { + extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource); + } + + AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition(); + String beanName = generator.generateBeanName(beanDefinition, registry); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Registering repository: " + beanName + " - Interface: " + configuration.getRepositoryInterface() + + " - Factory: " + extension.getRepositoryFactoryClassName()); + } + + registry.registerBeanDefinition(beanName, beanDefinition); + definitions.add(new BeanComponentDefinition(beanDefinition, beanName)); + } + + return definitions; + } +} diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java index b5aba3222..656e62f1a 100644 --- a/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/RepositoryBeanDefinitionRegistrarSupportUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.StandardAnnotationMetadata; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; @@ -87,6 +88,10 @@ public class RepositoryBeanDefinitionRegistrarSupportUnitTests { static class DummyRegistrar extends RepositoryBeanDefinitionRegistrarSupport { + DummyRegistrar() { + setResourceLoader(new DefaultResourceLoader()); + } + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport#getAnnotation()