Skip to content

Commit 14839b1

Browse files
committed
Sort detected PersistenceExceptionTranslator beans
Closes gh-24644
1 parent b345019 commit 14839b1

File tree

2 files changed

+73
-16
lines changed

2 files changed

+73
-16
lines changed

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

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,12 @@
1616

1717
package org.springframework.dao.support;
1818

19-
import java.util.Map;
20-
2119
import org.aopalliance.intercept.MethodInterceptor;
2220
import org.aopalliance.intercept.MethodInvocation;
2321

2422
import org.springframework.beans.BeansException;
2523
import org.springframework.beans.factory.BeanFactory;
2624
import org.springframework.beans.factory.BeanFactoryAware;
27-
import org.springframework.beans.factory.BeanFactoryUtils;
2825
import org.springframework.beans.factory.InitializingBean;
2926
import org.springframework.beans.factory.ListableBeanFactory;
3027
import org.springframework.lang.Nullable;
@@ -147,7 +144,8 @@ public Object invoke(MethodInvocation mi) throws Throwable {
147144
else {
148145
PersistenceExceptionTranslator translator = this.persistenceExceptionTranslator;
149146
if (translator == null) {
150-
Assert.state(this.beanFactory != null, "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
147+
Assert.state(this.beanFactory != null,
148+
"Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
151149
translator = detectPersistenceExceptionTranslators(this.beanFactory);
152150
this.persistenceExceptionTranslator = translator;
153151
}
@@ -158,20 +156,15 @@ public Object invoke(MethodInvocation mi) throws Throwable {
158156

159157
/**
160158
* Detect all PersistenceExceptionTranslators in the given BeanFactory.
161-
* @param beanFactory the ListableBeanFactory to obtaining all
162-
* PersistenceExceptionTranslators from
159+
* @param bf the ListableBeanFactory to obtain PersistenceExceptionTranslators from
163160
* @return a chained PersistenceExceptionTranslator, combining all
164-
* PersistenceExceptionTranslators found in the factory
161+
* PersistenceExceptionTranslators found in the given bean factory
165162
* @see ChainedPersistenceExceptionTranslator
166163
*/
167-
protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory beanFactory) {
164+
protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory bf) {
168165
// Find all translators, being careful not to activate FactoryBeans.
169-
Map<String, PersistenceExceptionTranslator> pets = BeanFactoryUtils.beansOfTypeIncludingAncestors(
170-
beanFactory, PersistenceExceptionTranslator.class, false, false);
171166
ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator();
172-
for (PersistenceExceptionTranslator pet : pets.values()) {
173-
cpet.addDelegate(pet);
174-
}
167+
bf.getBeanProvider(PersistenceExceptionTranslator.class, false).orderedStream().forEach(cpet::addDelegate);
175168
return cpet;
176169
}
177170

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

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2007 the original author or authors.
2+
* Copyright 2002-2020 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.
@@ -16,19 +16,35 @@
1616

1717
package org.springframework.dao.annotation;
1818

19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import org.aopalliance.intercept.MethodInvocation;
23+
import org.junit.jupiter.api.Test;
24+
1925
import org.springframework.aop.framework.ProxyFactory;
2026
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
2127
import org.springframework.beans.factory.support.RootBeanDefinition;
28+
import org.springframework.core.Ordered;
29+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
2230
import org.springframework.core.annotation.AnnotationUtils;
31+
import org.springframework.dao.DataAccessException;
2332
import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor;
2433
import org.springframework.dao.support.PersistenceExceptionTranslator;
2534
import org.springframework.stereotype.Repository;
2635

36+
import static org.assertj.core.api.Assertions.assertThat;
37+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
38+
import static org.mockito.BDDMockito.given;
39+
import static org.mockito.Mockito.mock;
40+
2741
/**
28-
* Tests for standalone usage of a PersistenceExceptionTranslationInterceptor, as explicit advice bean in a BeanFactory
29-
* rather than applied as part of a PersistenceExceptionTranslationAdvisor.
42+
* Tests for standalone usage of a PersistenceExceptionTranslationInterceptor,
43+
* as explicit advice bean in a BeanFactory rather than applied as part of a
44+
* PersistenceExceptionTranslationAdvisor.
3045
*
3146
* @author Juergen Hoeller
47+
* @author Tadaya Tsuyukubo
3248
*/
3349
public class PersistenceExceptionTranslationInterceptorTests extends PersistenceExceptionTranslationAdvisorTests {
3450

@@ -42,4 +58,52 @@ protected void addPersistenceExceptionTranslation(ProxyFactory pf, PersistenceEx
4258
}
4359
}
4460

61+
@Test
62+
void detectPersistenceExceptionTranslators() throws Throwable {
63+
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
64+
bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
65+
bf.registerBeanDefinition("peti", new RootBeanDefinition(PersistenceExceptionTranslationInterceptor.class));
66+
67+
List<Integer> callOrder = new ArrayList<>();
68+
bf.registerSingleton("pet20", new CallOrderAwareExceptionTranslator(20, callOrder));
69+
bf.registerSingleton("pet10", new CallOrderAwareExceptionTranslator(10, callOrder));
70+
bf.registerSingleton("pet30", new CallOrderAwareExceptionTranslator(30, callOrder));
71+
72+
PersistenceExceptionTranslationInterceptor interceptor =
73+
bf.getBean("peti", PersistenceExceptionTranslationInterceptor.class);
74+
interceptor.setAlwaysTranslate(true);
75+
76+
RuntimeException exception = new RuntimeException();
77+
MethodInvocation invocation = mock(MethodInvocation.class);
78+
given(invocation.proceed()).willThrow(exception);
79+
80+
assertThatThrownBy(() -> interceptor.invoke(invocation)).isSameAs(exception);
81+
82+
assertThat(callOrder).containsExactly(10, 20, 30);
83+
}
84+
85+
86+
private static class CallOrderAwareExceptionTranslator implements PersistenceExceptionTranslator, Ordered {
87+
88+
private final int order;
89+
90+
private final List<Integer> callOrder;
91+
92+
public CallOrderAwareExceptionTranslator(int order, List<Integer> callOrder) {
93+
this.order = order;
94+
this.callOrder = callOrder;
95+
}
96+
97+
@Override
98+
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
99+
callOrder.add(this.order);
100+
return null;
101+
}
102+
103+
@Override
104+
public int getOrder() {
105+
return this.order;
106+
}
107+
}
108+
45109
}

0 commit comments

Comments
 (0)