Lazily retrieve delegate beans in AsyncConfigurer and CachingConfigurer
Introduces a configure method pattern for Supplier-style configuration and a common SingletonSupplier decorator for method reference suppliers. Also declares jcache.config and jcache.interceptor for non-null conventions. Issue: SPR-17021
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -66,23 +66,21 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCacheManagerBean() throws Throwable {
|
||||
public void singleCacheManagerBean() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(SingleCacheManagerConfig.class);
|
||||
ctx.refresh();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void multipleCacheManagerBeans() throws Throwable {
|
||||
@Test
|
||||
public void multipleCacheManagerBeans() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MultiCacheManagerConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root.getMessage().contains("beans of type CacheManager"));
|
||||
throw root;
|
||||
catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("no unique bean of type CacheManager"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,8 +91,8 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
||||
ctx.refresh(); // does not throw an exception
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void multipleCachingConfigurers() throws Throwable {
|
||||
@Test
|
||||
public void multipleCachingConfigurers() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(MultiCacheManagerConfigurer.class, EnableCachingConfig.class);
|
||||
try {
|
||||
@@ -102,22 +100,20 @@ public class EnableCachingTests extends AbstractCacheAnnotationTests {
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root instanceof IllegalStateException);
|
||||
assertTrue(root.getMessage().contains("implementations of CachingConfigurer"));
|
||||
throw root;
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void noCacheManagerBeans() throws Throwable {
|
||||
@Test
|
||||
public void noCacheManagerBeans() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(EmptyConfig.class);
|
||||
try {
|
||||
ctx.refresh();
|
||||
}
|
||||
catch (BeanCreationException ex) {
|
||||
Throwable root = ex.getRootCause();
|
||||
assertTrue(root.getMessage().contains("No bean of type CacheManager"));
|
||||
throw root;
|
||||
catch (IllegalStateException ex) {
|
||||
assertTrue(ex.getMessage().contains("no bean of type CacheManager"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,16 +33,19 @@ import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Component;
|
||||
@@ -218,6 +221,29 @@ public class EnableAsyncTests {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customExecutorBeanConfig() throws InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(CustomExecutorBeanConfig.class, ExecutorPostProcessor.class);
|
||||
ctx.refresh();
|
||||
|
||||
AsyncBean asyncBean = ctx.getBean(AsyncBean.class);
|
||||
asyncBean.work();
|
||||
Thread.sleep(500);
|
||||
assertThat(asyncBean.getThreadOfExecution().getName(), startsWith("Post-"));
|
||||
|
||||
TestableAsyncUncaughtExceptionHandler exceptionHandler = (TestableAsyncUncaughtExceptionHandler)
|
||||
ctx.getBean("exceptionHandler");
|
||||
assertFalse("handler should not have been called yet", exceptionHandler.isCalled());
|
||||
|
||||
asyncBean.fail();
|
||||
Thread.sleep(500);
|
||||
Method method = ReflectionUtils.findMethod(AsyncBean.class, "fail");
|
||||
exceptionHandler.assertCalledWith(method, UnsupportedOperationException.class);
|
||||
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spr14949FindsOnInterfaceWithInterfaceProxy() throws InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Spr14949ConfigA.class);
|
||||
@@ -440,6 +466,53 @@ public class EnableAsyncTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
static class CustomExecutorBeanConfig implements AsyncConfigurer {
|
||||
|
||||
@Bean
|
||||
public AsyncBean asyncBean() {
|
||||
return new AsyncBean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Executor getAsyncExecutor() {
|
||||
return executor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ThreadPoolTaskExecutor executor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setThreadNamePrefix("Custom-");
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
|
||||
return exceptionHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AsyncUncaughtExceptionHandler exceptionHandler() {
|
||||
return new TestableAsyncUncaughtExceptionHandler();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ExecutorPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ThreadPoolTaskExecutor) {
|
||||
((ThreadPoolTaskExecutor) bean).setThreadNamePrefix("Post-");
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface AsyncInterface {
|
||||
|
||||
@Async
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.scheduling.config;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@@ -55,7 +57,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
||||
public void asyncPostProcessorExecutorReference() {
|
||||
Object executor = context.getBean("testExecutor");
|
||||
Object postProcessor = context.getBean(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
assertSame(executor, new DirectFieldAccessor(postProcessor).getPropertyValue("executor"));
|
||||
assertSame(executor, ((Supplier) new DirectFieldAccessor(postProcessor).getPropertyValue("executor")).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,7 +71,7 @@ public class AnnotationDrivenBeanDefinitionParserTests {
|
||||
public void asyncPostProcessorExceptionHandlerReference() {
|
||||
Object exceptionHandler = context.getBean("testExceptionHandler");
|
||||
Object postProcessor = context.getBean(TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
assertSame(exceptionHandler, new DirectFieldAccessor(postProcessor).getPropertyValue("exceptionHandler"));
|
||||
assertSame(exceptionHandler, ((Supplier) new DirectFieldAccessor(postProcessor).getPropertyValue("exceptionHandler")).get());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user