diff --git a/src/docbkx/jpa.xml b/src/docbkx/jpa.xml index 6d15c5eaf..555a5a64a 100644 --- a/src/docbkx/jpa.xml +++ b/src/docbkx/jpa.xml @@ -1151,56 +1151,38 @@ public interface UserRepository extends JpaRepository<User, Long> { your configuration: - Activating auditing in the Spring configuration + Activating auditing using XML configuration <jpa:auditing auditor-aware-ref="yourAuditorAwareBean" /> - Auditing via Java Config + Activating auditing via Java configuration - Since Spring Data JPA 1.5 Auditing can be enabled by annotating + As of Spring Data JPA 1.5, auditing can be enabled by annotating a configuration class with the EnableJpaAuditing annotation. @Configuration @EnableJpaAuditing -@EnableJpaRepositories class Config { - @Bean - public AuditorAware<AuditableUser> auditorProvider() { - return new AuditorAwareImpl(); - } + @Bean + public AuditorAware<AuditableUser> auditorProvider() { + return new AuditorAwareImpl(); + } } - Note that if you want to record information about the - create-User or modify-User, you can declare a bean that implements the - AuditorAware interface. The bean name can also - be configured explicitly via the auditorAwareRef - attribute of the EnableJpaAuditing annotation, - this can be used to resolve an ambiguity. If no auditorAwareRef is - specified explicitly we try to discover and autowire an - AuditorAware implementation. + If you expose a bean of type + AuditorAware to the + ApplicationContext, the auditing + infrastructure will pick it up automatically and use it to determine + the current user to be set on domain types. If you have multiple + implementations registered in the + ApplicationContext, you can select the + one to be used by explicitly setting the auditorAwareRef attribute of + @EnableJpaAuditing. - - As you can see you have to provide a bean that implements the - AuditorAware interface which looks as - follows: - - - <interfacename>AuditorAware</interfacename> interface - - public interface AuditorAware<T, ID extends Serializable> { - - T getCurrentAuditor(); -} - - - Usually you will have some kind of authentication component in - your application that tracks the user currently working with the system. - This component should be AuditorAware and - thus allow seamless tracking of the auditor. diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java index 44f6ff6c0..336f3b00e 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessor.java @@ -15,18 +15,23 @@ */ package org.springframework.data.jpa.domain.support; -import java.util.Arrays; -import java.util.List; +import static java.util.Arrays.*; +import static org.springframework.beans.factory.BeanFactoryUtils.*; +import static org.springframework.util.StringUtils.*; + +import java.util.HashSet; +import java.util.Set; import javax.persistence.EntityManagerFactory; -import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.data.domain.AuditorAware; -import org.springframework.util.StringUtils; +import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean; /** * {@link BeanFactoryPostProcessor} that ensures that the {@link AnnotationBeanConfigurerAspect} aspect is up and @@ -39,38 +44,52 @@ import org.springframework.util.StringUtils; */ public class AuditingBeanFactoryPostProcessor implements BeanFactoryPostProcessor { - static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; - private static final String ENTITY_MANAGER_FACTORY_BEAN_NAME = "entityManagerFactory"; - private static final String JPA_PACKAGE = "org.springframework.orm.jpa."; - private static final List CLASSES_TO_DEPEND = Arrays.asList(JPA_PACKAGE - + "LocalContainerEntityManagerFactoryBean", JPA_PACKAGE + "LocalEntityManagerFactoryBean"); + public static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; /* * (non-Javadoc) - * - * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor# - * postProcessBeanFactory - * (org.springframework.beans.factory.config.ConfigurableListableBeanFactory - * ) + * @see org.springframework.beans.factory.config.BeanFactoryPostProcessor#postProcessBeanFactory(org.springframework.beans.factory.config.ConfigurableListableBeanFactory) */ + @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { - List beanNamesToDepend = Arrays.asList(beanFactory.getBeanNamesForType(EntityManagerFactory.class)); - - for (String beanName : beanFactory.getBeanDefinitionNames()) { - - BeanDefinition definition = beanFactory.getBeanDefinition(beanName); - - if (CLASSES_TO_DEPEND.contains(definition.getBeanClassName()) || beanNamesToDepend.contains(beanName)) { - definition.setDependsOn(StringUtils.addStringToArray(definition.getDependsOn(), - BEAN_CONFIGURER_ASPECT_BEAN_NAME)); - } + try { + beanFactory.getBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME); + } catch (NoSuchBeanDefinitionException o_O) { + throw new IllegalStateException( + "Invalid auditing setup! Make sure you've used @EnableJpaAuditing or correctly!", o_O); } - for (String beanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, AuditorAware.class, true, - false)) { + for (String beanName : getEntityManagerFactoryBeanNames(beanFactory)) { + BeanDefinition definition = beanFactory.getBeanDefinition(beanName); + definition.setDependsOn(addStringToArray(definition.getDependsOn(), BEAN_CONFIGURER_ASPECT_BEAN_NAME)); + } + + for (String beanName : beanNamesForTypeIncludingAncestors(beanFactory, AuditorAware.class, true, false)) { BeanDefinition definition = beanFactory.getBeanDefinition(beanName); definition.setLazyInit(true); } } + + /** + * Return all bean names for bean definitions that will result in an {@link EntityManagerFactory} eventually. We're + * checking for {@link EntityManagerFactory} and the well-known factory beans here to avoid eager initialization of + * the factory beans. The double lookup is necessary especially for JavaConfig scenarios as people might declare an + * {@link EntityManagerFactory} directly. + * + * @param beanFactory + * @return + */ + private Iterable getEntityManagerFactoryBeanNames(ListableBeanFactory beanFactory) { + + Set names = new HashSet(); + names.addAll(asList(beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class, true, false))); + + for (String factoryBeanName : beanNamesForTypeIncludingAncestors(beanFactory, + AbstractEntityManagerFactoryBean.class, true, false)) { + names.add(factoryBeanName.substring(1)); + } + + return names; + } } diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index 148f825bc..4bd7dc11e 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -19,7 +19,6 @@ import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import org.springframework.beans.factory.annotation.Configurable; -import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect; import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.domain.Auditable; @@ -40,6 +39,14 @@ import org.springframework.data.domain.Auditable; * After that it's just a matter of activating auditing in your Spring config: * *
+ * @Configuration
+ * @EnableJpaAuditing
+ * class ApplicationConfig {
+ * 
+ * }
+ * 
+ * + *
  * <jpa:auditing auditor-aware-ref="yourAuditorAwarebean" />
  * 
* @@ -52,14 +59,8 @@ public class AuditingEntityListener { private AuditingHandler handler; /** - * Creates an {@link AuditingEntityListener} that auto-wires itself according to the appropriate Spring bean - * configuration. - */ - public AuditingEntityListener() { - AnnotationBeanConfigurerAspect.aspectOf().configureBean(this); - } - - /** + * Configures the {@link AuditingHandler} to be used to set the current auditor on the domain types touched. + * * @param auditingHandler the handler to set */ public void setAuditingHandler(AuditingHandler auditingHandler) { diff --git a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java index 68b5b003d..91727a3ac 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/EnableJpaAuditing.java @@ -22,13 +22,16 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.joda.time.DateTime; import org.springframework.context.annotation.Import; import org.springframework.data.auditing.DateTimeProvider; +import org.springframework.data.domain.AuditorAware; /** * Annotation to enable auditing in JPA via annotation configuration. * * @author Thomas Darimont + * @author Oliver Gierke */ @Inherited @Documented @@ -38,23 +41,31 @@ import org.springframework.data.auditing.DateTimeProvider; public @interface EnableJpaAuditing { /** - * @return References a bean of type AuditorAware to represent the current principal. + * Configures the {@link AuditorAware} bean to be used to lookup the current principal. + * + * @return */ String auditorAwareRef() default ""; /** - * @return Configures whether the creation and modification dates are set and defaults to {@literal true}. + * Configures whether the creation and modification dates are set. Defaults to {@literal true}. + * + * @return */ boolean setDates() default true; /** - * @return Configures whether the entity shall be marked as modified on creation and defaults to {@literal true}. + * Configures whether the entity shall be marked as modified on creation. Defaults to {@literal true}. + * + * @return */ boolean modifyOnCreate() default true; /** - * @return Configures a {@link DateTimeProvider} that allows customizing which DateTime shall be used for setting - * creation and modification dates. + * Configures a {@link DateTimeProvider} bean name that allows customizing the {@link DateTime} to be used for setting + * creation and modification dates. + * + * @return */ String dateTimeProviderRef() default ""; } diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java index b2edc8f36..796b8eb84 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaAuditingRegistrar.java @@ -15,6 +15,8 @@ */ package org.springframework.data.jpa.repository.config; +import static org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor.*; + import java.lang.annotation.Annotation; import org.springframework.beans.factory.BeanDefinitionStoreException; @@ -39,13 +41,10 @@ import org.springframework.util.ClassUtils; */ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { - /** - * The bean name of the internally managed bean configurer aspect. - */ - private static final String BEAN_CONFIGURER_ASPECT_BEAN_NAME = "org.springframework.context.config.internalBeanConfigurerAspect"; private static final String BEAN_CONFIGURER_ASPECT_CLASS_NAME = "org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect"; - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation() */ @Override @@ -53,7 +52,8 @@ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { return EnableJpaAuditing.class; } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry) */ @Override @@ -68,7 +68,8 @@ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { .getRawBeanDefinition(), AuditingBeanFactoryPostProcessor.class.getName(), registry); } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry) */ @Override @@ -86,7 +87,7 @@ class JpaAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport { */ private void registerBeanConfigurerAspectIfNecessary(BeanDefinitionRegistry registry) { - if (registry.containsBeanDefinition(BEAN_CONFIGURER_ASPECT_BEAN_NAME)) { + if (registry.containsBeanDefinition(BEAN_CONFIGURER_ASPECT_CLASS_NAME)) { return; } diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java new file mode 100644 index 000000000..e01713fe4 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/support/AnnotationAuditingBeanFactoryPostProcessorUnitTests.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013 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.jpa.domain.support; + +import static org.mockito.Mockito.*; + +import javax.persistence.EntityManagerFactory; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ConfigurationClassPostProcessor; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +/** + * Unit tests to check that the {@link AuditingBeanFactoryPostProcessor} does its job for annotation based + * configuration. + * + * @author Oliver Gierke + */ +public class AnnotationAuditingBeanFactoryPostProcessorUnitTests extends AuditingBeanFactoryPostProcessorUnitTests { + + @Configuration + @EnableJpaAuditing + static class TestConfig { + + @Bean + public EntityManagerFactory entityManagerFactory() { + return mock(EntityManagerFactory.class); + } + + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() { + return mock(LocalContainerEntityManagerFactoryBean.class); + } + } + + /* + * (non-Javadoc) + * @see org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessorUnitTests#getBeanFactory() + */ + @Override + protected DefaultListableBeanFactory getBeanFactory() { + + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerBeanDefinition("testConfig", new RootBeanDefinition(TestConfig.class)); + + ConfigurationClassPostProcessor processor = new ConfigurationClassPostProcessor(); + processor.postProcessBeanDefinitionRegistry(beanFactory); + + return beanFactory; + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java index 8d8cef7a0..43d5a268c 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingBeanFactoryPostProcessorUnitTests.java @@ -15,11 +15,14 @@ */ package org.springframework.data.jpa.domain.support; -import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import javax.persistence.EntityManagerFactory; + import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.core.io.ClassPathResource; @@ -38,18 +41,23 @@ public class AuditingBeanFactoryPostProcessorUnitTests { @Before public void setUp() { - beanFactory = new DefaultListableBeanFactory(); - XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); - reader.loadBeanDefinitions(new ClassPathResource("auditing/" + getConfigFile())); - - processor = new AuditingBeanFactoryPostProcessor(); + this.beanFactory = getBeanFactory(); + this.processor = new AuditingBeanFactoryPostProcessor(); } protected String getConfigFile() { - return "auditing-bfpp-context.xml"; } + protected DefaultListableBeanFactory getBeanFactory() { + + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory); + reader.loadBeanDefinitions(new ClassPathResource("auditing/" + getConfigFile())); + + return beanFactory; + } + @Test public void beanConfigurerAspectShouldBeConfiguredAfterPostProcessing() throws Exception { @@ -57,4 +65,30 @@ public class AuditingBeanFactoryPostProcessorUnitTests { assertThat(beanFactory.isBeanNameInUse(AuditingBeanFactoryPostProcessor.BEAN_CONFIGURER_ASPECT_BEAN_NAME), is(true)); } + + /** + * @see DATAJPA-265 + */ + @Test(expected = IllegalStateException.class) + public void rejectsConfigurationWithoutSpringConfigured() { + processor.postProcessBeanFactory(new DefaultListableBeanFactory()); + } + + /** + * @see DATAJPA-265 + */ + @Test + public void setsDependsOnOnEntityManagerFactory() { + + processor.postProcessBeanFactory(beanFactory); + + String[] emfDefinitionNames = beanFactory.getBeanNamesForType(EntityManagerFactory.class); + + for (String emfDefinitionName : emfDefinitionNames) { + BeanDefinition emfDefinition = beanFactory.getBeanDefinition(emfDefinitionName); + assertThat(emfDefinition, is(notNullValue())); + assertThat(emfDefinition.getDependsOn(), + is(arrayContaining(AuditingBeanFactoryPostProcessor.BEAN_CONFIGURER_ASPECT_BEAN_NAME))); + } + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java b/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java index 787e1f423..37c57d92f 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/AbstractAuditingViaJavaConfigRepositoriesTests.java @@ -38,6 +38,7 @@ import org.springframework.transaction.annotation.Transactional; * Integration tests for auditing via Java config. * * @author Thomas Darimont + * @author Oliver Gierke */ @RunWith(SpringJUnit4ClassRunner.class) @Transactional @@ -54,10 +55,11 @@ public abstract class AbstractAuditingViaJavaConfigRepositoriesTests { @Before public void setup() { + AuditableUser auditor = new AuditableUser(null); auditor.setFirstname("auditor"); - this.auditor = this.auditableUserRepository.save(auditor); + this.auditor = this.auditableUserRepository.save(auditor); doReturn(this.auditor).when(this.auditorAware).getCurrentAuditor(); } @@ -67,7 +69,6 @@ public abstract class AbstractAuditingViaJavaConfigRepositoriesTests { } @Test - @Transactional public void basicAuditing() throws Exception { AuditableUser user = new AuditableUser(null); diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java b/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java index 1e89e75ac..ede0713ee 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/AuditingBeanDefinitionParserTests.java @@ -76,7 +76,7 @@ public class AuditingBeanDefinitionParserTests { ShadowingClassLoader scl = new ShadowingClassLoader(getClass().getClassLoader()); scl.excludeClass(AuditingBeanDefinitionParser.AUDITING_ENTITY_LISTENER_CLASS_NAME); - DefaultListableBeanFactory factory = loadFactoryFrom("auditing/auditing-namespace-context.xml", scl); + loadFactoryFrom("auditing/auditing-namespace-context.xml", scl); } private void assertSetDatesIsSetTo(String configFile, String value) { diff --git a/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java b/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java index 870f10219..c38fe4c7a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/DefaultAuditingViaJavaConfigRepositoriesTests.java @@ -28,6 +28,7 @@ import org.springframework.test.context.ContextConfiguration; * Integration tests for auditing via Java config with default configuration. * * @author Thomas Darimont + * @author Oliver Gierke */ @ContextConfiguration public class DefaultAuditingViaJavaConfigRepositoriesTests extends AbstractAuditingViaJavaConfigRepositoriesTests { @@ -38,6 +39,7 @@ public class DefaultAuditingViaJavaConfigRepositoriesTests extends AbstractAudit static class Config { @Bean + @SuppressWarnings("unchecked") public AuditorAware auditorProvider() { return mock(AuditorAware.class); } diff --git a/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java b/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java index 7ffc2b409..e95e28a6e 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/ExplicitAuditingViaJavaConfigRepositoriesTests.java @@ -28,8 +28,8 @@ import org.springframework.test.context.ContextConfiguration; * Integration tests for auditing via Java config with explicit configuration. * * @author Thomas Darimont + * @author Oliver Gierke */ - @ContextConfiguration public class ExplicitAuditingViaJavaConfigRepositoriesTests extends AbstractAuditingViaJavaConfigRepositoriesTests { @@ -39,6 +39,7 @@ public class ExplicitAuditingViaJavaConfigRepositoriesTests extends AbstractAudi static class Config { @Bean + @SuppressWarnings("unchecked") public AuditorAware auditorProvider() { return mock(AuditorAware.class); }