Skip to content

Commit 4152034

Browse files
committed
Move transaction configuration check to transaction.annotation package
AbstractTransactionManagementConfiguration.transactionalEventListenerFactory() creating a RestrictedTransactionalEventListenerFactory now. See gh-30679
1 parent 3c9cfa8 commit 4152034

File tree

4 files changed

+60
-16
lines changed

4 files changed

+60
-16
lines changed

spring-tx/src/main/java/org/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2023 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.
@@ -80,7 +80,7 @@ void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
8080
@Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
8181
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
8282
public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
83-
return new TransactionalEventListenerFactory();
83+
return new RestrictedTransactionalEventListenerFactory();
8484
}
8585

8686
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.transaction.annotation;
18+
19+
import java.lang.reflect.Method;
20+
21+
import org.springframework.context.ApplicationListener;
22+
import org.springframework.core.annotation.AnnotatedElementUtils;
23+
import org.springframework.scheduling.annotation.Async;
24+
import org.springframework.transaction.event.TransactionalEventListenerFactory;
25+
26+
/**
27+
* Extension of {@link TransactionalEventListenerFactory},
28+
* detecting invalid transaction configuration for transactional event listeners:
29+
* {@link Transactional} only supported with {@link Propagation#REQUIRES_NEW} or
30+
* {@link Async}.
31+
*
32+
* @author Juergen Hoeller
33+
* @since 6.1
34+
* @see org.springframework.transaction.event.TransactionalEventListener
35+
* @see Transactional
36+
*/
37+
public class RestrictedTransactionalEventListenerFactory extends TransactionalEventListenerFactory {
38+
39+
@Override
40+
public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
41+
Transactional txAnn = AnnotatedElementUtils.findMergedAnnotation(method, Transactional.class);
42+
if (txAnn != null && txAnn.propagation() != Propagation.REQUIRES_NEW &&
43+
!AnnotatedElementUtils.hasAnnotation(method, Async.class)) {
44+
throw new IllegalStateException("@TransactionalEventListener method must not be annotated with " +
45+
"@Transactional unless when marked as REQUIRES_NEW or declared as @Async: " + method);
46+
}
47+
return super.createApplicationListener(beanName, type, method);
48+
}
49+
50+
}

spring-tx/src/main/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapter.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,6 @@
2525
import org.springframework.context.event.EventListener;
2626
import org.springframework.context.event.GenericApplicationListener;
2727
import org.springframework.core.annotation.AnnotatedElementUtils;
28-
import org.springframework.scheduling.annotation.Async;
29-
import org.springframework.transaction.annotation.Propagation;
30-
import org.springframework.transaction.annotation.Transactional;
3128
import org.springframework.transaction.support.TransactionSynchronizationManager;
3229
import org.springframework.util.Assert;
3330

@@ -71,12 +68,6 @@ public TransactionalApplicationListenerMethodAdapter(String beanName, Class<?> t
7168
if (eventAnn == null) {
7269
throw new IllegalStateException("No TransactionalEventListener annotation found on method: " + method);
7370
}
74-
Transactional txAnn = AnnotatedElementUtils.findMergedAnnotation(method, Transactional.class);
75-
if (txAnn != null && txAnn.propagation() != Propagation.REQUIRES_NEW &&
76-
!AnnotatedElementUtils.hasAnnotation(method, Async.class)) {
77-
throw new IllegalStateException("@TransactionalEventListener method must not be annotated with " +
78-
"@Transactional unless when marked as REQUIRES_NEW or declared as @Async: " + method);
79-
}
8071
this.annotation = eventAnn;
8172
this.transactionPhase = eventAnn.phase();
8273
}

spring-tx/src/test/java/org/springframework/transaction/event/TransactionalApplicationListenerMethodAdapterTests.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.core.annotation.AnnotatedElementUtils;
2727
import org.springframework.scheduling.annotation.Async;
2828
import org.springframework.transaction.annotation.Propagation;
29+
import org.springframework.transaction.annotation.RestrictedTransactionalEventListenerFactory;
2930
import org.springframework.transaction.annotation.Transactional;
3031
import org.springframework.transaction.support.TransactionSynchronization;
3132
import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -34,6 +35,7 @@
3435

3536
import static org.assertj.core.api.Assertions.assertThat;
3637
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
38+
import static org.assertj.core.api.Assertions.assertThatNoException;
3739
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
3840

3941
/**
@@ -129,22 +131,23 @@ public void usesAnnotatedIdentifier() {
129131

130132
@Test
131133
public void withTransactionalAnnotation() {
134+
RestrictedTransactionalEventListenerFactory factory = new RestrictedTransactionalEventListenerFactory();
132135
Method m = ReflectionUtils.findMethod(SampleEvents.class, "withTransactionalAnnotation", String.class);
133-
assertThatIllegalStateException().isThrownBy(() -> createTestInstance(m));
136+
assertThatIllegalStateException().isThrownBy(() -> factory.createApplicationListener("test", SampleEvents.class, m));
134137
}
135138

136139
@Test
137140
public void withTransactionalRequiresNewAnnotation() {
141+
RestrictedTransactionalEventListenerFactory factory = new RestrictedTransactionalEventListenerFactory();
138142
Method m = ReflectionUtils.findMethod(SampleEvents.class, "withTransactionalRequiresNewAnnotation", String.class);
139-
supportsEventType(true, m, createGenericEventType(String.class));
140-
supportsEventType(false, m, createGenericEventType(Double.class));
143+
assertThatNoException().isThrownBy(() -> factory.createApplicationListener("test", SampleEvents.class, m));
141144
}
142145

143146
@Test
144147
public void withAsyncTransactionalAnnotation() {
148+
RestrictedTransactionalEventListenerFactory factory = new RestrictedTransactionalEventListenerFactory();
145149
Method m = ReflectionUtils.findMethod(SampleEvents.class, "withAsyncTransactionalAnnotation", String.class);
146-
supportsEventType(true, m, createGenericEventType(String.class));
147-
supportsEventType(false, m, createGenericEventType(Double.class));
150+
assertThatNoException().isThrownBy(() -> factory.createApplicationListener("test", SampleEvents.class, m));
148151
}
149152

150153

0 commit comments

Comments
 (0)