@Scheduled reliably applies after other post-processors and shuts down before TaskScheduler
Issue: SPR-14692 Issue: SPR-15067
This commit is contained in:
parent
a30ceafc2c
commit
edc62be231
|
@ -983,7 +983,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||||
if (parent instanceof AutowireCapableBeanFactory) {
|
if (parent instanceof AutowireCapableBeanFactory) {
|
||||||
return ((AutowireCapableBeanFactory) parent).resolveNamedBean(requiredType);
|
return ((AutowireCapableBeanFactory) parent).resolveNamedBean(requiredType);
|
||||||
}
|
}
|
||||||
return null;
|
throw new NoSuchBeanDefinitionException(requiredType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|
|
@ -33,12 +33,18 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
|
import org.springframework.beans.factory.BeanNameAware;
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
|
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.NamedBeanHolder;
|
||||||
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.context.ApplicationListener;
|
import org.springframework.context.ApplicationListener;
|
||||||
|
@ -85,8 +91,9 @@ import org.springframework.util.StringValueResolver;
|
||||||
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
|
* @see org.springframework.scheduling.config.ScheduledTaskRegistrar
|
||||||
* @see AsyncAnnotationBeanPostProcessor
|
* @see AsyncAnnotationBeanPostProcessor
|
||||||
*/
|
*/
|
||||||
public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBeanPostProcessor,
|
public class ScheduledAnnotationBeanPostProcessor
|
||||||
Ordered, EmbeddedValueResolverAware, BeanFactoryAware, ApplicationContextAware,
|
implements MergedBeanDefinitionPostProcessor, DestructionAwareBeanPostProcessor,
|
||||||
|
Ordered, EmbeddedValueResolverAware, BeanNameAware, BeanFactoryAware, ApplicationContextAware,
|
||||||
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
|
SmartInitializingSingleton, ApplicationListener<ContextRefreshedEvent>, DisposableBean {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -104,6 +111,8 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea
|
||||||
|
|
||||||
private StringValueResolver embeddedValueResolver;
|
private StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
|
private String beanName;
|
||||||
|
|
||||||
private BeanFactory beanFactory;
|
private BeanFactory beanFactory;
|
||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
@ -140,6 +149,11 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea
|
||||||
this.embeddedValueResolver = resolver;
|
this.embeddedValueResolver = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBeanName(String beanName) {
|
||||||
|
this.beanName = beanName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Making a {@link BeanFactory} available is optional; if not set,
|
* Making a {@link BeanFactory} available is optional; if not set,
|
||||||
* {@link SchedulingConfigurer} beans won't get autodetected and
|
* {@link SchedulingConfigurer} beans won't get autodetected and
|
||||||
|
@ -199,12 +213,11 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea
|
||||||
Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
|
Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
|
||||||
try {
|
try {
|
||||||
// Search for TaskScheduler bean...
|
// Search for TaskScheduler bean...
|
||||||
this.registrar.setTaskScheduler(this.beanFactory.getBean(TaskScheduler.class));
|
this.registrar.setTaskScheduler(resolveSchedulerBean(TaskScheduler.class, false));
|
||||||
}
|
}
|
||||||
catch (NoUniqueBeanDefinitionException ex) {
|
catch (NoUniqueBeanDefinitionException ex) {
|
||||||
try {
|
try {
|
||||||
this.registrar.setTaskScheduler(
|
this.registrar.setTaskScheduler(resolveSchedulerBean(TaskScheduler.class, true));
|
||||||
this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, TaskScheduler.class));
|
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex2) {
|
catch (NoSuchBeanDefinitionException ex2) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
|
@ -220,12 +233,11 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea
|
||||||
logger.debug("Could not find default TaskScheduler bean", ex);
|
logger.debug("Could not find default TaskScheduler bean", ex);
|
||||||
// Search for ScheduledExecutorService bean next...
|
// Search for ScheduledExecutorService bean next...
|
||||||
try {
|
try {
|
||||||
this.registrar.setScheduler(this.beanFactory.getBean(ScheduledExecutorService.class));
|
this.registrar.setScheduler(resolveSchedulerBean(ScheduledExecutorService.class, false));
|
||||||
}
|
}
|
||||||
catch (NoUniqueBeanDefinitionException ex2) {
|
catch (NoUniqueBeanDefinitionException ex2) {
|
||||||
try {
|
try {
|
||||||
this.registrar.setScheduler(
|
this.registrar.setScheduler(resolveSchedulerBean(ScheduledExecutorService.class, true));
|
||||||
this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, ScheduledExecutorService.class));
|
|
||||||
}
|
}
|
||||||
catch (NoSuchBeanDefinitionException ex3) {
|
catch (NoSuchBeanDefinitionException ex3) {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
|
@ -248,6 +260,32 @@ public class ScheduledAnnotationBeanPostProcessor implements DestructionAwareBea
|
||||||
this.registrar.afterPropertiesSet();
|
this.registrar.afterPropertiesSet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> T resolveSchedulerBean(Class<T> schedulerType, boolean byName) {
|
||||||
|
if (byName) {
|
||||||
|
T scheduler = this.beanFactory.getBean(DEFAULT_TASK_SCHEDULER_BEAN_NAME, schedulerType);
|
||||||
|
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||||
|
((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(
|
||||||
|
DEFAULT_TASK_SCHEDULER_BEAN_NAME, this.beanName);
|
||||||
|
}
|
||||||
|
return scheduler;
|
||||||
|
}
|
||||||
|
else if (this.beanFactory instanceof AutowireCapableBeanFactory) {
|
||||||
|
NamedBeanHolder<T> holder = ((AutowireCapableBeanFactory) this.beanFactory).resolveNamedBean(schedulerType);
|
||||||
|
if (this.beanFactory instanceof ConfigurableBeanFactory) {
|
||||||
|
((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(
|
||||||
|
holder.getBeanName(), this.beanName);
|
||||||
|
}
|
||||||
|
return holder.getBeanInstance();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return this.beanFactory.getBean(schedulerType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.scheduling.annotation;
|
package org.springframework.scheduling.annotation;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ import org.springframework.scheduling.TriggerContext;
|
||||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||||
import org.springframework.scheduling.config.IntervalTask;
|
import org.springframework.scheduling.config.IntervalTask;
|
||||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||||
|
import org.springframework.scheduling.config.TaskManagementConfigUtils;
|
||||||
import org.springframework.tests.Assume;
|
import org.springframework.tests.Assume;
|
||||||
import org.springframework.tests.TestGroup;
|
import org.springframework.tests.TestGroup;
|
||||||
|
|
||||||
|
@ -86,6 +88,8 @@ public class EnableSchedulingTests {
|
||||||
Thread.sleep(100);
|
Thread.sleep(100);
|
||||||
assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10));
|
assertThat(ctx.getBean(AtomicInteger.class).get(), greaterThanOrEqualTo(10));
|
||||||
assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName, startsWith("explicitScheduler-"));
|
assertThat(ctx.getBean(ExplicitSchedulerConfig.class).threadName, startsWith("explicitScheduler-"));
|
||||||
|
assertTrue(Arrays.asList(ctx.getDefaultListableBeanFactory().getDependentBeans("myTaskScheduler")).contains(
|
||||||
|
TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -201,7 +205,7 @@ public class EnableSchedulingTests {
|
||||||
String threadName;
|
String threadName;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TaskScheduler taskScheduler() {
|
public TaskScheduler myTaskScheduler() {
|
||||||
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
|
||||||
scheduler.setThreadNamePrefix("explicitScheduler-");
|
scheduler.setThreadNamePrefix("explicitScheduler-");
|
||||||
return scheduler;
|
return scheduler;
|
||||||
|
@ -275,10 +279,6 @@ public class EnableSchedulingTests {
|
||||||
counter().incrementAndGet();
|
counter().incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getScheduler() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||||
taskRegistrar.setScheduler(taskScheduler1());
|
taskRegistrar.setScheduler(taskScheduler1());
|
||||||
|
|
|
@ -34,9 +34,10 @@ import org.springframework.beans.factory.BeanInitializationException;
|
||||||
import org.springframework.beans.factory.ListableBeanFactory;
|
import org.springframework.beans.factory.ListableBeanFactory;
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
|
||||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.EmbeddedValueResolver;
|
import org.springframework.beans.factory.config.EmbeddedValueResolver;
|
||||||
|
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.MethodIntrospector;
|
import org.springframework.core.MethodIntrospector;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
@ -81,7 +82,7 @@ import org.springframework.util.StringValueResolver;
|
||||||
* @see MethodJmsListenerEndpoint
|
* @see MethodJmsListenerEndpoint
|
||||||
*/
|
*/
|
||||||
public class JmsListenerAnnotationBeanPostProcessor
|
public class JmsListenerAnnotationBeanPostProcessor
|
||||||
implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
|
implements MergedBeanDefinitionPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bean name of the default {@link JmsListenerContainerFactory}.
|
* The bean name of the default {@link JmsListenerContainerFactory}.
|
||||||
|
@ -192,6 +193,10 @@ public class JmsListenerAnnotationBeanPostProcessor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||||
return bean;
|
return bean;
|
||||||
|
|
|
@ -18,11 +18,14 @@ package org.springframework.scheduling.annotation;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
@ -46,6 +49,7 @@ import static org.mockito.BDDMockito.*;
|
||||||
* as @Transactional or @Async processing.
|
* as @Transactional or @Async processing.
|
||||||
*
|
*
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
|
* @author Juergen Hoeller
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("resource")
|
@SuppressWarnings("resource")
|
||||||
|
@ -76,11 +80,11 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
|
ctx.register(Config.class, SubclassProxyTxConfig.class, RepoConfigA.class);
|
||||||
ctx.refresh();
|
ctx.refresh();
|
||||||
|
|
||||||
Thread.sleep(100); // allow @Scheduled method to be called several times
|
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||||
|
|
||||||
MyRepository repository = ctx.getBean(MyRepository.class);
|
MyRepository repository = ctx.getBean(MyRepository.class);
|
||||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||||
assertThat("repository is not a proxy", AopUtils.isAopProxy(repository), equalTo(true));
|
assertThat("repository is not a proxy", AopUtils.isCglibProxy(repository), equalTo(true));
|
||||||
assertThat("@Scheduled method never called", repository.getInvocationCount(), greaterThan(0));
|
assertThat("@Scheduled method never called", repository.getInvocationCount(), greaterThan(0));
|
||||||
assertThat("no transactions were committed", txManager.commits, greaterThan(0));
|
assertThat("no transactions were committed", txManager.commits, greaterThan(0));
|
||||||
}
|
}
|
||||||
|
@ -91,15 +95,28 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
|
ctx.register(Config.class, JdkProxyTxConfig.class, RepoConfigB.class);
|
||||||
ctx.refresh();
|
ctx.refresh();
|
||||||
|
|
||||||
Thread.sleep(50); // allow @Scheduled method to be called several times
|
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||||
|
|
||||||
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||||
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
CallCountingTransactionManager txManager = ctx.getBean(CallCountingTransactionManager.class);
|
||||||
assertThat("repository is not a proxy", AopUtils.isAopProxy(repository), is(true));
|
assertThat("repository is not a proxy", AopUtils.isJdkDynamicProxy(repository), is(true));
|
||||||
assertThat("@Scheduled method never called", repository.getInvocationCount(), greaterThan(0));
|
assertThat("@Scheduled method never called", repository.getInvocationCount(), greaterThan(0));
|
||||||
assertThat("no transactions were committed", txManager.commits, greaterThan(0));
|
assertThat("no transactions were committed", txManager.commits, greaterThan(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void withAspectConfig() throws InterruptedException {
|
||||||
|
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||||
|
ctx.register(AspectConfig.class, MyRepositoryWithScheduledMethodImpl.class);
|
||||||
|
ctx.refresh();
|
||||||
|
|
||||||
|
Thread.sleep(100); // allow @Scheduled method to be called several times
|
||||||
|
|
||||||
|
MyRepositoryWithScheduledMethod repository = ctx.getBean(MyRepositoryWithScheduledMethod.class);
|
||||||
|
assertThat("repository is not a proxy", AopUtils.isCglibProxy(repository), is(true));
|
||||||
|
assertThat("@Scheduled method never called", repository.getInvocationCount(), greaterThan(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement
|
@EnableTransactionManagement
|
||||||
|
@ -108,7 +125,7 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableTransactionManagement(proxyTargetClass=true)
|
@EnableTransactionManagement(proxyTargetClass = true)
|
||||||
static class SubclassProxyTxConfig {
|
static class SubclassProxyTxConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,11 +154,6 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
static class Config {
|
static class Config {
|
||||||
|
|
||||||
@Bean
|
|
||||||
public PersistenceExceptionTranslationPostProcessor peTranslationPostProcessor() {
|
|
||||||
return new PersistenceExceptionTranslationPostProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PlatformTransactionManager txManager() {
|
public PlatformTransactionManager txManager() {
|
||||||
return new CallCountingTransactionManager();
|
return new CallCountingTransactionManager();
|
||||||
|
@ -151,6 +163,41 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
public PersistenceExceptionTranslator peTranslator() {
|
public PersistenceExceptionTranslator peTranslator() {
|
||||||
return mock(PersistenceExceptionTranslator.class);
|
return mock(PersistenceExceptionTranslator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public PersistenceExceptionTranslationPostProcessor peTranslationPostProcessor() {
|
||||||
|
return new PersistenceExceptionTranslationPostProcessor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableScheduling
|
||||||
|
static class AspectConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static AnnotationAwareAspectJAutoProxyCreator autoProxyCreator() {
|
||||||
|
AnnotationAwareAspectJAutoProxyCreator apc = new AnnotationAwareAspectJAutoProxyCreator();
|
||||||
|
apc.setProxyTargetClass(true);
|
||||||
|
return apc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public static MyAspect myAspect() {
|
||||||
|
return new MyAspect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Aspect
|
||||||
|
public static class MyAspect {
|
||||||
|
|
||||||
|
private final AtomicInteger count = new AtomicInteger(0);
|
||||||
|
|
||||||
|
@org.aspectj.lang.annotation.Before("execution(* scheduled())")
|
||||||
|
public void checkTransaction() {
|
||||||
|
this.count.incrementAndGet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,6 +238,9 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
|
|
||||||
private final AtomicInteger count = new AtomicInteger(0);
|
private final AtomicInteger count = new AtomicInteger(0);
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private MyAspect myAspect;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Scheduled(fixedDelay = 5)
|
@Scheduled(fixedDelay = 5)
|
||||||
|
@ -200,6 +250,9 @@ public class ScheduledAndTransactionalAnnotationIntegrationTests {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInvocationCount() {
|
public int getInvocationCount() {
|
||||||
|
if (this.myAspect != null) {
|
||||||
|
assertEquals(this.count.get(), this.myAspect.count.get());
|
||||||
|
}
|
||||||
return this.count.get();
|
return this.count.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue