SimpleTransactionScope properly suspends and resumes scoped objects
Issue: SPR-14148
This commit is contained in:
parent
10554a85c9
commit
831f09cf48
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
@ -46,7 +46,7 @@ public class SimpleTransactionScope implements Scope {
|
|||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this);
|
||||
if (scopedObjects == null) {
|
||||
scopedObjects = new ScopedObjectsHolder();
|
||||
TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization());
|
||||
TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization(scopedObjects));
|
||||
TransactionSynchronizationManager.bindResource(this, scopedObjects);
|
||||
}
|
||||
Object scopedObject = scopedObjects.scopedInstances.get(name);
|
||||
|
@ -98,13 +98,30 @@ public class SimpleTransactionScope implements Scope {
|
|||
|
||||
private class CleanupSynchronization extends TransactionSynchronizationAdapter {
|
||||
|
||||
private final ScopedObjectsHolder scopedObjects;
|
||||
|
||||
public CleanupSynchronization(ScopedObjectsHolder scopedObjects) {
|
||||
this.scopedObjects = scopedObjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suspend() {
|
||||
TransactionSynchronizationManager.unbindResource(SimpleTransactionScope.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume() {
|
||||
TransactionSynchronizationManager.bindResource(SimpleTransactionScope.this, this.scopedObjects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(int status) {
|
||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder)
|
||||
TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this);
|
||||
for (Runnable callback : scopedObjects.destructionCallbacks.values()) {
|
||||
for (Runnable callback : this.scopedObjects.destructionCallbacks.values()) {
|
||||
callback.run();
|
||||
}
|
||||
this.scopedObjects.destructionCallbacks.clear();
|
||||
this.scopedObjects.scopedInstances.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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,6 +16,9 @@
|
|||
|
||||
package org.springframework.transaction.support;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
|
@ -23,6 +26,7 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
|
|||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.tests.sample.beans.DerivedTestBean;
|
||||
import org.springframework.tests.sample.beans.TestBean;
|
||||
import org.springframework.tests.transaction.CallCountingTransactionManager;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
@ -93,7 +97,6 @@ public class SimpleTransactionScopeTests {
|
|||
|
||||
bean2b = context.getBean(DerivedTestBean.class);
|
||||
assertSame(bean2b, context.getBean(DerivedTestBean.class));
|
||||
assertNotSame(bean2, bean2a);
|
||||
assertNotSame(bean2, bean2b);
|
||||
assertNotSame(bean2a, bean2b);
|
||||
}
|
||||
|
@ -125,4 +128,70 @@ public class SimpleTransactionScopeTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWithTransactionManager() throws Exception {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.getBeanFactory().registerScope("tx", new SimpleTransactionScope());
|
||||
|
||||
GenericBeanDefinition bd1 = new GenericBeanDefinition();
|
||||
bd1.setBeanClass(TestBean.class);
|
||||
bd1.setScope("tx");
|
||||
bd1.setPrimary(true);
|
||||
context.registerBeanDefinition("txScopedObject1", bd1);
|
||||
|
||||
GenericBeanDefinition bd2 = new GenericBeanDefinition();
|
||||
bd2.setBeanClass(DerivedTestBean.class);
|
||||
bd2.setScope("tx");
|
||||
context.registerBeanDefinition("txScopedObject2", bd2);
|
||||
|
||||
context.refresh();
|
||||
|
||||
CallCountingTransactionManager tm = new CallCountingTransactionManager();
|
||||
TransactionTemplate tt = new TransactionTemplate(tm);
|
||||
Set<DerivedTestBean> finallyDestroy = new HashSet<DerivedTestBean>();
|
||||
|
||||
tt.execute(status -> {
|
||||
TestBean bean1 = context.getBean(TestBean.class);
|
||||
assertSame(bean1, context.getBean(TestBean.class));
|
||||
|
||||
DerivedTestBean bean2 = context.getBean(DerivedTestBean.class);
|
||||
assertSame(bean2, context.getBean(DerivedTestBean.class));
|
||||
context.getBeanFactory().destroyScopedBean("txScopedObject2");
|
||||
assertFalse(TransactionSynchronizationManager.hasResource("txScopedObject2"));
|
||||
assertTrue(bean2.wasDestroyed());
|
||||
|
||||
DerivedTestBean bean2a = context.getBean(DerivedTestBean.class);
|
||||
assertSame(bean2a, context.getBean(DerivedTestBean.class));
|
||||
assertNotSame(bean2, bean2a);
|
||||
context.getBeanFactory().getRegisteredScope("tx").remove("txScopedObject2");
|
||||
assertFalse(TransactionSynchronizationManager.hasResource("txScopedObject2"));
|
||||
assertFalse(bean2a.wasDestroyed());
|
||||
|
||||
DerivedTestBean bean2b = context.getBean(DerivedTestBean.class);
|
||||
finallyDestroy.add(bean2b);
|
||||
assertSame(bean2b, context.getBean(DerivedTestBean.class));
|
||||
assertNotSame(bean2, bean2b);
|
||||
assertNotSame(bean2a, bean2b);
|
||||
|
||||
Set<DerivedTestBean> immediatelyDestroy = new HashSet<DerivedTestBean>();
|
||||
TransactionTemplate tt2 = new TransactionTemplate(tm);
|
||||
tt2.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
tt2.execute(status2 -> {
|
||||
DerivedTestBean bean2c = context.getBean(DerivedTestBean.class);
|
||||
immediatelyDestroy.add(bean2c);
|
||||
assertSame(bean2c, context.getBean(DerivedTestBean.class));
|
||||
assertNotSame(bean2, bean2c);
|
||||
assertNotSame(bean2a, bean2c);
|
||||
assertNotSame(bean2b, bean2c);
|
||||
return null;
|
||||
});
|
||||
assertTrue(immediatelyDestroy.iterator().next().wasDestroyed());
|
||||
assertFalse(bean2b.wasDestroyed());
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
assertTrue(finallyDestroy.iterator().next().wasDestroyed());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue