001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.transaction.support; 018 019import java.util.HashMap; 020import java.util.LinkedHashMap; 021import java.util.Map; 022 023import org.springframework.beans.factory.ObjectFactory; 024import org.springframework.beans.factory.config.Scope; 025 026/** 027 * A simple transaction-backed {@link Scope} implementation, delegating to 028 * {@link TransactionSynchronizationManager}'s resource binding mechanism. 029 * 030 * <p><b>NOTE:</b> Like {@link org.springframework.context.support.SimpleThreadScope}, 031 * this transaction scope is not registered by default in common contexts. Instead, 032 * you need to explicitly assign it to a scope key in your setup, either through 033 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope} 034 * or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean. 035 * 036 * @author Juergen Hoeller 037 * @since 4.2 038 * @see org.springframework.context.support.SimpleThreadScope 039 * @see org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope 040 * @see org.springframework.beans.factory.config.CustomScopeConfigurer 041 */ 042public class SimpleTransactionScope implements Scope { 043 044 @Override 045 public Object get(String name, ObjectFactory<?> objectFactory) { 046 ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); 047 if (scopedObjects == null) { 048 scopedObjects = new ScopedObjectsHolder(); 049 TransactionSynchronizationManager.registerSynchronization(new CleanupSynchronization(scopedObjects)); 050 TransactionSynchronizationManager.bindResource(this, scopedObjects); 051 } 052 Object scopedObject = scopedObjects.scopedInstances.get(name); 053 if (scopedObject == null) { 054 scopedObject = objectFactory.getObject(); 055 scopedObjects.scopedInstances.put(name, scopedObject); 056 } 057 return scopedObject; 058 } 059 060 @Override 061 public Object remove(String name) { 062 ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); 063 if (scopedObjects != null) { 064 scopedObjects.destructionCallbacks.remove(name); 065 return scopedObjects.scopedInstances.remove(name); 066 } 067 else { 068 return null; 069 } 070 } 071 072 @Override 073 public void registerDestructionCallback(String name, Runnable callback) { 074 ScopedObjectsHolder scopedObjects = (ScopedObjectsHolder) TransactionSynchronizationManager.getResource(this); 075 if (scopedObjects != null) { 076 scopedObjects.destructionCallbacks.put(name, callback); 077 } 078 } 079 080 @Override 081 public Object resolveContextualObject(String key) { 082 return null; 083 } 084 085 @Override 086 public String getConversationId() { 087 return TransactionSynchronizationManager.getCurrentTransactionName(); 088 } 089 090 091 static class ScopedObjectsHolder { 092 093 final Map<String, Object> scopedInstances = new HashMap<String, Object>(); 094 095 final Map<String, Runnable> destructionCallbacks = new LinkedHashMap<String, Runnable>(); 096 } 097 098 099 private class CleanupSynchronization extends TransactionSynchronizationAdapter { 100 101 private final ScopedObjectsHolder scopedObjects; 102 103 public CleanupSynchronization(ScopedObjectsHolder scopedObjects) { 104 this.scopedObjects = scopedObjects; 105 } 106 107 @Override 108 public void suspend() { 109 TransactionSynchronizationManager.unbindResource(SimpleTransactionScope.this); 110 } 111 112 @Override 113 public void resume() { 114 TransactionSynchronizationManager.bindResource(SimpleTransactionScope.this, this.scopedObjects); 115 } 116 117 @Override 118 public void afterCompletion(int status) { 119 TransactionSynchronizationManager.unbindResourceIfPossible(SimpleTransactionScope.this); 120 for (Runnable callback : this.scopedObjects.destructionCallbacks.values()) { 121 callback.run(); 122 } 123 this.scopedObjects.destructionCallbacks.clear(); 124 this.scopedObjects.scopedInstances.clear(); 125 } 126 } 127 128}