From 9fe35a24c9d92c6f7938b7d21545cba4d216f568 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Fri, 7 Dec 2018 11:18:46 +0100 Subject: [PATCH] DATACMNS-1439 - Use Java Beans decapitalization for default repository implementation bean names. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We now use Java Beans Introspector during default bean name derivation from class names. Previously, we used String.decapitalize(…) which decapitalizes always the first character. Java Beans do not decapitalize upper case sequences so a class name com.acme.UDPRepository translates to a bean name with UDPRepository instead of uDPRepository. Original pull request: #325. --- ...aultImplementationLookupConfiguration.java | 5 +- .../ImplementationDetectionConfiguration.java | 5 +- .../RepositoryFragmentConfiguration.java | 4 +- ...mentationLookupConfigurationUnitTests.java | 46 +++++++++++++ ...tationDetectionConfigurationUnitTests.java | 69 +++++++++++++++++++ ...ositoryFragmentConfigurationUnitTests.java | 39 +++++++++++ 6 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java create mode 100644 src/test/java/org/springframework/data/repository/config/ImplementationDetectionConfigurationUnitTests.java create mode 100644 src/test/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationUnitTests.java diff --git a/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java b/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java index c3e6c457a..2acfc1839 100644 --- a/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/DefaultImplementationLookupConfiguration.java @@ -15,6 +15,7 @@ */ package org.springframework.data.repository.config; +import java.beans.Introspector; import java.io.IOException; import org.springframework.beans.factory.config.BeanDefinition; @@ -32,6 +33,7 @@ import org.springframework.util.StringUtils; * Default implementation of {@link ImplementationLookupConfiguration}. * * @author Oliver Gierke + * @author Mark Paluch * @since 2.1 */ class DefaultImplementationLookupConfiguration implements ImplementationLookupConfiguration { @@ -54,8 +56,7 @@ class DefaultImplementationLookupConfiguration implements ImplementationLookupCo this.config = config; this.interfaceName = interfaceName; - this.beanName = StringUtils - .uncapitalize(ClassUtils.getShortName(interfaceName).concat(config.getImplementationPostfix())); + this.beanName = Introspector.decapitalize(ClassUtils.getShortName(interfaceName).concat(config.getImplementationPostfix())); } /* diff --git a/src/main/java/org/springframework/data/repository/config/ImplementationDetectionConfiguration.java b/src/main/java/org/springframework/data/repository/config/ImplementationDetectionConfiguration.java index a85a48392..1a38f4706 100644 --- a/src/main/java/org/springframework/data/repository/config/ImplementationDetectionConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/ImplementationDetectionConfiguration.java @@ -15,13 +15,14 @@ */ package org.springframework.data.repository.config; +import java.beans.Introspector; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.filter.TypeFilter; import org.springframework.data.util.Streamable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Expresses configuration to be used to detect implementation classes for repositories and repository fragments. @@ -75,7 +76,7 @@ public interface ImplementationDetectionConfiguration { throw new IllegalStateException("Cannot generate bean name for BeanDefinition without bean class name!"); } - return StringUtils.uncapitalize(ClassUtils.getShortName(beanName)); + return Introspector.decapitalize(ClassUtils.getShortName(beanName)); } /** diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfiguration.java b/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfiguration.java index f710d1cdc..5fde62f20 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfiguration.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfiguration.java @@ -17,13 +17,13 @@ package org.springframework.data.repository.config; import lombok.Value; +import java.beans.Introspector; import java.util.Optional; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.data.config.ConfigurationUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * Fragment configuration consisting of an interface name and the implementation class name. @@ -76,7 +76,7 @@ public class RepositoryFragmentConfiguration { * @return name of the implementation bean. */ public String getImplementationBeanName() { - return StringUtils.uncapitalize(ClassUtils.getShortName(getClassName())); + return Introspector.decapitalize(ClassUtils.getShortName(getClassName())); } /** diff --git a/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java b/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java new file mode 100644 index 000000000..20de2598f --- /dev/null +++ b/src/test/java/org/springframework/data/repository/config/DefaultImplementationLookupConfigurationUnitTests.java @@ -0,0 +1,46 @@ +/* + * Copyright 2018 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 static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; + +/** + * Unit tests for {@link DefaultImplementationLookupConfigurationUnitTests}. + * + * @author Mark Paluch + */ +public class DefaultImplementationLookupConfigurationUnitTests { + + @Test // DATACMNS-1439 + public void shouldConsiderBeanNameDecapitalization() { + + ImplementationDetectionConfiguration idcMock = mock(ImplementationDetectionConfiguration.class); + when(idcMock.getImplementationPostfix()).thenReturn("Impl"); + + assertThat(getImplementationBeanName(idcMock, "com.acme.UDPRepository")).isEqualTo("UDPRepositoryImpl"); + assertThat(getImplementationBeanName(idcMock, "com.acme.UdpRepository")).isEqualTo("udpRepositoryImpl"); + } + + private static String getImplementationBeanName(ImplementationDetectionConfiguration idcMock, String interfaceName) { + + DefaultImplementationLookupConfiguration configuration = new DefaultImplementationLookupConfiguration(idcMock, + interfaceName); + return configuration.getImplementationBeanName(); + } +} diff --git a/src/test/java/org/springframework/data/repository/config/ImplementationDetectionConfigurationUnitTests.java b/src/test/java/org/springframework/data/repository/config/ImplementationDetectionConfigurationUnitTests.java new file mode 100644 index 000000000..b739ef142 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/config/ImplementationDetectionConfigurationUnitTests.java @@ -0,0 +1,69 @@ +/* + * Copyright 2018 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 static org.assertj.core.api.Assertions.*; + +import org.junit.Test; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.filter.TypeFilter; +import org.springframework.data.util.Streamable; + +/** + * Unit tests for {@link ImplementationDetectionConfiguration}. + * + * @author Mark Paluch + */ +public class ImplementationDetectionConfigurationUnitTests { + + @Test // DATACMNS-1439 + public void shouldConsiderBeanNameDecapitalization() { + + assertThat(getBeanName("com.acme.UDPRepository")).isEqualTo("UDPRepository"); + assertThat(getBeanName("com.acme.UdpRepository")).isEqualTo("udpRepository"); + } + + private static String getBeanName(String className) { + + MockImplementationDetectionConfiguration configuration = new MockImplementationDetectionConfiguration(); + + return configuration.generateBeanName(BeanDefinitionBuilder.rootBeanDefinition(className).getBeanDefinition()); + } + + private static class MockImplementationDetectionConfiguration implements ImplementationDetectionConfiguration { + + @Override + public String getImplementationPostfix() { + return null; + } + + @Override + public Streamable getBasePackages() { + return null; + } + + @Override + public Streamable getExcludeFilters() { + return null; + } + + @Override + public MetadataReaderFactory getMetadataReaderFactory() { + return null; + } + } +} diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationUnitTests.java new file mode 100644 index 000000000..9932b3638 --- /dev/null +++ b/src/test/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationUnitTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 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 static org.assertj.core.api.Assertions.*; + +import org.junit.Test; + +/** + * Unit tests for {@link RepositoryFragmentConfiguration}. + * + * @author Mark Paluch + */ +public class RepositoryFragmentConfigurationUnitTests { + + @Test // DATACMNS-1439 + public void shouldConsiderBeanNameDecapitalization() { + + assertThat(getImplementationBeanName("com.acme.UDPRepository")).isEqualTo("UDPRepository"); + assertThat(getImplementationBeanName("com.acme.UdpRepository")).isEqualTo("udpRepository"); + } + + private static String getImplementationBeanName(String className) { + return new RepositoryFragmentConfiguration("interface", className).getImplementationBeanName(); + } +}