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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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);
|
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this);
|
||||||
if (scopedObjects == null) {
|
if (scopedObjects == null) {
|
||||||
scopedObjects = new ScopedObjectsHolder();
|
scopedObjects = new ScopedObjectsHolder();
|
||||||
TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization());
|
TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization(scopedObjects));
|
||||||
TransactionSynchronizationManager.bindResource(this, scopedObjects);
|
TransactionSynchronizationManager.bindResource(this, scopedObjects);
|
||||||
}
|
}
|
||||||
Object scopedObject = scopedObjects.scopedInstances.get(name);
|
Object scopedObject = scopedObjects.scopedInstances.get(name);
|
||||||
|
@ -98,13 +98,30 @@ public class SimpleTransactionScope implements Scope {
|
||||||
|
|
||||||
private class CleanupSynchronization extends TransactionSynchronizationAdapter {
|
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
|
@Override
|
||||||
public void afterCompletion(int status) {
|
public void afterCompletion(int status) {
|
||||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder)
|
|
||||||
TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this);
|
TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this);
|
||||||
for (Runnable callback : scopedObjects.destructionCallbacks.values()) {
|
for (Runnable callback : this.scopedObjects.destructionCallbacks.values()) {
|
||||||
callback.run();
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package org.springframework.transaction.support;
|
package org.springframework.transaction.support;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
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.context.support.GenericApplicationContext;
|
||||||
import org.springframework.tests.sample.beans.DerivedTestBean;
|
import org.springframework.tests.sample.beans.DerivedTestBean;
|
||||||
import org.springframework.tests.sample.beans.TestBean;
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
import org.springframework.tests.transaction.CallCountingTransactionManager;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
@ -93,7 +97,6 @@ public class SimpleTransactionScopeTests {
|
||||||
|
|
||||||
bean2b = context.getBean(DerivedTestBean.class);
|
bean2b = context.getBean(DerivedTestBean.class);
|
||||||
assertSame(bean2b, context.getBean(DerivedTestBean.class));
|
assertSame(bean2b, context.getBean(DerivedTestBean.class));
|
||||||
assertNotSame(bean2, bean2a);
|
|
||||||
assertNotSame(bean2, bean2b);
|
assertNotSame(bean2, bean2b);
|
||||||
assertNotSame(bean2a, 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