001/*
002 * Copyright 2002-2019 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.context.support;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.factory.ObjectFactory;
026import org.springframework.beans.factory.config.Scope;
027import org.springframework.core.NamedThreadLocal;
028
029/**
030 * A simple thread-backed {@link Scope} implementation.
031 *
032 * <p><b>NOTE:</b> This thread scope is not registered by default in common contexts.
033 * Instead, you need to explicitly assign it to a scope key in your setup, either through
034 * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory#registerScope}
035 * or through a {@link org.springframework.beans.factory.config.CustomScopeConfigurer} bean.
036 *
037 * <p>{@code SimpleThreadScope} <em>does not clean up any objects</em> associated with it.
038 * It is therefore typically preferable to use a request-bound scope implementation such
039 * as {@code org.springframework.web.context.request.RequestScope} in web environments,
040 * implementing the full lifecycle for scoped attributes (including reliable destruction).
041 *
042 * <p>For an implementation of a thread-based {@code Scope} with support for destruction
043 * callbacks, refer to
044 * <a href="https://www.springbyexample.org/examples/custom-thread-scope-module.html">Spring by Example</a>.
045 *
046 * <p>Thanks to Eugene Kuleshov for submitting the original prototype for a thread scope!
047 *
048 * @author Arjen Poutsma
049 * @author Juergen Hoeller
050 * @since 3.0
051 * @see org.springframework.web.context.request.RequestScope
052 */
053public class SimpleThreadScope implements Scope {
054
055        private static final Log logger = LogFactory.getLog(SimpleThreadScope.class);
056
057        private final ThreadLocal<Map<String, Object>> threadScope =
058                        new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
059                                @Override
060                                protected Map<String, Object> initialValue() {
061                                        return new HashMap<String, Object>();
062                                }
063                        };
064
065
066        @Override
067        public Object get(String name, ObjectFactory<?> objectFactory) {
068                Map<String, Object> scope = this.threadScope.get();
069                Object scopedObject = scope.get(name);
070                if (scopedObject == null) {
071                        scopedObject = objectFactory.getObject();
072                        scope.put(name, scopedObject);
073                }
074                return scopedObject;
075        }
076
077        @Override
078        public Object remove(String name) {
079                Map<String, Object> scope = this.threadScope.get();
080                return scope.remove(name);
081        }
082
083        @Override
084        public void registerDestructionCallback(String name, Runnable callback) {
085                logger.warn("SimpleThreadScope does not support destruction callbacks. " +
086                                "Consider using RequestScope in a web environment.");
087        }
088
089        @Override
090        public Object resolveContextualObject(String key) {
091                return null;
092        }
093
094        @Override
095        public String getConversationId() {
096                return Thread.currentThread().getName();
097        }
098
099}