spring AbstractReactiveTransactionAspectTests 源码
spring AbstractReactiveTransactionAspectTests 代码
文件路径:/spring-tx/src/test/java/org/springframework/transaction/interceptor/AbstractReactiveTransactionAspectTests.java
/*
* Copyright 2002-2021 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.transaction.interceptor;
import java.lang.reflect.Method;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.ReactiveTransaction;
import org.springframework.transaction.ReactiveTransactionManager;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.reactive.TransactionContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Fail.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Abstract support class to test {@link TransactionAspectSupport} with reactive methods.
*
* @author Mark Paluch
* @author Juergen Hoeller
*/
public abstract class AbstractReactiveTransactionAspectTests {
protected Method getNameMethod;
protected Method setNameMethod;
protected Method exceptionalMethod;
@BeforeEach
public void setup() throws Exception {
getNameMethod = TestBean.class.getMethod("getName");
setNameMethod = TestBean.class.getMethod("setName", String.class);
exceptionalMethod = TestBean.class.getMethod("exceptional", Throwable.class);
}
@Test
public void noTransaction() throws Exception {
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
DefaultTestBean tb = new DefaultTestBean();
TransactionAttributeSource tas = new MapTransactionAttributeSource();
// All the methods in this class use the advised() template method
// to obtain a transaction object, configured with the when PlatformTransactionManager
// and transaction attribute source
TestBean itb = (TestBean) advised(tb, rtm, tas);
checkReactiveTransaction(false);
itb.getName();
checkReactiveTransaction(false);
// expect no calls
verifyNoInteractions(rtm);
}
/**
* Check that a transaction is created and committed.
*/
@Test
public void transactionShouldSucceed() throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute();
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
tas.register(getNameMethod, txatt);
ReactiveTransaction status = mock(ReactiveTransaction.class);
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
// expect a transaction
given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
given(rtm.commit(status)).willReturn(Mono.empty());
DefaultTestBean tb = new DefaultTestBean();
TestBean itb = (TestBean) advised(tb, rtm, tas);
itb.getName()
.as(StepVerifier::create)
.verifyComplete();
verify(rtm).commit(status);
}
/**
* Check that two transactions are created and committed.
*/
@Test
public void twoTransactionsShouldSucceed() throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute();
MapTransactionAttributeSource tas1 = new MapTransactionAttributeSource();
tas1.register(getNameMethod, txatt);
MapTransactionAttributeSource tas2 = new MapTransactionAttributeSource();
tas2.register(setNameMethod, txatt);
ReactiveTransaction status = mock(ReactiveTransaction.class);
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
// expect a transaction
given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
given(rtm.commit(status)).willReturn(Mono.empty());
DefaultTestBean tb = new DefaultTestBean();
TestBean itb = (TestBean) advised(tb, rtm, new TransactionAttributeSource[] {tas1, tas2});
itb.getName()
.as(StepVerifier::create)
.verifyComplete();
Mono.from(itb.setName("myName"))
.as(StepVerifier::create)
.verifyComplete();
verify(rtm, times(2)).commit(status);
}
/**
* Check that a transaction is created and committed.
*/
@Test
public void transactionShouldSucceedWithNotNew() throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute();
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
tas.register(getNameMethod, txatt);
ReactiveTransaction status = mock(ReactiveTransaction.class);
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
// expect a transaction
given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
given(rtm.commit(status)).willReturn(Mono.empty());
DefaultTestBean tb = new DefaultTestBean();
TestBean itb = (TestBean) advised(tb, rtm, tas);
itb.getName()
.as(StepVerifier::create)
.verifyComplete();
verify(rtm).commit(status);
}
@Test
public void rollbackOnCheckedException() throws Throwable {
doTestRollbackOnException(new Exception(), true, false);
}
@Test
public void noRollbackOnCheckedException() throws Throwable {
doTestRollbackOnException(new Exception(), false, false);
}
@Test
public void rollbackOnUncheckedException() throws Throwable {
doTestRollbackOnException(new RuntimeException(), true, false);
}
@Test
public void noRollbackOnUncheckedException() throws Throwable {
doTestRollbackOnException(new RuntimeException(), false, false);
}
@Test
public void rollbackOnCheckedExceptionWithRollbackException() throws Throwable {
doTestRollbackOnException(new Exception(), true, true);
}
@Test
public void noRollbackOnCheckedExceptionWithRollbackException() throws Throwable {
doTestRollbackOnException(new Exception(), false, true);
}
@Test
public void rollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
doTestRollbackOnException(new RuntimeException(), true, true);
}
@Test
public void noRollbackOnUncheckedExceptionWithRollbackException() throws Throwable {
doTestRollbackOnException(new RuntimeException(), false, true);
}
/**
* Check that the when exception thrown by the target can produce the
* desired behavior with the appropriate transaction attribute.
* @param ex exception to be thrown by the target
* @param shouldRollback whether this should cause a transaction rollback
*/
@SuppressWarnings("serial")
protected void doTestRollbackOnException(
final Exception ex, final boolean shouldRollback, boolean rollbackException) throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute() {
@Override
public boolean rollbackOn(Throwable t) {
assertThat(t).isSameAs(ex);
return shouldRollback;
}
};
Method m = exceptionalMethod;
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
tas.register(m, txatt);
ReactiveTransaction status = mock(ReactiveTransaction.class);
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
// Gets additional call(s) from TransactionControl
given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
TransactionSystemException tex = new TransactionSystemException("system exception");
if (rollbackException) {
if (shouldRollback) {
given(rtm.rollback(status)).willReturn(Mono.error(tex));
}
else {
given(rtm.commit(status)).willReturn(Mono.error(tex));
}
}
else {
given(rtm.commit(status)).willReturn(Mono.empty());
given(rtm.rollback(status)).willReturn(Mono.empty());
}
DefaultTestBean tb = new DefaultTestBean();
TestBean itb = (TestBean) advised(tb, rtm, tas);
itb.exceptional(ex)
.as(StepVerifier::create)
.expectErrorSatisfies(actual -> {
if (rollbackException) {
assertThat(actual).isEqualTo(tex);
}
else {
assertThat(actual).isEqualTo(ex);
}
}).verify();
if (!rollbackException) {
if (shouldRollback) {
verify(rtm).rollback(status);
}
else {
verify(rtm).commit(status);
}
}
}
/**
* Simulate a transaction infrastructure failure.
* Shouldn't invoke target method.
*/
@Test
public void cannotCreateTransaction() throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute();
Method m = getNameMethod;
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
tas.register(m, txatt);
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
// Expect a transaction
CannotCreateTransactionException ex = new CannotCreateTransactionException("foobar", null);
given(rtm.getReactiveTransaction(txatt)).willThrow(ex);
DefaultTestBean tb = new DefaultTestBean() {
@Override
public Mono<String> getName() {
throw new UnsupportedOperationException(
"Shouldn't have invoked target method when couldn't create transaction for transactional method");
}
};
TestBean itb = (TestBean) advised(tb, rtm, tas);
itb.getName()
.as(StepVerifier::create)
.expectError(CannotCreateTransactionException.class)
.verify();
}
/**
* Simulate failure of the underlying transaction infrastructure to commit.
* Check that the target method was invoked, but that the transaction
* infrastructure exception was thrown to the client
*/
@Test
public void cannotCommitTransaction() throws Exception {
TransactionAttribute txatt = new DefaultTransactionAttribute();
Method m = setNameMethod;
MapTransactionAttributeSource tas = new MapTransactionAttributeSource();
tas.register(m, txatt);
// Method m2 = getNameMethod;
// No attributes for m2
ReactiveTransactionManager rtm = mock(ReactiveTransactionManager.class);
ReactiveTransaction status = mock(ReactiveTransaction.class);
given(rtm.getReactiveTransaction(txatt)).willReturn(Mono.just(status));
UnexpectedRollbackException ex = new UnexpectedRollbackException("foobar", null);
given(rtm.commit(status)).willReturn(Mono.error(ex));
given(rtm.rollback(status)).willReturn(Mono.empty());
DefaultTestBean tb = new DefaultTestBean();
TestBean itb = (TestBean) advised(tb, rtm, tas);
String name = "new name";
Mono.from(itb.setName(name))
.as(StepVerifier::create)
.consumeErrorWith(throwable -> {
assertThat(throwable.getClass()).isEqualTo(RuntimeException.class);
assertThat(throwable.getCause()).isEqualTo(ex);
})
.verify();
// Should have invoked target and changed name
itb.getName()
.as(StepVerifier::create)
.expectNext(name)
.verifyComplete();
}
private void checkReactiveTransaction(boolean expected) {
Mono.deferContextual(Mono::just)
.handle((context, sink) -> {
if (context.hasKey(TransactionContext.class) != expected) {
fail("Should have thrown NoTransactionException");
}
sink.complete();
})
.block();
}
protected Object advised(
Object target, ReactiveTransactionManager rtm, TransactionAttributeSource[] tas) throws Exception {
return advised(target, rtm, new CompositeTransactionAttributeSource(tas));
}
/**
* Subclasses must implement this to create an advised object based on the
* when target. In the case of AspectJ, the advised object will already
* have been created, as there's no distinction between target and proxy.
* In the case of Spring's own AOP framework, a proxy must be created
* using a suitably configured transaction interceptor
* @param target the target if there's a distinct target. If not (AspectJ),
* return target.
* @return transactional advised object
*/
protected abstract Object advised(
Object target, ReactiveTransactionManager rtm, TransactionAttributeSource tas) throws Exception;
public interface TestBean {
Mono<String> getName();
Publisher<Void> setName(String name);
Mono<Void> exceptional(Throwable t);
}
public class DefaultTestBean implements TestBean {
private String name;
@Override
public Mono<String> getName() {
return Mono.justOrEmpty(name);
}
@Override
public Publisher<Void> setName(String name) {
return Mono.fromRunnable(() -> this.name = name);
}
@Override
public Mono<Void> exceptional(Throwable t) {
if (t != null) {
return Mono.error(t);
}
return Mono.empty();
}
}
}
相关信息
相关文章
spring AbstractTransactionAspectTests 源码
spring BeanFactoryTransactionTests 源码
spring ImplementsNoInterfaces 源码
spring MapTransactionAttributeSource 源码
spring PlatformTransactionManagerFacade 源码
spring ReactiveTransactionInterceptorTests 源码
spring RollbackRuleAttributeTests 源码
0
赞
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
7、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦