Skip to content

Commit 65dbfd0

Browse files
committed
Defensive PersistenceExceptionTranslator bean retrieval on shutdown
Closes gh-33067
1 parent 203fa75 commit 65dbfd0

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
2020
import org.aopalliance.intercept.MethodInvocation;
2121

2222
import org.springframework.beans.BeansException;
23+
import org.springframework.beans.factory.BeanCreationNotAllowedException;
2324
import org.springframework.beans.factory.BeanFactory;
2425
import org.springframework.beans.factory.BeanFactoryAware;
2526
import org.springframework.beans.factory.InitializingBean;
@@ -146,7 +147,14 @@ public Object invoke(MethodInvocation mi) throws Throwable {
146147
if (translator == null) {
147148
Assert.state(this.beanFactory != null,
148149
"Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
149-
translator = detectPersistenceExceptionTranslators(this.beanFactory);
150+
try {
151+
translator = detectPersistenceExceptionTranslators(this.beanFactory);
152+
}
153+
catch (BeanCreationNotAllowedException ex2) {
154+
// Cannot create PersistenceExceptionTranslator bean on shutdown:
155+
// fall back to rethrowing original exception without translation
156+
throw ex;
157+
}
150158
this.persistenceExceptionTranslator = translator;
151159
}
152160
throw DataAccessUtils.translateIfNecessary(ex, translator);

spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java

+30-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.util.ArrayList;
2020
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicBoolean;
2122

2223
import org.aopalliance.intercept.MethodInvocation;
2324
import org.junit.jupiter.api.Test;
@@ -29,6 +30,7 @@
2930
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
3031
import org.springframework.core.annotation.AnnotationUtils;
3132
import org.springframework.dao.DataAccessException;
33+
import org.springframework.dao.support.ChainedPersistenceExceptionTranslator;
3234
import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor;
3335
import org.springframework.dao.support.PersistenceExceptionTranslator;
3436
import org.springframework.stereotype.Repository;
@@ -78,10 +80,37 @@ void detectPersistenceExceptionTranslators() throws Throwable {
7880
given(invocation.proceed()).willThrow(exception);
7981

8082
assertThatThrownBy(() -> interceptor.invoke(invocation)).isSameAs(exception);
81-
8283
assertThat(callOrder).containsExactly(10, 20, 30);
8384
}
8485

86+
@Test
87+
void detectPersistenceExceptionTranslatorsOnShutdown() throws Throwable {
88+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
89+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
90+
bf.registerBeanDefinition("peti", new RootBeanDefinition(PersistenceExceptionTranslationInterceptor.class));
91+
bf.registerBeanDefinition("pet", new RootBeanDefinition(ChainedPersistenceExceptionTranslator.class));
92+
93+
PersistenceExceptionTranslationInterceptor interceptor =
94+
bf.getBean("peti", PersistenceExceptionTranslationInterceptor.class);
95+
interceptor.setAlwaysTranslate(true);
96+
97+
RuntimeException exception = new RuntimeException();
98+
MethodInvocation invocation = mock();
99+
given(invocation.proceed()).willThrow(exception);
100+
101+
AtomicBoolean correctException = new AtomicBoolean(false);
102+
bf.registerDisposableBean("disposable", () -> {
103+
try {
104+
interceptor.invoke(invocation);
105+
}
106+
catch (Throwable ex) {
107+
correctException.set(ex == exception);
108+
}
109+
});
110+
bf.destroySingletons();
111+
assertThat(correctException).isTrue();
112+
}
113+
85114

86115
private static class CallOrderAwareExceptionTranslator implements PersistenceExceptionTranslator, Ordered {
87116

0 commit comments

Comments
 (0)