Juergen Hoeller
10 years ago
3 changed files with 247 additions and 4 deletions
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2002-2015 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 |
||||
* |
||||
* http://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.support; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
|
||||
import org.springframework.beans.factory.ObjectFactory; |
||||
import org.springframework.beans.factory.config.Scope; |
||||
|
||||
/** |
||||
* A simple transaction-backed {@link Scope} implementation, delegating to |
||||
* {@link TransactionSynchronizationManager}'s resource binding mechanism. |
||||
* |
||||
* <p><b>NOTE:</b> Like {@link org.springframework.context.support.SimpleThreadScope}, |
||||
* this transaction scope is not registered by default in common contexts. Instead, |
||||
* you need to explicitly assign it to a scope key in your setup, either through |
||||
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope} |
||||
* or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 4.2 |
||||
* @see org.springframework.context.support.SimpleThreadScope |
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope |
||||
* @see org.springframework.beans.factory.config.CustomScopeConfigurer |
||||
*/ |
||||
public class SimpleTransactionScope implements Scope { |
||||
|
||||
@Override |
||||
public Object get(String name, ObjectFactory<?> objectFactory) { |
||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); |
||||
if (scopedObjects == null) { |
||||
scopedObjects = new ScopedObjectsHolder(); |
||||
TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization()); |
||||
TransactionSynchronizationManager.bindResource(this, scopedObjects); |
||||
} |
||||
Object scopedObject = scopedObjects.scopedInstances.get(name); |
||||
if (scopedObject == null) { |
||||
scopedObject = objectFactory.getObject(); |
||||
scopedObjects.scopedInstances.put(name, scopedObject); |
||||
} |
||||
return scopedObject; |
||||
} |
||||
|
||||
@Override |
||||
public Object remove(String name) { |
||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); |
||||
if (scopedObjects != null) { |
||||
scopedObjects.destructionCallbacks.remove(name); |
||||
return scopedObjects.scopedInstances.remove(name); |
||||
} |
||||
else { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void registerDestructionCallback(String name, Runnable callback) { |
||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); |
||||
if (scopedObjects != null) { |
||||
scopedObjects.destructionCallbacks.put(name, callback); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Object resolveContextualObject(String key) { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String getConversationId() { |
||||
return TransactionSynchronizationManager.getCurrentTransactionName(); |
||||
} |
||||
|
||||
|
||||
static class ScopedObjectsHolder { |
||||
|
||||
final Map<String, Object> scopedInstances = new HashMap<String, Object>(); |
||||
|
||||
final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>(); |
||||
} |
||||
|
||||
|
||||
private class CleanupSynchronization extends TransactionSynchronizationAdapter { |
||||
|
||||
@Override |
||||
public void afterCompletion(int status) { |
||||
ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) |
||||
TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this); |
||||
for (Runnable callback : scopedObjects.destructionCallbacks.values()) { |
||||
callback.run(); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,127 @@
@@ -0,0 +1,127 @@
|
||||
/* |
||||
* Copyright 2002-2015 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 |
||||
* |
||||
* http://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.support; |
||||
|
||||
import org.junit.Test; |
||||
|
||||
import org.springframework.beans.factory.BeanCreationException; |
||||
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 static org.junit.Assert.*; |
||||
|
||||
/** |
||||
* @author Juergen Hoeller |
||||
*/ |
||||
public class SimpleTransactionScopeTests { |
||||
|
||||
@Test |
||||
public void getFromScope() 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(); |
||||
|
||||
try { |
||||
context.getBean(TestBean.class); |
||||
fail("Should have thrown BeanCreationException"); |
||||
} |
||||
catch (BeanCreationException ex) { |
||||
// expected - no synchronization active
|
||||
assertTrue(ex.getCause() instanceof IllegalStateException); |
||||
} |
||||
|
||||
try { |
||||
context.getBean(DerivedTestBean.class); |
||||
fail("Should have thrown BeanCreationException"); |
||||
} |
||||
catch (BeanCreationException ex) { |
||||
// expected - no synchronization active
|
||||
assertTrue(ex.getCause() instanceof IllegalStateException); |
||||
} |
||||
|
||||
TestBean bean1 = null; |
||||
DerivedTestBean bean2 = null; |
||||
DerivedTestBean bean2a = null; |
||||
DerivedTestBean bean2b = null; |
||||
|
||||
TransactionSynchronizationManager.initSynchronization(); |
||||
try { |
||||
bean1 = context.getBean(TestBean.class); |
||||
assertSame(bean1, context.getBean(TestBean.class)); |
||||
|
||||
bean2 = context.getBean(DerivedTestBean.class); |
||||
assertSame(bean2, context.getBean(DerivedTestBean.class)); |
||||
context.getBeanFactory().destroyScopedBean("txScopedObject2"); |
||||
assertFalse(TransactionSynchronizationManager.hasResource("txScopedObject2")); |
||||
assertTrue(bean2.wasDestroyed()); |
||||
|
||||
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()); |
||||
|
||||
bean2b = context.getBean(DerivedTestBean.class); |
||||
assertSame(bean2b, context.getBean(DerivedTestBean.class)); |
||||
assertNotSame(bean2, bean2a); |
||||
assertNotSame(bean2, bean2b); |
||||
assertNotSame(bean2a, bean2b); |
||||
} |
||||
finally { |
||||
TransactionSynchronizationUtils.triggerAfterCompletion(TransactionSynchronization.STATUS_COMMITTED); |
||||
TransactionSynchronizationManager.clearSynchronization(); |
||||
} |
||||
|
||||
assertFalse(bean2a.wasDestroyed()); |
||||
assertTrue(bean2b.wasDestroyed()); |
||||
assertTrue(TransactionSynchronizationManager.getResourceMap().isEmpty()); |
||||
|
||||
try { |
||||
context.getBean(TestBean.class); |
||||
fail("Should have thrown IllegalStateException"); |
||||
} |
||||
catch (BeanCreationException ex) { |
||||
// expected - no synchronization active
|
||||
assertTrue(ex.getCause() instanceof IllegalStateException); |
||||
} |
||||
|
||||
try { |
||||
context.getBean(DerivedTestBean.class); |
||||
fail("Should have thrown IllegalStateException"); |
||||
} |
||||
catch (BeanCreationException ex) { |
||||
// expected - no synchronization active
|
||||
assertTrue(ex.getCause() instanceof IllegalStateException); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue