Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSu

private final Map<Class<?>, Boolean> eligibleBeans = new ConcurrentHashMap<>(256);

private final Map<String, Object> earlyBeanReferences = new ConcurrentHashMap<>(16);


/**
* Set whether this post-processor's advisor is supposed to apply before
Expand Down Expand Up @@ -84,8 +86,23 @@ public Class<?> determineBeanType(Class<?> beanClass, String beanName) {
return beanClass;
}

/**
* Expose the advised bean early for circular references.
* @since 7.1
*/
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object proxy = postProcessAfterInitialization(bean, beanName);
this.earlyBeanReferences.put(beanName, AopProxyUtils.ultimateSingletonTarget(bean));
return proxy;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
Object earlyReference = this.earlyBeanReferences.remove(beanName);
if (earlyReference != null && earlyReference == AopProxyUtils.ultimateSingletonTarget(bean)) {
return bean;
}
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.springframework.scheduling.annotation;

import java.lang.reflect.Method;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
Expand All @@ -29,8 +30,15 @@
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -93,6 +101,24 @@ void invokedAsynchronouslyOnProxyTarget() {
context.close();
}

@Test
void cacheEvictionWithEarlySelfReference() throws Exception {
try (AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(ConfigWithEarlySelfReference.class)) {

SelfReferencingBean bean = context.getBean(SelfReferencingBean.class);
Cache cache = context.getBean(CacheManager.class).getCache("test");
cache.put("key", "value");
assertThat(bean.evictAsync("key").get(5, TimeUnit.SECONDS)).isNotSameAs(Thread.currentThread());
assertThat(cache.get("key")).isNull();

cache.put("key", "value");
bean.evict("key");
assertThat(cache.get("key")).isNull();
assertThat(bean.getSelf()).isSameAs(bean);
}
}

@Test
void threadNamePrefix() {
BeanDefinition processorDefinition = new RootBeanDefinition(AsyncAnnotationBeanPostProcessor.class);
Expand Down Expand Up @@ -318,6 +344,54 @@ public void execute(Runnable r) {
}


@Configuration(proxyBeanMethods = false)
@EnableAsync
@EnableCaching
static class ConfigWithEarlySelfReference {

@Bean
SelfReferencingBean selfReferencingBean(ApplicationContext context) {
return new SelfReferencingBean(context);
}

@Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("test");
}
}


static class SelfReferencingBean implements InitializingBean {

private final ApplicationContext context;

private SelfReferencingBean self;

SelfReferencingBean(ApplicationContext context) {
this.context = context;
}

@Override
public void afterPropertiesSet() {
this.self = this.context.getBean(SelfReferencingBean.class);
}

public SelfReferencingBean getSelf() {
return this.self;
}

@Async
public CompletableFuture<Thread> evictAsync(String key) {
this.self.evict(key);
return CompletableFuture.completedFuture(Thread.currentThread());
}

@CacheEvict(cacheNames = "test", key = "#key")
public void evict(String key) {
}
}


@Configuration
@EnableAsync
static class ConfigWithExceptionHandler implements AsyncConfigurer {
Expand Down