diff --git a/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java b/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java index f8fdf75727..8ac82c07b2 100644 --- a/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java +++ b/spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java @@ -16,15 +16,12 @@ package org.springframework.dao.support; -import java.util.Map; - import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.lang.Nullable; @@ -147,7 +144,8 @@ public class PersistenceExceptionTranslationInterceptor else { PersistenceExceptionTranslator translator = this.persistenceExceptionTranslator; if (translator == null) { - Assert.state(this.beanFactory != null, "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory"); + Assert.state(this.beanFactory != null, + "Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory"); translator = detectPersistenceExceptionTranslators(this.beanFactory); this.persistenceExceptionTranslator = translator; } @@ -158,20 +156,15 @@ public class PersistenceExceptionTranslationInterceptor /** * Detect all PersistenceExceptionTranslators in the given BeanFactory. - * @param beanFactory the ListableBeanFactory to obtaining all - * PersistenceExceptionTranslators from + * @param bf the ListableBeanFactory to obtain PersistenceExceptionTranslators from * @return a chained PersistenceExceptionTranslator, combining all - * PersistenceExceptionTranslators found in the factory + * PersistenceExceptionTranslators found in the given bean factory * @see ChainedPersistenceExceptionTranslator */ - protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory beanFactory) { + protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(ListableBeanFactory bf) { // Find all translators, being careful not to activate FactoryBeans. - Map pets = BeanFactoryUtils.beansOfTypeIncludingAncestors( - beanFactory, PersistenceExceptionTranslator.class, false, false); ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator(); - for (PersistenceExceptionTranslator pet : pets.values()) { - cpet.addDelegate(pet); - } + bf.getBeanProvider(PersistenceExceptionTranslator.class, false).orderedStream().forEach(cpet::addDelegate); return cpet; } diff --git a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java index 351e971b3e..92ad674318 100644 --- a/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java +++ b/spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2020 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,19 +16,35 @@ package org.springframework.dao.annotation; +import java.util.ArrayList; +import java.util.List; + +import org.aopalliance.intercept.MethodInvocation; +import org.junit.jupiter.api.Test; + import org.springframework.aop.framework.ProxyFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.dao.DataAccessException; import org.springframework.dao.support.PersistenceExceptionTranslationInterceptor; import org.springframework.dao.support.PersistenceExceptionTranslator; import org.springframework.stereotype.Repository; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + /** - * Tests for standalone usage of a PersistenceExceptionTranslationInterceptor, as explicit advice bean in a BeanFactory - * rather than applied as part of a PersistenceExceptionTranslationAdvisor. + * Tests for standalone usage of a PersistenceExceptionTranslationInterceptor, + * as explicit advice bean in a BeanFactory rather than applied as part of a + * PersistenceExceptionTranslationAdvisor. * * @author Juergen Hoeller + * @author Tadaya Tsuyukubo */ public class PersistenceExceptionTranslationInterceptorTests extends PersistenceExceptionTranslationAdvisorTests { @@ -42,4 +58,52 @@ public class PersistenceExceptionTranslationInterceptorTests extends Persistence } } + @Test + void detectPersistenceExceptionTranslators() throws Throwable { + DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); + bf.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); + bf.registerBeanDefinition("peti", new RootBeanDefinition(PersistenceExceptionTranslationInterceptor.class)); + + List callOrder = new ArrayList<>(); + bf.registerSingleton("pet20", new CallOrderAwareExceptionTranslator(20, callOrder)); + bf.registerSingleton("pet10", new CallOrderAwareExceptionTranslator(10, callOrder)); + bf.registerSingleton("pet30", new CallOrderAwareExceptionTranslator(30, callOrder)); + + PersistenceExceptionTranslationInterceptor interceptor = + bf.getBean("peti", PersistenceExceptionTranslationInterceptor.class); + interceptor.setAlwaysTranslate(true); + + RuntimeException exception = new RuntimeException(); + MethodInvocation invocation = mock(MethodInvocation.class); + given(invocation.proceed()).willThrow(exception); + + assertThatThrownBy(() -> interceptor.invoke(invocation)).isSameAs(exception); + + assertThat(callOrder).containsExactly(10, 20, 30); + } + + + private static class CallOrderAwareExceptionTranslator implements PersistenceExceptionTranslator, Ordered { + + private final int order; + + private final List callOrder; + + public CallOrderAwareExceptionTranslator(int order, List callOrder) { + this.order = order; + this.callOrder = callOrder; + } + + @Override + public DataAccessException translateExceptionIfPossible(RuntimeException ex) { + callOrder.add(this.order); + return null; + } + + @Override + public int getOrder() { + return this.order; + } + } + }