From 654801083b3e56cb7307324de61b4e2e06936ea9 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Fri, 24 Jun 2016 11:44:55 -0700 Subject: [PATCH] Provide unified @EntityScan annotation Add a new `@EntiyScan` annotation that's used by auto-configuration to: * Set JPA packagesToScan. * Set Neo4J's SessionFactory packages. * Set the initial entity set for Spring Data MongoDB, Cassandra and Couchbase mapping contexts. Additionally deprecate `@org.springframework.boot.orm.jpa.EntityScan`. See gh-6142 --- .../CassandraDataAutoConfiguration.java | 26 ++- .../SpringBootCouchbaseDataConfiguration.java | 18 +- .../mongo/MongoDataAutoConfiguration.java | 70 +------ .../neo4j/Neo4jDataAutoConfiguration.java | 97 +++------ .../boot/autoconfigure/domain/EntityScan.java | 93 +++++++++ .../domain/EntityScanPackages.java | 177 ++++++++++++++++ .../autoconfigure/domain/EntityScanner.java | 95 +++++++++ .../{neo4j => domain}/package-info.java | 4 +- .../orm/jpa/JpaBaseConfiguration.java | 12 +- .../CassandraDataAutoConfigurationTests.java | 27 +++ .../CouchbaseDataAutoConfigurationTests.java | 24 +++ ...ngoRepositoriesAutoConfigurationTests.java | 46 ++++- .../MongoDataAutoConfigurationTests.java | 23 +++ ...o4jRepositoriesAutoConfigurationTests.java | 2 +- .../Neo4jDataAutoConfigurationTests.java | 9 +- ...o4jRepositoriesAutoConfigurationTests.java | 1 + .../domain/EntityScanPackagesTests.java | 191 ++++++++++++++++++ .../domain/EntityScannerTests.java | 109 ++++++++++ .../domain/scan/a/EmbeddableA.java | 24 +++ .../autoconfigure/domain/scan/a/EntityA.java | 24 +++ .../domain/scan/b/EmbeddableB.java | 24 +++ .../autoconfigure/domain/scan/b/EntityB.java | 24 +++ .../domain/scan/c/EmbeddableC.java | 24 +++ .../autoconfigure/domain/scan/c/EntityC.java | 24 +++ .../mongo/MongoAutoConfigurationTests.java | 4 +- .../security/user/SecurityConfig.java | 2 +- .../boot/orm/jpa/EntityScan.java | 5 +- 27 files changed, 1030 insertions(+), 149 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScan.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/{neo4j => domain}/package-info.java (85%) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScanPackagesTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EmbeddableA.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EntityA.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EmbeddableB.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EntityB.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EmbeddableC.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EntityC.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java index 65594fb3e2b..a086da5a020 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfiguration.java @@ -16,18 +16,24 @@ package org.springframework.boot.autoconfigure.data.cassandra; +import java.util.List; + import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Session; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraProperties; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.domain.EntityScanPackages; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.cassandra.config.CassandraEntityClassScanner; import org.springframework.data.cassandra.config.CassandraSessionFactoryBean; import org.springframework.data.cassandra.config.SchemaAction; import org.springframework.data.cassandra.convert.CassandraConverter; @@ -50,20 +56,32 @@ import org.springframework.data.cassandra.mapping.CassandraMappingContext; @AutoConfigureAfter(CassandraAutoConfiguration.class) public class CassandraDataAutoConfiguration { + private final BeanFactory beanFactory; + private final CassandraProperties properties; private final Cluster cluster; - public CassandraDataAutoConfiguration(CassandraProperties properties, - Cluster cluster) { + public CassandraDataAutoConfiguration(BeanFactory beanFactory, + CassandraProperties properties, Cluster cluster) { + this.beanFactory = beanFactory; this.properties = properties; this.cluster = cluster; } @Bean @ConditionalOnMissingBean - public CassandraMappingContext cassandraMapping() { - return new BasicCassandraMappingContext(); + public CassandraMappingContext cassandraMapping() throws ClassNotFoundException { + BasicCassandraMappingContext context = new BasicCassandraMappingContext(); + List packages = EntityScanPackages.get(this.beanFactory) + .getPackageNames(); + if (packages.isEmpty() && AutoConfigurationPackages.has(this.beanFactory)) { + packages = AutoConfigurationPackages.get(this.beanFactory); + } + if (!packages.isEmpty()) { + context.setInitialEntitySet(CassandraEntityClassScanner.scan(packages)); + } + return context; } @Bean diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/SpringBootCouchbaseDataConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/SpringBootCouchbaseDataConfiguration.java index 22e0b363c2f..7ecae7bdbd6 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/SpringBootCouchbaseDataConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/couchbase/SpringBootCouchbaseDataConfiguration.java @@ -16,15 +16,21 @@ package org.springframework.boot.autoconfigure.data.couchbase; +import java.util.Set; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.domain.EntityScanner; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.annotation.Persistent; import org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration; import org.springframework.data.couchbase.config.BeanNames; import org.springframework.data.couchbase.config.CouchbaseConfigurer; import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.data.couchbase.core.mapping.Document; import org.springframework.data.couchbase.core.query.Consistency; import org.springframework.data.couchbase.repository.support.IndexManager; @@ -38,12 +44,16 @@ import org.springframework.data.couchbase.repository.support.IndexManager; @ConditionalOnBean(CouchbaseConfigurer.class) class SpringBootCouchbaseDataConfiguration extends AbstractCouchbaseDataConfiguration { + private final ApplicationContext applicationContext; + private final CouchbaseDataProperties properties; private final CouchbaseConfigurer couchbaseConfigurer; - SpringBootCouchbaseDataConfiguration(CouchbaseDataProperties properties, + SpringBootCouchbaseDataConfiguration(ApplicationContext applicationContext, + CouchbaseDataProperties properties, ObjectProvider couchbaseConfigurerProvider) { + this.applicationContext = applicationContext; this.properties = properties; this.couchbaseConfigurer = couchbaseConfigurerProvider.getIfAvailable(); } @@ -58,6 +68,12 @@ class SpringBootCouchbaseDataConfiguration extends AbstractCouchbaseDataConfigur return this.properties.getConsistency(); } + @Override + protected Set> getInitialEntitySet() throws ClassNotFoundException { + return new EntityScanner(this.applicationContext).scan(Document.class, + Persistent.class); + } + @Override @ConditionalOnMissingBean(name = BeanNames.COUCHBASE_TEMPLATE) @Bean(name = BeanNames.COUCHBASE_TEMPLATE) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java index 44fcc070a22..32544d4915a 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfiguration.java @@ -17,34 +17,25 @@ package org.springframework.boot.autoconfigure.data.mongo; import java.net.UnknownHostException; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; import com.mongodb.DB; import com.mongodb.Mongo; import com.mongodb.MongoClient; import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.domain.EntityScanner; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.type.filter.AnnotationTypeFilter; import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.data.annotation.Persistent; @@ -61,7 +52,6 @@ import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.gridfs.GridFsTemplate; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; /** @@ -84,26 +74,16 @@ import org.springframework.util.StringUtils; @ConditionalOnClass({ Mongo.class, MongoTemplate.class }) @EnableConfigurationProperties(MongoProperties.class) @AutoConfigureAfter(MongoAutoConfiguration.class) -public class MongoDataAutoConfiguration implements BeanClassLoaderAware { +public class MongoDataAutoConfiguration { - private final MongoProperties properties; - - private final Environment environment; + private final ApplicationContext applicationContext; - private final ResourceLoader resourceLoader; - - private ClassLoader classLoader; + private final MongoProperties properties; - public MongoDataAutoConfiguration(MongoProperties properties, Environment environment, - ResourceLoader resourceLoader) { + public MongoDataAutoConfiguration(ApplicationContext applicationContext, + MongoProperties properties) { + this.applicationContext = applicationContext; this.properties = properties; - this.environment = environment; - this.resourceLoader = resourceLoader; - } - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; } @Bean @@ -142,7 +122,8 @@ public class MongoDataAutoConfiguration implements BeanClassLoaderAware { public MongoMappingContext mongoMappingContext(BeanFactory beanFactory) throws ClassNotFoundException { MongoMappingContext context = new MongoMappingContext(); - context.setInitialEntitySet(getInitialEntitySet(beanFactory)); + context.setInitialEntitySet(new EntityScanner(this.applicationContext) + .scan(Document.class, Persistent.class)); Class strategyClass = this.properties.getFieldNamingStrategy(); if (strategyClass != null) { context.setFieldNamingStrategy( @@ -151,37 +132,6 @@ public class MongoDataAutoConfiguration implements BeanClassLoaderAware { return context; } - private Set> getInitialEntitySet(BeanFactory beanFactory) - throws ClassNotFoundException { - Set> entitySet = new HashSet>(); - ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( - false); - scanner.setEnvironment(this.environment); - scanner.setResourceLoader(this.resourceLoader); - scanner.addIncludeFilter(new AnnotationTypeFilter(Document.class)); - scanner.addIncludeFilter(new AnnotationTypeFilter(Persistent.class)); - for (String basePackage : getMappingBasePackages(beanFactory)) { - if (StringUtils.hasText(basePackage)) { - for (BeanDefinition candidate : scanner - .findCandidateComponents(basePackage)) { - entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), - this.classLoader)); - } - } - } - return entitySet; - } - - private static Collection getMappingBasePackages(BeanFactory beanFactory) { - try { - return AutoConfigurationPackages.get(beanFactory); - } - catch (IllegalStateException ex) { - // no auto-configuration package registered yet - return Collections.emptyList(); - } - } - @Bean @ConditionalOnMissingBean public GridFsTemplate gridFsTemplate(MongoDbFactory mongoDbFactory, diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java index dd99a552d61..45e9e00f992 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfiguration.java @@ -22,30 +22,22 @@ import org.neo4j.ogm.session.Neo4jSession; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.domain.EntityScanPackages; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.neo4j.SessionFactoryProvider; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.springframework.data.neo4j.config.Neo4jConfiguration; import org.springframework.data.neo4j.template.Neo4jOperations; -import org.springframework.data.neo4j.template.Neo4jTemplate; /** - * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Neo4j support. - *

- * Registers a {@link Neo4jTemplate} bean if no other bean of the same type is configured. + * {@link EnableAutoConfiguration Auto-configuration} for Spring Data Neo4j. * * @author Michael Hunger * @author Josh Long @@ -59,82 +51,51 @@ import org.springframework.data.neo4j.template.Neo4jTemplate; @EnableConfigurationProperties(Neo4jProperties.class) public class Neo4jDataAutoConfiguration { - @Configuration - @Import(SessionFactoryProviderConfiguration.class) - public static class SpringBootNeo4jConfiguration extends Neo4jConfiguration { - - private final ObjectProvider sessionFactoryProvider; - - public SpringBootNeo4jConfiguration( - ObjectProvider sessionFactoryProvider) { - this.sessionFactoryProvider = sessionFactoryProvider; - } - - @Override - public SessionFactory getSessionFactory() { - return this.sessionFactoryProvider.getObject().getSessionFactory(); - } + private final Neo4jProperties properties; - @Bean - @Scope(scopeName = "${spring.data.neo4j.session.scope:singleton}", proxyMode = ScopedProxyMode.TARGET_CLASS) - @Override - public Session getSession() throws Exception { - return getSessionFactory().openSession(); - } + public Neo4jDataAutoConfiguration(Neo4jProperties properties) { + this.properties = properties; + } + @Bean + @ConditionalOnMissingBean + public org.neo4j.ogm.config.Configuration configuration() { + return this.properties.createConfiguration(); } @Configuration - @Import(Neo4jConfigurationConfiguration.class) - static class SessionFactoryProviderConfiguration implements BeanFactoryAware { + static class SpringBootNeo4jConfiguration extends Neo4jConfiguration { - private final org.neo4j.ogm.config.Configuration configuration; + private final ApplicationContext applicationContext; - private ConfigurableListableBeanFactory beanFactory; + private final org.neo4j.ogm.config.Configuration configuration; - SessionFactoryProviderConfiguration( + SpringBootNeo4jConfiguration(ApplicationContext applicationContext, org.neo4j.ogm.config.Configuration configuration) { + this.applicationContext = applicationContext; this.configuration = configuration; } - @Bean - @ConditionalOnMissingBean - public SessionFactoryProvider sessionFactoryProvider() { - SessionFactoryProvider provider = new SessionFactoryProvider(); - provider.setConfiguration(this.configuration); - provider.setPackagesToScan(getPackagesToScan()); - return provider; - } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + public SessionFactory getSessionFactory() { + return new SessionFactory(this.configuration, getPackagesToScan()); } - protected String[] getPackagesToScan() { - if (AutoConfigurationPackages.has(this.beanFactory)) { - List basePackages = AutoConfigurationPackages - .get(this.beanFactory); - return basePackages.toArray(new String[basePackages.size()]); + private String[] getPackagesToScan() { + List packages = EntityScanPackages.get(this.applicationContext) + .getPackageNames(); + if (packages.isEmpty() + && AutoConfigurationPackages.has(this.applicationContext)) { + packages = AutoConfigurationPackages.get(this.applicationContext); } - return new String[0]; - } - - } - - @Configuration - static class Neo4jConfigurationConfiguration { - - private final Neo4jProperties properties; - - Neo4jConfigurationConfiguration(Neo4jProperties properties) { - this.properties = properties; + return packages.toArray(new String[packages.size()]); } + @Override @Bean - @ConditionalOnMissingBean - public org.neo4j.ogm.config.Configuration configuration() { - return this.properties.createConfiguration(); + @Scope(scopeName = "${spring.data.neo4j.session.scope:singleton}", proxyMode = ScopedProxyMode.TARGET_CLASS) + public Session getSession() throws Exception { + return super.getSession(); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScan.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScan.java new file mode 100644 index 00000000000..48508f61359 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScan.java @@ -0,0 +1,93 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.AliasFor; + +/** + * Configures the base packages used by auto-configuration when scanning for entity + * classes. + *

+ * Using {@code @EntityScan} will cause auto-configuration to: + *

    + *
  • Set the + * {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#setPackagesToScan(String...) + * packages scanned} for JPA entities.
  • + *
  • Set the packages used with Neo4J's {@link org.neo4j.ogm.session.SessionFactory + * SessionFactory}.
  • + *
  • Set the + * {@link org.springframework.data.mapping.context.AbstractMappingContext#setInitialEntitySet(java.util.Set) + * initial entity set} used with Spring Data + * {@link org.springframework.data.mongodb.core.mapping.MongoMappingContext MongoDB}, + * {@link org.springframework.data.cassandra.mapping.CassandraMappingContext Cassandra} + * and {@link org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext + * Couchbase} mapping contexts.
  • + *
+ *

+ * One of {@link #basePackageClasses()}, {@link #basePackages()} or its alias + * {@link #value()} may be specified to define specific packages to scan. If specific + * packages are not defined scanning will occur from the package of the class with this + * annotation. + * + * @author Phillip Webb + * @since 1.4.0 + * @see EntityScanPackages + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(EntityScanPackages.Registrar.class) +public @interface EntityScan { + + /** + * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation + * declarations e.g.: {@code @EntityScan("org.my.pkg")} instead of + * {@code @EntityScan(basePackages="org.my.pkg")}. + * @return the base packages to scan + */ + @AliasFor("basePackages") + String[] value() default {}; + + /** + * Base packages to scan for entities. {@link #value()} is an alias for (and mutually + * exclusive with) this attribute. + *

+ * Use {@link #basePackageClasses()} for a type-safe alternative to String-based + * package names. + * @return the base packages to scan + */ + @AliasFor("value") + String[] basePackages() default {}; + + /** + * Type-safe alternative to {@link #basePackages()} for specifying the packages to + * scan for entities. The package of each class specified will be scanned. + *

+ * Consider creating a special no-op marker class or interface in each package that + * serves no purpose other than being referenced by this attribute. + * @return classes from the base packages to scan + */ + Class[] basePackageClasses() default {}; + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java new file mode 100644 index 00000000000..9c652a1513a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanPackages.java @@ -0,0 +1,177 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConstructorArgumentValues; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.annotation.Order; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * Class for storing {@link EntityScan @EntityScan} specified packages for reference later + * (e.g. by JPA auto-configuration). + * + * @author Phillip Webb + * @since 1.4.0 + * @see EntityScan + * @see EntityScanner + */ +public class EntityScanPackages { + + private static final String BEAN = EntityScanPackages.class.getName(); + + private static final EntityScanPackages NONE = new EntityScanPackages(); + + private final List packageNames; + + EntityScanPackages(String... packageNames) { + List packages = new ArrayList(); + for (String name : packageNames) { + if (StringUtils.hasText(name)) { + packages.add(name); + } + } + this.packageNames = Collections.unmodifiableList(packages); + } + + /** + * Return the package names specified from all {@link EntityScan @EntityScan} + * annotations. + * @return the entity scan package names + */ + public List getPackageNames() { + return this.packageNames; + } + + /** + * Return the {@link EntityScanPackages} for the given bean factory. + * @param beanFactory the source bean factory + * @return the {@link EntityScanPackages} for the bean factory (never {@code null}) + */ + public static EntityScanPackages get(BeanFactory beanFactory) { + // Currently we only store a single base package, but we return a list to + // allow this to change in the future if needed + try { + return beanFactory.getBean(BEAN, EntityScanPackages.class); + } + catch (NoSuchBeanDefinitionException ex) { + return NONE; + } + } + + /** + * Register the specified entity scan packages with the system. + * @param registry the source registry + * @param packageNames the package names to register + */ + public static void register(BeanDefinitionRegistry registry, String... packageNames) { + Assert.notNull(registry, "Registry must not be null"); + Assert.notNull(packageNames, "PackageNames must not be null"); + register(registry, Arrays.asList(packageNames)); + } + + /** + * Register the specified entity scan packages with the system. + * @param registry the source registry + * @param packageNames the package names to register + */ + public static void register(BeanDefinitionRegistry registry, + Collection packageNames) { + Assert.notNull(registry, "Registry must not be null"); + Assert.notNull(packageNames, "PackageNames must not be null"); + if (registry.containsBeanDefinition(BEAN)) { + BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); + ConstructorArgumentValues constructorArguments = beanDefinition + .getConstructorArgumentValues(); + constructorArguments.addIndexedArgumentValue(0, + addPackageNames(constructorArguments, packageNames)); + } + else { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(EntityScanPackages.class); + beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, + packageNames.toArray(new String[packageNames.size()])); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN, beanDefinition); + } + } + + private static String[] addPackageNames( + ConstructorArgumentValues constructorArguments, + Collection packageNames) { + String[] existing = (String[]) constructorArguments + .getIndexedArgumentValue(0, String[].class).getValue(); + Set merged = new LinkedHashSet(); + merged.addAll(Arrays.asList(existing)); + merged.addAll(packageNames); + return merged.toArray(new String[merged.size()]); + } + + /** + * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing + * configuration. + */ + @Order(Ordered.HIGHEST_PRECEDENCE) + static class Registrar implements ImportBeanDefinitionRegistrar { + + @Override + public void registerBeanDefinitions(AnnotationMetadata metadata, + BeanDefinitionRegistry registry) { + register(registry, getPackagesToScan(metadata)); + } + + private Set getPackagesToScan(AnnotationMetadata metadata) { + AnnotationAttributes attributes = AnnotationAttributes.fromMap( + metadata.getAnnotationAttributes(EntityScan.class.getName())); + String[] basePackages = attributes.getStringArray("basePackages"); + Class[] basePackageClasses = attributes + .getClassArray("basePackageClasses"); + Set packagesToScan = new LinkedHashSet(); + packagesToScan.addAll(Arrays.asList(basePackages)); + for (Class basePackageClass : basePackageClasses) { + packagesToScan.add(ClassUtils.getPackageName(basePackageClass)); + } + if (packagesToScan.isEmpty()) { + String packageName = ClassUtils.getPackageName(metadata.getClassName()); + Assert.state(!StringUtils.isEmpty(packageName), + "@EntityScan cannot be used with the default package"); + return Collections.singleton(packageName); + } + return packagesToScan; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java new file mode 100644 index 00000000000..dc1a25ecd1f --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/EntityScanner.java @@ -0,0 +1,95 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain; + +import java.lang.annotation.Annotation; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.AutoConfigurationPackages; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * An entity scanner that searches the classpath from a {@link EntityScan @EntityScan} + * specified packages. + * + * @author Phillip Webb + * @since 1.4.0 + */ +public class EntityScanner { + + private final ApplicationContext context; + + /** + * Create a new {@link EntityScanner} instance. + * @param context the source application context + */ + public EntityScanner(ApplicationContext context) { + Assert.notNull(context, "Context must not be null"); + this.context = context; + } + + /** + * Scan for entities with the specified annotations. + * @param annotationTypes the annotation types used on the entities + * @return a set of entity classes + * @throws ClassNotFoundException if an entity class cannot be loaded + */ + @SafeVarargs + public final Set> scan(Class... annotationTypes) + throws ClassNotFoundException { + List packages = getPackages(); + if (packages.isEmpty()) { + return Collections.>emptySet(); + } + Set> entitySet = new HashSet>(); + ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider( + false); + scanner.setEnvironment(this.context.getEnvironment()); + scanner.setResourceLoader(this.context); + for (Class annotationType : annotationTypes) { + scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType)); + } + for (String basePackage : packages) { + if (StringUtils.hasText(basePackage)) { + for (BeanDefinition candidate : scanner + .findCandidateComponents(basePackage)) { + entitySet.add(ClassUtils.forName(candidate.getBeanClassName(), + this.context.getClassLoader())); + } + } + } + return entitySet; + } + + private List getPackages() { + List packages = EntityScanPackages.get(this.context).getPackageNames(); + if (packages.isEmpty() && AutoConfigurationPackages.has(this.context)) { + packages = AutoConfigurationPackages.get(this.context); + } + return packages; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java similarity index 85% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java index e53e6c52144..7773c659b44 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/neo4j/package-info.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/domain/package-info.java @@ -15,6 +15,6 @@ */ /** - * Auto-configuration for Neo4j. + * General purpose domain annotations and classes. */ -package org.springframework.boot.autoconfigure.neo4j; +package org.springframework.boot.autoconfigure.domain; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 67631387027..67d1ccbd962 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.domain.EntityScanPackages; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; import org.springframework.context.annotation.Bean; @@ -63,8 +64,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter @Import(DataSourceInitializedPublisher.Registrar.class) public abstract class JpaBaseConfiguration implements BeanFactoryAware { - private static final String[] NO_PACKAGES = new String[0]; - private final DataSource dataSource; private final JpaProperties properties; @@ -138,11 +137,12 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { } protected String[] getPackagesToScan() { - if (AutoConfigurationPackages.has(this.beanFactory)) { - List basePackages = AutoConfigurationPackages.get(this.beanFactory); - return basePackages.toArray(new String[basePackages.size()]); + List packages = EntityScanPackages.get(this.beanFactory) + .getPackageNames(); + if (packages.isEmpty() && AutoConfigurationPackages.has(this.beanFactory)) { + packages = AutoConfigurationPackages.get(this.beanFactory); } - return NO_PACKAGES; + return packages.toArray(new String[packages.size()]); } /** diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java index d7b632c8333..bd9b15fd7b6 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/cassandra/CassandraDataAutoConfigurationTests.java @@ -16,18 +16,24 @@ package org.springframework.boot.autoconfigure.data.cassandra; +import java.util.Set; + import com.datastax.driver.core.Session; import org.junit.After; import org.junit.Test; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration; +import org.springframework.boot.autoconfigure.data.cassandra.city.City; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.data.cassandra.core.CassandraTemplate; +import org.springframework.data.cassandra.mapping.CassandraMappingContext; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -59,6 +65,21 @@ public class CassandraDataAutoConfigurationTests { .isEqualTo(1); } + @Test + @SuppressWarnings("unchecked") + public void entityScanShouldSetInitialEntitySet() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(TestConfiguration.class, EntityScanConfig.class, + PropertyPlaceholderAutoConfiguration.class, + CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class); + this.context.refresh(); + CassandraMappingContext mappingContext = this.context + .getBean(CassandraMappingContext.class); + Set> initialEntitySet = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(initialEntitySet).containsOnly(City.class); + } + @Configuration @ComponentScan(excludeFilters = @ComponentScan.Filter(classes = { Session.class }, type = FilterType.ASSIGNABLE_TYPE)) @@ -76,4 +97,10 @@ public class CassandraDataAutoConfigurationTests { } + @Configuration + @EntityScan("org.springframework.boot.autoconfigure.data.cassandra.city") + static class EntityScanConfig { + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseDataAutoConfigurationTests.java index a1752256d68..b379ad6274d 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/couchbase/CouchbaseDataAutoConfigurationTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.data.couchbase; +import java.util.Set; + import javax.validation.Validator; import org.junit.After; @@ -25,6 +27,8 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration; import org.springframework.boot.autoconfigure.couchbase.CouchbaseTestConfigurer; +import org.springframework.boot.autoconfigure.data.couchbase.city.City; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -33,9 +37,11 @@ import org.springframework.context.annotation.Import; import org.springframework.data.couchbase.config.AbstractCouchbaseDataConfiguration; import org.springframework.data.couchbase.config.CouchbaseConfigurer; import org.springframework.data.couchbase.core.CouchbaseTemplate; +import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext; import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener; import org.springframework.data.couchbase.core.query.Consistency; import org.springframework.data.couchbase.repository.support.IndexManager; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -108,6 +114,17 @@ public class CouchbaseDataAutoConfigurationTests { .isEqualTo(Consistency.EVENTUALLY_CONSISTENT); } + @Test + @SuppressWarnings("unchecked") + public void entityScanShouldSetInitialEntitySet() throws Exception { + load(EntityScanConfig.class); + CouchbaseMappingContext mappingContext = this.context + .getBean(CouchbaseMappingContext.class); + Set> initialEntitySet = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(initialEntitySet).containsOnly(City.class); + } + private void load(Class config, String... environment) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(context, environment); @@ -146,4 +163,11 @@ public class CouchbaseDataAutoConfigurationTests { } + @Configuration + @EntityScan("org.springframework.boot.autoconfigure.data.couchbase.city") + @Import(CustomCouchbaseConfiguration.class) + static class EntityScanConfig { + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java index c3b5155d9a9..efc6fe60d02 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MixedMongoRepositoriesAutoConfigurationTests.java @@ -28,11 +28,11 @@ import org.springframework.boot.autoconfigure.data.jpa.city.City; import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; import org.springframework.boot.autoconfigure.data.mongo.country.Country; import org.springframework.boot.autoconfigure.data.mongo.country.CountryRepository; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfigurationTests; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.boot.orm.jpa.EntityScan; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; @@ -80,6 +80,19 @@ public class MixedMongoRepositoriesAutoConfigurationTests { assertThat(this.context.getBean(CityRepository.class)).isNotNull(); } + @Test + public void testMixedRepositoryConfigurationWithDeprecatedEntityScan() + throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.initialize:false"); + this.context.register(MixedConfigurationWithDeprecatedEntityScan.class, + BaseConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(CountryRepository.class)).isNotNull(); + assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + @Test public void testJpaRepositoryConfigurationWithMongoTemplate() throws Exception { this.context = new AnnotationConfigApplicationContext(); @@ -90,6 +103,18 @@ public class MixedMongoRepositoriesAutoConfigurationTests { assertThat(this.context.getBean(CityRepository.class)).isNotNull(); } + @Test + public void testJpaRepositoryConfigurationWithMongoTemplateAndDeprecatedEntityScan() + throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.initialize:false"); + this.context.register(JpaConfigurationWithDeprecatedEntityScan.class, + BaseConfiguration.class); + this.context.refresh(); + assertThat(this.context.getBean(CityRepository.class)).isNotNull(); + } + @Test public void testJpaRepositoryConfigurationWithMongoOverlap() throws Exception { this.context = new AnnotationConfigApplicationContext(); @@ -137,6 +162,25 @@ public class MixedMongoRepositoriesAutoConfigurationTests { } + @Configuration + @TestAutoConfigurationPackage(MongoAutoConfigurationTests.class) + @EnableMongoRepositories(basePackageClasses = Country.class) + @org.springframework.boot.orm.jpa.EntityScan(basePackageClasses = City.class) + @EnableJpaRepositories(basePackageClasses = CityRepository.class) + @SuppressWarnings("deprecation") + protected static class MixedConfigurationWithDeprecatedEntityScan { + + } + + @Configuration + @TestAutoConfigurationPackage(MongoAutoConfigurationTests.class) + @org.springframework.boot.orm.jpa.EntityScan(basePackageClasses = City.class) + @EnableJpaRepositories(basePackageClasses = CityRepository.class) + @SuppressWarnings("deprecation") + protected static class JpaConfigurationWithDeprecatedEntityScan { + + } + // In this one the Jpa repositories and the auto-configuration packages overlap, so // Mongo will try and configure the same repositories @Configuration diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java index 3dec3e1f57a..1f2349e119d 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/mongo/MongoDataAutoConfigurationTests.java @@ -30,6 +30,8 @@ import org.springframework.beans.factory.UnsatisfiedDependencyException; import org.springframework.boot.autoconfigure.AutoConfigurationPackages; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.data.mongo.city.City; +import org.springframework.boot.autoconfigure.data.mongo.country.Country; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -139,6 +141,21 @@ public class MongoDataAutoConfigurationTests { } } + @Test + @SuppressWarnings("unchecked") + public void entityScanShouldSetInitialEntitySet() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EntityScanConfig.class, + PropertyPlaceholderAutoConfiguration.class, MongoAutoConfiguration.class, + MongoDataAutoConfiguration.class); + this.context.refresh(); + MongoMappingContext mappingContext = this.context + .getBean(MongoMappingContext.class); + Set> initialEntitySet = (Set>) ReflectionTestUtils + .getField(mappingContext, "initialEntitySet"); + assertThat(initialEntitySet).containsOnly(City.class, Country.class); + } + public void testFieldNamingStrategy(String strategy, Class expectedType) { this.context = new AnnotationConfigApplicationContext(); @@ -173,6 +190,12 @@ public class MongoDataAutoConfigurationTests { } } + @Configuration + @EntityScan("org.springframework.boot.autoconfigure.data.mongo") + static class EntityScanConfig { + + } + private static class MyConverter implements Converter { @Override diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java index c5f0b324356..327fdfe0789 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/MixedNeo4jRepositoriesAutoConfigurationTests.java @@ -30,9 +30,9 @@ import org.springframework.boot.autoconfigure.data.jpa.city.CityRepository; import org.springframework.boot.autoconfigure.data.neo4j.country.Country; import org.springframework.boot.autoconfigure.data.neo4j.country.CountryRepository; import org.springframework.boot.autoconfigure.data.neo4j.empty.EmptyMarker; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; -import org.springframework.boot.orm.jpa.EntityScan; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java index 50fce64ee2b..e4cb81a0eb7 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jDataAutoConfigurationTests.java @@ -18,7 +18,9 @@ package org.springframework.boot.autoconfigure.data.neo4j; import org.assertj.core.api.Assertions; import org.junit.After; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.neo4j.ogm.drivers.http.driver.HttpDriver; import org.neo4j.ogm.session.Session; import org.neo4j.ogm.session.SessionFactory; @@ -38,7 +40,7 @@ import static org.mockito.Mockito.mock; /** * Tests for {@link Neo4jDataAutoConfiguration}. Tests can't use the embedded driver as we - * use lucene 4 and Neo4j still requires 3. + * use Lucene 4 and Neo4j still requires 3. * * @author Stephane Nicoll * @author Michael Hunger @@ -46,6 +48,9 @@ import static org.mockito.Mockito.mock; */ public class Neo4jDataAutoConfigurationTests { + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private AnnotationConfigApplicationContext context; @After @@ -107,7 +112,7 @@ public class Neo4jDataAutoConfigurationTests { City.class); } - public void load(Class config, String... environment) { + private void load(Class config, String... environment) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); EnvironmentTestUtils.addEnvironment(ctx, environment); if (config != null) { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java index 878eb483733..bfa3470470a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/data/neo4j/Neo4jRepositoriesAutoConfigurationTests.java @@ -86,6 +86,7 @@ public class Neo4jRepositoriesAutoConfigurationTests { "spring.data.neo4j.uri=http://localhost:9797"); this.context.register(configurationClasses); this.context.register(Neo4jDataAutoConfiguration.class, + Neo4jDataAutoConfiguration.class, Neo4jRepositoriesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScanPackagesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScanPackagesTests.java new file mode 100644 index 00000000000..03109199827 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScanPackagesTests.java @@ -0,0 +1,191 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain; + +import java.util.Collection; +import java.util.Collections; + +import org.junit.After; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationConfigurationException; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EntityScanPackages}. + * + * @author Phillip Webb + */ +public class EntityScanPackagesTests { + + private AnnotationConfigApplicationContext context; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @After + public void cleanup() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void getWhenNoneRegisteredShouldReturnNone() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + this.context.refresh(); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages).isNotNull(); + assertThat(packages.getPackageNames()).isEmpty(); + } + + @Test + public void getShouldReturnRegisterPackages() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + EntityScanPackages.register(this.context, "a", "b"); + EntityScanPackages.register(this.context, "b", "c"); + this.context.refresh(); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()).containsExactly("a", "b", "c"); + } + + @Test + public void registerFromArrayWhenRegistryIsNullShouldThrowException() + throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Registry must not be null"); + EntityScanPackages.register(null); + + } + + @Test + public void registerFromArrayWhenPackageNamesIsNullShouldThrowException() + throws Exception { + this.context = new AnnotationConfigApplicationContext(); + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("PackageNames must not be null"); + EntityScanPackages.register(this.context, (String[]) null); + } + + @Test + public void registerFromCollectionWhenRegistryIsNullShouldThrowException() + throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Registry must not be null"); + EntityScanPackages.register(null, Collections.emptyList()); + } + + @Test + public void registerFromCollectionWhenPackageNamesIsNullShouldThrowException() + throws Exception { + this.context = new AnnotationConfigApplicationContext(); + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("PackageNames must not be null"); + EntityScanPackages.register(this.context, (Collection) null); + } + + @Test + public void entityScanAnnotationWhenHasValueAttributeShouldSetupPackages() + throws Exception { + this.context = new AnnotationConfigApplicationContext( + EntityScanValueConfig.class); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()).containsExactly("a"); + } + + @Test + public void entityScanAnnotationWhenHasBasePackagesAttributeShouldSetupPackages() + throws Exception { + this.context = new AnnotationConfigApplicationContext( + EntityScanBasePackagesConfig.class); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()).containsExactly("b"); + } + + @Test + public void entityScanAnnotationWhenHasValueAndBasePackagesAttributeShouldThrow() + throws Exception { + this.thrown.expect(AnnotationConfigurationException.class); + this.context = new AnnotationConfigApplicationContext( + EntityScanValueAndBasePackagesConfig.class); + } + + @Test + public void entityScanAnnotationWhenHasBasePackageClassesAttributeShouldSetupPackages() + throws Exception { + this.context = new AnnotationConfigApplicationContext( + EntityScanBasePackageClassesConfig.class); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()) + .containsExactly(getClass().getPackage().getName()); + } + + @Test + public void entityScanAnnotationWhenNoAttributesShouldSetupPackages() + throws Exception { + this.context = new AnnotationConfigApplicationContext( + EntityScanNoAttributesConfig.class); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()) + .containsExactly(getClass().getPackage().getName()); + } + + @Test + public void entityScanAnnotationWhenLoadingFromMultipleConfigsShouldCombinePackages() + throws Exception { + this.context = new AnnotationConfigApplicationContext(EntityScanValueConfig.class, + EntityScanBasePackagesConfig.class); + EntityScanPackages packages = EntityScanPackages.get(this.context); + assertThat(packages.getPackageNames()).containsExactly("a", "b"); + } + + @Configuration + @EntityScan("a") + static class EntityScanValueConfig { + + } + + @Configuration + @EntityScan(basePackages = "b") + static class EntityScanBasePackagesConfig { + + } + + @Configuration + @EntityScan(value = "a", basePackages = "b") + static class EntityScanValueAndBasePackagesConfig { + + } + + @Configuration + @EntityScan(basePackageClasses = EntityScanPackagesTests.class) + static class EntityScanBasePackageClassesConfig { + + } + + @Configuration + @EntityScan + static class EntityScanNoAttributesConfig { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java new file mode 100644 index 00000000000..f3e899458d0 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/EntityScannerTests.java @@ -0,0 +1,109 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain; + +import java.util.Set; + +import javax.persistence.Embeddable; +import javax.persistence.Entity; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import org.springframework.boot.autoconfigure.domain.scan.a.EmbeddableA; +import org.springframework.boot.autoconfigure.domain.scan.a.EntityA; +import org.springframework.boot.autoconfigure.domain.scan.b.EmbeddableB; +import org.springframework.boot.autoconfigure.domain.scan.b.EntityB; +import org.springframework.boot.autoconfigure.domain.scan.c.EmbeddableC; +import org.springframework.boot.autoconfigure.domain.scan.c.EntityC; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link EntityScanner}. + * + * @author Phillip Webb + */ +public class EntityScannerTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void createWhenContextIsNullShouldThrowException() throws Exception { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Context must not be null"); + new EntityScanner(null); + } + + @Test + public void scanShouldScanFromSinglePackage() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ScanConfig.class); + EntityScanner scanner = new EntityScanner(context); + Set> scanned = scanner.scan(Entity.class); + assertThat(scanned).containsOnly(EntityA.class, EntityB.class, EntityC.class); + context.close(); + } + + @Test + public void scanShouldScanFromMultiplePackages() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ScanAConfig.class, ScanBConfig.class); + EntityScanner scanner = new EntityScanner(context); + Set> scanned = scanner.scan(Entity.class); + assertThat(scanned).containsOnly(EntityA.class, EntityB.class); + context.close(); + } + + @Test + public void scanShouldFilterOnAnnotation() throws Exception { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + ScanConfig.class); + EntityScanner scanner = new EntityScanner(context); + assertThat(scanner.scan(Entity.class)).containsOnly(EntityA.class, EntityB.class, + EntityC.class); + assertThat(scanner.scan(Embeddable.class)).containsOnly(EmbeddableA.class, + EmbeddableB.class, EmbeddableC.class); + assertThat(scanner.scan(Entity.class, Embeddable.class)).containsOnly( + EntityA.class, EntityB.class, EntityC.class, EmbeddableA.class, + EmbeddableB.class, EmbeddableC.class); + context.close(); + } + + @Configuration + @EntityScan("org.springframework.boot.autoconfigure.domain.scan") + static class ScanConfig { + + } + + @Configuration + @EntityScan(basePackageClasses = EntityA.class) + static class ScanAConfig { + + } + + @Configuration + @EntityScan(basePackageClasses = EntityB.class) + static class ScanBConfig { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EmbeddableA.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EmbeddableA.java new file mode 100644 index 00000000000..fa31fda6b84 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EmbeddableA.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.a; + +import javax.persistence.Embeddable; + +@Embeddable +public class EmbeddableA { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EntityA.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EntityA.java new file mode 100644 index 00000000000..f4966aaf16d --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/a/EntityA.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.a; + +import javax.persistence.Entity; + +@Entity +public class EntityA { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EmbeddableB.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EmbeddableB.java new file mode 100644 index 00000000000..0dd49e9f2e2 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EmbeddableB.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.b; + +import javax.persistence.Embeddable; + +@Embeddable +public class EmbeddableB { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EntityB.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EntityB.java new file mode 100644 index 00000000000..22fd9cbb654 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/b/EntityB.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.b; + +import javax.persistence.Entity; + +@Entity +public class EntityB { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EmbeddableC.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EmbeddableC.java new file mode 100644 index 00000000000..f2581882dd3 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EmbeddableC.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.c; + +import javax.persistence.Embeddable; + +@Embeddable +public class EmbeddableC { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EntityC.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EntityC.java new file mode 100644 index 00000000000..a1678a30246 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/domain/scan/c/EntityC.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012-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 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.boot.autoconfigure.domain.scan.c; + +import javax.persistence.Entity; + +@Entity +public class EntityC { + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java index 9307714b155..85bc2ee0a78 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfigurationTests.java @@ -97,7 +97,7 @@ public class MongoAutoConfigurationTests { } @Configuration - protected static class OptionsConfig { + static class OptionsConfig { @Bean public MongoClientOptions mongoOptions() { @@ -107,7 +107,7 @@ public class MongoAutoConfigurationTests { } @Configuration - protected static class SslOptionsConfig { + static class SslOptionsConfig { @Bean public MongoClientOptions mongoClientOptions() { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/user/SecurityConfig.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/user/SecurityConfig.java index d4379f66529..37539e8a6da 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/user/SecurityConfig.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/user/SecurityConfig.java @@ -16,7 +16,7 @@ package org.springframework.boot.autoconfigure.security.user; -import org.springframework.boot.orm.jpa.EntityScan; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; diff --git a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java index 5b53de003cd..a5c6cef09b6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java +++ b/spring-boot/src/main/java/org/springframework/boot/orm/jpa/EntityScan.java @@ -43,11 +43,14 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; * annotation. * * @author Phillip Webb + * @deprecated as of 1.4 in favor of explicit configuration or + * {@code @org.springframework.boot.autoconfigure.domain.EntityScan} */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import(JpaEntityScanRegistrar.class) +@Import(EntityScanRegistrar.class) +@Deprecated public @interface EntityScan { /**