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.aop.target;
018
019import java.util.HashSet;
020import java.util.Set;
021
022import org.springframework.aop.IntroductionAdvisor;
023import org.springframework.aop.support.DefaultIntroductionAdvisor;
024import org.springframework.aop.support.DelegatingIntroductionInterceptor;
025import org.springframework.beans.BeansException;
026import org.springframework.beans.factory.DisposableBean;
027import org.springframework.core.NamedThreadLocal;
028
029/**
030 * Alternative to an object pool. This {@link org.springframework.aop.TargetSource}
031 * uses a threading model in which every thread has its own copy of the target.
032 * There's no contention for targets. Target object creation is kept to a minimum
033 * on the running server.
034 *
035 * <p>Application code is written as to a normal pool; callers can't assume they
036 * will be dealing with the same instance in invocations in different threads.
037 * However, state can be relied on during the operations of a single thread:
038 * for example, if one caller makes repeated calls on the AOP proxy.
039 *
040 * <p>Cleanup of thread-bound objects is performed on BeanFactory destruction,
041 * calling their {@code DisposableBean.destroy()} method if available.
042 * Be aware that many thread-bound objects can be around until the application
043 * actually shuts down.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @author Rob Harrop
048 * @see ThreadLocalTargetSourceStats
049 * @see org.springframework.beans.factory.DisposableBean#destroy()
050 */
051@SuppressWarnings("serial")
052public class ThreadLocalTargetSource extends AbstractPrototypeBasedTargetSource
053                implements ThreadLocalTargetSourceStats, DisposableBean {
054
055        /**
056         * ThreadLocal holding the target associated with the current
057         * thread. Unlike most ThreadLocals, which are static, this variable
058         * is meant to be per thread per instance of the ThreadLocalTargetSource class.
059         */
060        private final ThreadLocal<Object> targetInThread =
061                        new NamedThreadLocal<>("Thread-local instance of bean '" + getTargetBeanName() + "'");
062
063        /**
064         * Set of managed targets, enabling us to keep track of the targets we've created.
065         */
066        private final Set<Object> targetSet = new HashSet<>();
067
068        private int invocationCount;
069
070        private int hitCount;
071
072
073        /**
074         * Implementation of abstract getTarget() method.
075         * We look for a target held in a ThreadLocal. If we don't find one,
076         * we create one and bind it to the thread. No synchronization is required.
077         */
078        @Override
079        public Object getTarget() throws BeansException {
080                ++this.invocationCount;
081                Object target = this.targetInThread.get();
082                if (target == null) {
083                        if (logger.isDebugEnabled()) {
084                                logger.debug("No target for prototype '" + getTargetBeanName() + "' bound to thread: " +
085                                                "creating one and binding it to thread '" + Thread.currentThread().getName() + "'");
086                        }
087                        // Associate target with ThreadLocal.
088                        target = newPrototypeInstance();
089                        this.targetInThread.set(target);
090                        synchronized (this.targetSet) {
091                                this.targetSet.add(target);
092                        }
093                }
094                else {
095                        ++this.hitCount;
096                }
097                return target;
098        }
099
100        /**
101         * Dispose of targets if necessary; clear ThreadLocal.
102         * @see #destroyPrototypeInstance
103         */
104        @Override
105        public void destroy() {
106                logger.debug("Destroying ThreadLocalTargetSource bindings");
107                synchronized (this.targetSet) {
108                        for (Object target : this.targetSet) {
109                                destroyPrototypeInstance(target);
110                        }
111                        this.targetSet.clear();
112                }
113                // Clear ThreadLocal, just in case.
114                this.targetInThread.remove();
115        }
116
117
118        @Override
119        public int getInvocationCount() {
120                return this.invocationCount;
121        }
122
123        @Override
124        public int getHitCount() {
125                return this.hitCount;
126        }
127
128        @Override
129        public int getObjectCount() {
130                synchronized (this.targetSet) {
131                        return this.targetSet.size();
132                }
133        }
134
135
136        /**
137         * Return an introduction advisor mixin that allows the AOP proxy to be
138         * cast to ThreadLocalInvokerStats.
139         */
140        public IntroductionAdvisor getStatsMixin() {
141                DelegatingIntroductionInterceptor dii = new DelegatingIntroductionInterceptor(this);
142                return new DefaultIntroductionAdvisor(dii, ThreadLocalTargetSourceStats.class);
143        }
144
145}