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}