/* * Copyright 2002-2014 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.test.context; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.springframework.context.ApplicationContext; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.style.ToStringCreator; import org.springframework.test.annotation.DirtiesContext.HierarchyMode; import org.springframework.util.Assert; /** * Cache for Spring {@link ApplicationContext ApplicationContexts} in a test environment. * *
Maintains a cache of {@code ApplicationContexts} keyed by * {@link MergedContextConfiguration} instances. * *
This has significant performance benefits if initializing the context would take time.
* While initializing a Spring context itself is very quick, some beans in a context, such
* as a {@code LocalSessionFactoryBean} for working with Hibernate, may take some time to
* initialize. Hence it often makes sense to perform that initialization only once per
* test suite.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 2.5
*/
class ContextCache {
private final Object monitor = new Object();
/**
* Map of context keys to Spring {@code ApplicationContext} instances.
*/
private final Map The {@link #getHitCount() hit} and {@link #getMissCount() miss} counts will be
* updated accordingly.
*
* @param key the context key (never {@code null})
* @return the corresponding {@code ApplicationContext} instance, or {@code null} if
* not found in the cache.
* @see #remove
*/
ApplicationContext get(MergedContextConfiguration key) {
Assert.notNull(key, "Key must not be null");
synchronized (monitor) {
ApplicationContext context = this.contextMap.get(key);
if (context == null) {
this.missCount.incrementAndGet();
}
else {
this.hitCount.incrementAndGet();
}
return context;
}
}
/**
* Get the overall hit count for this cache.
* A hit is an access to the cache, which returned a non-null context for
* a queried key.
*/
int getHitCount() {
return this.hitCount.get();
}
/**
* Get the overall miss count for this cache.
* A miss is an access to the cache, which returned a {@code null} context
* for a queried key.
*/
int getMissCount() {
return this.missCount.get();
}
/**
* Explicitly add an {@code ApplicationContext} instance to the cache under the given
* key.
*
* @param key the context key (never {@code null})
* @param context the {@code ApplicationContext} instance (never {@code null})
*/
void put(MergedContextConfiguration key, ApplicationContext context) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(context, "ApplicationContext must not be null");
synchronized (monitor) {
this.contextMap.put(key, context);
MergedContextConfiguration child = key;
MergedContextConfiguration parent = child.getParent();
while (parent != null) {
Set Generally speaking, you would only call this method if you change the
* state of a singleton bean, potentially affecting future interaction with
* the context.
*
* In addition, the semantics of the supplied {@code HierarchyMode} will
* be honored. See the Javadoc for {@link HierarchyMode} for details.
*
* @param key the context key; never {@code null}
* @param hierarchyMode the hierarchy mode; may be {@code null} if the context
* is not part of a hierarchy
*/
void remove(MergedContextConfiguration key, HierarchyMode hierarchyMode) {
Assert.notNull(key, "Key must not be null");
// startKey is the level at which to begin clearing the cache, depending
// on the configured hierarchy mode.
MergedContextConfiguration startKey = key;
if (hierarchyMode == HierarchyMode.EXHAUSTIVE) {
while (startKey.getParent() != null) {
startKey = startKey.getParent();
}
}
synchronized (monitor) {
final List