Merge branch 'master' into websocket-stomp

This commit is contained in:
Rossen Stoyanchev
2013-06-27 15:58:12 -04:00
307 changed files with 8375 additions and 4414 deletions

View File

@@ -42,14 +42,18 @@ import org.springframework.util.CollectionUtils;
public abstract class AbstractCachingConfiguration implements ImportAware {
protected AnnotationAttributes enableCaching;
protected CacheManager cacheManager;
protected KeyGenerator keyGenerator;
@Autowired(required=false)
private Collection<CacheManager> cacheManagerBeans;
@Autowired(required=false)
private Collection<CachingConfigurer> cachingConfigurers;
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
this.enableCaching = AnnotationAttributes.fromMap(
@@ -59,6 +63,7 @@ public abstract class AbstractCachingConfiguration implements ImportAware {
importMetadata.getClassName());
}
/**
* Determine which {@code CacheManager} bean to use. Prefer the result of
* {@link CachingConfigurer#cacheManager()} over any by-type matching. If none, fall

View File

@@ -120,9 +120,9 @@ import org.springframework.core.Ordered;
* customizing the strategy for cache key generation, per Spring's {@link
* org.springframework.cache.interceptor.KeyGenerator KeyGenerator} SPI. Normally,
* {@code @EnableCaching} will configure Spring's
* {@link org.springframework.cache.interceptor.DefaultKeyGenerator DefaultKeyGenerator}
* {@link org.springframework.cache.interceptor.SimpleKeyGenerator SimpleKeyGenerator}
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
* must be provided explicitly. Return {@code new DefaultKeyGenerator()} from this method
* must be provided explicitly. Return {@code new SimpleKeyGenerator()} from this method
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
* details.
*

View File

@@ -48,69 +48,17 @@ import org.w3c.dom.Element;
*/
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
/**
* Simple, reusable class used for overriding defaults.
*
* @author Costin Leau
*/
private static class Props {
private String key;
private String condition;
private String method;
private String[] caches = null;
Props(Element root) {
String defaultCache = root.getAttribute("cache");
key = root.getAttribute("key");
condition = root.getAttribute("condition");
method = root.getAttribute(METHOD_ATTRIBUTE);
if (StringUtils.hasText(defaultCache)) {
caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
}
}
<T extends CacheOperation> T merge(Element element, ReaderContext readerCtx, T op) {
String cache = element.getAttribute("cache");
// sanity check
String[] localCaches = caches;
if (StringUtils.hasText(cache)) {
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
} else {
if (caches == null) {
readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
}
}
op.setCacheNames(localCaches);
op.setKey(getAttributeValue(element, "key", this.key));
op.setCondition(getAttributeValue(element, "condition", this.condition));
return op;
}
String merge(Element element, ReaderContext readerCtx) {
String m = element.getAttribute(METHOD_ATTRIBUTE);
if (StringUtils.hasText(m)) {
return m.trim();
}
if (StringUtils.hasText(method)) {
return method;
}
readerCtx.error("No method specified for " + element.getNodeName(), element);
return null;
}
}
private static final String CACHEABLE_ELEMENT = "cacheable";
private static final String CACHE_EVICT_ELEMENT = "cache-evict";
private static final String CACHE_PUT_ELEMENT = "cache-put";
private static final String METHOD_ATTRIBUTE = "method";
private static final String DEFS_ELEMENT = "caching";
@Override
protected Class<?> getBeanClass(Element element) {
return CacheInterceptor.class;
@@ -226,4 +174,66 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
return defaultValue;
}
/**
* Simple, reusable class used for overriding defaults.
*
* @author Costin Leau
*/
private static class Props {
private String key;
private String condition;
private String method;
private String[] caches = null;
Props(Element root) {
String defaultCache = root.getAttribute("cache");
key = root.getAttribute("key");
condition = root.getAttribute("condition");
method = root.getAttribute(METHOD_ATTRIBUTE);
if (StringUtils.hasText(defaultCache)) {
caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
}
}
<T extends CacheOperation> T merge(Element element, ReaderContext readerCtx, T op) {
String cache = element.getAttribute("cache");
// sanity check
String[] localCaches = caches;
if (StringUtils.hasText(cache)) {
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
} else {
if (caches == null) {
readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
}
}
op.setCacheNames(localCaches);
op.setKey(getAttributeValue(element, "key", this.key));
op.setCondition(getAttributeValue(element, "condition", this.condition));
return op;
}
String merge(Element element, ReaderContext readerCtx) {
String m = element.getAttribute(METHOD_ATTRIBUTE);
if (StringUtils.hasText(m)) {
return m.trim();
}
if (StringUtils.hasText(method)) {
return method;
}
readerCtx.error("No method specified for " + element.getNodeName(), element);
return null;
}
}
}

View File

@@ -35,6 +35,7 @@ import org.w3c.dom.Element;
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
static String extractCacheManager(Element element) {

View File

@@ -19,8 +19,8 @@ package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -28,11 +28,15 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.expression.EvaluationContext;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -61,23 +65,212 @@ import org.springframework.util.StringUtils;
*/
public abstract class CacheAspectSupport implements InitializingBean {
public interface Invoker {
Object invoke();
}
protected final Log logger = LogFactory.getLog(getClass());
private CacheManager cacheManager;
private CacheOperationSource cacheOperationSource;
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
private KeyGenerator keyGenerator = new DefaultKeyGenerator();
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
private boolean initialized = false;
private static final String CACHEABLE = "cacheable", UPDATE = "cacheupdate", EVICT = "cacheevict";
@Override
public void afterPropertiesSet() {
Assert.state(this.cacheManager != null, "'cacheManager' is required");
Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSources' "
+ "property is required: If there are no cacheable methods, "
+ "then don't use a cache aspect.");
this.initialized = true;
}
/**
* Convenience method to return a String representation of this Method
* for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method.
* @param method the method we're interested in
* @param targetClass class the method is on
* @return log message identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/
protected String methodIdentification(Method method, Class<?> targetClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
return ClassUtils.getQualifiedMethodName(specificMethod);
}
protected Collection<Cache> getCaches(CacheOperation operation) {
Set<String> cacheNames = operation.getCacheNames();
Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache cache = this.cacheManager.getCache(cacheName);
Assert.notNull(cache, "Cannot find cache named [" + cacheName + "] for " + operation);
caches.add(cache);
}
return caches;
}
protected CacheOperationContext getOperationContext(CacheOperation operation,
Method method, Object[] args, Object target, Class<?> targetClass) {
return new CacheOperationContext(operation, method, args, target, targetClass);
}
protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (this.initialized) {
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().
getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, new CacheOperationContexts(operations,
method, args, target, targetClass));
}
}
return invoker.invoke();
}
private Class<?> getTargetClass(Object target) {
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
if (targetClass == null && target != null) {
targetClass = target.getClass();
}
return targetClass;
}
private Object execute(Invoker invoker, CacheOperationContexts contexts) {
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);
// Collect puts from any @Cachable miss
List<CachePutRequest> cachePutRequests = new ArrayList<CachePutRequest>();
collectPutRequests(contexts.get(CacheableOperation.class),
ExpressionEvaluator.NO_RESULT, cachePutRequests, true);
ValueWrapper result = null;
// We only attempt to get a cached result if there are no put requests
if(cachePutRequests.isEmpty() && contexts.get(CachePutOperation.class).isEmpty()) {
result = findCachedResult(contexts.get(CacheableOperation.class));
}
// Invoke the method if don't have a cache hit
if(result == null) {
result = new SimpleValueWrapper(invoker.invoke());
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.class), result.get(),
cachePutRequests, false);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for (CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(result.get());
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get());
return result.get();
}
private void processCacheEvicts(Collection<CacheOperationContext> contexts,
boolean beforeInvocation, Object result) {
for (CacheOperationContext context : contexts) {
CacheEvictOperation operation = (CacheEvictOperation) context.operation;
if (beforeInvocation == operation.isBeforeInvocation() &&
isConditionPassing(context, result)) {
performCacheEvict(context, operation, result);
}
}
}
private void performCacheEvict(CacheOperationContext context,
CacheEvictOperation operation, Object result) {
Object key = null;
for (Cache cache : context.getCaches()) {
if (operation.isCacheWide()) {
logInvalidating(context, operation, null);
cache.clear();
} else {
if(key == null) {
key = context.generateKey(result);
}
logInvalidating(context, operation, key);
cache.evict(key);
}
}
}
private void logInvalidating(CacheOperationContext context,
CacheEvictOperation operation, Object key) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Invalidating " +
(key == null ? "entire cache" : "cache key " + key) +
" for operation " + operation + " on method " + context.method);
}
}
private void collectPutRequests(Collection<CacheOperationContext> contexts,
Object result, Collection<CachePutRequest> putRequests, boolean whenNotInCache) {
for (CacheOperationContext context : contexts) {
if (isConditionPassing(context, result)) {
Object key = generateKey(context, result);
if (!whenNotInCache || findInCaches(context, key) == null) {
putRequests.add(new CachePutRequest(context, key));
}
}
}
}
private Cache.ValueWrapper findCachedResult(Collection<CacheOperationContext> contexts) {
ValueWrapper result = null;
for (CacheOperationContext context : contexts) {
if (isConditionPassing(context, ExpressionEvaluator.NO_RESULT)) {
if(result == null) {
result = findInCaches(context,
generateKey(context, ExpressionEvaluator.NO_RESULT));
}
}
}
return result;
}
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
for (Cache cache : context.getCaches()) {
Cache.ValueWrapper wrapper = cache.get(key);
if (wrapper != null) {
return wrapper;
}
}
return null;
}
private boolean isConditionPassing(CacheOperationContext context, Object result) {
boolean passing = context.isConditionPassing(result);
if(!passing && this.logger.isTraceEnabled()) {
this.logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
}
return passing;
}
private Object generateKey(CacheOperationContext context, Object result) {
Object key = context.generateKey(result);
Assert.notNull(key, "Null key returned for cache operation (maybe you "
+ "are using named params on classes without debug info?) "
+ context.operation);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Computed cache key " + key + " for operation " + context.operation);
}
return key;
}
/**
* Set the CacheManager that this cache aspect should delegate to.
@@ -116,7 +309,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
/**
* Set the KeyGenerator for this cache aspect.
* Default is {@link DefaultKeyGenerator}.
* Default is {@link SimpleKeyGenerator}.
*/
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
@@ -129,306 +322,30 @@ public abstract class CacheAspectSupport implements InitializingBean {
return this.keyGenerator;
}
@Override
public void afterPropertiesSet() {
if (this.cacheManager == null) {
throw new IllegalStateException("'cacheManager' is required");
}
if (this.cacheOperationSource == null) {
throw new IllegalStateException("The 'cacheOperationSources' property is required: "
+ "If there are no cacheable methods, then don't use a cache aspect.");
}
this.initialized = true;
public interface Invoker {
Object invoke();
}
/**
* Convenience method to return a String representation of this Method
* for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method.
* @param method the method we're interested in
* @param targetClass class the method is on
* @return log message identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/
protected String methodIdentification(Method method, Class<?> targetClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
return ClassUtils.getQualifiedMethodName(specificMethod);
}
protected Collection<Cache> getCaches(CacheOperation operation) {
Set<String> cacheNames = operation.getCacheNames();
Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
for (String cacheName : cacheNames) {
Cache cache = this.cacheManager.getCache(cacheName);
if (cache == null) {
throw new IllegalArgumentException("Cannot find cache named [" + cacheName + "] for " + operation);
}
caches.add(cache);
}
return caches;
}
private class CacheOperationContexts {
protected CacheOperationContext getOperationContext(CacheOperation operation, Method method, Object[] args,
Object target, Class<?> targetClass) {
private final MultiValueMap<Class<? extends CacheOperation>, CacheOperationContext> contexts =
new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();
return new CacheOperationContext(operation, method, args, target, targetClass);
}
protected Object execute(Invoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if (!this.initialized) {
return invoker.invoke();
}
// get backing class
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target);
if (targetClass == null && target != null) {
targetClass = target.getClass();
}
final Collection<CacheOperation> cacheOp = getCacheOperationSource().getCacheOperations(method, targetClass);
// analyze caching information
if (!CollectionUtils.isEmpty(cacheOp)) {
Map<String, Collection<CacheOperationContext>> ops = createOperationContext(cacheOp, method, args, target, targetClass);
// start with evictions
inspectBeforeCacheEvicts(ops.get(EVICT));
// follow up with cacheable
CacheStatus status = inspectCacheables(ops.get(CACHEABLE));
Object retVal = null;
Map<CacheOperationContext, Object> updates = inspectCacheUpdates(ops.get(UPDATE));
if (status != null) {
if (status.updateRequired) {
updates.putAll(status.cUpdates);
}
// return cached object
else {
return status.retVal;
}
}
retVal = invoker.invoke();
inspectAfterCacheEvicts(ops.get(EVICT), retVal);
if (!updates.isEmpty()) {
update(updates, retVal);
}
return retVal;
}
return invoker.invoke();
}
private void inspectBeforeCacheEvicts(Collection<CacheOperationContext> evictions) {
inspectCacheEvicts(evictions, true, ExpressionEvaluator.NO_RESULT);
}
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions,
Object result) {
inspectCacheEvicts(evictions, false, result);
}
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions,
boolean beforeInvocation, Object result) {
if (!evictions.isEmpty()) {
boolean log = logger.isTraceEnabled();
for (CacheOperationContext context : evictions) {
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
if (beforeInvocation == evictOp.isBeforeInvocation()) {
if (context.isConditionPassing(result)) {
// for each cache
// lazy key initialization
Object key = null;
for (Cache cache : context.getCaches()) {
// cache-wide flush
if (evictOp.isCacheWide()) {
cache.clear();
if (log) {
logger.trace("Invalidating entire cache for operation " + evictOp + " on method " + context.method);
}
} else {
// check key
if (key == null) {
key = context.generateKey();
}
if (log) {
logger.trace("Invalidating cache key " + key + " for operation " + evictOp + " on method " + context.method);
}
cache.evict(key);
}
}
} else {
if (log) {
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
}
}
}
}
}
}
private CacheStatus inspectCacheables(Collection<CacheOperationContext> cacheables) {
Map<CacheOperationContext, Object> cUpdates = new LinkedHashMap<CacheOperationContext, Object>(cacheables.size());
boolean updateRequired = false;
Object retVal = null;
if (!cacheables.isEmpty()) {
boolean log = logger.isTraceEnabled();
boolean atLeastOnePassed = false;
for (CacheOperationContext context : cacheables) {
if (context.isConditionPassing()) {
atLeastOnePassed = true;
Object key = context.generateKey();
if (log) {
logger.trace("Computed cache key " + key + " for operation " + context.operation);
}
if (key == null) {
throw new IllegalArgumentException(
"Null key returned for cache operation (maybe you are using named params on classes without debug info?) "
+ context.operation);
}
// add op/key (in case an update is discovered later on)
cUpdates.put(context, key);
boolean localCacheHit = false;
// check whether the cache needs to be inspected or not (the method will be invoked anyway)
if (!updateRequired) {
for (Cache cache : context.getCaches()) {
Cache.ValueWrapper wrapper = cache.get(key);
if (wrapper != null) {
retVal = wrapper.get();
localCacheHit = true;
break;
}
}
}
if (!localCacheHit) {
updateRequired = true;
}
}
else {
if (log) {
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
}
}
}
// return a status only if at least on cacheable matched
if (atLeastOnePassed) {
return new CacheStatus(cUpdates, updateRequired, retVal);
public CacheOperationContexts(Collection<? extends CacheOperation> operations,
Method method, Object[] args, Object target, Class<?> targetClass) {
for (CacheOperation operation : operations) {
this.contexts.add(operation.getClass(), new CacheOperationContext(operation,
method, args, target, targetClass));
}
}
return null;
}
private static class CacheStatus {
// caches/key
final Map<CacheOperationContext, Object> cUpdates;
final boolean updateRequired;
final Object retVal;
CacheStatus(Map<CacheOperationContext, Object> cUpdates, boolean updateRequired, Object retVal) {
this.cUpdates = cUpdates;
this.updateRequired = updateRequired;
this.retVal = retVal;
public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
return this.contexts.getOrDefault(operationClass, Collections.<CacheOperationContext> emptyList());
}
}
private Map<CacheOperationContext, Object> inspectCacheUpdates(Collection<CacheOperationContext> updates) {
Map<CacheOperationContext, Object> cUpdates = new LinkedHashMap<CacheOperationContext, Object>(updates.size());
if (!updates.isEmpty()) {
boolean log = logger.isTraceEnabled();
for (CacheOperationContext context : updates) {
if (context.isConditionPassing()) {
Object key = context.generateKey();
if (log) {
logger.trace("Computed cache key " + key + " for operation " + context.operation);
}
if (key == null) {
throw new IllegalArgumentException(
"Null key returned for cache operation (maybe you are using named params on classes without debug info?) "
+ context.operation);
}
// add op/key (in case an update is discovered later on)
cUpdates.put(context, key);
}
else {
if (log) {
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
}
}
}
}
return cUpdates;
}
private void update(Map<CacheOperationContext, Object> updates, Object retVal) {
for (Map.Entry<CacheOperationContext, Object> entry : updates.entrySet()) {
CacheOperationContext operationContext = entry.getKey();
if(operationContext.canPutToCache(retVal)) {
for (Cache cache : operationContext.getCaches()) {
cache.put(entry.getValue(), retVal);
}
}
}
}
private Map<String, Collection<CacheOperationContext>> createOperationContext(Collection<CacheOperation> cacheOp,
Method method, Object[] args, Object target, Class<?> targetClass) {
Map<String, Collection<CacheOperationContext>> map = new LinkedHashMap<String, Collection<CacheOperationContext>>(3);
Collection<CacheOperationContext> cacheables = new ArrayList<CacheOperationContext>();
Collection<CacheOperationContext> evicts = new ArrayList<CacheOperationContext>();
Collection<CacheOperationContext> updates = new ArrayList<CacheOperationContext>();
for (CacheOperation cacheOperation : cacheOp) {
CacheOperationContext opContext = getOperationContext(cacheOperation, method, args, target, targetClass);
if (cacheOperation instanceof CacheableOperation) {
cacheables.add(opContext);
}
if (cacheOperation instanceof CacheEvictOperation) {
evicts.add(opContext);
}
if (cacheOperation instanceof CachePutOperation) {
updates.add(opContext);
}
}
map.put(CACHEABLE, cacheables);
map.put(EVICT, evicts);
map.put(UPDATE, updates);
return map;
}
protected class CacheOperationContext {
@@ -444,6 +361,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
private final Collection<Cache> caches;
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
this.operation = operation;
this.method = method;
@@ -453,14 +371,10 @@ public abstract class CacheAspectSupport implements InitializingBean {
this.caches = CacheAspectSupport.this.getCaches(operation);
}
protected boolean isConditionPassing() {
return isConditionPassing(ExpressionEvaluator.NO_RESULT);
}
protected boolean isConditionPassing(Object result) {
if (StringUtils.hasText(this.operation.getCondition())) {
EvaluationContext evaluationContext = createEvaluationContext(result);
return evaluator.condition(this.operation.getCondition(), this.method,
return CacheAspectSupport.this.evaluator.condition(this.operation.getCondition(), this.method,
evaluationContext);
}
return true;
@@ -476,7 +390,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
}
if(StringUtils.hasText(unless)) {
EvaluationContext evaluationContext = createEvaluationContext(value);
return !evaluator.unless(unless, this.method, evaluationContext);
return !CacheAspectSupport.this.evaluator.unless(unless, this.method, evaluationContext);
}
return true;
}
@@ -485,16 +399,16 @@ public abstract class CacheAspectSupport implements InitializingBean {
* Computes the key for the given caching operation.
* @return generated key (null if none can be generated)
*/
protected Object generateKey() {
protected Object generateKey(Object result) {
if (StringUtils.hasText(this.operation.getKey())) {
EvaluationContext evaluationContext = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
return evaluator.key(this.operation.getKey(), this.method, evaluationContext);
EvaluationContext evaluationContext = createEvaluationContext(result);
return CacheAspectSupport.this.evaluator.key(this.operation.getKey(), this.method, evaluationContext);
}
return keyGenerator.generate(this.target, this.method, this.args);
return CacheAspectSupport.this.keyGenerator.generate(this.target, this.method, this.args);
}
private EvaluationContext createEvaluationContext(Object result) {
return evaluator.createEvaluationContext(this.caches, this.method, this.args,
return CacheAspectSupport.this.evaluator.createEvaluationContext(this.caches, this.method, this.args,
this.target, this.targetClass, result);
}
@@ -502,4 +416,25 @@ public abstract class CacheAspectSupport implements InitializingBean {
return this.caches;
}
}
private static class CachePutRequest {
private final CacheOperationContext context;
private final Object key;
public CachePutRequest(CacheOperationContext context, Object key) {
this.context = context;
this.key = key;
}
public void apply(Object result) {
if(this.context.canPutToCache(result)) {
for (Cache cache : this.context.getCaches()) {
cache.put(this.key, result);
}
}
}
}
}

View File

@@ -25,6 +25,7 @@ package org.springframework.cache.interceptor;
public class CacheEvictOperation extends CacheOperation {
private boolean cacheWide = false;
private boolean beforeInvocation = false;

View File

@@ -41,14 +41,6 @@ import org.aopalliance.intercept.MethodInvocation;
@SuppressWarnings("serial")
public class CacheInterceptor extends CacheAspectSupport implements MethodInterceptor, Serializable {
private static class ThrowableWrapper extends RuntimeException {
private final Throwable original;
ThrowableWrapper(Throwable original) {
this.original = original;
}
}
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
@@ -70,4 +62,14 @@ public class CacheInterceptor extends CacheAspectSupport implements MethodInterc
throw th.original;
}
}
private static class ThrowableWrapper extends RuntimeException {
private final Throwable original;
ThrowableWrapper(Throwable original) {
this.original = original;
}
}
}

View File

@@ -23,15 +23,18 @@ import java.util.Set;
import org.springframework.util.Assert;
/**
* Base class implementing {@link CacheOperation}.
* Base class for cache operations.
*
* @author Costin Leau
*/
public abstract class CacheOperation {
private Set<String> cacheNames = Collections.emptySet();
private String condition = "";
private String key = "";
private String name = "";

View File

@@ -42,8 +42,10 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
private Pointcut pointcut;
/**
* Set a pointcut, i.e a bean that can cause conditional invocation
* of the CacheInterceptor depending on method and attributes passed.
@@ -58,12 +60,11 @@ public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
@Override
protected Object createMainInterceptor() {
this.cachingInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
} else {
if (this.pointcut == null) {
// Rely on default pointcut.
throw new UnsupportedOperationException();
}
return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
}
/**

View File

@@ -35,6 +35,7 @@ public class CompositeCacheOperationSource implements CacheOperationSource, Seri
private final CacheOperationSource[] cacheOperationSources;
/**
* Create a new CompositeCacheOperationSource for the given sources.
* @param cacheOperationSources the CacheOperationSource instances to combine

View File

@@ -27,15 +27,25 @@ import org.springframework.cache.interceptor.KeyGenerator;
* Uses the constant value {@value #NULL_PARAM_KEY} for any
* {@code null} parameters given.
*
* <p>NOTE: As this implementation returns only a hash of the parameters
* it is possible for key collisions to occur. Since Spring 4.0 the
* {@link SimpleKeyGenerator} is used when no explicit key generator
* has been defined. This class remains for applications that do not
* wish to migrate to the {@link SimpleKeyGenerator}.
*
* @author Costin Leau
* @author Chris Beams
* @since 3.1
* @see SimpleKeyGenerator
* @see org.springframework.cache.annotation.CachingConfigurer
*/
public class DefaultKeyGenerator implements KeyGenerator {
public static final int NO_PARAM_KEY = 0;
public static final int NULL_PARAM_KEY = 53;
@Override
public Object generate(Object target, Method method, Object... params) {
if (params.length == 1) {

View File

@@ -42,6 +42,7 @@ class ExpressionEvaluator {
public static final Object NO_RESULT = new Object();
private final SpelExpressionParser parser = new SpelExpressionParser();
// shared param discoverer since it caches data internally

View File

@@ -85,14 +85,14 @@ class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
return;
}
String mKey = toString(this.method);
Method targetMethod = this.methodCache.get(mKey);
String methodKey = toString(this.method);
Method targetMethod = this.methodCache.get(methodKey);
if (targetMethod == null) {
targetMethod = AopUtils.getMostSpecificMethod(this.method, this.targetClass);
if (targetMethod == null) {
targetMethod = this.method;
}
this.methodCache.put(mKey, targetMethod);
this.methodCache.put(methodKey, targetMethod);
}
// save arguments as indexed variables

View File

@@ -42,9 +42,11 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
*/
protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class);
/** Keys are method names; values are TransactionAttributes */
private Map<String, Collection<CacheOperation>> nameMap = new LinkedHashMap<String, Collection<CacheOperation>>();
/**
* Set a name/attribute map, consisting of method names
* (e.g. "myMethod") and CacheOperation instances

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2013 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.cache.interceptor;
import java.io.Serializable;
import java.util.Arrays;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A simple key as returned from the {@link SimpleKeyGenerator}.
*
* @author Phillip Webb
* @since 4.0
* @see SimpleKeyGenerator
*/
public final class SimpleKey implements Serializable {
private static final long serialVersionUID = 1;
public static final SimpleKey EMPTY = new SimpleKey(new Object[] {});
private final Object[] params;
/**
* Create a new {@link SimpleKey} instance.
* @param elements the elements of the key
*/
public SimpleKey(Object[] elements) {
Assert.notNull(elements, "Elements must not be null");
this.params = new Object[elements.length];
System.arraycopy(elements, 0, this.params, 0, elements.length);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && getClass() == obj.getClass()) {
return Arrays.equals(this.params, ((SimpleKey) obj).params);
}
return false;
}
@Override
public int hashCode() {
return Arrays.hashCode(params);
}
@Override
public String toString() {
return "SimpleKey [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2002-2013 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.cache.interceptor;
import java.lang.reflect.Method;
/**
* Simple key generator. Returns the parameter itself if a single non-null value
* is given, otherwise returns a {@link SimpleKey} of the parameters.
*
* <p>Unlike {@link DefaultKeyGenerator}, no collisions will occur with the keys
* generated by this class. The returned {@link SimpleKey} object can be safely
* used with a {@link org.springframework.cache.concurrent.ConcurrentMapCache},
* however, might not be suitable for all {@link org.springframework.cache.Cache}
* implementations.
*
* @author Phillip Webb
* @since 4.0
* @see SimpleKey
* @see DefaultKeyGenerator
* @see org.springframework.cache.annotation.CachingConfigurer
*/
public class SimpleKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object target, Method method, Object... params) {
if(params.length == 0) {
return SimpleKey.EMPTY;
}
if(params.length == 1 && params[0] != null) {
return params[0];
}
return new SimpleKey(params);
}
}

View File

@@ -31,6 +31,7 @@ public class SimpleCacheManager extends AbstractCacheManager {
private Collection<? extends Cache> caches;
/**
* Specify the collection of Cache instances to use for this CacheManager.
*/

View File

@@ -45,12 +45,12 @@ public class AnnotatedBeanDefinitionReader {
private final BeanDefinitionRegistry registry;
private Environment environment;
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
private ConditionEvaluator conditionEvaluator;
/**
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
@@ -79,7 +79,8 @@ public class AnnotatedBeanDefinitionReader {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.environment = environment;
this.conditionEvaluator = new ConditionEvaluator(registry, environment,
null, null, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
@@ -92,12 +93,13 @@ public class AnnotatedBeanDefinitionReader {
/**
* Set the Environment to use when evaluating whether
* {@link Profile @Profile}-annotated component classes should be registered.
* {@link Conditional @Conditional}-annotated component classes should be registered.
* <p>The default is a {@link StandardEnvironment}.
* @see #registerBean(Class, String, Class...)
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
this.conditionEvaluator = new ConditionEvaluator(this.registry, environment,
null, null, null);
}
/**
@@ -133,8 +135,7 @@ public class AnnotatedBeanDefinitionReader {
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
if (ConditionalAnnotationHelper.shouldSkip(abd, this.registry,
this.environment, this.beanNameGenerator)) {
if (conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -117,7 +117,10 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
(metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
annotationType.equals("javax.annotation.ManagedBean") ||
annotationType.equals("javax.inject.Named");
return (isStereotype && attributes != null && attributes.containsKey("value"));
return (isStereotype && attributes != null &&
attributes.containsKey("value") &&
attributes.get("value") instanceof String);
}
/**

View File

@@ -31,6 +31,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils;
@@ -52,7 +53,6 @@ import org.springframework.util.PatternMatchUtils;
* @author Mark Fisher
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @since 2.5
* @see AnnotationConfigApplicationContext#scan
* @see org.springframework.stereotype.Component
@@ -299,10 +299,6 @@ public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateCo
* bean definition has been found for the specified name
*/
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (ConditionalAnnotationHelper.shouldSkip(beanDefinition, getRegistry(),
getEnvironment(), this.beanNameGenerator)) {
return false;
}
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}

View File

@@ -25,12 +25,11 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
@@ -39,7 +38,6 @@ import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -88,6 +86,8 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
private ConditionEvaluator conditionEvaluator;
/**
* Create a ClassPathScanningCandidateComponentProvider with a {@link StandardEnvironment}.
@@ -159,12 +159,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
/**
* Set the Environment to use when resolving placeholders and evaluating
* {@link Profile @Profile}-annotated component classes.
* {@link Conditional @Conditional}-annotated component classes.
* <p>The default is a {@link StandardEnvironment}
* @param environment the Environment to use
*/
public void setEnvironment(Environment environment) {
this.environment = environment;
this.conditionEvaluator = null;
}
@Override
@@ -172,6 +173,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
return this.environment;
}
/**
* Returns the {@link BeanDefinitionRegistry} used by this scanner or {@code null}.
*/
protected BeanDefinitionRegistry getRegistry() {
return null;
}
/**
* Set the resource pattern to use when scanning the classpath.
* This value will be appended to each base package name.
@@ -333,17 +341,26 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
if (!metadata.isAnnotated(Profile.class.getName())) {
return true;
}
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
return this.environment.acceptsProfiles(profile.getStringArray("value"));
return isConditionMatch(metadataReader);
}
}
return false;
}
/**
* Determine whether the given class is a candidate component based on any
* {@code @Conditional} annotations.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
private boolean isConditionMatch(MetadataReader metadataReader) {
if (this.conditionEvaluator == null) {
this.conditionEvaluator = new ConditionEvaluator(getRegistry(),
getEnvironment(), null, null, getResourceLoader());
}
return !conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}
/**
* Determine whether the given bean definition qualifies as candidate.
* <p>The default implementation checks whether the class is concrete

View File

@@ -25,15 +25,18 @@ import org.springframework.core.type.AnnotationMetadata;
* A single {@code condition} that must be {@linkplain #matches matched} in order
* for a component to be registered.
*
* <p>Conditions are checked immediately before a component bean-definition is due to be
* registered and are free to veto registration based on any criteria that can be
* determined at that point.
* <p>Conditions are checked immediately before the bean-definition is due to be
* registered and are free to veto registration based on any criteria that can
* be determined at that point.
*
* <p>Conditions must follow the same restrictions as {@link BeanFactoryPostProcessor}
* and take care to never interact with bean instances.
* and take care to never interact with bean instances. For more fine-grained control
* of conditions that interact with {@code @Configuration} beans consider the
* {@link ConfigurationCondition} interface.
*
* @author Phillip Webb
* @since 4.0
* @see ConfigurationCondition
* @see Conditional
* @see ConditionContext
*/

View File

@@ -18,6 +18,7 @@ package org.springframework.context.annotation;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
@@ -31,8 +32,8 @@ public interface ConditionContext {
/**
* Returns the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match.
* @return the registry (never {@code null})
* should the condition match or {@code null} if the registry is not available.
* @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry();
@@ -45,13 +46,11 @@ public interface ConditionContext {
/**
* Returns the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match. If a
* {@link ConfigurableListableBeanFactory} is unavailable an
* {@link IllegalStateException} will be thrown.
* @return the bean factory
* @throws IllegalStateException if the bean factory could not be obtained
* definition should the condition match or {@code null} if the bean factory is
* not available.
* @return the bean factory or {@code null}
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
ConfigurableListableBeanFactory getBeanFactory();
/**
* Returns the {@link ResourceLoader} currently being used or {@code null} if the
@@ -67,4 +66,6 @@ public interface ConditionContext {
*/
ClassLoader getClassLoader();
ApplicationContext getApplicationContext();
}

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2002-2013 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.context.annotation;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* Internal class used to evaluate {@link Conditional} annotations.
*
* @author Phillip Webb
* @since 4.0
*/
class ConditionEvaluator {
private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
private final ConditionContextImpl context;
/**
* Create a new {@link ConditionEvaluator} instance.
*/
public ConditionEvaluator(BeanDefinitionRegistry registry, Environment environment,
ApplicationContext applicationContext, ClassLoader classLoader,
ResourceLoader resourceLoader) {
this.context = new ConditionContextImpl(registry, environment,
applicationContext, classLoader, resourceLoader);
}
/**
* Determine if an item should be skipped based on {@code @Conditional} annotations.
* The {@link ConfigurationPhase} will be deduced from the type of item (i.e. a
* {@code @Configuration} class will be {@link ConfigurationPhase#PARSE_CONFIGURATION})
* @param metadata the meta data
* @return if the item should be skipped
*/
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
return shouldSkip(metadata, null);
}
/**
* Determine if an item should be skipped based on {@code @Conditional} annotations.
* @param metadata the meta data
* @param phase the phase of the call
* @return if the item should be skipped
*/
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(CONDITIONAL_ANNOTATION)) {
return false;
}
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, context.getClassLoader());
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(context, metadata)) {
return true;
}
}
}
}
return false;
}
@SuppressWarnings("unchecked")
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
CONDITIONAL_ANNOTATION, true);
Object values = attributes == null ? null : attributes.get("value");
return (List<String[]>) (values == null ? Collections.emptyList() : values);
}
private Condition getCondition(String conditionClassName,
ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private BeanDefinitionRegistry registry;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ApplicationContext applicationContext;
private ClassLoader classLoader;
private ResourceLoader resourceLoader;
public ConditionContextImpl(BeanDefinitionRegistry registry,
Environment environment, ApplicationContext applicationContext,
ClassLoader classLoader, ResourceLoader resourceLoader) {
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = environment;
this.applicationContext = applicationContext;
this.classLoader = classLoader;
this.resourceLoader = resourceLoader;
}
private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
if (source == null) {
return null;
}
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
else if (source instanceof ConfigurableApplicationContext) {
return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
@Override
public BeanDefinitionRegistry getRegistry() {
if (this.registry != null) {
return this.registry;
}
if(getBeanFactory() != null && getBeanFactory() instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) getBeanFactory();
}
return null;
}
@Override
public Environment getEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (getRegistry() != null && getRegistry() instanceof EnvironmentCapable) {
return ((EnvironmentCapable) getRegistry()).getEnvironment();
}
return null;
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
@Override
public ResourceLoader getResourceLoader() {
if (this.resourceLoader != null) {
return this.resourceLoader;
}
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
@Override
public ClassLoader getClassLoader() {
if (this.classLoader != null) {
return this.classLoader;
}
if (getResourceLoader() != null) {
return getResourceLoader().getClassLoader();
}
return null;
}
@Override
public ApplicationContext getApplicationContext() {
if (this.applicationContext != null) {
return this.applicationContext;
}
if (getRegistry() != null && getRegistry() instanceof ApplicationContext) {
return (ApplicationContext) getRegistry();
}
return null;
}
}
}

View File

@@ -22,11 +22,11 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that a component is is only eligible for registration when all
* Indicates that a component is only eligible for registration when all
* {@linkplain #value() specified conditions} match.
*
* <p>A <em>condition</em> is any state that can be determined programmatically
* immediately before the bean is due to be created (see {@link Condition} for details).
* before the bean definition is due to be registered (see {@link Condition} for details).
*
* <p>The {@code @Conditional} annotation may be used in any of the following ways:
* <ul>

View File

@@ -1,217 +0,0 @@
/*
* Copyright 2002-2013 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.context.annotation;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
/**
* Helper class used to determine if registration should be skipped based due to a
* {@code @Conditional} annotation.
*
* @author Phillip Webb
* @since 4.0
* @see Conditional
*/
abstract class ConditionalAnnotationHelper {
private static final String CONDITIONAL_ANNOTATION = Conditional.class.getName();
public static boolean shouldSkip(BeanDefinition beanDefinition,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanDefinition))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanDefinition), context);
}
return false;
}
public static boolean shouldSkip(BeanMethod beanMethod,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(getMetadata(beanMethod))) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(getMetadata(beanMethod), context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configurationClass,
BeanDefinitionRegistry registry, Environment environment,
BeanNameGenerator beanNameGenerator) {
if (hasCondition(configurationClass)) {
ConditionContextImpl context = new ConditionContextImpl(registry,
environment, beanNameGenerator);
return shouldSkip(configurationClass, context);
}
return false;
}
public static boolean shouldSkip(ConfigurationClass configClass,
ConditionContextImpl context) {
if (configClass == null) {
return false;
}
return shouldSkip(configClass.getMetadata(), context);
}
private static boolean shouldSkip(AnnotatedTypeMetadata metadata,
ConditionContextImpl context) {
if (metadata != null) {
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
if (!getCondition(conditionClass, context.getClassLoader()).matches(
context, metadata)) {
return true;
}
}
}
}
return false;
}
private static AnnotatedTypeMetadata getMetadata(BeanMethod beanMethod) {
return (beanMethod == null ? null : beanMethod.getMetadata());
}
private static AnnotatedTypeMetadata getMetadata(BeanDefinition beanDefinition) {
if (beanDefinition != null && beanDefinition instanceof AnnotatedBeanDefinition) {
return ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
}
return null;
}
private static boolean hasCondition(ConfigurationClass configurationClass) {
if (configurationClass == null) {
return false;
}
return hasCondition(configurationClass.getMetadata())
|| hasCondition(configurationClass.getImportedBy());
}
private static boolean hasCondition(AnnotatedTypeMetadata metadata) {
return (metadata != null) && metadata.isAnnotated(CONDITIONAL_ANNOTATION);
}
@SuppressWarnings("unchecked")
private static List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
CONDITIONAL_ANNOTATION, true);
Object values = attributes == null ? null : attributes.get("value");
return (List<String[]>) (values == null ? Collections.emptyList() : values);
}
private static Condition getCondition(String conditionClassName,
ClassLoader classloader) {
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
classloader);
return (Condition) BeanUtils.instantiateClass(conditionClass);
}
/**
* Implementation of a {@link ConditionContext}.
*/
private static class ConditionContextImpl implements ConditionContext {
private BeanDefinitionRegistry registry;
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
public ConditionContextImpl(BeanDefinitionRegistry registry,
Environment environment, BeanNameGenerator beanNameGenerator) {
Assert.notNull(registry, "Registry must not be null");
this.registry = registry;
this.beanFactory = deduceBeanFactory(registry);
this.environment = environment;
if (this.environment == null) {
this.environment = deduceEnvironment(registry);
}
}
private ConfigurableListableBeanFactory deduceBeanFactory(Object source) {
if (source instanceof ConfigurableListableBeanFactory) {
return (ConfigurableListableBeanFactory) source;
}
else if (source instanceof ConfigurableApplicationContext) {
return deduceBeanFactory(((ConfigurableApplicationContext) source).getBeanFactory());
}
return null;
}
private Environment deduceEnvironment(BeanDefinitionRegistry registry) {
if (registry instanceof EnvironmentCapable) {
return ((EnvironmentCapable) registry).getEnvironment();
}
return null;
}
@Override
public BeanDefinitionRegistry getRegistry() {
return this.registry;
}
@Override
public Environment getEnvironment() {
return this.environment;
}
@Override
public ConfigurableListableBeanFactory getBeanFactory() {
Assert.state(this.beanFactory != null, "Unable to locate the BeanFactory");
return this.beanFactory;
}
@Override
public ResourceLoader getResourceLoader() {
if (registry instanceof ResourceLoader) {
return (ResourceLoader) registry;
}
return null;
}
@Override
public ClassLoader getClassLoader() {
ResourceLoader resourceLoader = getResourceLoader();
return (resourceLoader == null ? null : resourceLoader.getClassLoader());
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -61,6 +62,9 @@ final class ConfigurationClass {
private final Map<String, Class<? extends BeanDefinitionReader>> importedResources =
new LinkedHashMap<String, Class<? extends BeanDefinitionReader>>();
private final Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars =
new LinkedHashSet<ImportBeanDefinitionRegistrar>();
/**
* Create a new {@link ConfigurationClass} with the given name.
@@ -173,11 +177,18 @@ final class ConfigurationClass {
this.importedResources.put(importedResource, readerClass);
}
public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar) {
this.importBeanDefinitionRegistrars.add(registrar);
}
public Set<ImportBeanDefinitionRegistrar> getImportBeanDefinitionRegistrars() {
return Collections.unmodifiableSet(importBeanDefinitionRegistrars);
}
public Map<String, Class<? extends BeanDefinitionReader>> getImportedResources() {
return this.importedResources;
}
public void validate(ProblemReporter problemReporter) {
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
@@ -208,7 +219,6 @@ final class ConfigurationClass {
}
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof ConfigurationClass &&

View File

@@ -16,8 +16,6 @@
package org.springframework.context.annotation;
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
@@ -28,6 +26,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.annotation.Autowire;
@@ -43,6 +42,8 @@ import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
@@ -52,6 +53,8 @@ import org.springframework.core.type.MethodMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
/**
* Reads a given fully-populated set of ConfigurationClass instances, registering bean
* definitions with the given {@link BeanDefinitionRegistry} based on its contents.
@@ -84,15 +87,17 @@ class ConfigurationClassBeanDefinitionReader {
private final BeanNameGenerator importBeanNameGenerator;
private final ConditionEvaluator conditionEvaluator;
/**
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
* to populate the given {@link BeanDefinitionRegistry}.
*/
public ConfigurationClassBeanDefinitionReader(
BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator) {
BeanDefinitionRegistry registry, ApplicationContext applicationContext,
SourceExtractor sourceExtractor, ProblemReporter problemReporter,
MetadataReaderFactory metadataReaderFactory, ResourceLoader resourceLoader,
Environment environment, BeanNameGenerator importBeanNameGenerator) {
this.registry = registry;
this.sourceExtractor = sourceExtractor;
@@ -101,6 +106,8 @@ class ConfigurationClassBeanDefinitionReader {
this.resourceLoader = resourceLoader;
this.environment = environment;
this.importBeanNameGenerator = importBeanNameGenerator;
this.conditionEvaluator = new ConditionEvaluator(registry, environment,
applicationContext, null, resourceLoader);
}
@@ -109,8 +116,9 @@ class ConfigurationClassBeanDefinitionReader {
* based on its contents.
*/
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
for (ConfigurationClass configClass : configurationModel) {
loadBeanDefinitionsForConfigurationClass(configClass);
loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
}
}
@@ -118,7 +126,13 @@ class ConfigurationClassBeanDefinitionReader {
* Read a particular {@link ConfigurationClass}, registering bean definitions for the
* class itself, all its {@link Bean} methods
*/
public void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
removeBeanDefinition(configClass);
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
@@ -126,6 +140,17 @@ class ConfigurationClassBeanDefinitionReader {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getMetadata(), configClass.getImportBeanDefinitionRegistrars());
}
private void removeBeanDefinition(ConfigurationClass configClass) {
if (StringUtils.hasLength(configClass.getBeanName())) {
try {
this.registry.removeBeanDefinition(configClass.getBeanName());
}
catch (NoSuchBeanDefinitionException ex) {
}
}
}
/**
@@ -153,8 +178,8 @@ class ConfigurationClassBeanDefinitionReader {
* with the BeanDefinitionRegistry based on its contents.
*/
private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
if (ConditionalAnnotationHelper.shouldSkip(beanMethod, this.registry,
this.environment, this.importBeanNameGenerator)) {
if (conditionEvaluator.shouldSkip(beanMethod.getMetadata(),
ConfigurationPhase.REGISTER_BEAN)) {
return;
}
ConfigurationClass configClass = beanMethod.getConfigurationClass();
@@ -300,6 +325,14 @@ class ConfigurationClassBeanDefinitionReader {
}
}
private void loadBeanDefinitionsFromRegistrars(
AnnotationMetadata importingClassMetadata,
Set<ImportBeanDefinitionRegistrar> importBeanDefinitionRegistrars) {
for (ImportBeanDefinitionRegistrar registrar : importBeanDefinitionRegistrars) {
registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
}
}
/**
* {@link RootBeanDefinition} marker subclass used to signify that a bean definition
@@ -357,4 +390,32 @@ class ConfigurationClassBeanDefinitionReader {
}
}
/**
* Evaluate {@Code @Conditional} annotations, tracking results and taking into
* account 'imported by'.
*/
private class TrackedConditionEvaluator {
private final Map<ConfigurationClass, Boolean> skipped = new HashMap<ConfigurationClass, Boolean>();
public boolean shouldSkip(ConfigurationClass configClass) {
Boolean skip = this.skipped.get(configClass);
if (skip == null) {
if (configClass.isImported()) {
if (shouldSkip(configClass.getImportedBy())) {
// The config that imported this one was skipped, therefore we are skipped
skip = true;
}
}
if (skip == null) {
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(),
ConfigurationPhase.REGISTER_BEAN);
}
this.skipped.put(configClass, skip);
}
return skip;
}
}
}

View File

@@ -411,7 +411,8 @@ class ConfigurationClassEnhancer {
Field field = ReflectionUtils.findField(enhancedConfigInstance.getClass(), BEAN_FACTORY_FIELD);
Assert.state(field != null, "Unable to find generated bean factory field");
Object beanFactory = ReflectionUtils.getField(field, enhancedConfigInstance);
Assert.isInstanceOf(ConfigurableBeanFactory.class, beanFactory);
Assert.state(beanFactory != null, "The BeanFactory has not been injected into the @Configuration class");
Assert.state(beanFactory instanceof ConfigurableBeanFactory, "The injected BeanFactory is not a ConfigurableBeanFactory");
return (ConfigurableBeanFactory) beanFactory;
}
}

View File

@@ -17,8 +17,7 @@
package org.springframework.context.annotation;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -47,7 +46,10 @@ import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionReader;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.NestedIOException;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
@@ -62,7 +64,6 @@ import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import static org.springframework.context.annotation.MetadataUtils.*;
@@ -75,7 +76,8 @@ import static org.springframework.context.annotation.MetadataUtils.*;
*
* <p>This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering BeanDefinition objects based on the
* content of that model.
* content of that model (with the exception of {@code @ComponentScan} annotations which
* need to be registered immediately).
*
* <p>This ASM-based implementation avoids reflection and eager class loading in order to
* interoperate effectively with lazy class loading in a Spring ApplicationContext.
@@ -107,8 +109,6 @@ class ConfigurationClassParser {
private final BeanDefinitionRegistry registry;
private final BeanNameGenerator beanNameGenerator;
private final ComponentScanAnnotationParser componentScanParser;
private final Set<ConfigurationClass> configurationClasses = new LinkedHashSet<ConfigurationClass>();
@@ -121,6 +121,8 @@ class ConfigurationClassParser {
private final List<DeferredImportSelectorHolder> deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
private final ConditionEvaluator conditionEvaluator;
/**
* Create a new {@link ConfigurationClassParser} instance that will be used
@@ -128,16 +130,18 @@ class ConfigurationClassParser {
*/
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry,
ApplicationContext applicationContext) {
this.metadataReaderFactory = metadataReaderFactory;
this.problemReporter = problemReporter;
this.environment = environment;
this.resourceLoader = resourceLoader;
this.registry = registry;
this.beanNameGenerator = componentScanBeanNameGenerator;
this.componentScanParser = new ComponentScanAnnotationParser(
resourceLoader, environment, componentScanBeanNameGenerator, registry);
this.conditionEvaluator = new ConditionEvaluator(registry, environment,
applicationContext, null, resourceLoader);
}
@@ -165,7 +169,7 @@ class ConfigurationClassParser {
* @param beanName may be null, but if populated represents the bean id
* (assumes that this configuration class was configured via XML)
*/
public void parse(String className, String beanName) throws IOException {
protected final void parse(String className, String beanName) throws IOException {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
processConfigurationClass(new ConfigurationClass(reader, beanName));
}
@@ -175,13 +179,14 @@ class ConfigurationClassParser {
* @param clazz the Class to parse
* @param beanName must not be null (as of Spring 3.1.1)
*/
public void parse(Class<?> clazz, String beanName) throws IOException {
protected final void parse(Class<?> clazz, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
if (ConditionalAnnotationHelper.shouldSkip(configClass, this.registry, this.environment, this.beanNameGenerator)) {
if (conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
@@ -197,56 +202,58 @@ class ConfigurationClassParser {
}
// Recursively process the configuration class and its superclass hierarchy.
AnnotationMetadata metadata = configClass.getMetadata();
SourceClass sourceClass = asSourceClass(configClass);
do {
metadata = doProcessConfigurationClass(configClass, metadata);
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (metadata != null);
while (sourceClass != null);
this.configurationClasses.add(configClass);
}
/**
* @return annotation metadata of superclass, {@code null} if none found or previously processed
* Apply processing and build a complete {@link ConfigurationClass} by reading the
* annotations, members and methods from the source class. This method can be called
* multiple times as relevant sources are discovered.
* @param configClass the configuration class being build
* @param sourceClass a source class
* @return the superclass, {@code null} if none found or previously processed
*/
protected AnnotationMetadata doProcessConfigurationClass(
ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
// recursively process any member (nested) classes first
processMemberClasses(configClass, metadata);
processMemberClasses(configClass, sourceClass);
// process any @PropertySource annotations
AnnotationAttributes propertySource = attributesFor(metadata, org.springframework.context.annotation.PropertySource.class);
AnnotationAttributes propertySource = attributesFor(sourceClass.getMetadata(), org.springframework.context.annotation.PropertySource.class);
if (propertySource != null) {
processPropertySource(propertySource);
}
// process any @ComponentScan annotations
AnnotationAttributes componentScan = attributesFor(metadata, ComponentScan.class);
AnnotationAttributes componentScan = attributesFor(sourceClass.getMetadata(), ComponentScan.class);
if (componentScan != null) {
// the config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, metadata.getClassName());
if (!conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
// check the set of scanned definitions for any further config classes and parse recursively if necessary
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
// process any @Import annotations
Set<Object> imports = new LinkedHashSet<Object>();
Set<Object> visited = new LinkedHashSet<Object>();
collectImports(metadata, imports, visited);
if (!imports.isEmpty()) {
processImport(configClass, imports, true);
}
processImports(configClass, getImports(sourceClass), true);
// process any @ImportResource annotations
if (metadata.isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = attributesFor(metadata, ImportResource.class);
if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
AnnotationAttributes importResource = attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("value");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
@@ -255,34 +262,22 @@ class ConfigurationClassParser {
}
// process individual @Bean methods
Set<MethodMetadata> beanMethods = metadata.getAnnotatedMethods(Bean.class.getName());
Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// process superclass, if any
if (metadata.hasSuperClass()) {
String superclass = metadata.getSuperClassName();
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// superclass found, return its annotation metadata and recurse
if (metadata instanceof StandardAnnotationMetadata) {
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
try {
return sourceClass.getSuperClass();
}
else if (superclass.startsWith("java")) {
// never load core JDK classes via ASM, in particular not java.lang.Object!
try {
return new StandardAnnotationMetadata(
this.resourceLoader.getClassLoader().loadClass(superclass), true);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
else {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
return reader.getAnnotationMetadata();
catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
}
@@ -293,24 +288,13 @@ class ConfigurationClassParser {
/**
* Register member (nested) classes that happen to be configuration classes themselves.
* @param metadata the metadata representation of the containing class
* @param sourceClass the source class to process
* @throws IOException if there is any problem reading metadata from a member class
*/
private void processMemberClasses(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
if (metadata instanceof StandardAnnotationMetadata) {
for (Class<?> memberClass : ((StandardAnnotationMetadata) metadata).getIntrospectedClass().getDeclaredClasses()) {
if (ConfigurationClassUtils.isConfigurationCandidate(new StandardAnnotationMetadata(memberClass))) {
processConfigurationClass(new ConfigurationClass(memberClass, configClass));
}
}
}
else {
for (String memberClassName : metadata.getMemberClassNames()) {
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
if (ConfigurationClassUtils.isConfigurationCandidate(memberClassMetadata)) {
processConfigurationClass(new ConfigurationClass(reader, configClass));
}
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
for (SourceClass memberClass : sourceClass.getMemberClasses()) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())) {
processConfigurationClass(memberClass.asConfigClass(configClass));
}
}
}
@@ -350,59 +334,46 @@ class ConfigurationClassParser {
}
}
/**
* Returns {@code @Import} class, considering all meta-annotations.
*/
private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
collectImports(sourceClass, imports, visited);
return imports;
}
/**
* Recursively collect all declared {@code @Import} values. Unlike most
* meta-annotations it is valid to have several {@code @Import}s declared with
* different values, the usual process or returning values from the first
* meta-annotation on a class is not sufficient.
* <p>For example, it is common for a {@code @Configuration} class to declare direct
* <p>
* For example, it is common for a {@code @Configuration} class to declare direct
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
* annotation.
* @param metadata the metadata representation of the class to search
*
* @param sourceClass the class to search
* @param imports the imports collected so far
* @param visited used to track visited classes to prevent infinite recursion
* @throws IOException if there is any problem reading metadata from the named class
*/
private void collectImports(AnnotationMetadata metadata, Set<Object> imports, Set<Object> visited) throws IOException {
String className = metadata.getClassName();
if (visited.add(className)) {
if (metadata instanceof StandardAnnotationMetadata) {
StandardAnnotationMetadata stdMetadata = (StandardAnnotationMetadata) metadata;
for (Annotation ann : stdMetadata.getIntrospectedClass().getAnnotations()) {
if (!ann.annotationType().getName().startsWith("java") && !(ann instanceof Import)) {
collectImports(new StandardAnnotationMetadata(ann.annotationType()), imports, visited);
}
}
Map<String, Object> attributes = stdMetadata.getAnnotationAttributes(Import.class.getName(), false);
if (attributes != null) {
Class[] value = (Class[]) attributes.get("value");
if (!ObjectUtils.isEmpty(value)) {
imports.addAll(Arrays.asList(value));
}
}
}
else {
for (String annotationType : metadata.getAnnotationTypes()) {
if (!className.startsWith("java") && !className.equals(Import.class.getName())) {
try {
collectImports(
new StandardAnnotationMetadata(this.resourceLoader.getClassLoader().loadClass(annotationType)),
imports, visited);
}
catch (ClassNotFoundException ex) {
//
}
}
}
Map<String, Object> attributes = metadata.getAnnotationAttributes(Import.class.getName(), true);
if (attributes != null) {
String[] value = (String[]) attributes.get("value");
if (!ObjectUtils.isEmpty(value)) {
imports.addAll(Arrays.asList(value));
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports,
Set<SourceClass> visited) throws IOException {
try {
if (visited.add(sourceClass)) {
for (SourceClass annotation : sourceClass.getAnnotations()) {
if(!annotation.getMetadata().getClassName().startsWith("java") && !annotation.isAssignable(Import.class)) {
collectImports(annotation, imports, visited);
}
}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
}
}
catch (ClassNotFoundException ex) {
throw new NestedIOException("Unable to collect imports", ex);
}
}
private void processDeferredImportSelectors() {
@@ -411,16 +382,21 @@ class ConfigurationClassParser {
try {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
processImport(configClass, Arrays.asList(imports), false);
processImports(configClass, asSourceClasses(imports), false);
}
catch (IOException ex) {
catch (Exception ex) {
throw new BeanDefinitionStoreException("Failed to load bean class: ", ex);
}
}
this.deferredImportSelectors.clear();
}
private void processImport(ConfigurationClass configClass, Collection<?> classesToImport, boolean checkForCircularImports) throws IOException {
private void processImports(ConfigurationClass configClass,
Collection<SourceClass> sourceClasses, boolean checkForCircularImports)
throws IOException {
if(sourceClasses.isEmpty()) {
return;
}
if (checkForCircularImports && this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata()));
}
@@ -428,37 +404,33 @@ class ConfigurationClassParser {
this.importStack.push(configClass);
AnnotationMetadata importingClassMetadata = configClass.getMetadata();
try {
for (Object candidate : classesToImport) {
Object candidateToCheck = (candidate instanceof Class ? (Class) candidate :
this.metadataReaderFactory.getMetadataReader((String) candidate));
if (checkAssignability(ImportSelector.class, candidateToCheck)) {
for (SourceClass candidate : sourceClasses) {
if (candidate.isAssignable(ImportSelector.class)) {
// the candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
invokeAwareMethods(selector);
if(selector instanceof DeferredImportSelector) {
this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
}
else {
processImport(configClass, Arrays.asList(selector.selectImports(importingClassMetadata)), false);
String[] importClassNames = selector.selectImports(importingClassMetadata);
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
processImports(configClass, importSourceClasses, false);
}
}
else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) {
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate :
this.resourceLoader.getClassLoader().loadClass((String) candidate));
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
invokeAwareMethods(registrar);
registrar.registerBeanDefinitions(importingClassMetadata, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar);
}
else {
// candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> process it as a @Configuration class
this.importStack.registerImport(importingClassMetadata.getClassName(),
(candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate));
processConfigurationClass((candidateToCheck instanceof Class ?
new ConfigurationClass((Class) candidateToCheck, configClass) :
new ConfigurationClass((MetadataReader) candidateToCheck, configClass)));
candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass));
}
}
}
@@ -471,21 +443,15 @@ class ConfigurationClassParser {
}
}
private boolean checkAssignability(Class<?> clazz, Object candidate) throws IOException {
if (candidate instanceof Class) {
return clazz.isAssignableFrom((Class) candidate);
}
else {
return new AssignableTypeFilter(clazz).match((MetadataReader) candidate, this.metadataReaderFactory);
}
}
/**
* Invoke {@link ResourceLoaderAware}, {@link BeanClassLoaderAware} and
* {@link BeanFactoryAware} contracts if implemented by the given {@code bean}.
*/
private void invokeAwareMethods(Object importStrategyBean) {
if (importStrategyBean instanceof Aware) {
if (importStrategyBean instanceof EnvironmentAware) {
((EnvironmentAware) importStrategyBean).setEnvironment(this.environment);
}
if (importStrategyBean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) importStrategyBean).setResourceLoader(this.resourceLoader);
}
@@ -524,10 +490,68 @@ class ConfigurationClassParser {
return this.importStack;
}
/**
* Factory method to obtain a {@link SourceClass} from a {@link ConfigurationClass}.
*/
public SourceClass asSourceClass(ConfigurationClass configurationClass)
throws IOException {
try {
AnnotationMetadata metadata = configurationClass.getMetadata();
if (metadata instanceof StandardAnnotationMetadata) {
return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());
}
return asSourceClass(configurationClass.getMetadata().getClassName());
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(ex);
}
}
/**
* Factory method to obtain a {@link SourceClass} from a {@link Class}.
*/
public SourceClass asSourceClass(Class<?> classType)
throws ClassNotFoundException, IOException {
try {
// Sanity test that we can read annotations, if not fall back to ASM
classType.getAnnotations();
}
catch (Throwable ex) {
return asSourceClass(classType.getName());
}
return new SourceClass(classType);
}
/**
* Factory method to obtain {@link SourceClass}s from class names.
*/
public Collection<SourceClass> asSourceClasses(String[] classNamess)
throws ClassNotFoundException, IOException {
List<SourceClass> annotatedClasses = new ArrayList<SourceClass>();
for (String className : classNamess) {
annotatedClasses.add(asSourceClass(className));
}
return annotatedClasses;
}
/**
* Factory method to obtain a {@link SourceClass} from a class name.
*/
public SourceClass asSourceClass(String className)
throws ClassNotFoundException, IOException {
if (className.startsWith("java")) {
// Never use ASM for core java types
return new SourceClass(this.resourceLoader.getClassLoader().loadClass(
className));
}
return new SourceClass(this.metadataReaderFactory.getMetadataReader(className));
}
interface ImportRegistry {
String getImportingClassFor(String importedClass);
}
@@ -607,6 +631,151 @@ class ConfigurationClassParser {
}
/**
* Simple wrapper that allows annotated source classes to be dealt with in a uniform
* manor, regardless of how they are loaded.
*/
private class SourceClass {
private final Object source; // Class or MetaDataReader
private final AnnotationMetadata metadata;
private SourceClass(Object source) {
this.source = source;
if (source instanceof Class<?>) {
this.metadata = new StandardAnnotationMetadata((Class<?>) source, true);
}
else {
this.metadata = ((MetadataReader) source).getAnnotationMetadata();
}
}
public Class<?> loadClass() throws ClassNotFoundException {
if(source instanceof Class<?>) {
return (Class<?>) source;
}
String className = ((MetadataReader) source).getClassMetadata().getClassName();
return resourceLoader.getClassLoader().loadClass(className);
}
public boolean isAssignable(Class<?> clazz) throws IOException {
if (source instanceof Class) {
return clazz.isAssignableFrom((Class) source);
}
return new AssignableTypeFilter(clazz).match((MetadataReader) source,
metadataReaderFactory);
}
public ConfigurationClass asConfigClass(ConfigurationClass importedBy)
throws IOException {
if (this.source instanceof Class<?>) {
return new ConfigurationClass((Class<?>) this.source, importedBy);
}
return new ConfigurationClass((MetadataReader) source, importedBy);
}
public Collection<SourceClass> getMemberClasses() throws IOException {
List<SourceClass> members = new ArrayList<SourceClass>();
if (source instanceof Class<?>) {
Class<?> sourceClass = (Class<?>) source;
for (Class<?> declaredClass : sourceClass.getDeclaredClasses()) {
try {
members.add(asSourceClass(declaredClass));
}
catch (ClassNotFoundException e) {
}
}
}
else {
MetadataReader sourceReader = (MetadataReader) source;
for (String memberClassName : sourceReader.getClassMetadata().getMemberClassNames()) {
try {
members.add(asSourceClass(memberClassName));
}
catch (ClassNotFoundException e) {
}
}
}
return members;
}
public SourceClass getSuperClass() throws ClassNotFoundException, IOException {
if (source instanceof Class<?>) {
return asSourceClass(((Class<?>) source).getSuperclass());
}
return asSourceClass(((MetadataReader) source).getClassMetadata().getSuperClassName());
}
public Set<SourceClass> getAnnotations() throws ClassNotFoundException, IOException {
Set<SourceClass> annotations = new LinkedHashSet<SourceClass>();
for(String annotation : getMetadata().getAnnotationTypes()) {
annotations.add(getRelated(annotation));
}
return annotations;
}
public Collection<SourceClass> getAnnotationAttributes(String annotationType,
String attribute) throws ClassNotFoundException, IOException {
Map<String, Object> annotationAttributes = getMetadata().getAnnotationAttributes(
annotationType, true);
if (annotationAttributes == null
|| !annotationAttributes.containsKey(attribute)) {
return Collections.emptySet();
}
String[] classNames = (String[]) annotationAttributes.get(attribute);
Set<SourceClass> rtn = new LinkedHashSet<SourceClass>();
for (String className : classNames) {
rtn.add(getRelated(className));
}
return rtn;
}
private SourceClass getRelated(String className) throws IOException,
ClassNotFoundException {
if (source instanceof Class<?>) {
try {
Class<?> clazz = resourceLoader.getClassLoader().loadClass(className);
return asSourceClass(clazz);
}
catch (ClassNotFoundException ex) {
}
}
return asSourceClass(className);
}
public AnnotationMetadata getMetadata() {
return this.metadata;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (obj instanceof SourceClass) {
return toString().equals(obj.toString());
}
return false;
}
@Override
public String toString() {
return getMetadata().getClassName();
}
}
/**
* {@link Problem} registered upon detection of a circular {@link Import}.
*/

View File

@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedHashMap;
@@ -26,17 +27,19 @@ import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.beans.factory.parsing.FailFastProblemReporter;
import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
@@ -47,8 +50,11 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ConfigurationClassEnhancer.EnhancedConfiguration;
import org.springframework.context.annotation.ConfigurationClassParser.ImportRegistry;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
@@ -86,7 +92,8 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
* @since 3.0
*/
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware, ApplicationContextAware,
Ordered {
private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
@@ -94,6 +101,9 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private static final String IMPORT_REGISTRY_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
private static final String ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME =
ConfigurationClassPostProcessor.class.getName() + ".enhancedConfigurationProcessor";
private final Log logger = LogFactory.getLog(getClass());
@@ -103,6 +113,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
private Environment environment;
private ApplicationContext applicationContext;
private ResourceLoader resourceLoader = new DefaultResourceLoader();
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
@@ -131,6 +143,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
};
/**
* Set the {@link SourceExtractor} to use for generated bean definitions
* that correspond to {@link Bean} factory methods.
@@ -190,6 +203,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
this.environment = environment;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
@@ -214,6 +233,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
@@ -280,7 +303,8 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
this.resourceLoader, this.componentScanBeanNameGenerator, registry,
this.applicationContext);
parser.parse(configCandidates);
parser.validate();
@@ -302,15 +326,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
registry, this.applicationContext, this.sourceExtractor,
this.problemReporter, this.metadataReaderFactory,
this.resourceLoader, this.environment, this.importBeanNameGenerator);
}
for (ConfigurationClass configurationClass : parser.getConfigurationClasses()) {
if (!ConditionalAnnotationHelper.shouldSkip(configurationClass, registry,
this.environment, this.importBeanNameGenerator)) {
reader.loadBeanDefinitionsForConfigurationClass(configurationClass);
}
}
reader.loadBeanDefinitions(parser.getConfigurationClasses());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (singletonRegistry != null) {
@@ -366,6 +387,11 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
private static class ImportAwareBeanPostProcessor implements PriorityOrdered, BeanFactoryAware, BeanPostProcessor {
@@ -410,4 +436,40 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
}
}
/**
* {@link InstantiationAwareBeanPostProcessorAdapter} that ensures
* {@link EnhancedConfiguration} beans are injected with the {@link BeanFactory}
* before the {@link AutowiredAnnotationBeanPostProcessor} runs (SPR-10668).
*/
private static class EnhancedConfigurationBeanPostProcessor extends
InstantiationAwareBeanPostProcessorAdapter implements PriorityOrdered,
BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
// Inject the BeanFactory before AutowiredAnnotationBeanPostProcessor's
// postProcessPropertyValues method attempts to auto-wire other configuration
// beans.
if (bean instanceof EnhancedConfiguration) {
((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
}
return pvs;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -27,7 +27,6 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.stereotype.Component;
/**
* Utilities for processing @{@link Configuration} classes.
@@ -105,7 +104,6 @@ abstract class ConfigurationClassUtils {
return false; // do not consider an interface or an annotation
}
return metadata.isAnnotated(Import.class.getName()) ||
metadata.isAnnotated(Component.class.getName()) ||
metadata.hasAnnotatedMethods(Bean.class.getName());
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2013 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.context.annotation;
/**
* A {@link Condition} that offers more fine-grained control when used with
* {@code @Configuration}. Allows certain {@link Condition}s to adapt when they match
* based on the configuration phase. For example, a condition that checks if a bean has
* already been registered might choose to only be evaluated during the
* {@link ConfigurationPhase#REGISTER_BEAN REGISTER_BEAN} {@link ConfigurationPhase}.
*
* @author Phillip Webb
* @since 4.0
*/
public interface ConfigurationCondition extends Condition {
/**
* Returns the {@link ConfigurationPhase} in which the condition should be evaluated.
*/
ConfigurationPhase getConfigurationPhase();
/**
* The various configuration phases where the condition could be evaluated.
*/
public static enum ConfigurationPhase {
/**
* The {@link Condition} should be evaluated as a {@code @Configuration} class is
* being parsed.
*
* <p>If the condition does not match at this point the {@code @Configuration}
* class will not be added.
*/
PARSE_CONFIGURATION,
/**
* The {@link Condition} should be evaluated when adding a regular (non
* {@code @Configuration}) bean. The condition will not prevent
* {@code @Configuration} classes from being added.
*
* <p>At the time that the condition is evaluated all {@code @Configuration}s
* will have been parsed.
*/
REGISTER_BEAN
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -33,6 +33,7 @@ import org.springframework.core.type.AnnotationMetadata;
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}

View File

@@ -27,6 +27,7 @@ import org.springframework.core.type.AnnotationMetadata;
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #selectImports}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}</li>
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}</li>
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}</li>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2013 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.
@@ -181,7 +181,7 @@ public class RmiRegistryFactoryBean implements FactoryBean<Registry>, Initializi
throws RemoteException {
if (registryHost != null) {
// Host explictly specified: only lookup possible.
// Host explicitly specified: only lookup possible.
if (logger.isInfoEnabled()) {
logger.info("Looking for RMI registry at port '" + registryPort + "' of host [" + registryHost + "]");
}

View File

@@ -83,7 +83,12 @@ public class MethodValidationPostProcessor extends AbstractAdvisingBeanPostProce
* <p>Default is the default ValidatorFactory's default Validator.
*/
public void setValidator(Validator validator) {
this.validator = validator;
if(validator instanceof LocalValidatorFactoryBean) {
this.validator = ((LocalValidatorFactoryBean) validator).getValidator();
}
else {
this.validator = validator;
}
}
/**