DATACMNS-821 - Improved type prediction for FactoryBean implementations.

Previously, we registered an InstantiationAwareBeanPostProcessor to predict the type to be created by RepositoryFactoryBeanSupport by inspecting a particular property value of the registered BeanDefinition.

This has now been elevated to a more generic mechanism that can get a FactoryBean type configured with a set of properties to inspect for a configured type. That new infrastructure now replaces the explicit configuration for RepositoryFactoryBeanSupport with one that's set up via configuration.
This commit is contained in:
Oliver Gierke
2016-02-24 19:20:43 +01:00
parent 2b2882635a
commit deb884dfb1
4 changed files with 133 additions and 66 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2016 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.
@@ -20,26 +20,22 @@ import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.SpringVersion;
import org.springframework.data.querydsl.User;
import org.springframework.data.repository.Repository;
/**
* Integration test to make sure Spring Data repository factory beans are found without the factories already
* instantiated. Only executed for Spring version newer than 3.2.2.RELEASE.
* instantiated.
*
* @see SPR-10517
* @author Oliver Gierke
*/
public class RepositoryInterfaceAwareBeanPostProcessorIntegrationTests {
static final String LAST_ERROR_VERSION = "3.2.2.RELEASE";
public class FactoryBeanTypePredictingPostProcessorIntegrationTests {
DefaultListableBeanFactory factory;
@@ -54,7 +50,8 @@ public class RepositoryInterfaceAwareBeanPostProcessorIntegrationTests {
factory.registerBeanDefinition("repository", builder.getBeanDefinition());
// Register predicting BeanPostProcessor
RepositoryInterfaceAwareBeanPostProcessor processor = new RepositoryInterfaceAwareBeanPostProcessor();
FactoryBeanTypePredictingBeanPostProcessor processor = new FactoryBeanTypePredictingBeanPostProcessor(
RepositoryFactoryBeanSupport.class, "repositoryInterface");
processor.setBeanFactory(factory);
factory.addBeanPostProcessor(processor);
}
@@ -62,8 +59,6 @@ public class RepositoryInterfaceAwareBeanPostProcessorIntegrationTests {
@Test
public void lookupBeforeInstantiation() {
Assume.assumeTrue(isFixedSpringVersion());
String[] strings = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, RepositoryFactoryInformation.class,
false, false);
assertThat(Arrays.asList(strings), hasItem("&repository"));
@@ -72,8 +67,6 @@ public class RepositoryInterfaceAwareBeanPostProcessorIntegrationTests {
@Test
public void lookupAfterInstantiation() {
Assume.assumeTrue(isFixedSpringVersion());
factory.getBean(UserRepository.class);
String[] strings = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(factory, RepositoryFactoryInformation.class,
@@ -81,11 +74,5 @@ public class RepositoryInterfaceAwareBeanPostProcessorIntegrationTests {
assertThat(Arrays.asList(strings), hasItem("&repository"));
}
private static boolean isFixedSpringVersion() {
return SpringVersion.getVersion().compareTo(LAST_ERROR_VERSION) == 1;
}
interface UserRepository extends Repository<User, Long> {
}
interface UserRepository extends Repository<User, Long> {}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2010 the original author or authors.
* Copyright 2008-2016 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
@@ -15,10 +15,12 @@
*/
package org.springframework.data.repository.core.support;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,8 +31,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.core.support.RepositoryInterfaceAwareBeanPostProcessor;
import org.springframework.jndi.JndiObjectFactoryBean;
/**
* Unit tests for {@link RepositoryInterfaceAwareBeanPostProcessor}.
@@ -38,29 +39,26 @@ import org.springframework.data.repository.core.support.RepositoryInterfaceAware
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class RepositoryInterfaceAwareBeanPostProcessorUnitTests {
public class FactoryBeanTypePredictingPostProcessorUnitTests {
private static final Class<?> FACTORY_CLASS = RepositoryFactoryBeanSupport.class;
private static final String BEAN_NAME = "foo";
private static final String DAO_INTERFACE_PROPERTY = "repositoryInterface";
private RepositoryInterfaceAwareBeanPostProcessor processor;
FactoryBeanTypePredictingBeanPostProcessor processor;
BeanDefinition beanDefinition;
@Mock
private ConfigurableListableBeanFactory beanFactory;
private BeanDefinition beanDefinition;
@Mock ConfigurableListableBeanFactory beanFactory;
@Before
public void setUp() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FACTORY_CLASS).addPropertyValue(
DAO_INTERFACE_PROPERTY, UserDao.class);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FACTORY_CLASS)
.addPropertyValue(DAO_INTERFACE_PROPERTY, UserDao.class);
this.beanDefinition = builder.getBeanDefinition();
this.processor = new FactoryBeanTypePredictingBeanPostProcessor(FACTORY_CLASS, "repositoryInterface");
when(beanFactory.getBeanDefinition(BEAN_NAME)).thenReturn(beanDefinition);
processor = new RepositoryInterfaceAwareBeanPostProcessor();
}
@Test
@@ -80,8 +78,8 @@ public class RepositoryInterfaceAwareBeanPostProcessorUnitTests {
@Test
public void doesNotResolveInterfaceForUnloadableClass() throws Exception {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FACTORY_CLASS).addPropertyValue(
DAO_INTERFACE_PROPERTY, "com.acme.Foo");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(FACTORY_CLASS)
.addPropertyValue(DAO_INTERFACE_PROPERTY, "com.acme.Foo");
when(beanFactory.getBeanDefinition(BEAN_NAME)).thenReturn(builder.getBeanDefinition());
@@ -97,16 +95,36 @@ public class RepositoryInterfaceAwareBeanPostProcessorUnitTests {
assertNotTypeDetected(FACTORY_CLASS);
}
private void assertNotTypeDetected(Class<?> beanClass) {
@Test(expected = IllegalArgumentException.class)
public void rejectsNonFactoryBeanType() {
new FactoryBeanTypePredictingBeanPostProcessor(Object.class, "property");
}
/**
* @see DATACMNS-821
*/
@Test
public void usesFirstValueIfPropertyIsOfArrayType() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(JndiObjectFactoryBean.class);
builder.addPropertyValue("proxyInterfaces",
new String[] { Serializable.class.getName(), Iterable.class.getName() });
when(beanFactory.getBeanDefinition(BEAN_NAME)).thenReturn(builder.getBeanDefinition());
processor = new FactoryBeanTypePredictingBeanPostProcessor(JndiObjectFactoryBean.class, "proxyInterface",
"proxyInterfaces");
processor.setBeanFactory(beanFactory);
assertThat(processor.predictBeanType(JndiObjectFactoryBean.class, BEAN_NAME),
is(typeCompatibleWith(Serializable.class)));
}
private void assertNotTypeDetected(Class<?> beanClass) {
assertThat(processor.predictBeanType(beanClass, BEAN_NAME), is(nullValue()));
}
private class User {
private class User {}
}
private interface UserDao extends Repository<User, Long> {
}
private interface UserDao extends Repository<User, Long> {}
}