Rename modules {org.springframework.*=>spring-*}
This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.
Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example
$ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history up until the renaming event, where
$ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java
will show history for all changes to the file, before and after the
renaming.
See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
82
spring-context/src/main/java/org/springframework/cache/Cache.java
vendored
Normal file
82
spring-context/src/main/java/org/springframework/cache/Cache.java
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Interface that defines the common cache operations.
|
||||
*
|
||||
* <b>Note:</b> Due to the generic use of caching, it is recommended that
|
||||
* implementations allow storage of <tt>null</tt> values (for example to
|
||||
* cache methods that return {@code null}).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface Cache {
|
||||
|
||||
/**
|
||||
* Return the cache name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return the the underlying native cache provider.
|
||||
*/
|
||||
Object getNativeCache();
|
||||
|
||||
/**
|
||||
* Return the value to which this cache maps the specified key. Returns
|
||||
* <code>null</code> if the cache contains no mapping for this key.
|
||||
* @param key key whose associated value is to be returned.
|
||||
* @return the value to which this cache maps the specified key,
|
||||
* or <code>null</code> if the cache contains no mapping for this key
|
||||
*/
|
||||
ValueWrapper get(Object key);
|
||||
|
||||
/**
|
||||
* Associate the specified value with the specified key in this cache.
|
||||
* <p>If the cache previously contained a mapping for this key, the old
|
||||
* value is replaced by the specified value.
|
||||
* @param key the key with which the specified value is to be associated
|
||||
* @param value the value to be associated with the specified key
|
||||
*/
|
||||
void put(Object key, Object value);
|
||||
|
||||
/**
|
||||
* Evict the mapping for this key from this cache if it is present.
|
||||
* @param key the key whose mapping is to be removed from the cache
|
||||
*/
|
||||
void evict(Object key);
|
||||
|
||||
/**
|
||||
* Remove all mappings from the cache.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
|
||||
/**
|
||||
* A (wrapper) object representing a cache value.
|
||||
*/
|
||||
interface ValueWrapper {
|
||||
|
||||
/**
|
||||
* Return the actual value in the cache.
|
||||
*/
|
||||
Object get();
|
||||
}
|
||||
|
||||
}
|
||||
42
spring-context/src/main/java/org/springframework/cache/CacheManager.java
vendored
Normal file
42
spring-context/src/main/java/org/springframework/cache/CacheManager.java
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* A manager for a set of {@link Cache}s.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface CacheManager {
|
||||
|
||||
/**
|
||||
* Return the cache associated with the given name.
|
||||
* @param name cache identifier (must not be {@code null})
|
||||
* @return associated cache, or {@code null} if none is found
|
||||
*/
|
||||
Cache getCache(String name);
|
||||
|
||||
/**
|
||||
* Return a collection of the caches known by this cache manager.
|
||||
* @return names of caches known by the cache manager.
|
||||
*/
|
||||
Collection<String> getCacheNames();
|
||||
|
||||
}
|
||||
103
spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
vendored
Normal file
103
spring-context/src/main/java/org/springframework/cache/annotation/AbstractCachingConfiguration.java
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Abstract base {@code @Configuration} class providing common structure for enabling
|
||||
* Spring's annotation-driven cache management capability.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
*/
|
||||
@Configuration
|
||||
public abstract class AbstractCachingConfiguration implements ImportAware {
|
||||
|
||||
/** Parsed annotation metadata for {@code @EnableCaching} on the importing class. */
|
||||
protected Map<String, Object> enableCaching;
|
||||
protected CacheManager cacheManager;
|
||||
protected KeyGenerator keyGenerator;
|
||||
|
||||
@Autowired(required=false)
|
||||
private Collection<CacheManager> cacheManagerBeans;
|
||||
@Autowired(required=false)
|
||||
private Collection<CachingConfigurer> cachingConfigurers;
|
||||
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
this.enableCaching = importMetadata.getAnnotationAttributes(
|
||||
EnableCaching.class.getName(), false);
|
||||
Assert.notNull(this.enableCaching,
|
||||
"@EnableCaching is not present on importing class " +
|
||||
importMetadata.getClassName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which {@code CacheManager} bean to use. Prefer the result of
|
||||
* {@link CachingConfigurer#cacheManager()} over any by-type matching. If none, fall
|
||||
* back to by-type matching on {@code CacheManager}.
|
||||
* @throws IllegalArgumentException if no CacheManager can be found; if more than one
|
||||
* CachingConfigurer implementation exists; if multiple CacheManager beans and no
|
||||
* CachingConfigurer exists to disambiguate.
|
||||
*/
|
||||
@PostConstruct
|
||||
protected void reconcileCacheManager() {
|
||||
if (!CollectionUtils.isEmpty(cachingConfigurers)) {
|
||||
int nConfigurers = cachingConfigurers.size();
|
||||
if (nConfigurers > 1) {
|
||||
throw new IllegalStateException(nConfigurers + " implementations of " +
|
||||
"CachingConfigurer were found when only 1 was expected. " +
|
||||
"Refactor the configuration such that CachingConfigurer is " +
|
||||
"implemented only once or not at all.");
|
||||
}
|
||||
CachingConfigurer cachingConfigurer = cachingConfigurers.iterator().next();
|
||||
this.cacheManager = cachingConfigurer.cacheManager();
|
||||
this.keyGenerator = cachingConfigurer.keyGenerator();
|
||||
}
|
||||
else if (!CollectionUtils.isEmpty(cacheManagerBeans)) {
|
||||
int nManagers = cacheManagerBeans.size();
|
||||
if (nManagers > 1) {
|
||||
throw new IllegalStateException(nManagers + " beans of type CacheManager " +
|
||||
"were found when only 1 was expected. Remove all but one of the " +
|
||||
"CacheManager bean definitions, or implement CachingConfigurer " +
|
||||
"to make explicit which CacheManager should be used for " +
|
||||
"annotation-driven cache management.");
|
||||
}
|
||||
CacheManager cacheManager = cacheManagerBeans.iterator().next();
|
||||
this.cacheManager = cacheManager;
|
||||
// keyGenerator remains null; will fall back to default within CacheInterceptor
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No bean of type CacheManager could be found. " +
|
||||
"Register a CacheManager bean or remove the @EnableCaching annotation " +
|
||||
"from your configuration.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.cache.interceptor.AbstractFallbackCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link org.springframework.cache.interceptor.CacheOperationSource
|
||||
* CacheOperationSource} interface for working with caching metadata in annotation format.
|
||||
*
|
||||
* <p>This class reads Spring's {@link Cacheable}, {@link CachePut} and {@link CacheEvict}
|
||||
* annotations and exposes corresponding caching operation definition to Spring's cache
|
||||
* infrastructure. This class may also serve as base class for a custom
|
||||
* {@code CacheOperationSource}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperationSource
|
||||
implements Serializable {
|
||||
|
||||
private final boolean publicMethodsOnly;
|
||||
|
||||
private final Set<CacheAnnotationParser> annotationParsers;
|
||||
|
||||
|
||||
/**
|
||||
* Create a default AnnotationCacheOperationSource, supporting public methods
|
||||
* that carry the {@code Cacheable} and {@code CacheEvict} annotations.
|
||||
*/
|
||||
public AnnotationCacheOperationSource() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default {@code AnnotationCacheOperationSource}, supporting public methods
|
||||
* that carry the {@code Cacheable} and {@code CacheEvict} annotations.
|
||||
* @param publicMethodsOnly whether to support only annotated public methods
|
||||
* typically for use with proxy-based AOP), or protected/private methods as well
|
||||
* (typically used with AspectJ class weaving)
|
||||
*/
|
||||
public AnnotationCacheOperationSource(boolean publicMethodsOnly) {
|
||||
this.publicMethodsOnly = publicMethodsOnly;
|
||||
this.annotationParsers = new LinkedHashSet<CacheAnnotationParser>(1);
|
||||
this.annotationParsers.add(new SpringCacheAnnotationParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheOperationSource(CacheAnnotationParser... annotationParsers) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
|
||||
Set<CacheAnnotationParser> parsers = new LinkedHashSet<CacheAnnotationParser>(annotationParsers.length);
|
||||
Collections.addAll(parsers, annotationParsers);
|
||||
this.annotationParsers = parsers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
|
||||
return determineCacheOperations(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<CacheOperation> findCacheOperations(Method method) {
|
||||
return determineCacheOperations(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the cache operation(s) for the given method or class.
|
||||
* <p>This implementation delegates to configured
|
||||
* {@link CacheAnnotationParser}s for parsing known annotations into
|
||||
* Spring's metadata attribute class.
|
||||
* <p>Can be overridden to support custom annotations that carry
|
||||
* caching metadata.
|
||||
* @param ae the annotated method or class
|
||||
* @return the configured caching operations, or {@code null} if none found
|
||||
*/
|
||||
protected Collection<CacheOperation> determineCacheOperations(AnnotatedElement ae) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
|
||||
Collection<CacheOperation> annOps = annotationParser.parseCacheAnnotations(ae);
|
||||
if (annOps != null) {
|
||||
if (ops == null) {
|
||||
ops = new ArrayList<CacheOperation>();
|
||||
}
|
||||
ops.addAll(annOps);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, only public methods can be made cacheable.
|
||||
*/
|
||||
@Override
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return this.publicMethodsOnly;
|
||||
}
|
||||
}
|
||||
47
spring-context/src/main/java/org/springframework/cache/annotation/CacheAnnotationParser.java
vendored
Normal file
47
spring-context/src/main/java/org/springframework/cache/annotation/CacheAnnotationParser.java
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
|
||||
/**
|
||||
* Strategy interface for parsing known caching annotation types.
|
||||
* {@link AnnotationCacheOperationSource} delegates to such
|
||||
* parsers for supporting specific annotation types such as Spring's own
|
||||
* {@link Cacheable}, {@link CachePut} or {@link CacheEvict}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface CacheAnnotationParser {
|
||||
|
||||
/**
|
||||
* Parses the cache definition for the given method or class,
|
||||
* based on a known annotation type.
|
||||
* <p>This essentially parses a known cache annotation into Spring's
|
||||
* metadata attribute class. Returns {@code null} if the method/class
|
||||
* is not cacheable.
|
||||
* @param ae the annotated method or class
|
||||
* @return CacheOperation the configured caching operation,
|
||||
* or {@code null} if none was found
|
||||
* @see AnnotationCacheOperationSource#determineCacheOperation
|
||||
*/
|
||||
Collection<CacheOperation> parseCacheAnnotations(AnnotatedElement ae);
|
||||
}
|
||||
72
spring-context/src/main/java/org/springframework/cache/annotation/CacheEvict.java
vendored
Normal file
72
spring-context/src/main/java/org/springframework/cache/annotation/CacheEvict.java
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method (or all methods on a class) trigger(s)
|
||||
* a cache invalidate operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface CacheEvict {
|
||||
|
||||
/**
|
||||
* Qualifier value for the specified cached operation.
|
||||
* <p>May be used to determine the target cache (or caches), matching the qualifier
|
||||
* value (or the bean name(s)) of (a) specific bean definition.
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
|
||||
* <p>Default is "", meaning all method parameters are considered as a key.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||
* <p>Default is "", meaning the method is always cached.
|
||||
*/
|
||||
String condition() default "";
|
||||
|
||||
/**
|
||||
* Whether or not all the entries inside the cache(s) are removed or not. By
|
||||
* default, only the value under the associated key is removed.
|
||||
* <p>Note that specifying setting this parameter to true and specifying a
|
||||
* {@link CacheKey key} is not allowed.
|
||||
*/
|
||||
boolean allEntries() default false;
|
||||
|
||||
/**
|
||||
* Whether the eviction should occur after the method is successfully invoked (default)
|
||||
* or before. The latter causes the eviction to occur irrespective of the method outcome (whether
|
||||
* it threw an exception or not) while the former does not.
|
||||
*/
|
||||
boolean beforeInvocation() default false;
|
||||
}
|
||||
61
spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java
vendored
Normal file
61
spring-context/src/main/java/org/springframework/cache/annotation/CachePut.java
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2011 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method (or all methods on a class) trigger(s)
|
||||
* a {@link Cache#put(Object, Object)} operation. As opposed to {@link Cacheable} annotation,
|
||||
* this annotation does not cause the target method to be skipped - rather it
|
||||
* always causes the method to be invoked and its result to be placed into the cache.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface CachePut {
|
||||
|
||||
/**
|
||||
* Name of the caches in which the update takes place.
|
||||
* <p>May be used to determine the target cache (or caches), matching the
|
||||
* qualifier value (or the bean name(s)) of (a) specific bean definition.
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
|
||||
* <p>Default is "", meaning all method parameters are considered as a key.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the cache update.
|
||||
* <p>Default is "", meaning the method result is always cached.
|
||||
*/
|
||||
String condition() default "";
|
||||
}
|
||||
59
spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java
vendored
Normal file
59
spring-context/src/main/java/org/springframework/cache/annotation/Cacheable.java
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method (or all the methods on a class) can be cached.
|
||||
*
|
||||
* <p>The method arguments and signature are used for computing the key while the
|
||||
* returned instance is used as the cache value.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Cacheable {
|
||||
|
||||
/**
|
||||
* Name of the caches in which the update takes place.
|
||||
* <p>May be used to determine the target cache (or caches), matching the
|
||||
* qualifier value (or the bean name(s)) of (a) specific bean definition.
|
||||
*/
|
||||
String[] value();
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute for computing the key dynamically.
|
||||
* <p>Default is "", meaning all method parameters are considered as a key.
|
||||
*/
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||
* <p>Default is "", meaning the method is always cached.
|
||||
*/
|
||||
String condition() default "";
|
||||
}
|
||||
44
spring-context/src/main/java/org/springframework/cache/annotation/Caching.java
vendored
Normal file
44
spring-context/src/main/java/org/springframework/cache/annotation/Caching.java
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Group annotation for multiple cache annotations (of different or the same type).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Caching {
|
||||
|
||||
Cacheable[] cacheable() default {};
|
||||
|
||||
CachePut[] put() default {};
|
||||
|
||||
CacheEvict[] evict() default {};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AdviceModeImportSelector;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
|
||||
/**
|
||||
* Selects which implementation of {@link AbstractCachingConfiguration} should be used
|
||||
* based on the value of {@link EnableCaching#mode} on the importing {@code @Configuration}
|
||||
* class.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
* @see ProxyCachingConfiguration
|
||||
* @see AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME
|
||||
*/
|
||||
public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return {@link ProxyCachingConfiguration} or {@code AspectJCacheConfiguration} for
|
||||
* {@code PROXY} and {@code ASPECTJ} values of {@link EnableCaching#mode()}, respectively
|
||||
*/
|
||||
public String[] selectImports(AdviceMode adviceMode) {
|
||||
switch (adviceMode) {
|
||||
case PROXY:
|
||||
return new String[] { AutoProxyRegistrar.class.getName(), ProxyCachingConfiguration.class.getName() };
|
||||
case ASPECTJ:
|
||||
return new String[] { AnnotationConfigUtils.CACHE_ASPECT_CONFIGURATION_CLASS_NAME };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
77
spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java
vendored
Normal file
77
spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurer.java
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableCaching} that wish or need to
|
||||
* specify explicitly the {@link CacheManager} and {@link KeyGenerator} beans to be used
|
||||
* for annotation-driven cache management.
|
||||
*
|
||||
* <p>See @{@link EnableCaching} for general examples and context; see
|
||||
* {@link #cacheManager()} and {@link #keyGenerator()} for detailed instructions.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
*/
|
||||
public interface CachingConfigurer {
|
||||
|
||||
/**
|
||||
* Return the cache manager bean to use for annotation-driven cache management.
|
||||
* Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheManager cacheManager() {
|
||||
* // configure and return CacheManager instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See @{@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheManager cacheManager();
|
||||
|
||||
/**
|
||||
* Return the key generator bean to use for annotation-driven cache management.
|
||||
* Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public KeyGenerator keyGenerator() {
|
||||
* // configure and return KeyGenerator instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See @{@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
KeyGenerator keyGenerator();
|
||||
|
||||
}
|
||||
177
spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
vendored
Normal file
177
spring-context/src/main/java/org/springframework/cache/annotation/EnableCaching.java
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* Enables Spring's annotation-driven cache management capability, similar to
|
||||
* the support found in Spring's {@code <cache:*>} XML namespace. To be used together
|
||||
* with @{@link org.springframework.context.annotation.Configuration Configuration}
|
||||
* classes as follows:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public CacheManager cacheManager() {
|
||||
* // configure and return an implementation of Spring's CacheManager SPI
|
||||
* SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
* cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
|
||||
* return cacheManager;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>For reference, the example above can be compared to the following Spring XML
|
||||
* configuration:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* <beans>
|
||||
* <cache:annotation-driven/>
|
||||
* <bean id="myService" class="com.foo.MyService"/>
|
||||
* <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
* <property name="caches">
|
||||
* <set>
|
||||
* <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
* <property name="name" value="default"/>
|
||||
* </bean>
|
||||
* </set>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </beans>
|
||||
* }</pre>
|
||||
* In both of the scenarios above, {@code @EnableCaching} and {@code
|
||||
* <cache:annotation-driven/>} are responsible for registering the necessary Spring
|
||||
* components that power annotation-driven cache management, such as the
|
||||
* {@link org.springframework.cache.interceptor.CacheInterceptor CacheInterceptor} and the
|
||||
* proxy- or AspectJ-based advice that weaves the interceptor into the call stack when
|
||||
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} methods are invoked.
|
||||
*
|
||||
* <p><strong>A bean of type {@link org.springframework.cache.CacheManager CacheManager}
|
||||
* must be registered</strong>, as there is no reasonable default that the framework can
|
||||
* use as a convention. And whereas the {@code <cache:annotation-driven>} element assumes
|
||||
* a bean <em>named</em> "cacheManager", {@code @EnableCaching} searches for a cache
|
||||
* manager bean <em>by type</em>. Therefore, naming of the cache manager bean method is
|
||||
* not significant.
|
||||
*
|
||||
* <p>For those that wish to establish a more direct relationship between
|
||||
* {@code @EnableCaching} and the exact cache manager bean to be used,
|
||||
* the {@link CachingConfigurer} callback interface may be implemented - notice the
|
||||
* {@code implements} clause and the {@code @Override}-annotated methods below:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
* return new MyService();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Override
|
||||
* public CacheManager cacheManager() {
|
||||
* // configure and return an implementation of Spring's CacheManager SPI
|
||||
* SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
* cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
|
||||
* return cacheManager;
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Override
|
||||
* public KeyGenerator keyGenerator() {
|
||||
* // configure and return an implementation of Spring's KeyGenerator SPI
|
||||
* return new MyKeyGenerator();
|
||||
* }
|
||||
* }</pre>
|
||||
* This approach may be desirable simply because it is more explicit, or it may be
|
||||
* necessary in order to distinguish between two {@code CacheManager} beans present in the
|
||||
* same container.
|
||||
*
|
||||
* <p>Notice also the {@code keyGenerator} method in the example above. This allows for
|
||||
* 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}
|
||||
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
|
||||
* must be provided explicitly. Return {@code new DefaultKeyGenerator()} from this method
|
||||
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
|
||||
* details.
|
||||
*
|
||||
* <p>The {@link #mode()} attribute controls how advice is applied; if the mode is
|
||||
* {@link AdviceMode#PROXY} (the default), then the other attributes such as
|
||||
* {@link #proxyTargetClass()} control the behavior of the proxying.
|
||||
*
|
||||
* <p>If the {@linkplain #mode} is set to {@link AdviceMode#ASPECTJ}, then the
|
||||
* {@link #proxyTargetClass()} attribute is obsolete. Note also that in this case the
|
||||
* {@code spring-aspects} module JAR must be present on the classpath.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see CachingConfigurer
|
||||
* @see CachingConfigurationSelector
|
||||
* @see ProxyCachingConfiguration
|
||||
* @see org.springframework.cache.aspectj.AspectJCachingConfiguration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(CachingConfigurationSelector.class)
|
||||
public @interface EnableCaching {
|
||||
|
||||
/**
|
||||
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed
|
||||
* to standard Java interface-based proxies. The default is {@code false}. <strong>
|
||||
* Applicable only if {@link #mode()} is set to {@link AdviceMode#PROXY}</strong>.
|
||||
*
|
||||
* <p>Note that setting this attribute to {@code true} will affect <em>all</em>
|
||||
* Spring-managed beans requiring proxying, not just those marked with
|
||||
* {@code @Cacheable}. For example, other beans marked with Spring's
|
||||
* {@code @Transactional} annotation will be upgraded to subclass proxying at the same
|
||||
* time. This approach has no negative impact in practice unless one is explicitly
|
||||
* expecting one type of proxy vs another, e.g. in tests.
|
||||
*/
|
||||
boolean proxyTargetClass() default false;
|
||||
|
||||
/**
|
||||
* Indicate how caching advice should be applied. The default is
|
||||
* {@link AdviceMode.PROXY}.
|
||||
* @see AdviceMode
|
||||
*/
|
||||
AdviceMode mode() default AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the caching advisor
|
||||
* when multiple advices are applied at a specific joinpoint.
|
||||
* The default is {@link Ordered#LOWEST_PRECEDENCE}.
|
||||
*/
|
||||
int order() default Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
71
spring-context/src/main/java/org/springframework/cache/annotation/ProxyCachingConfiguration.java
vendored
Normal file
71
spring-context/src/main/java/org/springframework/cache/annotation/ProxyCachingConfiguration.java
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.cache.interceptor.CacheOperationSource;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
* {@code @Configuration} class that registers the Spring infrastructure beans necessary
|
||||
* to enable proxy-based annotation-driven cache management.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
* @see CachingConfigurationSelector
|
||||
*/
|
||||
@Configuration
|
||||
public class ProxyCachingConfiguration extends AbstractCachingConfiguration {
|
||||
|
||||
@Bean(name=AnnotationConfigUtils.CACHE_ADVISOR_BEAN_NAME)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public BeanFactoryCacheOperationSourceAdvisor cacheAdvisor() {
|
||||
BeanFactoryCacheOperationSourceAdvisor advisor =
|
||||
new BeanFactoryCacheOperationSourceAdvisor();
|
||||
advisor.setCacheOperationSource(cacheOperationSource());
|
||||
advisor.setAdvice(cacheInterceptor());
|
||||
advisor.setOrder(((Integer)this.enableCaching.get("order")));
|
||||
return advisor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public CacheOperationSource cacheOperationSource() {
|
||||
return new AnnotationCacheOperationSource();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public CacheInterceptor cacheInterceptor() {
|
||||
CacheInterceptor interceptor = new CacheInterceptor();
|
||||
interceptor.setCacheOperationSources(cacheOperationSource());
|
||||
if (this.cacheManager != null) {
|
||||
interceptor.setCacheManager(this.cacheManager);
|
||||
}
|
||||
if (this.keyGenerator != null) {
|
||||
interceptor.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
157
spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java
vendored
Normal file
157
spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.annotation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.cache.interceptor.CachePutOperation;
|
||||
import org.springframework.cache.interceptor.CacheableOperation;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Strategy implementation for parsing Spring's {@link Caching}, {@link Cacheable},
|
||||
* {@link CacheEvict} and {@link CachePut} annotations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
|
||||
|
||||
public Collection<CacheOperation> parseCacheAnnotations(AnnotatedElement ae) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
Collection<Cacheable> cacheables = getAnnotations(ae, Cacheable.class);
|
||||
if (cacheables != null) {
|
||||
ops = lazyInit(ops);
|
||||
for (Cacheable cacheable : cacheables) {
|
||||
ops.add(parseCacheableAnnotation(ae, cacheable));
|
||||
}
|
||||
}
|
||||
Collection<CacheEvict> evicts = getAnnotations(ae, CacheEvict.class);
|
||||
if (evicts != null) {
|
||||
ops = lazyInit(ops);
|
||||
for (CacheEvict e : evicts) {
|
||||
ops.add(parseEvictAnnotation(ae, e));
|
||||
}
|
||||
}
|
||||
Collection<CachePut> updates = getAnnotations(ae, CachePut.class);
|
||||
if (updates != null) {
|
||||
ops = lazyInit(ops);
|
||||
for (CachePut p : updates) {
|
||||
ops.add(parseUpdateAnnotation(ae, p));
|
||||
}
|
||||
}
|
||||
Collection<Caching> caching = getAnnotations(ae, Caching.class);
|
||||
if (caching != null) {
|
||||
ops = lazyInit(ops);
|
||||
for (Caching c : caching) {
|
||||
ops.addAll(parseCachingAnnotation(ae, c));
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
private <T extends Annotation> Collection<CacheOperation> lazyInit(Collection<CacheOperation> ops) {
|
||||
return (ops != null ? ops : new ArrayList<CacheOperation>(1));
|
||||
}
|
||||
|
||||
CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, Cacheable caching) {
|
||||
CacheableOperation cuo = new CacheableOperation();
|
||||
cuo.setCacheNames(caching.value());
|
||||
cuo.setCondition(caching.condition());
|
||||
cuo.setKey(caching.key());
|
||||
cuo.setName(ae.toString());
|
||||
return cuo;
|
||||
}
|
||||
|
||||
CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, CacheEvict caching) {
|
||||
CacheEvictOperation ceo = new CacheEvictOperation();
|
||||
ceo.setCacheNames(caching.value());
|
||||
ceo.setCondition(caching.condition());
|
||||
ceo.setKey(caching.key());
|
||||
ceo.setCacheWide(caching.allEntries());
|
||||
ceo.setBeforeInvocation(caching.beforeInvocation());
|
||||
ceo.setName(ae.toString());
|
||||
return ceo;
|
||||
}
|
||||
|
||||
CacheOperation parseUpdateAnnotation(AnnotatedElement ae, CachePut caching) {
|
||||
CachePutOperation cuo = new CachePutOperation();
|
||||
cuo.setCacheNames(caching.value());
|
||||
cuo.setCondition(caching.condition());
|
||||
cuo.setKey(caching.key());
|
||||
cuo.setName(ae.toString());
|
||||
return cuo;
|
||||
}
|
||||
|
||||
Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, Caching caching) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
Cacheable[] cacheables = caching.cacheable();
|
||||
if (!ObjectUtils.isEmpty(cacheables)) {
|
||||
ops = lazyInit(ops);
|
||||
for (Cacheable cacheable : cacheables) {
|
||||
ops.add(parseCacheableAnnotation(ae, cacheable));
|
||||
}
|
||||
}
|
||||
CacheEvict[] evicts = caching.evict();
|
||||
if (!ObjectUtils.isEmpty(evicts)) {
|
||||
ops = lazyInit(ops);
|
||||
for (CacheEvict evict : evicts) {
|
||||
ops.add(parseEvictAnnotation(ae, evict));
|
||||
}
|
||||
}
|
||||
CachePut[] updates = caching.put();
|
||||
if (!ObjectUtils.isEmpty(updates)) {
|
||||
ops = lazyInit(ops);
|
||||
for (CachePut update : updates) {
|
||||
ops.add(parseUpdateAnnotation(ae, update));
|
||||
}
|
||||
}
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
private static <T extends Annotation> Collection<T> getAnnotations(AnnotatedElement ae, Class<T> annotationType) {
|
||||
Collection<T> anns = new ArrayList<T>(2);
|
||||
|
||||
// look at raw annotation
|
||||
T ann = ae.getAnnotation(annotationType);
|
||||
if (ann != null) {
|
||||
anns.add(ann);
|
||||
}
|
||||
|
||||
// scan meta-annotations
|
||||
for (Annotation metaAnn : ae.getAnnotations()) {
|
||||
ann = metaAnn.annotationType().getAnnotation(annotationType);
|
||||
if (ann != null) {
|
||||
anns.add(ann);
|
||||
}
|
||||
}
|
||||
|
||||
return (anns.isEmpty() ? null : anns);
|
||||
}
|
||||
}
|
||||
8
spring-context/src/main/java/org/springframework/cache/annotation/package-info.java
vendored
Normal file
8
spring-context/src/main/java/org/springframework/cache/annotation/package-info.java
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
* Annotations and supporting classes for declarative cache management.
|
||||
* Hooked into Spring's caching interception infrastructure
|
||||
* via {@link org.springframework.cache.interceptor.CacheOperationSource
|
||||
* CacheOperationSource} implementation.
|
||||
*/
|
||||
package org.springframework.cache.annotation;
|
||||
145
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java
vendored
Normal file
145
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCache.java
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.concurrent;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
|
||||
/**
|
||||
* Simple {@link Cache} implementation based on the core JDK
|
||||
* {@code java.util.concurrent} package.
|
||||
*
|
||||
* <p>Useful for testing or simple caching scenarios, typically in combination
|
||||
* with {@link org.springframework.cache.support.SimpleCacheManager} or
|
||||
* dynamically through {@link ConcurrentMapCacheManager}.
|
||||
*
|
||||
* <p><b>Note:</b> As {@link ConcurrentHashMap} (the default implementation used)
|
||||
* does not allow for {@code null} values to be stored, this class will replace
|
||||
* them with a predefined internal object. This behavior can be changed through the
|
||||
* {@link #ConcurrentMapCache(String, ConcurrentMap, boolean)} constructor.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ConcurrentMapCache implements Cache {
|
||||
|
||||
private static final Object NULL_HOLDER = new NullHolder();
|
||||
|
||||
private final String name;
|
||||
|
||||
private final ConcurrentMap<Object, Object> store;
|
||||
|
||||
private final boolean allowNullValues;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ConcurrentMapCache with the specified name.
|
||||
* @param name the name of the cache
|
||||
*/
|
||||
public ConcurrentMapCache(String name) {
|
||||
this(name, new ConcurrentHashMap<Object, Object>(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConcurrentMapCache with the specified name.
|
||||
* @param name the name of the cache
|
||||
*/
|
||||
public ConcurrentMapCache(String name, boolean allowNullValues) {
|
||||
this(name, new ConcurrentHashMap<Object, Object>(), allowNullValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConcurrentMapCache with the specified name and the
|
||||
* given internal ConcurrentMap to use.
|
||||
* @param name the name of the cache
|
||||
* @param store the ConcurrentMap to use as an internal store
|
||||
* @param allowNullValues whether to allow <code>null</code> values
|
||||
* (adapting them to an internal null holder value)
|
||||
*/
|
||||
public ConcurrentMapCache(String name, ConcurrentMap<Object, Object> store, boolean allowNullValues) {
|
||||
this.name = name;
|
||||
this.store = store;
|
||||
this.allowNullValues = allowNullValues;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public ConcurrentMap getNativeCache() {
|
||||
return this.store;
|
||||
}
|
||||
|
||||
public boolean isAllowNullValues() {
|
||||
return this.allowNullValues;
|
||||
}
|
||||
|
||||
public ValueWrapper get(Object key) {
|
||||
Object value = this.store.get(key);
|
||||
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
|
||||
}
|
||||
|
||||
public void put(Object key, Object value) {
|
||||
this.store.put(key, toStoreValue(value));
|
||||
}
|
||||
|
||||
public void evict(Object key) {
|
||||
this.store.remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the given value from the internal store to a user value
|
||||
* returned from the get method (adapting <code>null</code>).
|
||||
* @param userValue the store value
|
||||
* @return the value to return to the user
|
||||
*/
|
||||
protected Object fromStoreValue(Object storeValue) {
|
||||
if (this.allowNullValues && storeValue == NULL_HOLDER) {
|
||||
return null;
|
||||
}
|
||||
return storeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given user value, as passed into the put method,
|
||||
* to a value in the internal store (adapting <code>null</code>).
|
||||
* @param userValue the given user value
|
||||
* @return the value to store
|
||||
*/
|
||||
protected Object toStoreValue(Object userValue) {
|
||||
if (this.allowNullValues && userValue == null) {
|
||||
return NULL_HOLDER;
|
||||
}
|
||||
return userValue;
|
||||
}
|
||||
|
||||
|
||||
private static class NullHolder implements Serializable {
|
||||
}
|
||||
|
||||
}
|
||||
101
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCacheFactoryBean.java
vendored
Normal file
101
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCacheFactoryBean.java
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.concurrent;
|
||||
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} for easy configuration of a {@link ConcurrentMapCache}
|
||||
* when used within a Spring container. Can be configured through bean properties;
|
||||
* uses the assigned Spring bean name as the default cache name.
|
||||
*
|
||||
* <p>Useful for testing or simple caching scenarios, typically in combination
|
||||
* with {@link org.springframework.cache.support.SimpleCacheManager} or
|
||||
* dynamically through {@link ConcurrentMapCacheManager}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ConcurrentMapCacheFactoryBean
|
||||
implements FactoryBean<ConcurrentMapCache>, BeanNameAware, InitializingBean {
|
||||
|
||||
private String name = "";
|
||||
|
||||
private ConcurrentMap<Object, Object> store;
|
||||
|
||||
private boolean allowNullValues = true;
|
||||
|
||||
private ConcurrentMapCache cache;
|
||||
|
||||
|
||||
/**
|
||||
* Specify the name of the cache.
|
||||
* <p>Default is "" (empty String).
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the ConcurrentMap to use as an internal store
|
||||
* (possibly pre-populated).
|
||||
* <p>Default is a standard {@link java.util.concurrent.ConcurrentHashMap}.
|
||||
*/
|
||||
public void setStore(ConcurrentMap<Object, Object> store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to allow {@code null} values
|
||||
* (adapting them to an internal null holder value).
|
||||
* <p>Default is "true".
|
||||
*/
|
||||
public void setAllowNullValues(boolean allowNullValues) {
|
||||
this.allowNullValues = allowNullValues;
|
||||
}
|
||||
|
||||
public void setBeanName(String beanName) {
|
||||
if (!StringUtils.hasLength(this.name)) {
|
||||
setName(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.cache = (this.store != null ? new ConcurrentMapCache(this.name, this.store, this.allowNullValues) :
|
||||
new ConcurrentMapCache(this.name, this.allowNullValues));
|
||||
}
|
||||
|
||||
|
||||
public ConcurrentMapCache getObject() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
return ConcurrentMapCache.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
101
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCacheManager.java
vendored
Normal file
101
spring-context/src/main/java/org/springframework/cache/concurrent/ConcurrentMapCacheManager.java
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.concurrent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* {@link CacheManager} implementation that lazily builds {@link ConcurrentMapCache}
|
||||
* instances for each {@link #getCache} request. Also supports a 'static' mode where
|
||||
* the set of cache names is pre-defined through {@link #setCacheNames}, with no
|
||||
* dynamic creation of further cache regions at runtime.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class ConcurrentMapCacheManager implements CacheManager {
|
||||
|
||||
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();
|
||||
|
||||
private boolean dynamic = true;
|
||||
|
||||
|
||||
/**
|
||||
* Construct a dynamic ConcurrentMapCacheManager,
|
||||
* lazily creating cache instances as they are being requested.
|
||||
*/
|
||||
public ConcurrentMapCacheManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a static ConcurrentMapCacheManager,
|
||||
* managing caches for the specified cache names only.
|
||||
*/
|
||||
public ConcurrentMapCacheManager(String... cacheNames) {
|
||||
setCacheNames(Arrays.asList(cacheNames));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify the set of cache names for this CacheManager's 'static' mode.
|
||||
* <p>The number of caches and their names will be fixed after a call to this method,
|
||||
* with no creation of further cache regions at runtime.
|
||||
*/
|
||||
public void setCacheNames(Collection<String> cacheNames) {
|
||||
if (cacheNames != null) {
|
||||
for (String name : cacheNames) {
|
||||
this.cacheMap.put(name, createConcurrentMapCache(name));
|
||||
}
|
||||
this.dynamic = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheMap.keySet());
|
||||
}
|
||||
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = this.cacheMap.get(name);
|
||||
if (cache == null && this.dynamic) {
|
||||
synchronized (this.cacheMap) {
|
||||
cache = this.cacheMap.get(name);
|
||||
if (cache == null) {
|
||||
cache = createConcurrentMapCache(name);
|
||||
this.cacheMap.put(name, cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConcurrentMapCache instance for the specified cache name.
|
||||
* @param name the name of the cache
|
||||
* @return the ConcurrentMapCache (or a decorator thereof)
|
||||
*/
|
||||
protected Cache createConcurrentMapCache(String name) {
|
||||
return new ConcurrentMapCache(name);
|
||||
}
|
||||
|
||||
}
|
||||
9
spring-context/src/main/java/org/springframework/cache/concurrent/package-info.java
vendored
Normal file
9
spring-context/src/main/java/org/springframework/cache/concurrent/package-info.java
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
/**
|
||||
* Implementation package for {@code java.util.concurrent} based caches.
|
||||
* Provides a {@link org.springframework.cache.CacheManager CacheManager}
|
||||
* and {@link org.springframework.cache.Cache Cache} implementation for
|
||||
* use in a Spring context.
|
||||
*/
|
||||
package org.springframework.cache.concurrent;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import static org.springframework.context.annotation.AnnotationConfigUtils.*;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation that allows users to easily configure all the
|
||||
* infrastructure beans required to enable annotation-driven cache
|
||||
* demarcation.
|
||||
*
|
||||
* <p>By default, all proxies are created as JDK proxies. This may cause
|
||||
* some problems if you are injecting objects as concrete classes rather
|
||||
* than interfaces. To overcome this restriction you can set the
|
||||
* '{@code proxy-target-class}' attribute to '{@code true}', which will
|
||||
* result in class-based proxies being created.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Parses the '{@code <cache:annotation-driven>}' tag. Will
|
||||
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary
|
||||
* register an AutoProxyCreator} with the container as necessary.
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String mode = element.getAttribute("mode");
|
||||
if ("aspectj".equals(mode)) {
|
||||
// mode="aspectj"
|
||||
registerCacheAspect(element, parserContext);
|
||||
}
|
||||
else {
|
||||
// mode="proxy"
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void parseCacheManagerProperty(Element element, BeanDefinition def) {
|
||||
def.getPropertyValues().add("cacheManager",
|
||||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a
|
||||
* <pre>
|
||||
* <bean id="cacheAspect" class="org.springframework.cache.aspectj.AnnotationCacheAspect" factory-method="aspectOf">
|
||||
* <property name="cacheManager" ref="cacheManager"/>
|
||||
* <property name="keyGenerator" ref="keyGenerator"/>
|
||||
* </bean>
|
||||
*
|
||||
* </pre>
|
||||
* @param element
|
||||
* @param parserContext
|
||||
*/
|
||||
private void registerCacheAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(CACHE_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
parseCacheManagerProperty(element, def);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, def);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, CACHE_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(CACHE_ADVISOR_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
// Create the CacheOperationSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationCacheOperationSource.class);
|
||||
sourceDef.setSource(eleSource);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the CacheInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(CacheInterceptor.class);
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parseCacheManagerProperty(element, interceptorDef);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryCacheOperationSourceAdvisor.class);
|
||||
advisorDef.setSource(eleSource);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (element.hasAttribute("order")) {
|
||||
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
|
||||
}
|
||||
parserContext.getRegistry().registerBeanDefinition(CACHE_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(),
|
||||
eleSource);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, CACHE_ADVISOR_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
225
spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
vendored
Normal file
225
spring-context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright 2011 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.parsing.ReaderContext;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.cache.interceptor.CachePutOperation;
|
||||
import org.springframework.cache.interceptor.CacheableOperation;
|
||||
import org.springframework.cache.interceptor.NameMatchCacheOperationSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Simple, reusable class used for overriding defaults.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
private static class Props {
|
||||
|
||||
private String key, condition, 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");
|
||||
String k = element.getAttribute("key");
|
||||
String c = element.getAttribute("condition");
|
||||
|
||||
String[] localCaches = caches;
|
||||
String localKey = key, localCondition = condition;
|
||||
|
||||
// sanity check
|
||||
if (StringUtils.hasText(cache)) {
|
||||
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
|
||||
} else {
|
||||
if (caches == null) {
|
||||
readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(k)) {
|
||||
localKey = k.trim();
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(c)) {
|
||||
localCondition = c.trim();
|
||||
}
|
||||
op.setCacheNames(localCaches);
|
||||
op.setKey(localKey);
|
||||
op.setCondition(localCondition);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element));
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, builder.getBeanDefinition());
|
||||
|
||||
List<Element> cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT);
|
||||
if (cacheDefs.size() >= 1) {
|
||||
// Using attributes source.
|
||||
List<RootBeanDefinition> attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext);
|
||||
builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions);
|
||||
} else {
|
||||
// Assume annotations source.
|
||||
builder.addPropertyValue("cacheOperationSources", new RootBeanDefinition(
|
||||
AnnotationCacheOperationSource.class));
|
||||
}
|
||||
}
|
||||
|
||||
private List<RootBeanDefinition> parseDefinitionsSources(List<Element> definitions, ParserContext parserContext) {
|
||||
ManagedList<RootBeanDefinition> defs = new ManagedList<RootBeanDefinition>(definitions.size());
|
||||
|
||||
// extract default param for the definition
|
||||
for (Element element : definitions) {
|
||||
defs.add(parseDefinitionSource(element, parserContext));
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
private RootBeanDefinition parseDefinitionSource(Element definition, ParserContext parserContext) {
|
||||
Props prop = new Props(definition);
|
||||
// add cacheable first
|
||||
|
||||
ManagedMap<TypedStringValue, Collection<CacheOperation>> cacheOpMap = new ManagedMap<TypedStringValue, Collection<CacheOperation>>();
|
||||
cacheOpMap.setSource(parserContext.extractSource(definition));
|
||||
|
||||
List<Element> cacheableCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT);
|
||||
|
||||
for (Element opElement : cacheableCacheMethods) {
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
List<Element> evictCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_EVICT_ELEMENT);
|
||||
|
||||
for (Element opElement : evictCacheMethods) {
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheEvictOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());
|
||||
|
||||
String wide = opElement.getAttribute("all-entries");
|
||||
if (StringUtils.hasText(wide)) {
|
||||
op.setCacheWide(Boolean.valueOf(wide.trim()));
|
||||
}
|
||||
|
||||
String after = opElement.getAttribute("before-invocation");
|
||||
if (StringUtils.hasText(after)) {
|
||||
op.setBeforeInvocation(Boolean.valueOf(after.trim()));
|
||||
}
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
List<Element> putCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_PUT_ELEMENT);
|
||||
|
||||
for (Element opElement : putCacheMethods) {
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchCacheOperationSource.class);
|
||||
attributeSourceDefinition.setSource(parserContext.extractSource(definition));
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
}
|
||||
58
spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java
vendored
Normal file
58
spring-context/src/main/java/org/springframework/cache/config/CacheNamespaceHandler.java
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.config;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@code NamespaceHandler} allowing for the configuration of declarative
|
||||
* cache management using either XML or using annotations.
|
||||
*
|
||||
* <p>This namespace handler is the central piece of functionality in the
|
||||
* Spring cache management facilities.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
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) {
|
||||
return (element.hasAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE) ? element
|
||||
.getAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE)
|
||||
: CacheNamespaceHandler.DEFAULT_CACHE_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
static BeanDefinition parseKeyGenerator(Element element, BeanDefinition def) {
|
||||
String name = element.getAttribute("key-generator");
|
||||
if (StringUtils.hasText(name)) {
|
||||
def.getPropertyValues().add("keyGenerator", new RuntimeBeanReference(name.trim()));
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("advice", new CacheAdviceParser());
|
||||
}
|
||||
}
|
||||
8
spring-context/src/main/java/org/springframework/cache/config/package-info.java
vendored
Normal file
8
spring-context/src/main/java/org/springframework/cache/config/package-info.java
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
* Support package for declarative caching configuration, with XML
|
||||
* schema being the primary configuration format. See {@link
|
||||
* org.springframework.cache.annotation.EnableCaching EnableCaching}
|
||||
* for details on code-based configuration without XML.
|
||||
*/
|
||||
package org.springframework.cache.config;
|
||||
77
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java
vendored
Normal file
77
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.ehcache;
|
||||
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.Element;
|
||||
import net.sf.ehcache.Status;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link Cache} implementation on top of an {@link Ehcache} instance.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class EhCacheCache implements Cache {
|
||||
|
||||
private final Ehcache cache;
|
||||
|
||||
|
||||
/**
|
||||
* Create an {@link EhCacheCache} instance.
|
||||
* @param ehcache backing Ehcache instance
|
||||
*/
|
||||
public EhCacheCache(Ehcache ehcache) {
|
||||
Assert.notNull(ehcache, "Ehcache must not be null");
|
||||
Status status = ehcache.getStatus();
|
||||
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
|
||||
"An 'alive' Ehcache is required - current cache is " + status.toString());
|
||||
this.cache = ehcache;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return this.cache.getName();
|
||||
}
|
||||
|
||||
public Ehcache getNativeCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.cache.removeAll();
|
||||
}
|
||||
|
||||
public ValueWrapper get(Object key) {
|
||||
Element element = this.cache.get(key);
|
||||
return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
|
||||
}
|
||||
|
||||
public void put(Object key, Object value) {
|
||||
this.cache.put(new Element(key, value));
|
||||
}
|
||||
|
||||
public void evict(Object key) {
|
||||
this.cache.remove(key);
|
||||
}
|
||||
|
||||
}
|
||||
86
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java
vendored
Normal file
86
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.ehcache;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.Status;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.support.AbstractCacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* CacheManager backed by an EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class EhCacheCacheManager extends AbstractCacheManager {
|
||||
|
||||
private net.sf.ehcache.CacheManager cacheManager;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the backing Ehcache {@link net.sf.ehcache.CacheManager}.
|
||||
* @return
|
||||
*/
|
||||
public net.sf.ehcache.CacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*/
|
||||
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<Cache> loadCaches() {
|
||||
Assert.notNull(this.cacheManager, "A backing EhCache CacheManager is required");
|
||||
Status status = this.cacheManager.getStatus();
|
||||
Assert.isTrue(Status.STATUS_ALIVE.equals(status),
|
||||
"An 'alive' EhCache CacheManager is required - current cache is " + status.toString());
|
||||
|
||||
String[] names = this.cacheManager.getCacheNames();
|
||||
Collection<Cache> caches = new LinkedHashSet<Cache>(names.length);
|
||||
for (String name : names) {
|
||||
caches.add(new EhCacheCache(this.cacheManager.getEhcache(name)));
|
||||
}
|
||||
return caches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = super.getCache(name);
|
||||
if (cache == null) {
|
||||
// check the EhCache cache again
|
||||
// (in case the cache was added at runtime)
|
||||
Ehcache ehcache = this.cacheManager.getEhcache(name);
|
||||
if (ehcache != null) {
|
||||
cache = new EhCacheCache(ehcache);
|
||||
addCache(cache);
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
409
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java
vendored
Normal file
409
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheFactoryBean.java
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.ehcache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.bootstrap.BootstrapCacheLoader;
|
||||
import net.sf.ehcache.constructs.blocking.BlockingCache;
|
||||
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
|
||||
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
|
||||
import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;
|
||||
import net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache;
|
||||
import net.sf.ehcache.event.CacheEventListener;
|
||||
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} that creates a named EHCache {@link net.sf.ehcache.Cache} instance
|
||||
* (or a decorator that implements the {@link net.sf.ehcache.Ehcache} interface),
|
||||
* representing a cache region within an EHCache {@link net.sf.ehcache.CacheManager}.
|
||||
*
|
||||
* <p>If the specified named cache is not configured in the cache configuration descriptor,
|
||||
* this FactoryBean will construct an instance of a Cache with the provided name and the
|
||||
* specified cache properties and add it to the CacheManager for later retrieval. If some
|
||||
* or all properties are not set at configuration time, this FactoryBean will use defaults.
|
||||
*
|
||||
* <p>Note: If the named Cache instance is found, the properties will be ignored and the
|
||||
* Cache instance will be retrieved from the CacheManager.
|
||||
*
|
||||
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
|
||||
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see #setCacheManager
|
||||
* @see EhCacheManagerFactoryBean
|
||||
* @see net.sf.ehcache.Cache
|
||||
*/
|
||||
public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware, InitializingBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private String cacheName;
|
||||
|
||||
private int maxElementsInMemory = 10000;
|
||||
|
||||
private int maxElementsOnDisk = 10000000;
|
||||
|
||||
private MemoryStoreEvictionPolicy memoryStoreEvictionPolicy = MemoryStoreEvictionPolicy.LRU;
|
||||
|
||||
private boolean overflowToDisk = true;
|
||||
|
||||
private boolean eternal = false;
|
||||
|
||||
private int timeToLive = 120;
|
||||
|
||||
private int timeToIdle = 120;
|
||||
|
||||
private boolean diskPersistent = false;
|
||||
|
||||
private int diskExpiryThreadIntervalSeconds = 120;
|
||||
|
||||
private int diskSpoolBufferSize = 0;
|
||||
|
||||
private boolean clearOnFlush = true;
|
||||
|
||||
private boolean blocking = false;
|
||||
|
||||
private CacheEntryFactory cacheEntryFactory;
|
||||
|
||||
private BootstrapCacheLoader bootstrapCacheLoader;
|
||||
|
||||
private Set<CacheEventListener> cacheEventListeners;
|
||||
|
||||
private boolean statisticsEnabled = false;
|
||||
|
||||
private boolean sampledStatisticsEnabled = false;
|
||||
|
||||
private boolean disabled = false;
|
||||
|
||||
private String beanName;
|
||||
|
||||
private Ehcache cache;
|
||||
|
||||
|
||||
/**
|
||||
* Set a CacheManager from which to retrieve a named Cache instance.
|
||||
* By default, <code>CacheManager.getInstance()</code> will be called.
|
||||
* <p>Note that in particular for persistent caches, it is advisable to
|
||||
* properly handle the shutdown of the CacheManager: Set up a separate
|
||||
* EhCacheManagerFactoryBean and pass a reference to this bean property.
|
||||
* <p>A separate EhCacheManagerFactoryBean is also necessary for loading
|
||||
* EHCache configuration from a non-default config location.
|
||||
* @see EhCacheManagerFactoryBean
|
||||
* @see net.sf.ehcache.CacheManager#getInstance
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a name for which to retrieve or create a cache instance.
|
||||
* Default is the bean name of this EhCacheFactoryBean.
|
||||
*/
|
||||
public void setCacheName(String cacheName) {
|
||||
this.cacheName = cacheName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the maximum number of cached objects in memory.
|
||||
* Default is 10000 elements.
|
||||
*/
|
||||
public void setMaxElementsInMemory(int maxElementsInMemory) {
|
||||
this.maxElementsInMemory = maxElementsInMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the maximum number of cached objects on disk.
|
||||
* Default is 10000000 elements.
|
||||
*/
|
||||
public void setMaxElementsOnDisk(int maxElementsOnDisk) {
|
||||
this.maxElementsOnDisk = maxElementsOnDisk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the memory style eviction policy for this cache.
|
||||
* <p>Supported values are "LRU", "LFU" and "FIFO", according to the
|
||||
* constants defined in EHCache's MemoryStoreEvictionPolicy class.
|
||||
* Default is "LRU".
|
||||
*/
|
||||
public void setMemoryStoreEvictionPolicy(MemoryStoreEvictionPolicy memoryStoreEvictionPolicy) {
|
||||
Assert.notNull(memoryStoreEvictionPolicy, "memoryStoreEvictionPolicy must not be null");
|
||||
this.memoryStoreEvictionPolicy = memoryStoreEvictionPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether elements can overflow to disk when the in-memory cache
|
||||
* has reached the maximum size limit. Default is "true".
|
||||
*/
|
||||
public void setOverflowToDisk(boolean overflowToDisk) {
|
||||
this.overflowToDisk = overflowToDisk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether elements are considered as eternal. If "true", timeouts
|
||||
* are ignored and the element is never expired. Default is "false".
|
||||
*/
|
||||
public void setEternal(boolean eternal) {
|
||||
this.eternal = eternal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds to live for an element before it expires,
|
||||
* i.e. the maximum time between creation time and when an element expires.
|
||||
* <p>This is only used if the element is not eternal. Default is 120 seconds.
|
||||
*/
|
||||
public void setTimeToLive(int timeToLive) {
|
||||
this.timeToLive = timeToLive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the time in seconds to idle for an element before it expires, that is,
|
||||
* the maximum amount of time between accesses before an element expires.
|
||||
* <p>This is only used if the element is not eternal. Default is 120 seconds.
|
||||
*/
|
||||
public void setTimeToIdle(int timeToIdle) {
|
||||
this.timeToIdle = timeToIdle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the disk store persists between restarts of the Virtual Machine.
|
||||
* Default is "false".
|
||||
*/
|
||||
public void setDiskPersistent(boolean diskPersistent) {
|
||||
this.diskPersistent = diskPersistent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds between runs of the disk expiry thread.
|
||||
* Default is 120 seconds.
|
||||
*/
|
||||
public void setDiskExpiryThreadIntervalSeconds(int diskExpiryThreadIntervalSeconds) {
|
||||
this.diskExpiryThreadIntervalSeconds = diskExpiryThreadIntervalSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount of memory to allocate the write buffer for puts to the disk store.
|
||||
* Default is 0.
|
||||
*/
|
||||
public void setDiskSpoolBufferSize(int diskSpoolBufferSize) {
|
||||
this.diskSpoolBufferSize = diskSpoolBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the memory store should be cleared when flush is called on the cache.
|
||||
* Default is "true".
|
||||
*/
|
||||
public void setClearOnFlush(boolean clearOnFlush) {
|
||||
this.clearOnFlush = clearOnFlush;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use a blocking cache that lets read attempts block
|
||||
* until the requested element is created.
|
||||
* <p>If you intend to build a self-populating blocking cache,
|
||||
* consider specifying a {@link #setCacheEntryFactory CacheEntryFactory}.
|
||||
* @see net.sf.ehcache.constructs.blocking.BlockingCache
|
||||
* @see #setCacheEntryFactory
|
||||
*/
|
||||
public void setBlocking(boolean blocking) {
|
||||
this.blocking = blocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an EHCache {@link net.sf.ehcache.constructs.blocking.CacheEntryFactory}
|
||||
* to use for a self-populating cache. If such a factory is specified,
|
||||
* the cache will be decorated with EHCache's
|
||||
* {@link net.sf.ehcache.constructs.blocking.SelfPopulatingCache}.
|
||||
* <p>The specified factory can be of type
|
||||
* {@link net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory},
|
||||
* which will lead to the use of an
|
||||
* {@link net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache}.
|
||||
* <p>Note: Any such self-populating cache is automatically a blocking cache.
|
||||
* @see net.sf.ehcache.constructs.blocking.SelfPopulatingCache
|
||||
* @see net.sf.ehcache.constructs.blocking.UpdatingSelfPopulatingCache
|
||||
* @see net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory
|
||||
*/
|
||||
public void setCacheEntryFactory(CacheEntryFactory cacheEntryFactory) {
|
||||
this.cacheEntryFactory = cacheEntryFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an EHCache {@link net.sf.ehcache.bootstrap.BootstrapCacheLoader}
|
||||
* for this cache, if any.
|
||||
*/
|
||||
public void setBootstrapCacheLoader(BootstrapCacheLoader bootstrapCacheLoader) {
|
||||
this.bootstrapCacheLoader = bootstrapCacheLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify EHCache {@link net.sf.ehcache.event.CacheEventListener cache event listeners}
|
||||
* to registered with this cache.
|
||||
*/
|
||||
public void setCacheEventListeners(Set<CacheEventListener> cacheEventListeners) {
|
||||
this.cacheEventListeners = cacheEventListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to enable EhCache statistics on this cache.
|
||||
* @see net.sf.ehcache.Cache#setStatisticsEnabled
|
||||
*/
|
||||
public void setStatisticsEnabled(boolean statisticsEnabled) {
|
||||
this.statisticsEnabled = statisticsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to enable EhCache's sampled statistics on this cache.
|
||||
* @see net.sf.ehcache.Cache#setSampledStatisticsEnabled
|
||||
*/
|
||||
public void setSampledStatisticsEnabled(boolean sampledStatisticsEnabled) {
|
||||
this.sampledStatisticsEnabled = sampledStatisticsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether this cache should be marked as disabled.
|
||||
* @see net.sf.ehcache.Cache#setDisabled
|
||||
*/
|
||||
public void setDisabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
public void setBeanName(String name) {
|
||||
this.beanName = name;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws CacheException, IOException {
|
||||
// If no CacheManager given, fetch the default.
|
||||
if (this.cacheManager == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using default EHCache CacheManager for cache region '" + this.cacheName + "'");
|
||||
}
|
||||
this.cacheManager = CacheManager.getInstance();
|
||||
}
|
||||
|
||||
// If no cache name given, use bean name as cache name.
|
||||
if (this.cacheName == null) {
|
||||
this.cacheName = this.beanName;
|
||||
}
|
||||
|
||||
// Fetch cache region: If none with the given name exists,
|
||||
// create one on the fly.
|
||||
Ehcache rawCache;
|
||||
if (this.cacheManager.cacheExists(this.cacheName)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Using existing EHCache cache region '" + this.cacheName + "'");
|
||||
}
|
||||
rawCache = this.cacheManager.getEhcache(this.cacheName);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Creating new EHCache cache region '" + this.cacheName + "'");
|
||||
}
|
||||
rawCache = createCache();
|
||||
this.cacheManager.addCache(rawCache);
|
||||
}
|
||||
|
||||
// Decorate cache if necessary.
|
||||
Ehcache decoratedCache = decorateCache(rawCache);
|
||||
if (decoratedCache != rawCache) {
|
||||
this.cacheManager.replaceCacheWithDecoratedCache(rawCache, decoratedCache);
|
||||
}
|
||||
this.cache = decoratedCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a raw Cache object based on the configuration of this FactoryBean.
|
||||
*/
|
||||
protected Cache createCache() {
|
||||
// Only call EHCache 1.6 constructor if actually necessary (for compatibility with EHCache 1.3+)
|
||||
Cache cache = (!this.clearOnFlush) ?
|
||||
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
|
||||
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize,
|
||||
this.clearOnFlush) :
|
||||
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
|
||||
this.bootstrapCacheLoader, this.maxElementsOnDisk, this.diskSpoolBufferSize);
|
||||
|
||||
if (this.cacheEventListeners != null) {
|
||||
for (CacheEventListener listener : this.cacheEventListeners) {
|
||||
cache.getCacheEventNotificationService().registerListener(listener);
|
||||
}
|
||||
}
|
||||
if (this.statisticsEnabled) {
|
||||
cache.setStatisticsEnabled(true);
|
||||
}
|
||||
if (this.sampledStatisticsEnabled) {
|
||||
cache.setSampledStatisticsEnabled(true);
|
||||
}
|
||||
if (this.disabled) {
|
||||
cache.setDisabled(true);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate the given Cache, if necessary.
|
||||
* @param cache the raw Cache object, based on the configuration of this FactoryBean
|
||||
* @return the (potentially decorated) cache object to be registered with the CacheManager
|
||||
*/
|
||||
protected Ehcache decorateCache(Ehcache cache) {
|
||||
if (this.cacheEntryFactory != null) {
|
||||
if (this.cacheEntryFactory instanceof UpdatingCacheEntryFactory) {
|
||||
return new UpdatingSelfPopulatingCache(cache, (UpdatingCacheEntryFactory) this.cacheEntryFactory);
|
||||
}
|
||||
else {
|
||||
return new SelfPopulatingCache(cache, this.cacheEntryFactory);
|
||||
}
|
||||
}
|
||||
if (this.blocking) {
|
||||
return new BlockingCache(cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
public Ehcache getObject() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public Class<? extends Ehcache> getObjectType() {
|
||||
return (this.cache != null ? this.cache.getClass() : Ehcache.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
137
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java
vendored
Normal file
137
spring-context/src/main/java/org/springframework/cache/ehcache/EhCacheManagerFactoryBean.java
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.ehcache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} that exposes an EHCache {@link net.sf.ehcache.CacheManager}
|
||||
* instance (independent or shared), configured from a specified config location.
|
||||
*
|
||||
* <p>If no config location is specified, a CacheManager will be configured from
|
||||
* "ehcache.xml" in the root of the class path (that is, default EHCache initialization
|
||||
* - as defined in the EHCache docs - will apply).
|
||||
*
|
||||
* <p>Setting up a separate EhCacheManagerFactoryBean is also advisable when using
|
||||
* EhCacheFactoryBean, as it provides a (by default) independent CacheManager instance
|
||||
* and cares for proper shutdown of the CacheManager. EhCacheManagerFactoryBean is
|
||||
* also necessary for loading EHCache configuration from a non-default config location.
|
||||
*
|
||||
* <p>Note: As of Spring 3.0, Spring's EHCache support requires EHCache 1.3 or higher.
|
||||
*
|
||||
* @author Dmitriy Kopylenko
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see #setConfigLocation
|
||||
* @see #setShared
|
||||
* @see EhCacheFactoryBean
|
||||
* @see net.sf.ehcache.CacheManager
|
||||
*/
|
||||
public class EhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private boolean shared = false;
|
||||
|
||||
private String cacheManagerName;
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
|
||||
/**
|
||||
* Set the location of the EHCache config file. A typical value is "/WEB-INF/ehcache.xml".
|
||||
* <p>Default is "ehcache.xml" in the root of the class path, or if not found,
|
||||
* "ehcache-failsafe.xml" in the EHCache jar (default EHCache initialization).
|
||||
* @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
|
||||
* @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
|
||||
*/
|
||||
public void setConfigLocation(Resource configLocation) {
|
||||
this.configLocation = configLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the EHCache CacheManager should be shared (as a singleton at the VM level)
|
||||
* or independent (typically local within the application). Default is "false", creating
|
||||
* an independent instance.
|
||||
* @see net.sf.ehcache.CacheManager#create()
|
||||
* @see net.sf.ehcache.CacheManager#CacheManager()
|
||||
*/
|
||||
public void setShared(boolean shared) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the EHCache CacheManager (if a specific name is desired).
|
||||
* @see net.sf.ehcache.CacheManager#setName(String)
|
||||
*/
|
||||
public void setCacheManagerName(String cacheManagerName) {
|
||||
this.cacheManagerName = cacheManagerName;
|
||||
}
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws IOException, CacheException {
|
||||
logger.info("Initializing EHCache CacheManager");
|
||||
if (this.configLocation != null) {
|
||||
InputStream is = this.configLocation.getInputStream();
|
||||
try {
|
||||
this.cacheManager = (this.shared ? CacheManager.create(is) : new CacheManager(is));
|
||||
}
|
||||
finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.cacheManager = (this.shared ? CacheManager.create() : new CacheManager());
|
||||
}
|
||||
if (this.cacheManagerName != null) {
|
||||
this.cacheManager.setName(this.cacheManagerName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public CacheManager getObject() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
public Class<? extends CacheManager> getObjectType() {
|
||||
return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class);
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public void destroy() {
|
||||
logger.info("Shutting down EHCache CacheManager");
|
||||
this.cacheManager.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
11
spring-context/src/main/java/org/springframework/cache/ehcache/package-info.java
vendored
Normal file
11
spring-context/src/main/java/org/springframework/cache/ehcache/package-info.java
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for the open source cache
|
||||
* <a href="http://ehcache.sourceforge.net">Ehcache</a>,
|
||||
* allowing to set up an EHCache CacheManager and Caches
|
||||
* as beans in a Spring context.
|
||||
*
|
||||
*/
|
||||
package org.springframework.cache.ehcache;
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link CacheOperation} that caches
|
||||
* attributes for methods and implements a fallback policy: 1. specific
|
||||
* target method; 2. target class; 3. declaring method; 4. declaring
|
||||
* class/interface.
|
||||
*
|
||||
* <p>Defaults to using the target class's caching attribute if none is
|
||||
* associated with the target method. Any caching attribute associated
|
||||
* with the target method completely overrides a class caching attribute.
|
||||
* If none found on the target class, the interface that the invoked
|
||||
* method has been called through (in case of a JDK proxy) will be
|
||||
* checked.
|
||||
*
|
||||
* <p>This implementation caches attributes by method after they are
|
||||
* first used. If it is ever desirable to allow dynamic changing of
|
||||
* cacheable attributes (which is very unlikely), caching could be made
|
||||
* configurable.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractFallbackCacheOperationSource implements CacheOperationSource {
|
||||
|
||||
/**
|
||||
* Canonical value held in cache to indicate no caching attribute was
|
||||
* found for this method and we don't need to look again.
|
||||
*/
|
||||
private final static Collection<CacheOperation> NULL_CACHING_ATTRIBUTE = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
* <p>As this base class is not marked Serializable, the logger will be recreated
|
||||
* after serialization - provided that the concrete subclass is Serializable.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Cache of CacheOperations, keyed by DefaultCacheKey (Method + target Class).
|
||||
* <p>As this base class is not marked Serializable, the cache will be recreated
|
||||
* after serialization - provided that the concrete subclass is Serializable.
|
||||
*/
|
||||
final Map<Object, Collection<CacheOperation>> attributeCache = new ConcurrentHashMap<Object, Collection<CacheOperation>>();
|
||||
|
||||
|
||||
/**
|
||||
* Determine the caching attribute for this method invocation.
|
||||
* <p>Defaults to the class's caching attribute if no method attribute is found.
|
||||
* @param method the method for the current invocation (never {@code null})
|
||||
* @param targetClass the target class for this invocation (may be {@code null})
|
||||
* @return {@link CacheOperation} for this method, or {@code null} if the method
|
||||
* is not cacheable
|
||||
*/
|
||||
public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
|
||||
// First, see if we have a cached value.
|
||||
Object cacheKey = getCacheKey(method, targetClass);
|
||||
Collection<CacheOperation> cached = this.attributeCache.get(cacheKey);
|
||||
if (cached != null) {
|
||||
if (cached == NULL_CACHING_ATTRIBUTE) {
|
||||
return null;
|
||||
}
|
||||
// Value will either be canonical value indicating there is no caching attribute,
|
||||
// or an actual caching attribute.
|
||||
return cached;
|
||||
}
|
||||
else {
|
||||
// We need to work it out.
|
||||
Collection<CacheOperation> cacheOps = computeCacheOperations(method, targetClass);
|
||||
// Put it in the cache.
|
||||
if (cacheOps == null) {
|
||||
this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
|
||||
}
|
||||
this.attributeCache.put(cacheKey, cacheOps);
|
||||
}
|
||||
return cacheOps;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a cache key for the given method and target class.
|
||||
* <p>Must not produce same key for overloaded methods.
|
||||
* Must produce same key for different instances of the same method.
|
||||
* @param method the method (never {@code null})
|
||||
* @param targetClass the target class (may be {@code null})
|
||||
* @return the cache key (never {@code null})
|
||||
*/
|
||||
protected Object getCacheKey(Method method, Class<?> targetClass) {
|
||||
return new DefaultCacheKey(method, targetClass);
|
||||
}
|
||||
|
||||
private Collection<CacheOperation> computeCacheOperations(Method method, Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The method may be on an interface, but we need attributes from the target class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
// If we are dealing with method with generic parameters, find the original method.
|
||||
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
|
||||
// First try is the method in the target class.
|
||||
Collection<CacheOperation> opDef = findCacheOperations(specificMethod);
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
|
||||
// Second try is the caching operation on the target class.
|
||||
opDef = findCacheOperations(specificMethod.getDeclaringClass());
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
|
||||
if (specificMethod != method) {
|
||||
// Fall back is to look at the original method.
|
||||
opDef = findCacheOperations(method);
|
||||
if (opDef != null) {
|
||||
return opDef;
|
||||
}
|
||||
// Last fall back is the class of the original method.
|
||||
return findCacheOperations(method.getDeclaringClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching attribute
|
||||
* for the given method, if any.
|
||||
* @param method the method to retrieve the attribute for
|
||||
* @return all caching attribute associated with this method
|
||||
* (or {@code null} if none)
|
||||
*/
|
||||
protected abstract Collection<CacheOperation> findCacheOperations(Method method);
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this to return the caching attribute
|
||||
* for the given class, if any.
|
||||
* @param clazz the class to retrieve the attribute for
|
||||
* @return all caching attribute associated with this class
|
||||
* (or {@code null} if none)
|
||||
*/
|
||||
protected abstract Collection<CacheOperation> findCacheOperations(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Should only public methods be allowed to have caching semantics?
|
||||
* <p>The default implementation returns {@code false}.
|
||||
*/
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Default cache key for the CacheOperation cache.
|
||||
*/
|
||||
private static class DefaultCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
public DefaultCacheKey(Method method, Class<?> targetClass) {
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof DefaultCacheKey)) {
|
||||
return false;
|
||||
}
|
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other;
|
||||
return (this.method.equals(otherKey.method) && ObjectUtils.nullSafeEquals(this.targetClass,
|
||||
otherKey.targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * 29 + (this.targetClass != null ? this.targetClass.hashCode() : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.aop.ClassFilter;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Advisor driven by a {@link CacheOperationSource}, used to include a
|
||||
* cache advice bean for methods that are cacheable.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
|
||||
|
||||
private CacheOperationSource cacheOperationSource;
|
||||
|
||||
private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
|
||||
@Override
|
||||
protected CacheOperationSource getCacheOperationSource() {
|
||||
return cacheOperationSource;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Set the cache operation attribute source which is used to find cache
|
||||
* attributes. This should usually be identical to the source reference
|
||||
* set on the cache interceptor itself.
|
||||
* @see CacheInterceptor#setCacheAttributeSource
|
||||
*/
|
||||
public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
|
||||
this.cacheOperationSource = cacheOperationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ClassFilter} to use for this pointcut.
|
||||
* Default is {@link ClassFilter#TRUE}.
|
||||
*/
|
||||
public void setClassFilter(ClassFilter classFilter) {
|
||||
this.pointcut.setClassFilter(classFilter);
|
||||
}
|
||||
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
}
|
||||
473
spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
vendored
Normal file
473
spring-context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java
vendored
Normal file
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
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.CacheManager;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for caching aspects, such as the {@link CacheInterceptor}
|
||||
* or an AspectJ aspect.
|
||||
*
|
||||
* <p>This enables the underlying Spring caching infrastructure to be
|
||||
* used easily to implement an aspect for any aspect system.
|
||||
*
|
||||
* <p>Subclasses are responsible for calling methods in this class in
|
||||
* the correct order.
|
||||
*
|
||||
* <p>Uses the <b>Strategy</b> design pattern. A {@link CacheManager}
|
||||
* implementation will perform the actual cache management, and a
|
||||
* {@link CacheOperationSource} is used for determining caching
|
||||
* operations.
|
||||
*
|
||||
* <p>A cache aspect is serializable if its {@code CacheManager} and
|
||||
* {@code CacheOperationSource} are serializable.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
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 boolean initialized = false;
|
||||
|
||||
private static final String CACHEABLE = "cacheable", UPDATE = "cacheupdate", EVICT = "cacheevict";
|
||||
|
||||
/**
|
||||
* Set the CacheManager that this cache aspect should delegate to.
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheManager that this cache aspect delegates to.
|
||||
*/
|
||||
public CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more cache operation sources which are used to find the cache
|
||||
* attributes. If more than one source is provided, they will be aggregated using a
|
||||
* {@link CompositeCacheOperationSource}.
|
||||
* @param cacheOperationSources must not be {@code null}
|
||||
*/
|
||||
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
|
||||
Assert.notEmpty(cacheOperationSources);
|
||||
this.cacheOperationSource =
|
||||
(cacheOperationSources.length > 1 ?
|
||||
new CompositeCacheOperationSource(cacheOperationSources) :
|
||||
cacheOperationSources[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the CacheOperationSource for this cache aspect.
|
||||
*/
|
||||
public CacheOperationSource getCacheOperationSource() {
|
||||
return this.cacheOperationSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the KeyGenerator for this cache aspect.
|
||||
* Default is {@link DefaultKeyGenerator}.
|
||||
*/
|
||||
public void setKeyGenerator(KeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the KeyGenerator for this cache aspect,
|
||||
*/
|
||||
public KeyGenerator getKeyGenerator() {
|
||||
return this.keyGenerator;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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));
|
||||
|
||||
if (!updates.isEmpty()) {
|
||||
update(updates, retVal);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return invoker.invoke();
|
||||
}
|
||||
|
||||
private void inspectBeforeCacheEvicts(Collection<CacheOperationContext> evictions) {
|
||||
inspectCacheEvicts(evictions, true);
|
||||
}
|
||||
|
||||
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions) {
|
||||
inspectCacheEvicts(evictions, false);
|
||||
}
|
||||
|
||||
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions, boolean beforeInvocation) {
|
||||
|
||||
if (!evictions.isEmpty()) {
|
||||
|
||||
boolean log = logger.isTraceEnabled();
|
||||
|
||||
for (CacheOperationContext context : evictions) {
|
||||
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
|
||||
|
||||
if (beforeInvocation == evictOp.isBeforeInvocation()) {
|
||||
if (context.isConditionPassing()) {
|
||||
// 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 updateRequire = 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 (!updateRequire) {
|
||||
for (Cache cache : context.getCaches()) {
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
if (wrapper != null) {
|
||||
retVal = wrapper.get();
|
||||
localCacheHit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!localCacheHit) {
|
||||
updateRequire = 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, updateRequire, retVal);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
for (Cache cache : entry.getKey().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 {
|
||||
|
||||
private final CacheOperation operation;
|
||||
|
||||
private final Collection<Cache> caches;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
// context passed around to avoid multiple creations
|
||||
private final EvaluationContext evalContext;
|
||||
|
||||
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
this.operation = operation;
|
||||
this.caches = CacheAspectSupport.this.getCaches(operation);
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
|
||||
this.evalContext = evaluator.createEvaluationContext(caches, method, args, target, targetClass);
|
||||
}
|
||||
|
||||
protected boolean isConditionPassing() {
|
||||
if (StringUtils.hasText(this.operation.getCondition())) {
|
||||
return evaluator.condition(this.operation.getCondition(), this.method, this.evalContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the key for the given caching operation.
|
||||
* @return generated key (null if none can be generated)
|
||||
*/
|
||||
protected Object generateKey() {
|
||||
if (StringUtils.hasText(this.operation.getKey())) {
|
||||
return evaluator.key(this.operation.getKey(), this.method, this.evalContext);
|
||||
}
|
||||
return keyGenerator.generate(this.target, this.method, this.args);
|
||||
}
|
||||
|
||||
protected Collection<Cache> getCaches() {
|
||||
return this.caches;
|
||||
}
|
||||
}
|
||||
}
|
||||
55
spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
vendored
Normal file
55
spring-context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Class describing a cache 'evict' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CacheEvictOperation extends CacheOperation {
|
||||
|
||||
private boolean cacheWide = false;
|
||||
private boolean beforeInvocation = false;
|
||||
|
||||
public void setCacheWide(boolean cacheWide) {
|
||||
this.cacheWide = cacheWide;
|
||||
}
|
||||
|
||||
public boolean isCacheWide() {
|
||||
return this.cacheWide;
|
||||
}
|
||||
|
||||
public void setBeforeInvocation(boolean beforeInvocation) {
|
||||
this.beforeInvocation = beforeInvocation;
|
||||
}
|
||||
|
||||
public boolean isBeforeInvocation() {
|
||||
return this.beforeInvocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getOperationDescription() {
|
||||
StringBuilder sb = super.getOperationDescription();
|
||||
sb.append(",");
|
||||
sb.append(this.cacheWide);
|
||||
sb.append(",");
|
||||
sb.append(this.beforeInvocation);
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Class describing the root object used during the expression evaluation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
class CacheExpressionRootObject {
|
||||
|
||||
private final Collection<Cache> caches;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
|
||||
public CacheExpressionRootObject(
|
||||
Collection<Cache> caches, Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
|
||||
Assert.notNull(method, "Method is required");
|
||||
Assert.notNull(targetClass, "targetClass is required");
|
||||
this.method = method;
|
||||
this.target = target;
|
||||
this.targetClass = targetClass;
|
||||
this.args = args;
|
||||
this.caches = caches;
|
||||
}
|
||||
|
||||
|
||||
public Collection<Cache> getCaches() {
|
||||
return this.caches;
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return this.method.getName();
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return this.args;
|
||||
}
|
||||
|
||||
public Object getTarget() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
public Class<?> getTargetClass() {
|
||||
return this.targetClass;
|
||||
}
|
||||
|
||||
}
|
||||
71
spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java
vendored
Normal file
71
spring-context/src/main/java/org/springframework/cache/interceptor/CacheInterceptor.java
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
/**
|
||||
* AOP Alliance MethodInterceptor for declarative cache
|
||||
* management using the common Spring caching infrastructure
|
||||
* ({@link org.springframework.cache.Cache}).
|
||||
*
|
||||
* <p>Derives from the {@link CacheAspectSupport} class which
|
||||
* contains the integration with Spring's underlying caching API.
|
||||
* CacheInterceptor simply calls the relevant superclass methods
|
||||
* in the correct order.
|
||||
*
|
||||
* <p>CacheInterceptors are thread-safe.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Method method = invocation.getMethod();
|
||||
|
||||
Invoker aopAllianceInvoker = new Invoker() {
|
||||
public Object invoke() {
|
||||
try {
|
||||
return invocation.proceed();
|
||||
} catch (Throwable ex) {
|
||||
throw new ThrowableWrapper(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
|
||||
} catch (ThrowableWrapper th) {
|
||||
throw th.original;
|
||||
}
|
||||
}
|
||||
}
|
||||
129
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
vendored
Normal file
129
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperation.java
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class implementing {@link CacheOperation}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class CacheOperation {
|
||||
|
||||
private Set<String> cacheNames = Collections.emptySet();
|
||||
private String condition = "";
|
||||
private String key = "";
|
||||
private String name = "";
|
||||
|
||||
|
||||
public Set<String> getCacheNames() {
|
||||
return cacheNames;
|
||||
}
|
||||
|
||||
public String getCondition() {
|
||||
return condition;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setCacheName(String cacheName) {
|
||||
Assert.hasText(cacheName);
|
||||
this.cacheNames = Collections.singleton(cacheName);
|
||||
}
|
||||
|
||||
public void setCacheNames(String[] cacheNames) {
|
||||
Assert.notEmpty(cacheNames);
|
||||
this.cacheNames = new LinkedHashSet<String>(cacheNames.length);
|
||||
for (String string : cacheNames) {
|
||||
this.cacheNames.add(string);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCondition(String condition) {
|
||||
Assert.notNull(condition);
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
Assert.notNull(key);
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
Assert.hasText(name);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation compares the {@code toString()} results.
|
||||
* @see #toString()
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (other instanceof CacheOperation && toString().equals(other.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns {@code toString()}'s hash code.
|
||||
* @see #toString()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifying description for this cache operation.
|
||||
* <p>Has to be overridden in subclasses for correct {@code equals}
|
||||
* and {@code hashCode} behavior. Alternatively, {@link #equals}
|
||||
* and {@link #hashCode} can be overridden themselves.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return getOperationDescription().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifying description for this caching operation.
|
||||
* <p>Available to subclasses, for inclusion in their {@code toString()} result.
|
||||
*/
|
||||
protected StringBuilder getOperationDescription() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append(getClass().getSimpleName());
|
||||
result.append("[");
|
||||
result.append(this.name);
|
||||
result.append("] caches=");
|
||||
result.append(this.cacheNames);
|
||||
result.append(" | condition='");
|
||||
result.append(this.condition);
|
||||
result.append("' | key='");
|
||||
result.append(this.key);
|
||||
result.append("'");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
43
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java
vendored
Normal file
43
spring-context/src/main/java/org/springframework/cache/interceptor/CacheOperationSource.java
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Interface used by CacheInterceptor. Implementations know
|
||||
* how to source cache operation attributes, whether from configuration,
|
||||
* metadata attributes at source level, or anywhere else.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface CacheOperationSource {
|
||||
|
||||
/**
|
||||
* Return the collection of cache operations for this method,
|
||||
* or {@code null} if the method contains no "cacheable" annotations.
|
||||
* @param method the method to introspect
|
||||
* @param targetClass the target class (may be {@code null},
|
||||
* in which case the declaring class of the method must be used)
|
||||
* @return all cache operations for this method, or {@code null} if
|
||||
* none found
|
||||
*/
|
||||
Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.reflect.Method;
|
||||
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A Pointcut that matches if the underlying {@link CacheOperationSource}
|
||||
* has an attribute for a given method.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
CacheOperationSource cas = getCacheOperationSource();
|
||||
return (cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof CacheOperationSourcePointcut)) {
|
||||
return false;
|
||||
}
|
||||
CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(),
|
||||
otherPc.getCacheOperationSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return CacheOperationSourcePointcut.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + getCacheOperationSource();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
|
||||
* To be implemented by subclasses.
|
||||
*/
|
||||
protected abstract CacheOperationSource getCacheOperationSource();
|
||||
|
||||
}
|
||||
76
spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java
vendored
Normal file
76
spring-context/src/main/java/org/springframework/cache/interceptor/CacheProxyFactoryBean.java
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2010-2011 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 org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
|
||||
/**
|
||||
* Proxy factory bean for simplified declarative caching handling.
|
||||
* This is a convenient alternative to a standard AOP
|
||||
* {@link org.springframework.aop.framework.ProxyFactoryBean}
|
||||
* with a separate {@link CachingInterceptor} definition.
|
||||
*
|
||||
* <p>This class is designed to facilitate declarative cache demarcation: namely, wrapping
|
||||
* a singleton target object with a caching proxy, proxying all the interfaces that the
|
||||
* target implements. Exists primarily for third-party framework integration.
|
||||
* <strong>Users should favor the {@code cache:} XML namespace
|
||||
* {@link org.springframework.cache.annotation.Cacheable @Cacheable} annotation.</strong>
|
||||
* See the <a href="http://bit.ly/p9rIvx">declarative annotation-based caching</a> section
|
||||
* of the Spring reference documentation for more information.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @see org.springframework.aop.framework.ProxyFactoryBean
|
||||
* @see CachingInterceptor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
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.
|
||||
* Note: Additional interceptors are always invoked.
|
||||
* @see #setPreInterceptors
|
||||
* @see #setPostInterceptors
|
||||
*/
|
||||
public void setPointcut(Pointcut pointcut) {
|
||||
this.pointcut = pointcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object createMainInterceptor() {
|
||||
this.cachingInterceptor.afterPropertiesSet();
|
||||
if (this.pointcut != null) {
|
||||
return new DefaultPointcutAdvisor(this.pointcut, this.cachingInterceptor);
|
||||
} else {
|
||||
// Rely on default pointcut.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sources used to find cache operations.
|
||||
*/
|
||||
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
|
||||
this.cachingInterceptor.setCacheOperationSources(cacheOperationSources);
|
||||
}
|
||||
|
||||
}
|
||||
27
spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java
vendored
Normal file
27
spring-context/src/main/java/org/springframework/cache/interceptor/CachePutOperation.java
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Class describing a cache 'put' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CachePutOperation extends CacheOperation {
|
||||
|
||||
}
|
||||
27
spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java
vendored
Normal file
27
spring-context/src/main/java/org/springframework/cache/interceptor/CacheableOperation.java
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Class describing a cache 'cacheable' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CacheableOperation extends CacheOperation {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Composite {@link CacheOperationSource} implementation that iterates
|
||||
* over a given array of {@code CacheOperationSource} instances.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CompositeCacheOperationSource implements CacheOperationSource, Serializable {
|
||||
|
||||
private final CacheOperationSource[] cacheOperationSources;
|
||||
|
||||
/**
|
||||
* Create a new CompositeCacheOperationSource for the given sources.
|
||||
* @param cacheOperationSources the CacheOperationSource instances to combine
|
||||
*/
|
||||
public CompositeCacheOperationSource(CacheOperationSource... cacheOperationSources) {
|
||||
Assert.notEmpty(cacheOperationSources, "cacheOperationSources array must not be empty");
|
||||
this.cacheOperationSources = cacheOperationSources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@code CacheOperationSource} instances that this
|
||||
* {@code CompositeCacheOperationSource} combines.
|
||||
*/
|
||||
public final CacheOperationSource[] getCacheOperationSources() {
|
||||
return this.cacheOperationSources;
|
||||
}
|
||||
|
||||
public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
for (CacheOperationSource source : this.cacheOperationSources) {
|
||||
Collection<CacheOperation> cacheOperations = source.getCacheOperations(method, targetClass);
|
||||
if (cacheOperations != null) {
|
||||
if (ops == null) {
|
||||
ops = new ArrayList<CacheOperation>();
|
||||
}
|
||||
|
||||
ops.addAll(cacheOperations);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
53
spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java
vendored
Normal file
53
spring-context/src/main/java/org/springframework/cache/interceptor/DefaultKeyGenerator.java
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
/**
|
||||
* Default key generator. Returns {@value #NO_PARAM_KEY} if no
|
||||
* parameters are provided, the parameter itself if only one is given or
|
||||
* a hash code computed from all given parameters' hash code values.
|
||||
* Uses the constant value {@value #NULL_PARAM_KEY} for any
|
||||
* {@code null} parameters given.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DefaultKeyGenerator implements KeyGenerator {
|
||||
|
||||
public static final int NO_PARAM_KEY = 0;
|
||||
public static final int NULL_PARAM_KEY = 53;
|
||||
|
||||
public Object generate(Object target, Method method, Object... params) {
|
||||
if (params.length == 1) {
|
||||
return (params[0] == null ? NULL_PARAM_KEY : params[0]);
|
||||
}
|
||||
if (params.length == 0) {
|
||||
return NO_PARAM_KEY;
|
||||
}
|
||||
int hashCode = 17;
|
||||
for (Object object : params) {
|
||||
hashCode = 31 * hashCode + (object == null ? NULL_PARAM_KEY : object.hashCode());
|
||||
}
|
||||
return Integer.valueOf(hashCode);
|
||||
}
|
||||
|
||||
}
|
||||
92
spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
vendored
Normal file
92
spring-context/src/main/java/org/springframework/cache/interceptor/ExpressionEvaluator.java
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
|
||||
/**
|
||||
* Utility class handling the SpEL expression parsing.
|
||||
* Meant to be used as a reusable, thread-safe component.
|
||||
*
|
||||
* <p>Performs internal caching for performance reasons.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
class ExpressionEvaluator {
|
||||
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// shared param discoverer since it caches data internally
|
||||
private ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>();
|
||||
|
||||
|
||||
public EvaluationContext createEvaluationContext(
|
||||
Collection<Cache> caches, Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
|
||||
CacheExpressionRootObject rootObject =
|
||||
new CacheExpressionRootObject(caches, method, args, target, targetClass);
|
||||
return new LazyParamAwareEvaluationContext(rootObject,
|
||||
this.paramNameDiscoverer, method, args, targetClass, this.targetMethodCache);
|
||||
}
|
||||
|
||||
public boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
|
||||
String key = toString(method, conditionExpression);
|
||||
Expression condExp = this.conditionCache.get(key);
|
||||
if (condExp == null) {
|
||||
condExp = this.parser.parseExpression(conditionExpression);
|
||||
this.conditionCache.put(key, condExp);
|
||||
}
|
||||
return condExp.getValue(evalContext, boolean.class);
|
||||
}
|
||||
|
||||
public Object key(String keyExpression, Method method, EvaluationContext evalContext) {
|
||||
String key = toString(method, keyExpression);
|
||||
Expression keyExp = this.keyCache.get(key);
|
||||
if (keyExp == null) {
|
||||
keyExp = this.parser.parseExpression(keyExpression);
|
||||
this.keyCache.put(key, keyExp);
|
||||
}
|
||||
return keyExp.getValue(evalContext);
|
||||
}
|
||||
|
||||
private String toString(Method method, String expression) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(method.getDeclaringClass().getName());
|
||||
sb.append("#");
|
||||
sb.append(method.toString());
|
||||
sb.append("#");
|
||||
sb.append(expression);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
33
spring-context/src/main/java/org/springframework/cache/interceptor/KeyGenerator.java
vendored
Normal file
33
spring-context/src/main/java/org/springframework/cache/interceptor/KeyGenerator.java
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Cache key generator. Used for creating a key based on the given method
|
||||
* (used as context) and its parameters.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface KeyGenerator {
|
||||
|
||||
Object generate(Object target, Method method, Object... params);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Evaluation context class that adds a method parameters as SpEL
|
||||
* variables, in a lazy manner. The lazy nature eliminates unneeded
|
||||
* parsing of classes byte code for parameter discovery.
|
||||
*
|
||||
* <p>To limit the creation of objects, an ugly constructor is used
|
||||
* (rather then a dedicated 'closure'-like class for deferred execution).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
class LazyParamAwareEvaluationContext extends StandardEvaluationContext {
|
||||
|
||||
private final ParameterNameDiscoverer paramDiscoverer;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final Map<String, Method> methodCache;
|
||||
|
||||
private boolean paramLoaded = false;
|
||||
|
||||
|
||||
LazyParamAwareEvaluationContext(Object rootObject, ParameterNameDiscoverer paramDiscoverer, Method method,
|
||||
Object[] args, Class<?> targetClass, Map<String, Method> methodCache) {
|
||||
super(rootObject);
|
||||
|
||||
this.paramDiscoverer = paramDiscoverer;
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
this.targetClass = targetClass;
|
||||
this.methodCache = methodCache;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the param information only when needed.
|
||||
*/
|
||||
@Override
|
||||
public Object lookupVariable(String name) {
|
||||
Object variable = super.lookupVariable(name);
|
||||
if (variable != null) {
|
||||
return variable;
|
||||
}
|
||||
if (!this.paramLoaded) {
|
||||
loadArgsAsVariables();
|
||||
this.paramLoaded = true;
|
||||
variable = super.lookupVariable(name);
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
private void loadArgsAsVariables() {
|
||||
// shortcut if no args need to be loaded
|
||||
if (ObjectUtils.isEmpty(this.args)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String mKey = toString(this.method);
|
||||
Method targetMethod = this.methodCache.get(mKey);
|
||||
if (targetMethod == null) {
|
||||
targetMethod = AopUtils.getMostSpecificMethod(this.method, this.targetClass);
|
||||
if (targetMethod == null) {
|
||||
targetMethod = this.method;
|
||||
}
|
||||
this.methodCache.put(mKey, targetMethod);
|
||||
}
|
||||
|
||||
// save arguments as indexed variables
|
||||
for (int i = 0; i < this.args.length; i++) {
|
||||
setVariable("a" + i, this.args[i]);
|
||||
setVariable("p" + i, this.args[i]);
|
||||
}
|
||||
|
||||
String[] parameterNames = this.paramDiscoverer.getParameterNames(targetMethod);
|
||||
// save parameter names (if discovered)
|
||||
if (parameterNames != null) {
|
||||
for (int i = 0; i < parameterNames.length; i++) {
|
||||
setVariable(parameterNames[i], this.args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String toString(Method m) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(m.getDeclaringClass().getName());
|
||||
sb.append("#");
|
||||
sb.append(m.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright 2011 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.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* Simple {@link CacheOperationSource} implementation that allows attributes to be matched
|
||||
* by registered name.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
* <p>Static for optimal serialization.
|
||||
*/
|
||||
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
|
||||
* (or Strings to be converted to CacheOperation instances).
|
||||
* @see CacheOperation
|
||||
* @see CacheOperationEditor
|
||||
*/
|
||||
public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) {
|
||||
for (Map.Entry<String, Collection<CacheOperation>> entry : nameMap.entrySet()) {
|
||||
addCacheMethod(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute for a cacheable method.
|
||||
* <p>Method names can be exact matches, or of the pattern "xxx*",
|
||||
* "*xxx" or "*xxx*" for matching multiple methods.
|
||||
* @param methodName the name of the method
|
||||
* @param ops operation associated with the method
|
||||
*/
|
||||
public void addCacheMethod(String methodName, Collection<CacheOperation> ops) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding method [" + methodName + "] with cache operations [" + ops + "]");
|
||||
}
|
||||
this.nameMap.put(methodName, ops);
|
||||
}
|
||||
|
||||
public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
|
||||
// look for direct name match
|
||||
String methodName = method.getName();
|
||||
Collection<CacheOperation> ops = this.nameMap.get(methodName);
|
||||
|
||||
if (ops == null) {
|
||||
// Look for most specific name match.
|
||||
String bestNameMatch = null;
|
||||
for (String mappedName : this.nameMap.keySet()) {
|
||||
if (isMatch(methodName, mappedName)
|
||||
&& (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
|
||||
ops = this.nameMap.get(mappedName);
|
||||
bestNameMatch = mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given method name matches the mapped name.
|
||||
* <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
|
||||
* as well as direct equality. Can be overridden in subclasses.
|
||||
* @param methodName the method name of the class
|
||||
* @param mappedName the name in the descriptor
|
||||
* @return if the names match
|
||||
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
|
||||
*/
|
||||
protected boolean isMatch(String methodName, String mappedName) {
|
||||
return PatternMatchUtils.simpleMatch(mappedName, methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof NameMatchCacheOperationSource)) {
|
||||
return false;
|
||||
}
|
||||
NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other;
|
||||
return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return NameMatchCacheOperationSource.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + this.nameMap;
|
||||
}
|
||||
}
|
||||
6
spring-context/src/main/java/org/springframework/cache/interceptor/package-info.java
vendored
Normal file
6
spring-context/src/main/java/org/springframework/cache/interceptor/package-info.java
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* AOP-based solution for declarative caching demarcation.
|
||||
* Builds on the AOP infrastructure in org.springframework.aop.framework.
|
||||
* Any POJO can be cache-advised with Spring.
|
||||
*/
|
||||
package org.springframework.cache.interceptor;
|
||||
5
spring-context/src/main/java/org/springframework/cache/package-info.java
vendored
Normal file
5
spring-context/src/main/java/org/springframework/cache/package-info.java
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Spring's generic cache abstraction.
|
||||
* Concrete implementations are provided in the subpackages.
|
||||
*/
|
||||
package org.springframework.cache;
|
||||
79
spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java
vendored
Normal file
79
spring-context/src/main/java/org/springframework/cache/support/AbstractCacheManager.java
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class implementing the common {@link CacheManager}
|
||||
* methods. Useful for 'static' environments where the backing caches do
|
||||
* not change.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractCacheManager implements CacheManager, InitializingBean {
|
||||
|
||||
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();
|
||||
|
||||
private Set<String> cacheNames = new LinkedHashSet<String>();
|
||||
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Collection<? extends Cache> caches = loadCaches();
|
||||
Assert.notEmpty(caches, "loadCaches must not return an empty Collection");
|
||||
this.cacheMap.clear();
|
||||
|
||||
// preserve the initial order of the cache names
|
||||
for (Cache cache : caches) {
|
||||
this.cacheMap.put(cache.getName(), cache);
|
||||
this.cacheNames.add(cache.getName());
|
||||
}
|
||||
}
|
||||
|
||||
protected final void addCache(Cache cache) {
|
||||
this.cacheMap.put(cache.getName(), cache);
|
||||
this.cacheNames.add(cache.getName());
|
||||
}
|
||||
|
||||
public Cache getCache(String name) {
|
||||
return this.cacheMap.get(name);
|
||||
}
|
||||
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(this.cacheNames);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load the caches for this cache manager. Occurs at startup.
|
||||
* The returned collection must not be null.
|
||||
*/
|
||||
protected abstract Collection<? extends Cache> loadCaches();
|
||||
|
||||
}
|
||||
87
spring-context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java
vendored
Normal file
87
spring-context/src/main/java/org/springframework/cache/support/CompositeCacheManager.java
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Composite {@link CacheManager} implementation that iterates
|
||||
* over a given collection of {@link CacheManager} instances.
|
||||
*
|
||||
* Allows {@link NoOpCacheManager} to be automatically added to the list for handling
|
||||
* the cache declarations without a backing store.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CompositeCacheManager implements InitializingBean, CacheManager {
|
||||
|
||||
private List<CacheManager> cacheManagers;
|
||||
|
||||
private boolean fallbackToNoOpCache = false;
|
||||
|
||||
|
||||
public void setCacheManagers(Collection<CacheManager> cacheManagers) {
|
||||
Assert.notEmpty(cacheManagers, "cacheManagers Collection must not be empty");
|
||||
this.cacheManagers = new ArrayList<CacheManager>();
|
||||
this.cacheManagers.addAll(cacheManagers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether a {@link NoOpCacheManager} should be added at the end of the manager lists.
|
||||
* In this case, any {@code getCache} requests not handled by the configured cache managers will
|
||||
* be automatically handled by the {@link NoOpCacheManager} (and hence never return {@code null}).
|
||||
*/
|
||||
public void setFallbackToNoOpCache(boolean fallbackToNoOpCache) {
|
||||
this.fallbackToNoOpCache = fallbackToNoOpCache;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (this.fallbackToNoOpCache) {
|
||||
this.cacheManagers.add(new NoOpCacheManager());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Cache getCache(String name) {
|
||||
for (CacheManager cacheManager : this.cacheManagers) {
|
||||
Cache cache = cacheManager.getCache(name);
|
||||
if (cache != null) {
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<String> getCacheNames() {
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (CacheManager manager : this.cacheManagers) {
|
||||
names.addAll(manager.getCacheNames());
|
||||
}
|
||||
return Collections.unmodifiableList(names);
|
||||
}
|
||||
|
||||
}
|
||||
101
spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java
vendored
Normal file
101
spring-context/src/main/java/org/springframework/cache/support/NoOpCacheManager.java
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2011 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.support;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* A basic, no operation {@link CacheManager} implementation suitable
|
||||
* for disabling caching, typically used for backing cache declarations
|
||||
* without an actual backing store.
|
||||
*
|
||||
* <p>Will simply accept any items into the cache not actually storing them.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
* @see CompositeCacheManager
|
||||
*/
|
||||
public class NoOpCacheManager implements CacheManager {
|
||||
|
||||
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
|
||||
private Set<String> names = new LinkedHashSet<String>();
|
||||
|
||||
private static class NoOpCache implements Cache {
|
||||
|
||||
private final String name;
|
||||
|
||||
public NoOpCache(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
public void evict(Object key) {
|
||||
}
|
||||
|
||||
public ValueWrapper get(Object key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Object getNativeCache() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void put(Object key, Object value) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation always returns a {@link Cache} implementation that will not
|
||||
* store items. Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = caches.get(name);
|
||||
if (cache == null) {
|
||||
caches.putIfAbsent(name, new NoOpCache(name));
|
||||
synchronized (names) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(names);
|
||||
}
|
||||
}
|
||||
46
spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java
vendored
Normal file
46
spring-context/src/main/java/org/springframework/cache/support/SimpleCacheManager.java
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.support;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* Simple cache manager working against a given collection of caches.
|
||||
* Useful for testing or simple caching declarations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleCacheManager extends AbstractCacheManager {
|
||||
|
||||
private Collection<? extends Cache> caches;
|
||||
|
||||
/**
|
||||
* Specify the collection of Cache instances to use for this CacheManager.
|
||||
*/
|
||||
public void setCaches(Collection<? extends Cache> caches) {
|
||||
this.caches = caches;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<? extends Cache> loadCaches() {
|
||||
return this.caches;
|
||||
}
|
||||
|
||||
}
|
||||
49
spring-context/src/main/java/org/springframework/cache/support/SimpleValueWrapper.java
vendored
Normal file
49
spring-context/src/main/java/org/springframework/cache/support/SimpleValueWrapper.java
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.support;
|
||||
|
||||
import org.springframework.cache.Cache.ValueWrapper;
|
||||
|
||||
/**
|
||||
* Straightforward implementation of {@link org.springframework.cache.Cache.ValueWrapper},
|
||||
* simply holding the value as given at construction and returning it from {@link #get()}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class SimpleValueWrapper implements ValueWrapper {
|
||||
|
||||
private final Object value;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleValueWrapper instance for exposing the given value.
|
||||
* @param value the value to expose (may be <code>null</code>)
|
||||
*/
|
||||
public SimpleValueWrapper(Object value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simply returns the value as given at construction time.
|
||||
*/
|
||||
public Object get() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
5
spring-context/src/main/java/org/springframework/cache/support/package-info.java
vendored
Normal file
5
spring-context/src/main/java/org/springframework/cache/support/package-info.java
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Support classes for the the org.springframework.cache package.
|
||||
* Provides abstract classes for cache managers and caches.
|
||||
*/
|
||||
package org.springframework.cache.support;
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import org.springframework.beans.factory.HierarchicalBeanFactory;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
|
||||
/**
|
||||
* Central interface to provide configuration for an application.
|
||||
* This is read-only while the application is running, but may be
|
||||
* reloaded if the implementation supports this.
|
||||
*
|
||||
* <p>An ApplicationContext provides:
|
||||
* <ul>
|
||||
* <li>Bean factory methods for accessing application components.
|
||||
* Inherited from {@link org.springframework.beans.factory.ListableBeanFactory}.
|
||||
* <li>The ability to load file resources in a generic fashion.
|
||||
* Inherited from the {@link org.springframework.core.io.ResourceLoader} interface.
|
||||
* <li>The ability to publish events to registered listeners.
|
||||
* Inherited from the {@link ApplicationEventPublisher} interface.
|
||||
* <li>The ability to resolve messages, supporting internationalization.
|
||||
* Inherited from the {@link MessageSource} interface.
|
||||
* <li>Inheritance from a parent context. Definitions in a descendant context
|
||||
* will always take priority. This means, for example, that a single parent
|
||||
* context can be used by an entire web application, while each servlet has
|
||||
* its own child context that is independent of that of any other servlet.
|
||||
* </ul>
|
||||
*
|
||||
* <p>In addition to standard {@link org.springframework.beans.factory.BeanFactory}
|
||||
* lifecycle capabilities, ApplicationContext implementations detect and invoke
|
||||
* {@link ApplicationContextAware} beans as well as {@link ResourceLoaderAware},
|
||||
* {@link ApplicationEventPublisherAware} and {@link MessageSourceAware} beans.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see ConfigurableApplicationContext
|
||||
* @see org.springframework.beans.factory.BeanFactory
|
||||
* @see org.springframework.core.io.ResourceLoader
|
||||
*/
|
||||
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
|
||||
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
|
||||
|
||||
/**
|
||||
* Return the unique id of this application context.
|
||||
* @return the unique id of the context, or <code>null</code> if none
|
||||
*/
|
||||
String getId();
|
||||
|
||||
/**
|
||||
* Return a friendly name for this context.
|
||||
* @return a display name for this context (never <code>null</code>)
|
||||
*/
|
||||
String getDisplayName();
|
||||
|
||||
/**
|
||||
* Return the timestamp when this context was first loaded.
|
||||
* @return the timestamp (ms) when this context was first loaded
|
||||
*/
|
||||
long getStartupDate();
|
||||
|
||||
/**
|
||||
* Return the parent context, or <code>null</code> if there is no parent
|
||||
* and this is the root of the context hierarchy.
|
||||
* @return the parent context, or <code>null</code> if there is no parent
|
||||
*/
|
||||
ApplicationContext getParent();
|
||||
|
||||
/**
|
||||
* Expose AutowireCapableBeanFactory functionality for this context.
|
||||
* <p>This is not typically used by application code, except for the purpose
|
||||
* of initializing bean instances that live outside the application context,
|
||||
* applying the Spring bean lifecycle (fully or partly) to them.
|
||||
* <p>Alternatively, the internal BeanFactory exposed by the
|
||||
* {@link ConfigurableApplicationContext} interface offers access to the
|
||||
* AutowireCapableBeanFactory interface too. The present method mainly
|
||||
* serves as convenient, specific facility on the ApplicationContext
|
||||
* interface itself.
|
||||
* @return the AutowireCapableBeanFactory for this context
|
||||
* @throws IllegalStateException if the context does not support
|
||||
* the AutowireCapableBeanFactory interface or does not hold an autowire-capable
|
||||
* bean factory yet (usually if <code>refresh()</code> has never been called)
|
||||
* @see ConfigurableApplicationContext#refresh()
|
||||
* @see ConfigurableApplicationContext#getBeanFactory()
|
||||
*/
|
||||
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to be notified
|
||||
* of the {@link ApplicationContext} that it runs in.
|
||||
*
|
||||
* <p>Implementing this interface makes sense for example when an object
|
||||
* requires access to a set of collaborating beans. Note that configuration
|
||||
* via bean references is preferable to implementing this interface just
|
||||
* for bean lookup purposes.
|
||||
*
|
||||
* <p>This interface can also be implemented if an object needs access to file
|
||||
* resources, i.e. wants to call <code>getResource</code>, wants to publish
|
||||
* an application event, or requires access to the MessageSource. However,
|
||||
* it is preferable to implement the more specific {@link ResourceLoaderAware},
|
||||
* {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
|
||||
* in such a specific scenario.
|
||||
*
|
||||
* <p>Note that file resource dependencies can also be exposed as bean properties
|
||||
* of type {@link org.springframework.core.io.Resource}, populated via Strings
|
||||
* with automatic type conversion by the bean factory. This removes the need
|
||||
* for implementing any callback interface just for the purpose of accessing
|
||||
* a specific file resource.
|
||||
*
|
||||
* <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
|
||||
* convenience base class for application objects, implementing this interface.
|
||||
*
|
||||
* <p>For a list of all bean lifecycle methods, see the
|
||||
* {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @see ResourceLoaderAware
|
||||
* @see ApplicationEventPublisherAware
|
||||
* @see MessageSourceAware
|
||||
* @see org.springframework.context.support.ApplicationObjectSupport
|
||||
* @see org.springframework.beans.factory.BeanFactoryAware
|
||||
*/
|
||||
public interface ApplicationContextAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the ApplicationContext that this object runs in.
|
||||
* Normally this call will be used to initialize the object.
|
||||
* <p>Invoked after population of normal bean properties but before an init callback such
|
||||
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
|
||||
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
|
||||
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
|
||||
* {@link MessageSourceAware}, if applicable.
|
||||
* @param applicationContext the ApplicationContext object to be used by this object
|
||||
* @throws ApplicationContextException in case of context initialization errors
|
||||
* @throws BeansException if thrown by application context methods
|
||||
* @see org.springframework.beans.factory.BeanInitializationException
|
||||
*/
|
||||
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2002-2006 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;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
|
||||
/**
|
||||
* Exception thrown during application context initialization.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class ApplicationContextException extends FatalBeanException {
|
||||
|
||||
/**
|
||||
* Create a new <code>ApplicationContextException</code>
|
||||
* with the specified detail message and no root cause.
|
||||
* @param msg the detail message
|
||||
*/
|
||||
public ApplicationContextException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>ApplicationContextException</code>
|
||||
* with the specified detail message and the given root cause.
|
||||
* @param msg the detail message
|
||||
* @param cause the root cause
|
||||
*/
|
||||
public ApplicationContextException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Callback interface for initializing a Spring {@link ConfigurableApplicationContext}
|
||||
* prior to being {@linkplain ConfigurableApplicationContext#refresh() refreshed}.
|
||||
*
|
||||
* <p>Typically used within web applications that require some programmatic initialization
|
||||
* of the application context. For example, registering property sources or activating
|
||||
* profiles against the {@linkplain ConfigurableApplicationContext#getEnvironment()
|
||||
* context's environment}. See {@code ContextLoader} and {@code FrameworkServlet} support
|
||||
* for declaring a "contextInitializerClasses" context-param and init-param, respectively.
|
||||
*
|
||||
* <p>{@code ApplicationContextInitializer} processors are encouraged to detect
|
||||
* whether Spring's {@link org.springframework.core.Ordered Ordered} interface has been
|
||||
* implemented or if the @{@link org.springframework.core.annotation.Order Order}
|
||||
* annotation is present and to sort instances accordingly if so prior to invocation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see org.springframework.web.context.ContextLoader#customizeContext
|
||||
* @see org.springframework.web.context.ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM
|
||||
* @see org.springframework.web.servlet.FrameworkServlet#setContextInitializerClasses
|
||||
* @see org.springframework.web.servlet.FrameworkServlet#applyInitializers
|
||||
*/
|
||||
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
|
||||
|
||||
/**
|
||||
* Initialize the given application context.
|
||||
* @param applicationContext the application to configure
|
||||
*/
|
||||
void initialize(C applicationContext);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
* Class to be extended by all application events. Abstract as it
|
||||
* doesn't make sense for generic events to be published directly.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public abstract class ApplicationEvent extends EventObject {
|
||||
|
||||
/** use serialVersionUID from Spring 1.2 for interoperability */
|
||||
private static final long serialVersionUID = 7099057708183571937L;
|
||||
|
||||
/** System time when the event happened */
|
||||
private final long timestamp;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ApplicationEvent.
|
||||
* @param source the component that published the event (never <code>null</code>)
|
||||
*/
|
||||
public ApplicationEvent(Object source) {
|
||||
super(source);
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the system time in milliseconds when the event happened.
|
||||
*/
|
||||
public final long getTimestamp() {
|
||||
return this.timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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;
|
||||
|
||||
/**
|
||||
* Interface that encapsulates event publication functionality.
|
||||
* Serves as super-interface for ApplicationContext.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 1.1.1
|
||||
* @see ApplicationContext
|
||||
* @see ApplicationEventPublisherAware
|
||||
* @see org.springframework.context.ApplicationEvent
|
||||
* @see org.springframework.context.event.EventPublicationInterceptor
|
||||
*/
|
||||
public interface ApplicationEventPublisher {
|
||||
|
||||
/**
|
||||
* Notify all listeners registered with this application of an application
|
||||
* event. Events may be framework events (such as RequestHandledEvent)
|
||||
* or application-specific events.
|
||||
* @param event the event to publish
|
||||
* @see org.springframework.web.context.support.RequestHandledEvent
|
||||
*/
|
||||
void publishEvent(ApplicationEvent event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to be notified
|
||||
* of the ApplicationEventPublisher (typically the ApplicationContext)
|
||||
* that it runs in.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 1.1.1
|
||||
* @see ApplicationContextAware
|
||||
*/
|
||||
public interface ApplicationEventPublisherAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the ApplicationEventPublisher that this object runs in.
|
||||
* <p>Invoked after population of normal bean properties but before an init
|
||||
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
|
||||
* Invoked before ApplicationContextAware's setApplicationContext.
|
||||
* @param applicationEventPublisher event publisher to be used by this object
|
||||
*/
|
||||
void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by application event listeners.
|
||||
* Based on the standard <code>java.util.EventListener</code> interface
|
||||
* for the Observer design pattern.
|
||||
*
|
||||
* <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
|
||||
* that it is interested in. When registered with a Spring ApplicationContext, events
|
||||
* will be filtered accordingly, with the listener getting invoked for matching event
|
||||
* objects only.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @param <E> the specific ApplicationEvent subclass to listen to
|
||||
* @see org.springframework.context.event.ApplicationEventMulticaster
|
||||
*/
|
||||
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
|
||||
|
||||
/**
|
||||
* Handle an application event.
|
||||
* @param event the event to respond to
|
||||
*/
|
||||
void onApplicationEvent(E event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* SPI interface to be implemented by most if not all application contexts.
|
||||
* Provides facilities to configure an application context in addition
|
||||
* to the application context client methods in the
|
||||
* {@link org.springframework.context.ApplicationContext} interface.
|
||||
*
|
||||
* <p>Configuration and lifecycle methods are encapsulated here to avoid
|
||||
* making them obvious to ApplicationContext client code. The present
|
||||
* methods should only be used by startup and shutdown code.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 03.11.2003
|
||||
*/
|
||||
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle {
|
||||
|
||||
/**
|
||||
* Any number of these characters are considered delimiters between
|
||||
* multiple context config paths in a single String value.
|
||||
* @see org.springframework.context.support.AbstractXmlApplicationContext#setConfigLocation
|
||||
* @see org.springframework.web.context.ContextLoader#CONFIG_LOCATION_PARAM
|
||||
* @see org.springframework.web.servlet.FrameworkServlet#setContextConfigLocation
|
||||
*/
|
||||
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
|
||||
|
||||
/**
|
||||
* Name of the ConversionService bean in the factory.
|
||||
* If none is supplied, default conversion rules apply.
|
||||
* @see org.springframework.core.convert.ConversionService
|
||||
*/
|
||||
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
|
||||
|
||||
/**
|
||||
* Name of the LoadTimeWeaver bean in the factory. If such a bean is supplied,
|
||||
* the context will use a temporary ClassLoader for type matching, in order
|
||||
* to allow the LoadTimeWeaver to process all actual bean classes.
|
||||
* @see org.springframework.instrument.classloading.LoadTimeWeaver
|
||||
*/
|
||||
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
|
||||
|
||||
/**
|
||||
* Name of the {@link Environment} bean in the factory.
|
||||
*/
|
||||
String ENVIRONMENT_BEAN_NAME = "environment";
|
||||
|
||||
/**
|
||||
* Name of the System properties bean in the factory.
|
||||
* @see java.lang.System#getProperties()
|
||||
*/
|
||||
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
|
||||
|
||||
/**
|
||||
* Name of the System environment bean in the factory.
|
||||
* @see java.lang.System#getenv()
|
||||
*/
|
||||
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
|
||||
|
||||
|
||||
/**
|
||||
* Set the unique id of this application context.
|
||||
*/
|
||||
void setId(String id);
|
||||
|
||||
/**
|
||||
* Set the parent of this application context.
|
||||
* <p>Note that the parent shouldn't be changed: It should only be set outside
|
||||
* a constructor if it isn't available when an object of this class is created,
|
||||
* for example in case of WebApplicationContext setup.
|
||||
* @param parent the parent context
|
||||
* @see org.springframework.web.context.ConfigurableWebApplicationContext
|
||||
*/
|
||||
void setParent(ApplicationContext parent);
|
||||
|
||||
/**
|
||||
* Return the Environment for this application context in configurable form.
|
||||
*/
|
||||
ConfigurableEnvironment getEnvironment();
|
||||
|
||||
/**
|
||||
* Set the {@code Environment} for this application context.
|
||||
* @param environment the new environment
|
||||
*/
|
||||
void setEnvironment(ConfigurableEnvironment environment);
|
||||
|
||||
/**
|
||||
* Add a new BeanFactoryPostProcessor that will get applied to the internal
|
||||
* bean factory of this application context on refresh, before any of the
|
||||
* bean definitions get evaluated. To be invoked during context configuration.
|
||||
* @param beanFactoryPostProcessor the factory processor to register
|
||||
*/
|
||||
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
|
||||
|
||||
/**
|
||||
* Add a new ApplicationListener that will be notified on context events
|
||||
* such as context refresh and context shutdown.
|
||||
* <p>Note that any ApplicationListener registered here will be applied
|
||||
* on refresh if the context is not active yet, or on the fly with the
|
||||
* current event multicaster in case of a context that is already active.
|
||||
* @param listener the ApplicationListener to register
|
||||
* @see org.springframework.context.event.ContextRefreshedEvent
|
||||
* @see org.springframework.context.event.ContextClosedEvent
|
||||
*/
|
||||
void addApplicationListener(ApplicationListener<?> listener);
|
||||
|
||||
/**
|
||||
* Load or refresh the persistent representation of the configuration,
|
||||
* which might an XML file, properties file, or relational database schema.
|
||||
* <p>As this is a startup method, it should destroy already created singletons
|
||||
* if it fails, to avoid dangling resources. In other words, after invocation
|
||||
* of that method, either all or no singletons at all should be instantiated.
|
||||
* @throws BeansException if the bean factory could not be initialized
|
||||
* @throws IllegalStateException if already initialized and multiple refresh
|
||||
* attempts are not supported
|
||||
*/
|
||||
void refresh() throws BeansException, IllegalStateException;
|
||||
|
||||
/**
|
||||
* Register a shutdown hook with the JVM runtime, closing this context
|
||||
* on JVM shutdown unless it has already been closed at that time.
|
||||
* <p>This method can be called multiple times. Only one shutdown hook
|
||||
* (at max) will be registered for each context instance.
|
||||
* @see java.lang.Runtime#addShutdownHook
|
||||
* @see #close()
|
||||
*/
|
||||
void registerShutdownHook();
|
||||
|
||||
/**
|
||||
* Close this application context, releasing all resources and locks that the
|
||||
* implementation might hold. This includes destroying all cached singleton beans.
|
||||
* <p>Note: Does <i>not</i> invoke <code>close</code> on a parent context;
|
||||
* parent contexts have their own, independent lifecycle.
|
||||
* <p>This method can be called multiple times without side effects: Subsequent
|
||||
* <code>close</code> calls on an already closed context will be ignored.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Determine whether this application context is active, that is,
|
||||
* whether it has been refreshed at least once and has not been closed yet.
|
||||
* @return whether the context is still active
|
||||
* @see #refresh()
|
||||
* @see #close()
|
||||
* @see #getBeanFactory()
|
||||
*/
|
||||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Return the internal bean factory of this application context.
|
||||
* Can be used to access specific functionality of the underlying factory.
|
||||
* <p>Note: Do not use this to post-process the bean factory; singletons
|
||||
* will already have been instantiated before. Use a BeanFactoryPostProcessor
|
||||
* to intercept the BeanFactory setup process before beans get touched.
|
||||
* <p>Generally, this internal factory will only be accessible while the context
|
||||
* is active, that is, inbetween {@link #refresh()} and {@link #close()}.
|
||||
* The {@link #isActive()} flag can be used to check whether the context
|
||||
* is in an appropriate state.
|
||||
* @return the underlying bean factory
|
||||
* @throws IllegalStateException if the context does not hold an internal
|
||||
* bean factory (usually if {@link #refresh()} hasn't been called yet or
|
||||
* if {@link #close()} has already been called)
|
||||
* @see #isActive()
|
||||
* @see #refresh()
|
||||
* @see #close()
|
||||
* @see #addBeanFactoryPostProcessor
|
||||
*/
|
||||
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.util.StringValueResolver;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to be notified of a
|
||||
* <b>StringValueResolver</b> for the <b> resolution of embedded definition values.
|
||||
*
|
||||
* <p>This is an alternative to a full ConfigurableBeanFactory dependency via the
|
||||
* ApplicationContextAware/BeanFactoryAware interfaces.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0.3
|
||||
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#resolveEmbeddedValue
|
||||
*/
|
||||
public interface EmbeddedValueResolverAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the StringValueResolver to use for resolving embedded definition values.
|
||||
*/
|
||||
void setEmbeddedValueResolver(StringValueResolver resolver);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any bean that wishes to be notified
|
||||
* of the {@link Environment} that it runs in.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface EnvironmentAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the {@code Environment} that this object runs in.
|
||||
*/
|
||||
void setEnvironment(Environment environment);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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;
|
||||
|
||||
/**
|
||||
* Sub-interface of MessageSource to be implemented by objects that
|
||||
* can resolve messages hierarchically.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public interface HierarchicalMessageSource extends MessageSource {
|
||||
|
||||
/**
|
||||
* Set the parent that will be used to try to resolve messages
|
||||
* that this object can't resolve.
|
||||
* @param parent the parent MessageSource that will be used to
|
||||
* resolve messages that this object can't resolve.
|
||||
* May be <code>null</code>, in which case no further resolution is possible.
|
||||
*/
|
||||
void setParentMessageSource(MessageSource parent);
|
||||
|
||||
/**
|
||||
* Return the parent of this MessageSource, or <code>null</code> if none.
|
||||
*/
|
||||
MessageSource getParentMessageSource();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
/**
|
||||
* Interface defining methods for start/stop lifecycle control.
|
||||
* The typical use case for this is to control asynchronous processing.
|
||||
*
|
||||
* <p>Can be implemented by both components (typically a Spring bean defined in
|
||||
* a Spring {@link org.springframework.beans.factory.BeanFactory}) and containers
|
||||
* (typically a Spring {@link ApplicationContext}). Containers will propagate
|
||||
* start/stop signals to all components that apply.
|
||||
*
|
||||
* <p>Can be used for direct invocations or for management operations via JMX.
|
||||
* In the latter case, the {@link org.springframework.jmx.export.MBeanExporter}
|
||||
* will typically be defined with an
|
||||
* {@link org.springframework.jmx.export.assembler.InterfaceBasedMBeanInfoAssembler},
|
||||
* restricting the visibility of activity-controlled components to the Lifecycle
|
||||
* interface.
|
||||
*
|
||||
* <p>Note that the Lifecycle interface is only supported on <b>top-level singleton beans</b>.
|
||||
* On any other component, the Lifecycle interface will remain undetected and hence ignored.
|
||||
* Also, note that the extended {@link SmartLifecycle} interface provides more sophisticated
|
||||
* integration with the container's startup and shutdown phases.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SmartLifecycle
|
||||
* @see ConfigurableApplicationContext
|
||||
* @see org.springframework.jms.listener.AbstractMessageListenerContainer
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean
|
||||
*/
|
||||
public interface Lifecycle {
|
||||
|
||||
/**
|
||||
* Start this component.
|
||||
* Should not throw an exception if the component is already running.
|
||||
* <p>In the case of a container, this will propagate the start signal
|
||||
* to all components that apply.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop this component, typically in a synchronous fashion, such that
|
||||
* the component is fully stopped upon return of this method. Consider
|
||||
* implementing {@link SmartLifecycle} and its {@code stop(Runnable)}
|
||||
* variant in cases where asynchronous stop behavior is necessary.
|
||||
* <p>Should not throw an exception if the component isn't started yet.
|
||||
* <p>In the case of a container, this will propagate the stop signal
|
||||
* to all components that apply.
|
||||
* @see SmartLifecycle#stop(Runnable)
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Check whether this component is currently running.
|
||||
* <p>In the case of a container, this will return <code>true</code>
|
||||
* only if <i>all</i> components that apply are currently running.
|
||||
* @return whether the component is currently running
|
||||
*/
|
||||
boolean isRunning();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
/**
|
||||
* Strategy interface for processing Lifecycle beans within the ApplicationContext.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface LifecycleProcessor extends Lifecycle {
|
||||
|
||||
/**
|
||||
* Notification of context refresh, e.g. for auto-starting components.
|
||||
*/
|
||||
void onRefresh();
|
||||
|
||||
/**
|
||||
* Notification of context close phase, e.g. for auto-stopping components.
|
||||
*/
|
||||
void onClose();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Strategy interface for resolving messages, with support for the parameterization
|
||||
* and internationalization of such messages.
|
||||
*
|
||||
* <p>Spring provides two out-of-the-box implementations for production:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.context.support.ResourceBundleMessageSource},
|
||||
* built on top of the standard {@link java.util.ResourceBundle}
|
||||
* <li>{@link org.springframework.context.support.ReloadableResourceBundleMessageSource},
|
||||
* being able to reload message definitions without restarting the VM
|
||||
* </ul>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.context.support.ResourceBundleMessageSource
|
||||
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
*/
|
||||
public interface MessageSource {
|
||||
|
||||
/**
|
||||
* Try to resolve the message. Return default message if no message was found.
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet'. Users of
|
||||
* this class are encouraged to base message names on the relevant fully
|
||||
* qualified class name, thus avoiding conflict and ensuring maximum clarity.
|
||||
* @param args array of arguments that will be filled in for params within
|
||||
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
|
||||
* or <code>null</code> if none.
|
||||
* @param defaultMessage String to return if the lookup fails
|
||||
* @param locale the Locale in which to do the lookup
|
||||
* @return the resolved message if the lookup was successful;
|
||||
* otherwise the default message passed as a parameter
|
||||
* @see java.text.MessageFormat
|
||||
*/
|
||||
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
|
||||
|
||||
/**
|
||||
* Try to resolve the message. Treat as an error if the message can't be found.
|
||||
* @param code the code to lookup up, such as 'calculator.noRateSet'
|
||||
* @param args Array of arguments that will be filled in for params within
|
||||
* the message (params look like "{0}", "{1,date}", "{2,time}" within a message),
|
||||
* or <code>null</code> if none.
|
||||
* @param locale the Locale in which to do the lookup
|
||||
* @return the resolved message
|
||||
* @throws NoSuchMessageException if the message wasn't found
|
||||
* @see java.text.MessageFormat
|
||||
*/
|
||||
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
|
||||
|
||||
/**
|
||||
* Try to resolve the message using all the attributes contained within the
|
||||
* <code>MessageSourceResolvable</code> argument that was passed in.
|
||||
* <p>NOTE: We must throw a <code>NoSuchMessageException</code> on this method
|
||||
* since at the time of calling this method we aren't able to determine if the
|
||||
* <code>defaultMessage</code> property of the resolvable is null or not.
|
||||
* @param resolvable value object storing attributes required to properly resolve a message
|
||||
* @param locale the Locale in which to do the lookup
|
||||
* @return the resolved message
|
||||
* @throws NoSuchMessageException if the message wasn't found
|
||||
* @see java.text.MessageFormat
|
||||
*/
|
||||
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to be notified
|
||||
* of the MessageSource (typically the ApplicationContext) that it runs in.
|
||||
*
|
||||
* <p>Note that the MessageSource can usually also be passed on as bean
|
||||
* reference (to arbitrary bean properties or constructor arguments), because
|
||||
* it is defined as bean with name "messageSource" in the application context.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 1.1.1
|
||||
* @see ApplicationContextAware
|
||||
*/
|
||||
public interface MessageSourceAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the MessageSource that this object runs in.
|
||||
* <p>Invoked after population of normal bean properties but before an init
|
||||
* callback like InitializingBean's afterPropertiesSet or a custom init-method.
|
||||
* Invoked before ApplicationContextAware's setApplicationContext.
|
||||
* @param messageSource message sourceto be used by this object
|
||||
*/
|
||||
void setMessageSource(MessageSource messageSource);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2002-2010 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;
|
||||
|
||||
/**
|
||||
* Interface for objects that are suitable for message resolution in a
|
||||
* {@link MessageSource}.
|
||||
*
|
||||
* <p>Spring's own validation error classes implement this interface.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @see MessageSource#getMessage(MessageSourceResolvable, java.util.Locale)
|
||||
* @see org.springframework.validation.ObjectError
|
||||
* @see org.springframework.validation.FieldError
|
||||
*/
|
||||
public interface MessageSourceResolvable {
|
||||
|
||||
/**
|
||||
* Return the codes to be used to resolve this message, in the order that
|
||||
* they should get tried. The last code will therefore be the default one.
|
||||
* @return a String array of codes which are associated with this message
|
||||
*/
|
||||
String[] getCodes();
|
||||
|
||||
/**
|
||||
* Return the array of arguments to be used to resolve this message.
|
||||
* @return an array of objects to be used as parameters to replace
|
||||
* placeholders within the message text
|
||||
* @see java.text.MessageFormat
|
||||
*/
|
||||
Object[] getArguments();
|
||||
|
||||
/**
|
||||
* Return the default message to be used to resolve this message.
|
||||
* @return the default message, or <code>null</code> if no default
|
||||
*/
|
||||
String getDefaultMessage();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Exception thrown when a message can't be resolved.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
public class NoSuchMessageException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Create a new exception.
|
||||
* @param code code that could not be resolved for given locale
|
||||
* @param locale locale that was used to search for the code within
|
||||
*/
|
||||
public NoSuchMessageException(String code, Locale locale) {
|
||||
super("No message found under code '" + code + "' for locale '" + locale + "'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new exception.
|
||||
* @param code code that could not be resolved for given locale
|
||||
*/
|
||||
public NoSuchMessageException(String code) {
|
||||
super("No message found under code '" + code + "' for locale '" + Locale.getDefault() + "'.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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;
|
||||
|
||||
/**
|
||||
* Interface for objects that may participate in a phased
|
||||
* process such as lifecycle management.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
* @see SmartLifecycle
|
||||
*/
|
||||
public interface Phased {
|
||||
|
||||
/**
|
||||
* Return the phase value of this object.
|
||||
*/
|
||||
int getPhase();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by any object that wishes to be notified of
|
||||
* the <b>ResourceLoader</b> (typically the ApplicationContext) that it runs in.
|
||||
* This is an alternative to a full ApplicationContext dependency via the
|
||||
* ApplicationContextAware interface.
|
||||
*
|
||||
* <p>Note that Resource dependencies can also be exposed as bean properties
|
||||
* of type Resource, populated via Strings with automatic type conversion by
|
||||
* the bean factory. This removes the need for implementing any callback
|
||||
* interface just for the purpose of accessing a specific file resource.
|
||||
*
|
||||
* <p>You typically need a ResourceLoader when your application object has
|
||||
* to access a variety of file resources whose names are calculated. A good
|
||||
* strategy is to make the object use a DefaultResourceLoader but still
|
||||
* implement ResourceLoaderAware to allow for overriding when running in an
|
||||
* ApplicationContext. See ReloadableResourceBundleMessageSource for an example.
|
||||
*
|
||||
* <p>A passed-in ResourceLoader can also be checked for the
|
||||
* <b>ResourcePatternResolver</b> interface and cast accordingly, to be able
|
||||
* to resolve resource patterns into arrays of Resource objects. This will always
|
||||
* work when running in an ApplicationContext (the context interface extends
|
||||
* ResourcePatternResolver). Use a PathMatchingResourcePatternResolver as default.
|
||||
* See also the <code>ResourcePatternUtils.getResourcePatternResolver</code> method.
|
||||
*
|
||||
* <p>As alternative to a ResourcePatternResolver dependency, consider exposing
|
||||
* bean properties of type Resource array, populated via pattern Strings with
|
||||
* automatic type conversion by the bean factory.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 10.03.2004
|
||||
* @see ApplicationContextAware
|
||||
* @see org.springframework.beans.factory.InitializingBean
|
||||
* @see org.springframework.core.io.Resource
|
||||
* @see org.springframework.core.io.support.ResourcePatternResolver
|
||||
* @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
|
||||
* @see org.springframework.core.io.DefaultResourceLoader
|
||||
* @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
||||
* @see org.springframework.context.support.ReloadableResourceBundleMessageSource
|
||||
*/
|
||||
public interface ResourceLoaderAware extends Aware {
|
||||
|
||||
/**
|
||||
* Set the ResourceLoader that this object runs in.
|
||||
* <p>This might be a ResourcePatternResolver, which can be checked
|
||||
* through <code>instanceof ResourcePatternResolver</code>. See also the
|
||||
* <code>ResourcePatternUtils.getResourcePatternResolver</code> method.
|
||||
* <p>Invoked after population of normal bean properties but before an init callback
|
||||
* like InitializingBean's <code>afterPropertiesSet</code> or a custom init-method.
|
||||
* Invoked before ApplicationContextAware's <code>setApplicationContext</code>.
|
||||
* @param resourceLoader ResourceLoader object to be used by this object
|
||||
* @see org.springframework.core.io.support.ResourcePatternResolver
|
||||
* @see org.springframework.core.io.support.ResourcePatternUtils#getResourcePatternResolver
|
||||
*/
|
||||
void setResourceLoader(ResourceLoader resourceLoader);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* An extension of the {@link Lifecycle} interface for those objects that require to be
|
||||
* started upon ApplicationContext refresh and/or shutdown in a particular order.
|
||||
* The {@link #isAutoStartup()} return value indicates whether this object should
|
||||
* be started at the time of a context refresh. The callback-accepting
|
||||
* {@link #stop(Runnable)} method is useful for objects that have an asynchronous
|
||||
* shutdown process. Any implementation of this interface <i>must</i> invoke the
|
||||
* callback's run() method upon shutdown completion to avoid unnecessary delays
|
||||
* in the overall ApplicationContext shutdown.
|
||||
*
|
||||
* <p>This interface extends {@link Phased}, and the {@link #getPhase()} method's
|
||||
* return value indicates the phase within which this Lifecycle component should
|
||||
* be started and stopped. The startup process begins with the <i>lowest</i>
|
||||
* phase value and ends with the <i>highest</i> phase value (Integer.MIN_VALUE
|
||||
* is the lowest possible, and Integer.MAX_VALUE is the highest possible). The
|
||||
* shutdown process will apply the reverse order. Any components with the
|
||||
* same value will be arbitrarily ordered within the same phase.
|
||||
*
|
||||
* <p>Example: if component B depends on component A having already started, then
|
||||
* component A should have a lower phase value than component B. During the
|
||||
* shutdown process, component B would be stopped before component A.
|
||||
*
|
||||
* <p>Any explicit "depends-on" relationship will take precedence over
|
||||
* the phase order such that the dependent bean always starts after its
|
||||
* dependency and always stops before its dependency.
|
||||
*
|
||||
* <p>Any Lifecycle components within the context that do not also implement
|
||||
* SmartLifecycle will be treated as if they have a phase value of 0. That
|
||||
* way a SmartLifecycle implementation may start before those Lifecycle
|
||||
* components if it has a negative phase value, or it may start after
|
||||
* those components if it has a positive phase value.
|
||||
*
|
||||
* <p>Note that, due to the auto-startup support in SmartLifecycle,
|
||||
* a SmartLifecycle bean instance will get initialized on startup of the
|
||||
* application context in any case. As a consequence, the bean definition
|
||||
* lazy-init flag has very limited actual effect on SmartLifecycle beans.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface SmartLifecycle extends Lifecycle, Phased {
|
||||
|
||||
/**
|
||||
* Return whether this Lifecycle component should be started automatically
|
||||
* by the container when the ApplicationContext is refreshed. A value of
|
||||
* "false" indicates that the component is intended to be started manually.
|
||||
*/
|
||||
boolean isAutoStartup();
|
||||
|
||||
/**
|
||||
* Indicates that a Lifecycle component must stop if it is currently running.
|
||||
* <p>The provided callback is used by the {@link LifecycleProcessor} to support an
|
||||
* ordered, and potentially concurrent, shutdown of all components having a
|
||||
* common shutdown order value. The callback <b>must</b> be executed after
|
||||
* the SmartLifecycle component does indeed stop.
|
||||
* <p>The {@code LifecycleProcessor} will call <i>only</i> this variant of the
|
||||
* {@code stop} method; i.e. {@link Lifecycle#stop()} will not be called for
|
||||
* {@link SmartLifecycle} implementations unless explicitly delegated to within
|
||||
* this method.
|
||||
*/
|
||||
void stop(Runnable callback);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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.access;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* ApplicationContext-specific implementation of BeanFactoryReference,
|
||||
* wrapping a newly created ApplicationContext, closing it on release.
|
||||
*
|
||||
* <p>As per BeanFactoryReference contract, <code>release</code> may be called
|
||||
* more than once, with subsequent calls not doing anything. However, calling
|
||||
* <code>getFactory</code> after a <code>release</code> call will cause an exception.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Colin Sampaleanu
|
||||
* @since 13.02.2004
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close
|
||||
*/
|
||||
public class ContextBeanFactoryReference implements BeanFactoryReference {
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ContextBeanFactoryReference for the given context.
|
||||
* @param applicationContext the ApplicationContext to wrap
|
||||
*/
|
||||
public ContextBeanFactoryReference(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
|
||||
public BeanFactory getFactory() {
|
||||
if (this.applicationContext == null) {
|
||||
throw new IllegalStateException(
|
||||
"ApplicationContext owned by this BeanFactoryReference has been released");
|
||||
}
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
if (this.applicationContext != null) {
|
||||
ApplicationContext savedCtx;
|
||||
|
||||
// We don't actually guarantee thread-safety, but it's not a lot of extra work.
|
||||
synchronized (this) {
|
||||
savedCtx = this.applicationContext;
|
||||
this.applicationContext = null;
|
||||
}
|
||||
|
||||
if (savedCtx != null && savedCtx instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) savedCtx).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.access;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springframework.beans.factory.access.BootstrapException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* BeanFactoryLocator implementation that creates the BeanFactory from one or
|
||||
* more classpath locations specified in a JNDI environment variable.
|
||||
*
|
||||
* <p>This default implementation creates a
|
||||
* {@link org.springframework.context.support.ClassPathXmlApplicationContext}.
|
||||
* Subclasses may override {@link #createBeanFactory} for custom instantiation.
|
||||
*
|
||||
* @author Colin Sampaleanu
|
||||
* @author Juergen Hoeller
|
||||
* @see #createBeanFactory
|
||||
*/
|
||||
public class ContextJndiBeanFactoryLocator extends JndiLocatorSupport implements BeanFactoryLocator {
|
||||
|
||||
/**
|
||||
* Any number of these characters are considered delimiters between
|
||||
* multiple bean factory config paths in a single String value.
|
||||
*/
|
||||
public static final String BEAN_FACTORY_PATH_DELIMITERS = ",; \t\n";
|
||||
|
||||
|
||||
/**
|
||||
* Load/use a bean factory, as specified by a factory key which is a JNDI
|
||||
* address, of the form <code>java:comp/env/ejb/BeanFactoryPath</code>. The
|
||||
* contents of this JNDI location must be a string containing one or more
|
||||
* classpath resource names (separated by any of the delimiters '<code>,; \t\n</code>'
|
||||
* if there is more than one. The resulting BeanFactory (or ApplicationContext)
|
||||
* will be created from the combined resources.
|
||||
* @see #createBeanFactory
|
||||
*/
|
||||
public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {
|
||||
try {
|
||||
String beanFactoryPath = lookup(factoryKey, String.class);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Bean factory path from JNDI environment variable [" + factoryKey +
|
||||
"] is: " + beanFactoryPath);
|
||||
}
|
||||
String[] paths = StringUtils.tokenizeToStringArray(beanFactoryPath, BEAN_FACTORY_PATH_DELIMITERS);
|
||||
return createBeanFactory(paths);
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
throw new BootstrapException("Define an environment variable [" + factoryKey + "] containing " +
|
||||
"the class path locations of XML bean definition files", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the BeanFactory instance, given an array of class path resource Strings
|
||||
* which should be combined. This is split out as a separate method so that
|
||||
* subclasses can override the actual BeanFactory implementation class.
|
||||
* <p>Delegates to <code>createApplicationContext</code> by default,
|
||||
* wrapping the result in a ContextBeanFactoryReference.
|
||||
* @param resources an array of Strings representing classpath resource names
|
||||
* @return the created BeanFactory, wrapped in a BeanFactoryReference
|
||||
* (for example, a ContextBeanFactoryReference wrapping an ApplicationContext)
|
||||
* @throws BeansException if factory creation failed
|
||||
* @see #createApplicationContext
|
||||
* @see ContextBeanFactoryReference
|
||||
*/
|
||||
protected BeanFactoryReference createBeanFactory(String[] resources) throws BeansException {
|
||||
ApplicationContext ctx = createApplicationContext(resources);
|
||||
return new ContextBeanFactoryReference(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the ApplicationContext instance, given an array of class path resource
|
||||
* Strings which should be combined
|
||||
* @param resources an array of Strings representing classpath resource names
|
||||
* @return the created ApplicationContext
|
||||
* @throws BeansException if context creation failed
|
||||
*/
|
||||
protected ApplicationContext createApplicationContext(String[] resources) throws BeansException {
|
||||
return new ClassPathXmlApplicationContext(resources);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.access;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.SingletonBeanFactoryLocator;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternUtils;
|
||||
|
||||
/**
|
||||
* <p>Variant of {@link org.springframework.beans.factory.access.SingletonBeanFactoryLocator}
|
||||
* which creates its internal bean factory reference as an
|
||||
* {@link org.springframework.context.ApplicationContext} instead of
|
||||
* SingletonBeanFactoryLocator's simple BeanFactory. For almost all usage scenarios,
|
||||
* this will not make a difference, since within that ApplicationContext or BeanFactory
|
||||
* you are still free to define either BeanFactory or ApplicationContext instances.
|
||||
* The main reason one would need to use this class is if bean post-processing
|
||||
* (or other ApplicationContext specific features are needed in the bean reference
|
||||
* definition itself).
|
||||
*
|
||||
* <p><strong>Note:</strong> This class uses <strong>classpath*:beanRefContext.xml</strong>
|
||||
* as the default resource location for the bean factory reference definition files.
|
||||
* It is not possible nor legal to share definitions with SingletonBeanFactoryLocator
|
||||
* at the same time.
|
||||
*
|
||||
* @author Colin Sampaleanu
|
||||
* @author Juergen Hoeller
|
||||
* @see org.springframework.beans.factory.access.SingletonBeanFactoryLocator
|
||||
* @see org.springframework.context.access.DefaultLocatorFactory
|
||||
*/
|
||||
public class ContextSingletonBeanFactoryLocator extends SingletonBeanFactoryLocator {
|
||||
|
||||
private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefContext.xml";
|
||||
|
||||
/** The keyed singleton instances */
|
||||
private static final Map<String, BeanFactoryLocator> instances = new HashMap<String, BeanFactoryLocator>();
|
||||
|
||||
|
||||
/**
|
||||
* Returns an instance which uses the default "classpath*:beanRefContext.xml", as
|
||||
* the name of the definition file(s). All resources returned by the current
|
||||
* thread's context class loader's <code>getResources</code> method with this
|
||||
* name will be combined to create a definition, which is just a BeanFactory.
|
||||
* @return the corresponding BeanFactoryLocator instance
|
||||
* @throws BeansException in case of factory loading failure
|
||||
*/
|
||||
public static BeanFactoryLocator getInstance() throws BeansException {
|
||||
return getInstance(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance which uses the the specified selector, as the name of the
|
||||
* definition file(s). In the case of a name with a Spring "classpath*:" prefix,
|
||||
* or with no prefix, which is treated the same, the current thread's context class
|
||||
* loader's <code>getResources</code> method will be called with this value to get
|
||||
* all resources having that name. These resources will then be combined to form a
|
||||
* definition. In the case where the name uses a Spring "classpath:" prefix, or
|
||||
* a standard URL prefix, then only one resource file will be loaded as the
|
||||
* definition.
|
||||
* @param selector the location of the resource(s) which will be read and
|
||||
* combined to form the definition for the BeanFactoryLocator instance.
|
||||
* Any such files must form a valid ApplicationContext definition.
|
||||
* @return the corresponding BeanFactoryLocator instance
|
||||
* @throws BeansException in case of factory loading failure
|
||||
*/
|
||||
public static BeanFactoryLocator getInstance(String selector) throws BeansException {
|
||||
String resourceLocation = selector;
|
||||
if (resourceLocation == null) {
|
||||
resourceLocation = DEFAULT_RESOURCE_LOCATION;
|
||||
}
|
||||
|
||||
// For backwards compatibility, we prepend "classpath*:" to the selector name if there
|
||||
// is no other prefix (i.e. "classpath*:", "classpath:", or some URL prefix).
|
||||
if (!ResourcePatternUtils.isUrl(resourceLocation)) {
|
||||
resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation;
|
||||
}
|
||||
|
||||
synchronized (instances) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("ContextSingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
|
||||
instances.hashCode() + ", instances=" + instances);
|
||||
}
|
||||
BeanFactoryLocator bfl = instances.get(resourceLocation);
|
||||
if (bfl == null) {
|
||||
bfl = new ContextSingletonBeanFactoryLocator(resourceLocation);
|
||||
instances.put(resourceLocation, bfl);
|
||||
}
|
||||
return bfl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor which uses the the specified name as the resource name
|
||||
* of the definition file(s).
|
||||
* @param resourceLocation the Spring resource location to use
|
||||
* (either a URL or a "classpath:" / "classpath*:" pseudo URL)
|
||||
*/
|
||||
protected ContextSingletonBeanFactoryLocator(String resourceLocation) {
|
||||
super(resourceLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default method to create definition object as an ApplicationContext
|
||||
* instead of the default BeanFactory. This does not affect what can actually
|
||||
* be loaded by that definition.
|
||||
* <p>The default implementation simply builds a
|
||||
* {@link org.springframework.context.support.ClassPathXmlApplicationContext}.
|
||||
*/
|
||||
@Override
|
||||
protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
|
||||
return new ClassPathXmlApplicationContext(new String[] {resourceLocation}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default method to refresh the ApplicationContext, invoking
|
||||
* {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.refresh()}.
|
||||
*/
|
||||
@Override
|
||||
protected void initializeDefinition(BeanFactory groupDef) {
|
||||
if (groupDef instanceof ConfigurableApplicationContext) {
|
||||
((ConfigurableApplicationContext) groupDef).refresh();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the default method to operate on an ApplicationContext, invoking
|
||||
* {@link ConfigurableApplicationContext#refresh ConfigurableApplicationContext.close()}.
|
||||
*/
|
||||
@Override
|
||||
protected void destroyDefinition(BeanFactory groupDef, String selector) {
|
||||
if (groupDef instanceof ConfigurableApplicationContext) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Context group with selector '" + selector +
|
||||
"' being released, as there are no more references to it");
|
||||
}
|
||||
((ConfigurableApplicationContext) groupDef).close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2002-2005 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.access;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
|
||||
/**
|
||||
* A factory class to get a default ContextSingletonBeanFactoryLocator instance.
|
||||
*
|
||||
* @author Colin Sampaleanu
|
||||
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
|
||||
*/
|
||||
public class DefaultLocatorFactory {
|
||||
|
||||
/**
|
||||
* Return an instance object implementing BeanFactoryLocator. This will normally
|
||||
* be a singleton instance of the specific ContextSingletonBeanFactoryLocator class,
|
||||
* using the default resource selector.
|
||||
*/
|
||||
public static BeanFactoryLocator getInstance() throws FatalBeanException {
|
||||
return ContextSingletonBeanFactoryLocator.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an instance object implementing BeanFactoryLocator. This will normally
|
||||
* be a singleton instance of the specific ContextSingletonBeanFactoryLocator class,
|
||||
* using the specified resource selector.
|
||||
* @param selector a selector variable which provides a hint to the factory as to
|
||||
* which instance to return.
|
||||
*/
|
||||
public static BeanFactoryLocator getInstance(String selector) throws FatalBeanException {
|
||||
return ContextSingletonBeanFactoryLocator.getInstance(selector);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Helper infrastructure to locate and access shared application contexts.
|
||||
*
|
||||
* <p><b>Note: This package is only relevant for special sharing of application
|
||||
* contexts, for example behind EJB facades. It is <i>not</i> used in a typical
|
||||
* web application or standalone application.</b>
|
||||
*
|
||||
*/
|
||||
package org.springframework.context.access;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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;
|
||||
|
||||
/**
|
||||
* Enumeration used to determine whether JDK proxy-based or AspectJ weaving-based advice
|
||||
* should be applied.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see org.springframework.scheduling.annotation.EnableAsync#mode()
|
||||
* @see org.springframework.scheduling.annotation.AsyncConfigurationSelector#selectImports
|
||||
* @see org.springframework.transaction.annotation.EnableTransactionManagement#mode()
|
||||
*/
|
||||
public enum AdviceMode {
|
||||
PROXY,
|
||||
ASPECTJ
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Annotation;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenient base class for {@link ImportSelector} implementations that select imports
|
||||
* based on an {@link AdviceMode} value from an annotation (such as the {@code @Enable*}
|
||||
* annotations).
|
||||
*
|
||||
* @param <A> Annotation containing {@linkplain #getAdviceModeAttributeName() AdviceMode
|
||||
* attribute}
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AdviceModeImportSelector<A extends Annotation> implements ImportSelector {
|
||||
|
||||
public static final String DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME = "mode";
|
||||
|
||||
/**
|
||||
* The name of the {@link AdviceMode} attribute for the annotation specified by the
|
||||
* generic type {@code A}. The default is {@value #DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME},
|
||||
* but subclasses may override in order to customize.
|
||||
*/
|
||||
protected String getAdviceModeAttributeName() {
|
||||
return DEFAULT_ADVICE_MODE_ATTRIBUTE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>This implementation resolves the type of annotation from generic metadata and
|
||||
* validates that (a) the annotation is in fact present on the importing
|
||||
* {@code @Configuration} class and (b) that the given annotation has an
|
||||
* {@linkplain #getAdviceModeAttributeName() advice mode attribute} of type
|
||||
* {@link AdviceMode}.
|
||||
*
|
||||
* <p>The {@link #selectImports(AdviceMode)} method is then invoked, allowing the
|
||||
* concrete implementation to choose imports in a safe and convenient fashion.
|
||||
*
|
||||
* @throws IllegalArgumentException if expected annotation {@code A} is not present
|
||||
* on the importing {@code @Configuration} class or if {@link #selectImports(AdviceMode)}
|
||||
* returns {@code null}
|
||||
*/
|
||||
public final String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
Class<?> annoType = GenericTypeResolver.resolveTypeArgument(getClass(), AdviceModeImportSelector.class);
|
||||
|
||||
Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(annoType.getName());
|
||||
Assert.notNull(attributes, String.format(
|
||||
"@%s is not present on importing class '%s' as expected",
|
||||
annoType.getSimpleName(), importingClassMetadata.getClassName()));
|
||||
|
||||
String modeAttrName = getAdviceModeAttributeName();
|
||||
Assert.hasText(modeAttrName);
|
||||
|
||||
Object adviceMode = attributes.get(modeAttrName);
|
||||
Assert.notNull(adviceMode, String.format(
|
||||
"Advice mode attribute @%s#%s() does not exist",
|
||||
annoType.getSimpleName(), modeAttrName));
|
||||
|
||||
Assert.isInstanceOf(AdviceMode.class, adviceMode, String.format(
|
||||
"Incorrect type for advice mode attribute '@%s#%s()': ",
|
||||
annoType.getSimpleName(), modeAttrName));
|
||||
|
||||
String[] imports = selectImports((AdviceMode) adviceMode);
|
||||
Assert.notNull(imports, String.format("Unknown AdviceMode: '%s'", adviceMode));
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which classes should be imported based on the given {@code AdviceMode}.
|
||||
*
|
||||
* <p>Returning {@code null} from this method indicates that the {@code AdviceMode} could
|
||||
* not be handled or was unknown and that an {@code IllegalArgumentException} should
|
||||
* be thrown.
|
||||
*
|
||||
* @param adviceMode the value of the {@linkplain #getAdviceModeAttributeName()
|
||||
* advice mode attribute} for the annotation specified via generics.
|
||||
*
|
||||
* @return array containing classes to import; empty array if none, {@code null} if
|
||||
* the given {@code AdviceMode} is unknown.
|
||||
*/
|
||||
protected abstract String[] selectImports(AdviceMode adviceMode);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Annotation;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Convenient adapter for programmatic registration of annotated bean classes.
|
||||
* This is an alternative to {@link ClassPathBeanDefinitionScanner}, applying
|
||||
* the same resolution of annotations but for explicitly registered classes only.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
* @see AnnotationConfigApplicationContext#register
|
||||
*/
|
||||
public class AnnotatedBeanDefinitionReader {
|
||||
|
||||
private final BeanDefinitionRegistry registry;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
|
||||
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
|
||||
|
||||
/**
|
||||
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
|
||||
* If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext},
|
||||
* the {@link Environment} will be inherited, otherwise a new
|
||||
* {@link StandardEnvironment} will be created and used.
|
||||
* @param registry the {@code BeanFactory} to load bean definitions into,
|
||||
* in the form of a {@code BeanDefinitionRegistry}
|
||||
* @see #AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment)
|
||||
* @see #setEnvironment(Environment)
|
||||
*/
|
||||
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
|
||||
this(registry, getOrCreateEnvironment(registry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code AnnotatedBeanDefinitionReader} for the given registry and using
|
||||
* the given {@link Environment}.
|
||||
* @param registry the {@code BeanFactory} to load bean definitions into,
|
||||
* in the form of a {@code BeanDefinitionRegistry}
|
||||
* @param environment the {@code Environment} to use when evaluating bean definition
|
||||
* profiles.
|
||||
* @since 3.1
|
||||
*/
|
||||
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
Assert.notNull(environment, "Environment must not be null");
|
||||
|
||||
this.registry = registry;
|
||||
this.environment = environment;
|
||||
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BeanDefinitionRegistry that this scanner operates on.
|
||||
*/
|
||||
public final BeanDefinitionRegistry getRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Environment to use when evaluating whether
|
||||
* {@link Profile @Profile}-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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the BeanNameGenerator to use for detected bean classes.
|
||||
* <p>The default is a {@link AnnotationBeanNameGenerator}.
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ScopeMetadataResolver to use for detected bean classes.
|
||||
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver
|
||||
: new AnnotationScopeMetadataResolver());
|
||||
}
|
||||
|
||||
public void register(Class<?>... annotatedClasses) {
|
||||
for (Class<?> annotatedClass : annotatedClasses) {
|
||||
registerBean(annotatedClass);
|
||||
}
|
||||
}
|
||||
|
||||
public void registerBean(Class<?> annotatedClass) {
|
||||
registerBean(annotatedClass, null, (Class<? extends Annotation>[]) null);
|
||||
}
|
||||
|
||||
public void registerBean(Class<?> annotatedClass, Class<? extends Annotation>... qualifiers) {
|
||||
registerBean(annotatedClass, null, qualifiers);
|
||||
}
|
||||
|
||||
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
|
||||
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
|
||||
AnnotationMetadata metadata = abd.getMetadata();
|
||||
|
||||
if (ProfileHelper.isProfileAnnotationPresent(metadata)) {
|
||||
if (!this.environment.acceptsProfiles(ProfileHelper.getCandidateProfiles(metadata))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
|
||||
abd.setScope(scopeMetadata.getScopeName());
|
||||
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
|
||||
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
|
||||
if (qualifiers != null) {
|
||||
for (Class<? extends Annotation> qualifier : qualifiers) {
|
||||
if (Primary.class.equals(qualifier)) {
|
||||
abd.setPrimary(true);
|
||||
} else if (Lazy.class.equals(qualifier)) {
|
||||
abd.setLazyInit(true);
|
||||
} else {
|
||||
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
|
||||
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Environment from the given registry if possible, otherwise return a new
|
||||
* StandardEnvironment.
|
||||
*/
|
||||
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
if (registry instanceof EnvironmentCapable) {
|
||||
return ((EnvironmentCapable) registry).getEnvironment();
|
||||
}
|
||||
return new StandardEnvironment();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.beans.Introspector;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.support.BeanNameGenerator}
|
||||
* implementation for bean classes annotated with the
|
||||
* {@link org.springframework.stereotype.Component @Component} annotation
|
||||
* or with another annotation that is itself annotated with
|
||||
* {@link org.springframework.stereotype.Component @Component} as a
|
||||
* meta-annotation. For example, Spring's stereotype annotations (such as
|
||||
* {@link org.springframework.stereotype.Repository @Repository}) are
|
||||
* themselves annotated with
|
||||
* {@link org.springframework.stereotype.Component @Component}.
|
||||
*
|
||||
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
|
||||
* JSR-330's {@link javax.inject.Named} annotations, if available. Note that
|
||||
* Spring component annotations always override such standard annotations.
|
||||
*
|
||||
* <p>If the annotation's value doesn't indicate a bean name, an appropriate
|
||||
* name will be built based on the short name of the class (with the first
|
||||
* letter lower-cased). For example:
|
||||
*
|
||||
* <pre class="code">com.xyz.FooServiceImpl -> fooServiceImpl</pre>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Mark Fisher
|
||||
* @since 2.5
|
||||
* @see org.springframework.stereotype.Component#value()
|
||||
* @see org.springframework.stereotype.Repository#value()
|
||||
* @see org.springframework.stereotype.Service#value()
|
||||
* @see org.springframework.stereotype.Controller#value()
|
||||
* @see javax.inject.Named#value()
|
||||
*/
|
||||
public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
|
||||
private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
|
||||
|
||||
|
||||
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
|
||||
if (StringUtils.hasText(beanName)) {
|
||||
// Explicit bean name found.
|
||||
return beanName;
|
||||
}
|
||||
}
|
||||
// Fallback: generate a unique default bean name.
|
||||
return buildDefaultBeanName(definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a bean name from one of the annotations on the class.
|
||||
* @param annotatedDef the annotation-aware bean definition
|
||||
* @return the bean name, or <code>null</code> if none is found
|
||||
*/
|
||||
protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
|
||||
AnnotationMetadata amd = annotatedDef.getMetadata();
|
||||
Set<String> types = amd.getAnnotationTypes();
|
||||
String beanName = null;
|
||||
for (String type : types) {
|
||||
Map<String, Object> attributes = amd.getAnnotationAttributes(type);
|
||||
if (isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
|
||||
String value = (String) attributes.get("value");
|
||||
if (StringUtils.hasLength(value)) {
|
||||
if (beanName != null && !value.equals(beanName)) {
|
||||
throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
|
||||
"component names: '" + beanName + "' versus '" + value + "'");
|
||||
}
|
||||
beanName = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return beanName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given annotation is a stereotype that is allowed
|
||||
* to suggest a component name through its annotation <code>value()</code>.
|
||||
* @param annotationType the name of the annotation class to check
|
||||
* @param metaAnnotationTypes the names of meta-annotations on the given annotation
|
||||
* @param attributes the map of attributes for the given annotation
|
||||
* @return whether the annotation qualifies as a stereotype with component name
|
||||
*/
|
||||
protected boolean isStereotypeWithNameValue(String annotationType,
|
||||
Set<String> metaAnnotationTypes, Map<String, Object> attributes) {
|
||||
|
||||
boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
|
||||
(metaAnnotationTypes != null && metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME)) ||
|
||||
annotationType.equals("javax.annotation.ManagedBean") ||
|
||||
annotationType.equals("javax.inject.Named");
|
||||
return (isStereotype && attributes != null && attributes.containsKey("value"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a default bean name from the given bean definition.
|
||||
* <p>The default implementation simply builds a decapitalized version
|
||||
* of the short class name: e.g. "mypackage.MyJdbcDao" -> "myJdbcDao".
|
||||
* <p>Note that inner classes will thus have names of the form
|
||||
* "outerClassName.innerClassName", which because of the period in the
|
||||
* name may be an issue if you are autowiring by name.
|
||||
* @param definition the bean definition to build a bean name for
|
||||
* @return the default bean name (never <code>null</code>)
|
||||
*/
|
||||
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||
String shortClassName = ClassUtils.getShortName(definition.getBeanClassName());
|
||||
return Introspector.decapitalize(shortClassName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Standalone application context, accepting annotated classes as input - in particular
|
||||
* {@link Configuration @Configuration}-annotated classes, but also plain
|
||||
* {@link org.springframework.stereotype.Component @Component} types and JSR-330 compliant
|
||||
* classes using {@code javax.inject} annotations. Allows for registering classes one by
|
||||
* one using {@link #register(Class...)} as well as for classpath scanning using
|
||||
* {@link #scan(String...)}.
|
||||
*
|
||||
* <p>In case of multiple {@code @Configuration} classes, @{@link Bean} methods defined in
|
||||
* later classes will override those defined in earlier classes. This can be leveraged to
|
||||
* deliberately override certain bean definitions via an extra {@code @Configuration}
|
||||
* class.
|
||||
*
|
||||
* <p>See @{@link Configuration} Javadoc for usage examples.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see #register
|
||||
* @see #scan
|
||||
* @see AnnotatedBeanDefinitionReader
|
||||
* @see ClassPathBeanDefinitionScanner
|
||||
* @see org.springframework.context.support.GenericXmlApplicationContext
|
||||
*/
|
||||
public class AnnotationConfigApplicationContext extends GenericApplicationContext {
|
||||
|
||||
private final AnnotatedBeanDefinitionReader reader;
|
||||
|
||||
private final ClassPathBeanDefinitionScanner scanner;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new AnnotationConfigApplicationContext that needs to be populated
|
||||
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
|
||||
*/
|
||||
public AnnotationConfigApplicationContext() {
|
||||
this.reader = new AnnotatedBeanDefinitionReader(this);
|
||||
this.scanner = new ClassPathBeanDefinitionScanner(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AnnotationConfigApplicationContext, deriving bean definitions
|
||||
* from the given annotated classes and automatically refreshing the context.
|
||||
* @param annotatedClasses one or more annotated classes,
|
||||
* e.g. {@link Configuration @Configuration} classes
|
||||
*/
|
||||
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
|
||||
this();
|
||||
register(annotatedClasses);
|
||||
refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AnnotationConfigApplicationContext, scanning for bean definitions
|
||||
* in the given packages and automatically refreshing the context.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
*/
|
||||
public AnnotationConfigApplicationContext(String... basePackages) {
|
||||
this();
|
||||
scan(basePackages);
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>Delegates given environment to underlying {@link AnnotatedBeanDefinitionReader}
|
||||
* and {@link ClassPathBeanDefinitionScanner} members.
|
||||
*/
|
||||
@Override
|
||||
public void setEnvironment(ConfigurableEnvironment environment) {
|
||||
super.setEnvironment(environment);
|
||||
this.reader.setEnvironment(environment);
|
||||
this.scanner.setEnvironment(environment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a custom {@link BeanNameGenerator} for use with {@link AnnotatedBeanDefinitionReader}
|
||||
* and/or {@link ClassPathBeanDefinitionScanner}, if any.
|
||||
* <p>Default is {@link org.springframework.context.annotation.AnnotationBeanNameGenerator}.
|
||||
* <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
|
||||
* and/or {@link #scan(String...)}.
|
||||
* @see AnnotatedBeanDefinitionReader#setBeanNameGenerator
|
||||
* @see ClassPathBeanDefinitionScanner#setBeanNameGenerator
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.reader.setBeanNameGenerator(beanNameGenerator);
|
||||
this.scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link ScopeMetadataResolver} to use for detected bean classes.
|
||||
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
|
||||
* <p>Any call to this method must occur prior to calls to {@link #register(Class...)}
|
||||
* and/or {@link #scan(String...)}.
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.reader.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register one or more annotated classes to be processed.
|
||||
* Note that {@link #refresh()} must be called in order for the context to fully
|
||||
* process the new class.
|
||||
* <p>Calls to {@link #register} are idempotent; adding the same
|
||||
* annotated class more than once has no additional effect.
|
||||
* @param annotatedClasses one or more annotated classes,
|
||||
* e.g. {@link Configuration @Configuration} classes
|
||||
* @see #scan(String...)
|
||||
* @see #refresh()
|
||||
*/
|
||||
public void register(Class<?>... annotatedClasses) {
|
||||
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
|
||||
this.reader.register(annotatedClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a scan within the specified base packages.
|
||||
* Note that {@link #refresh()} must be called in order for the context to
|
||||
* fully process the new class.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @see #register(Class...)
|
||||
* @see #refresh()
|
||||
*/
|
||||
public void scan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
this.scanner.scan(basePackages);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.Set;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
|
||||
/**
|
||||
* Parser for the <context:annotation-config/> element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Christian Dupuis
|
||||
* @since 2.5
|
||||
* @see AnnotationConfigUtils
|
||||
*/
|
||||
public class AnnotationConfigBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
Object source = parserContext.extractSource(element);
|
||||
|
||||
// Obtain bean definitions for all relevant BeanPostProcessors.
|
||||
Set<BeanDefinitionHolder> processorDefinitions =
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);
|
||||
|
||||
// Register component for the surrounding <context:annotation-config> element.
|
||||
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
parserContext.pushContainingComponent(compDefinition);
|
||||
|
||||
// Nest the concrete beans in the surrounding component.
|
||||
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
|
||||
parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
|
||||
}
|
||||
|
||||
// Finally register the composite component.
|
||||
parserContext.popAndRegisterContainingComponent();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Utility class that allows for convenient registration of common
|
||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor} and
|
||||
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor}
|
||||
* definitions for annotation-based configuration.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 2.5
|
||||
* @see CommonAnnotationBeanPostProcessor
|
||||
* @see org.springframework.context.annotation.ConfigurationClassPostProcessor
|
||||
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
||||
* @see org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor
|
||||
* @see org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
|
||||
*/
|
||||
public class AnnotationConfigUtils {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Configuration annotation processor.
|
||||
*/
|
||||
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Autowired annotation processor.
|
||||
*/
|
||||
public static final String AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalAutowiredAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Required annotation processor.
|
||||
*/
|
||||
public static final String REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalRequiredAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JSR-250 annotation processor.
|
||||
*/
|
||||
public static final String COMMON_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalCommonAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Scheduled annotation processor.
|
||||
*/
|
||||
public static final String SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalScheduledAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed Async annotation processor.
|
||||
*/
|
||||
public static final String ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalAsyncAnnotationProcessor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed AspectJ async execution aspect.
|
||||
*/
|
||||
public static final String ASYNC_EXECUTION_ASPECT_BEAN_NAME =
|
||||
"org.springframework.scheduling.config.internalAsyncExecutionAspect";
|
||||
|
||||
/**
|
||||
* The class name of the AspectJ async execution aspect.
|
||||
*/
|
||||
public static final String ASYNC_EXECUTION_ASPECT_CLASS_NAME =
|
||||
"org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect";
|
||||
|
||||
/**
|
||||
* The name of the AspectJ async execution aspect @{@code Configuration} class.
|
||||
*/
|
||||
public static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
|
||||
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed cache advisor.
|
||||
*/
|
||||
public static final String CACHE_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.cache.config.internalCacheAdvisor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed cache aspect.
|
||||
*/
|
||||
public static final String CACHE_ASPECT_BEAN_NAME =
|
||||
"org.springframework.cache.config.internalCacheAspect";
|
||||
|
||||
/**
|
||||
* The class name of the AspectJ caching aspect.
|
||||
*/
|
||||
public static final String CACHE_ASPECT_CLASS_NAME =
|
||||
"org.springframework.cache.aspectj.AnnotationCacheAspect";
|
||||
|
||||
/**
|
||||
* The name of the AspectJ caching aspect @{@code Configuration} class.
|
||||
*/
|
||||
public static final String CACHE_ASPECT_CONFIGURATION_CLASS_NAME =
|
||||
"org.springframework.cache.aspectj.AspectJCachingConfiguration";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed JPA annotation processor.
|
||||
*/
|
||||
public static final String PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME =
|
||||
"org.springframework.context.annotation.internalPersistenceAnnotationProcessor";
|
||||
|
||||
|
||||
private static final String PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME =
|
||||
"org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor";
|
||||
|
||||
|
||||
private static final boolean jsr250Present =
|
||||
ClassUtils.isPresent("javax.annotation.Resource", AnnotationConfigUtils.class.getClassLoader());
|
||||
|
||||
private static final boolean jpaPresent =
|
||||
ClassUtils.isPresent("javax.persistence.EntityManagerFactory", AnnotationConfigUtils.class.getClassLoader()) &&
|
||||
ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, AnnotationConfigUtils.class.getClassLoader());
|
||||
|
||||
|
||||
/**
|
||||
* Register all relevant annotation post processors in the given registry.
|
||||
* @param registry the registry to operate on
|
||||
*/
|
||||
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
|
||||
registerAnnotationConfigProcessors(registry, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register all relevant annotation post processors in the given registry.
|
||||
* @param registry the registry to operate on
|
||||
* @param source the configuration source element (already extracted)
|
||||
* that this registration was triggered from. May be <code>null</code>.
|
||||
* @return a Set of BeanDefinitionHolders, containing all bean definitions
|
||||
* that have actually been registered by this call
|
||||
*/
|
||||
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
|
||||
BeanDefinitionRegistry registry, Object source) {
|
||||
|
||||
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);
|
||||
|
||||
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
|
||||
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
// Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
|
||||
if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
try {
|
||||
ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
|
||||
def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
|
||||
}
|
||||
def.setSource(source);
|
||||
beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
|
||||
}
|
||||
|
||||
return beanDefs;
|
||||
}
|
||||
|
||||
private static BeanDefinitionHolder registerPostProcessor(
|
||||
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
|
||||
|
||||
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(beanName, definition);
|
||||
return new BeanDefinitionHolder(definition, beanName);
|
||||
}
|
||||
|
||||
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
|
||||
if (abd.getMetadata().isAnnotated(Primary.class.getName())) {
|
||||
abd.setPrimary(true);
|
||||
}
|
||||
if (abd.getMetadata().isAnnotated(Lazy.class.getName())) {
|
||||
Boolean value = (Boolean) abd.getMetadata().getAnnotationAttributes(Lazy.class.getName()).get("value");
|
||||
abd.setLazyInit(value);
|
||||
}
|
||||
if (abd.getMetadata().isAnnotated(DependsOn.class.getName())) {
|
||||
String[] value = (String[]) abd.getMetadata().getAnnotationAttributes(DependsOn.class.getName()).get("value");
|
||||
abd.setDependsOn(value);
|
||||
}
|
||||
if (abd instanceof AbstractBeanDefinition) {
|
||||
if (abd.getMetadata().isAnnotated(Role.class.getName())) {
|
||||
int value = (Integer) abd.getMetadata().getAnnotationAttributes(Role.class.getName()).get("value");
|
||||
((AbstractBeanDefinition)abd).setRole(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static BeanDefinitionHolder applyScopedProxyMode(
|
||||
ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
|
||||
|
||||
ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
|
||||
if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
|
||||
return definition;
|
||||
}
|
||||
boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
|
||||
return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Annotation;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link ScopeMetadataResolver} implementation that by default checks for
|
||||
* the presence of Spring's {@link Scope} annotation on the bean class.
|
||||
*
|
||||
* <p>The exact type of annotation that is checked for is configurable via the
|
||||
* {@link #setScopeAnnotationType(Class)} property.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see org.springframework.context.annotation.Scope
|
||||
*/
|
||||
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
private final ScopedProxyMode defaultProxyMode;
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* @see #AnnotationScopeMetadataResolver(ScopedProxyMode)
|
||||
* @see ScopedProxyMode#NO
|
||||
*/
|
||||
public AnnotationScopeMetadataResolver() {
|
||||
this.defaultProxyMode = ScopedProxyMode.NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* @param defaultProxyMode the desired scoped-proxy mode
|
||||
*/
|
||||
public AnnotationScopeMetadataResolver(ScopedProxyMode defaultProxyMode) {
|
||||
Assert.notNull(defaultProxyMode, "'defaultProxyMode' must not be null");
|
||||
this.defaultProxyMode = defaultProxyMode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the type of annotation that is checked for by this
|
||||
* {@link AnnotationScopeMetadataResolver}.
|
||||
* @param scopeAnnotationType the target annotation type
|
||||
*/
|
||||
public void setScopeAnnotationType(Class<? extends Annotation> scopeAnnotationType) {
|
||||
Assert.notNull(scopeAnnotationType, "'scopeAnnotationType' must not be null");
|
||||
this.scopeAnnotationType = scopeAnnotationType;
|
||||
}
|
||||
|
||||
|
||||
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
|
||||
ScopeMetadata metadata = new ScopeMetadata();
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
||||
Map<String, Object> attributes =
|
||||
annDef.getMetadata().getAnnotationAttributes(this.scopeAnnotationType.getName());
|
||||
if (attributes != null) {
|
||||
metadata.setScopeName((String) attributes.get("value"));
|
||||
ScopedProxyMode proxyMode = (ScopedProxyMode) attributes.get("proxyMode");
|
||||
if (proxyMode == null || proxyMode == ScopedProxyMode.DEFAULT) {
|
||||
proxyMode = this.defaultProxyMode;
|
||||
}
|
||||
metadata.setScopedProxyMode(proxyMode);
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.Map;
|
||||
|
||||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
|
||||
* AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
|
||||
* as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see EnableAspectJAutoProxy
|
||||
* @since 3.1
|
||||
*/
|
||||
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
/**
|
||||
* Register, escalate, and configure the AspectJ auto proxy creator based on the value
|
||||
* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
|
||||
* {@code @Configuration} class.
|
||||
*/
|
||||
public void registerBeanDefinitions(
|
||||
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
Map<String, Object> enableAJAutoProxy =
|
||||
importingClassMetadata.getAnnotationAttributes(EnableAspectJAutoProxy.class.getName());
|
||||
|
||||
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
|
||||
|
||||
if ((Boolean)enableAJAutoProxy.get("proxyTargetClass")) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Registers an auto proxy creator against the current {@link BeanDefinitionRegistry}
|
||||
* as appropriate based on an {@code @Enable*} annotation having {@code mode} and
|
||||
* {@code proxyTargetClass} attributes set to the correct values.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @see EnableAspectJAutoProxy
|
||||
* @since 3.1
|
||||
*/
|
||||
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* Register, escalate, and configure the standard auto proxy creator (APC) against the
|
||||
* given registry. Works by finding the nearest annotation declared on the importing
|
||||
* {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass}
|
||||
* attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if
|
||||
* {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use
|
||||
* subclass (CGLIB) proxying.
|
||||
*
|
||||
* <p>Several {@code @Enable*} annotations expose both {@code mode} and
|
||||
* {@code proxyTargetClass} attributes. It is important to note that most of these
|
||||
* capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME
|
||||
* single APC}. For this reason, this implementation doesn't "care" exactly which
|
||||
* annotation it finds -- as long as it exposes the right {@code mode} and
|
||||
* {@code proxyTargetClass} attributes, the APC can be registered and configured all
|
||||
* the same.
|
||||
*/
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
boolean candidateFound = false;
|
||||
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
|
||||
for (String annoType : annoTypes) {
|
||||
Map<String, Object> candidate = importingClassMetadata.getAnnotationAttributes(annoType);
|
||||
Object mode = candidate.get("mode");
|
||||
Object proxyTargetClass = candidate.get("proxyTargetClass");
|
||||
if (mode != null && proxyTargetClass != null
|
||||
&& mode.getClass().equals(AdviceMode.class)
|
||||
&& proxyTargetClass.getClass().equals(Boolean.class)) {
|
||||
candidateFound = true;
|
||||
if (mode == AdviceMode.PROXY) {
|
||||
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
|
||||
if ((Boolean)proxyTargetClass) {
|
||||
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!candidateFound) {
|
||||
String name = getClass().getSimpleName();
|
||||
logger.warn(String.format("%s was imported but no annotations were found " +
|
||||
"having both 'mode' and 'proxyTargetClass' attributes of type " +
|
||||
"AdviceMode and boolean respectively. This means that auto proxy " +
|
||||
"creator registration and configuration may not have occured as " +
|
||||
"intended, and components may not be proxied as expected. Check to " +
|
||||
"ensure that %s has been @Import'ed on the same class where these " +
|
||||
"annotations are declared; otherwise remove the import of %s " +
|
||||
"altogether.", name, name, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
|
||||
/**
|
||||
* Indicates that a method produces a bean to be managed by the Spring container. The
|
||||
* names and semantics of the attributes to this annotation are intentionally similar
|
||||
* to those of the {@code <bean/>} element in the Spring XML schema. For example:
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* // instantiate and configure MyBean obj
|
||||
* return obj;
|
||||
* }</pre>
|
||||
*
|
||||
* <p>While a {@link #name()} attribute is available, the default strategy for determining
|
||||
* the name of a bean is to use the name of the Bean method. This is convenient and
|
||||
* intuitive, but if explicit naming is desired, the {@code name()} attribute may be used.
|
||||
* Also note that {@code name()} accepts an array of Strings. This is in order to allow
|
||||
* for specifying multiple names (i.e., aliases) for a single bean.
|
||||
* <pre class="code">
|
||||
* @Bean(name={"b1","b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
|
||||
* public MyBean myBean() {
|
||||
* // instantiate and configure MyBean obj
|
||||
* return obj;
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Note that the {@code @Bean} annotation does not provide attributes for scope,
|
||||
* primary or lazy. Rather, it should be used in conjunction with {@link Scope @Scope},
|
||||
* {@link Primary @Primary}, and {@link Lazy @Lazy} annotations to achieve
|
||||
* those semantics. For example:
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* @Scope("prototype")
|
||||
* public MyBean myBean() {
|
||||
* // instantiate and configure MyBean obj
|
||||
* return obj;
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Typically, {@code @Bean} methods are declared within {@code @Configuration}
|
||||
* classes. In this case, bean methods may reference other <code>@Bean</code> methods
|
||||
* on the same class by calling them <i>directly</i>. This ensures that references between
|
||||
* beans are strongly typed and navigable. Such so-called 'inter-bean references' are
|
||||
* guaranteed to respect scoping and AOP semantics, just like <code>getBean</code> lookups
|
||||
* would. These are the semantics known from the original 'Spring JavaConfig' project
|
||||
* which require CGLIB subclassing of each such configuration class at runtime. As a
|
||||
* consequence, {@code @Configuration} classes and their factory methods must not be
|
||||
* marked as final or private in this mode. For example:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public FooService fooService() {
|
||||
* return new FooService(fooRepository());
|
||||
* }
|
||||
* @Bean
|
||||
* public FooRepository fooRepository() {
|
||||
* return new JdbcFooRepository(dataSource());
|
||||
* }
|
||||
* // ...
|
||||
* }</pre>
|
||||
*
|
||||
* <p>{@code @Bean} methods may also be declared wihtin any {@code @Component} class, in
|
||||
* which case they will get processed in a configuration class 'lite' mode in which
|
||||
* they will simply be called as plain factory methods from the container (similar to
|
||||
* {@code factory-method} declarations in XML). The containing component classes remain
|
||||
* unmodified in this case, and there are no unusual constraints for factory methods,
|
||||
* however, scoping semantics are not respected as described above for inter-bean method
|
||||
* invocations in this mode. For example:
|
||||
* <pre class="code">
|
||||
* @Component
|
||||
* public class Calculator {
|
||||
* public int sum(int a, int b) {
|
||||
* return a+b;
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* return new MyBean();
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>See @{@link Configuration} Javadoc for further details including how to bootstrap
|
||||
* the container using {@link AnnotationConfigApplicationContext} and friends.
|
||||
*
|
||||
* <h3>A note on {@code BeanFactoryPostProcessor}-returning {@code @Bean} methods</h3>
|
||||
* <p>Special consideration must be taken for {@code @Bean} methods that return Spring
|
||||
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
|
||||
* ({@code BFPP}) types. Because {@code BFPP} objects must be instantiated very early in the
|
||||
* container lifecycle, they can interfere with processing of annotations such as {@code @Autowired},
|
||||
* {@code @Value}, and {@code @PostConstruct} within {@code @Configuration} classes. To avoid these
|
||||
* lifecycle issues, mark {@code BFPP}-returning {@code @Bean} methods as {@code static}. For example:
|
||||
* <pre class="code">
|
||||
* @Bean
|
||||
* public static PropertyPlaceholderConfigurer ppc() {
|
||||
* // instantiate, configure and return ppc ...
|
||||
* }</pre>
|
||||
* By marking this method as {@code static}, it can be invoked without causing instantiation of its
|
||||
* declaring {@code @Configuration} class, thus avoiding the above-mentioned lifecycle conflicts.
|
||||
* Note however that {@code static} {@code @Bean} methods will not be enhanced for scoping and AOP
|
||||
* semantics as mentioned above. This works out in {@code BFPP} cases, as they are not typically
|
||||
* referenced by other {@code @Bean} methods. As a reminder, a WARN-level log message will be
|
||||
* issued for any non-static {@code @Bean} methods having a return type assignable to
|
||||
* {@code BeanFactoryPostProcessor}.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see Configuration
|
||||
* @see Scope
|
||||
* @see DependsOn
|
||||
* @see Lazy
|
||||
* @see Primary
|
||||
* @see org.springframework.stereotype.Component
|
||||
* @see org.springframework.beans.factory.annotation.Autowired
|
||||
* @see org.springframework.beans.factory.annotation.Value
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Bean {
|
||||
|
||||
/**
|
||||
* The name of this bean, or if plural, aliases for this bean. If left unspecified
|
||||
* the name of the bean is the name of the annotated method. If specified, the method
|
||||
* name is ignored.
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* Are dependencies to be injected via autowiring?
|
||||
*/
|
||||
Autowire autowire() default Autowire.NO;
|
||||
|
||||
/**
|
||||
* The optional name of a method to call on the bean instance during initialization.
|
||||
* Not commonly used, given that the method may be called programmatically directly
|
||||
* within the body of a Bean-annotated method. Default value is {@code ""}, indicating
|
||||
* that no init method should be called.
|
||||
*/
|
||||
String initMethod() default "";
|
||||
|
||||
/**
|
||||
* The optional name of a method to call on the bean instance upon closing the
|
||||
* application context, for example a {@code close()} method on a JDBC {@code
|
||||
* DataSource} implementation, or a Hibernate {@code SessionFactory} object.
|
||||
* The method must have no arguments but may throw any exception.
|
||||
* <p>As a convenience to the user, the container will attempt to infer a destroy
|
||||
* method against object returned from the {@code @Bean} method. For example, given a
|
||||
* {@code @Bean} method returning an Apache Commons DBCP {@code BasicDataSource}, the
|
||||
* container will notice the {@code close()} method available on that object and
|
||||
* automatically register it as the {@code destroyMethod}. This 'destroy method
|
||||
* inference' is currently limited to detecting only public, no-arg methods named
|
||||
* 'close'. The method may be declared at any level of the inheritance hierarchy, and
|
||||
* will be detected regardless of the return type of the {@code @Bean} method, i.e.
|
||||
* detection occurs reflectively against the bean instance itself at creation time.
|
||||
* <p>To disable destroy method inference for a particular {@code @Bean}, specify an
|
||||
* empty string as the value, e.g. {@code @Bean(destroyMethod="")}.
|
||||
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the
|
||||
* factory, which is always the case for singletons but not guaranteed
|
||||
* for any other scope.
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||
*/
|
||||
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Utilities for processing {@link Bean}-annotated methods.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
class BeanAnnotationHelper {
|
||||
|
||||
/**
|
||||
* Return whether the given method is annotated directly or indirectly with @Bean.
|
||||
*/
|
||||
public static boolean isBeanAnnotated(Method method) {
|
||||
return AnnotationUtils.findAnnotation(method, Bean.class) != null;
|
||||
}
|
||||
|
||||
public static String determineBeanNameFor(Method beanMethod) {
|
||||
// by default the bean name is the name of the @Bean-annotated method
|
||||
String beanName = beanMethod.getName();
|
||||
|
||||
// check to see if the user has explicitly set the bean name
|
||||
Bean bean = AnnotationUtils.findAnnotation(beanMethod, Bean.class);
|
||||
if (bean != null && bean.name().length > 0) {
|
||||
beanName = bean.name()[0];
|
||||
}
|
||||
|
||||
return beanName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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 org.springframework.beans.factory.parsing.Problem;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
|
||||
/**
|
||||
* Represents a {@link Configuration} class method marked with the
|
||||
* {@link Bean} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see ConfigurationClass
|
||||
* @see ConfigurationClassParser
|
||||
* @see ConfigurationClassBeanDefinitionReader
|
||||
*/
|
||||
final class BeanMethod extends ConfigurationMethod {
|
||||
|
||||
public BeanMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
|
||||
super(metadata, configurationClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(ProblemReporter problemReporter) {
|
||||
if (getMetadata().isStatic()) {
|
||||
// static @Bean methods have no constraints to validate -> return immediately
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
|
||||
if (!getMetadata().isOverridable()) {
|
||||
// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
|
||||
problemReporter.error(new NonOverridableMethodError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class NonOverridableMethodError extends Problem {
|
||||
|
||||
public NonOverridableMethodError() {
|
||||
super(String.format("@Bean method '%s' must not be private or final; change the method's modifiers to continue",
|
||||
getMetadata().getMethodName()), getResourceLocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionDefaults;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
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.util.Assert;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* A bean definition scanner that detects bean candidates on the classpath,
|
||||
* registering corresponding bean definitions with a given registry ({@code BeanFactory}
|
||||
* or {@code ApplicationContext}).
|
||||
*
|
||||
* <p>Candidate classes are detected through configurable type filters. The
|
||||
* default filters include classes that are annotated with Spring's
|
||||
* {@link org.springframework.stereotype.Component @Component},
|
||||
* {@link org.springframework.stereotype.Repository @Repository},
|
||||
* {@link org.springframework.stereotype.Service @Service}, or
|
||||
* {@link org.springframework.stereotype.Controller @Controller} stereotype.
|
||||
*
|
||||
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
|
||||
* JSR-330's {@link javax.inject.Named} annotations, if available.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 2.5
|
||||
* @see AnnotationConfigApplicationContext#scan
|
||||
* @see org.springframework.stereotype.Component
|
||||
* @see org.springframework.stereotype.Repository
|
||||
* @see org.springframework.stereotype.Service
|
||||
* @see org.springframework.stereotype.Controller
|
||||
*/
|
||||
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
|
||||
|
||||
private final BeanDefinitionRegistry registry;
|
||||
|
||||
private BeanDefinitionDefaults beanDefinitionDefaults = new BeanDefinitionDefaults();
|
||||
|
||||
private String[] autowireCandidatePatterns;
|
||||
|
||||
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
|
||||
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
|
||||
|
||||
private boolean includeAnnotationConfig = true;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
|
||||
* @param registry the {@code BeanFactory} to load bean definitions into, in the form
|
||||
* of a {@code BeanDefinitionRegistry}
|
||||
*/
|
||||
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
|
||||
this(registry, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory.
|
||||
* <p>If the passed-in bean factory does not only implement the
|
||||
* {@code BeanDefinitionRegistry} interface but also the {@code ResourceLoader}
|
||||
* interface, it will be used as default {@code ResourceLoader} as well. This will
|
||||
* usually be the case for {@link org.springframework.context.ApplicationContext}
|
||||
* implementations.
|
||||
* <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
|
||||
* will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
|
||||
* <p>If the the passed-in bean factory also implements {@link EnvironmentCapable} its
|
||||
* environment will be used by this reader. Otherwise, the reader will initialize and
|
||||
* use a {@link org.springframework.core.env.StandardEnvironment}. All
|
||||
* {@code ApplicationContext} implementations are {@code EnvironmentCapable}, while
|
||||
* normal {@code BeanFactory} implementations are not.
|
||||
* @param registry the {@code BeanFactory} to load bean definitions into, in the form
|
||||
* of a {@code BeanDefinitionRegistry}
|
||||
* @param useDefaultFilters whether to include the default filters for the
|
||||
* {@link org.springframework.stereotype.Component @Component},
|
||||
* {@link org.springframework.stereotype.Repository @Repository},
|
||||
* {@link org.springframework.stereotype.Service @Service}, and
|
||||
* {@link org.springframework.stereotype.Controller @Controller} stereotype
|
||||
* annotations.
|
||||
* @see #setResourceLoader
|
||||
* @see #setEnvironment
|
||||
*/
|
||||
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
|
||||
this(registry, useDefaultFilters, getOrCreateEnvironment(registry));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code ClassPathBeanDefinitionScanner} for the given bean factory and
|
||||
* using the given {@link Environment} when evaluating bean definition profile metadata.
|
||||
* <p>If the passed-in bean factory does not only implement the {@code
|
||||
* BeanDefinitionRegistry} interface but also the {@link ResourceLoader} interface, it
|
||||
* will be used as default {@code ResourceLoader} as well. This will usually be the
|
||||
* case for {@link org.springframework.context.ApplicationContext} implementations.
|
||||
* <p>If given a plain {@code BeanDefinitionRegistry}, the default {@code ResourceLoader}
|
||||
* will be a {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
|
||||
* @param registry the {@code BeanFactory} to load bean definitions into, in the form
|
||||
* of a {@code BeanDefinitionRegistry}
|
||||
* @param useDefaultFilters whether to include the default filters for the
|
||||
* @param environment the Spring {@link Environment} to use when evaluating bean
|
||||
* definition profile metadata.
|
||||
* {@link org.springframework.stereotype.Component @Component},
|
||||
* {@link org.springframework.stereotype.Repository @Repository},
|
||||
* {@link org.springframework.stereotype.Service @Service}, and
|
||||
* {@link org.springframework.stereotype.Controller @Controller} stereotype
|
||||
* annotations.
|
||||
* @since 3.1
|
||||
* @see #setResourceLoader
|
||||
*/
|
||||
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
|
||||
super(useDefaultFilters, environment);
|
||||
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
this.registry = registry;
|
||||
|
||||
// Determine ResourceLoader to use.
|
||||
if (this.registry instanceof ResourceLoader) {
|
||||
setResourceLoader((ResourceLoader) this.registry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the BeanDefinitionRegistry that this scanner operates on.
|
||||
*/
|
||||
public final BeanDefinitionRegistry getRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the defaults to use for detected beans.
|
||||
* @see BeanDefinitionDefaults
|
||||
*/
|
||||
public void setBeanDefinitionDefaults(BeanDefinitionDefaults beanDefinitionDefaults) {
|
||||
this.beanDefinitionDefaults =
|
||||
(beanDefinitionDefaults != null ? beanDefinitionDefaults : new BeanDefinitionDefaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name-matching patterns for determining autowire candidates.
|
||||
* @param autowireCandidatePatterns the patterns to match against
|
||||
*/
|
||||
public void setAutowireCandidatePatterns(String[] autowireCandidatePatterns) {
|
||||
this.autowireCandidatePatterns = autowireCandidatePatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the BeanNameGenerator to use for detected bean classes.
|
||||
* <p>Default is a {@link AnnotationBeanNameGenerator}.
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : new AnnotationBeanNameGenerator());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ScopeMetadataResolver to use for detected bean classes.
|
||||
* Note that this will override any custom "scopedProxyMode" setting.
|
||||
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
|
||||
* @see #setScopedProxyMode
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the proxy behavior for non-singleton scoped beans.
|
||||
* Note that this will override any custom "scopeMetadataResolver" setting.
|
||||
* <p>The default is {@link ScopedProxyMode#NO}.
|
||||
* @see #setScopeMetadataResolver
|
||||
*/
|
||||
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
|
||||
this.scopeMetadataResolver = new AnnotationScopeMetadataResolver(scopedProxyMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether to register annotation config post-processors.
|
||||
* <p>The default is to register the post-processors. Turn this off
|
||||
* to be able to ignore the annotations or to process them differently.
|
||||
*/
|
||||
public void setIncludeAnnotationConfig(boolean includeAnnotationConfig) {
|
||||
this.includeAnnotationConfig = includeAnnotationConfig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform a scan within the specified base packages.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @return number of beans registered
|
||||
*/
|
||||
public int scan(String... basePackages) {
|
||||
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
|
||||
|
||||
doScan(basePackages);
|
||||
|
||||
// Register annotation config processors, if necessary.
|
||||
if (this.includeAnnotationConfig) {
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
|
||||
}
|
||||
|
||||
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a scan within the specified base packages,
|
||||
* returning the registered bean definitions.
|
||||
* <p>This method does <i>not</i> register an annotation config processor
|
||||
* but rather leaves this up to the caller.
|
||||
* @param basePackages the packages to check for annotated classes
|
||||
* @return set of beans registered if any for tooling registration purposes (never {@code null})
|
||||
*/
|
||||
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
|
||||
Assert.notEmpty(basePackages, "At least one base package must be specified");
|
||||
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
|
||||
for (String basePackage : basePackages) {
|
||||
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
|
||||
for (BeanDefinition candidate : candidates) {
|
||||
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
|
||||
candidate.setScope(scopeMetadata.getScopeName());
|
||||
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
|
||||
if (candidate instanceof AbstractBeanDefinition) {
|
||||
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
|
||||
}
|
||||
if (candidate instanceof AnnotatedBeanDefinition) {
|
||||
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
|
||||
}
|
||||
if (checkCandidate(beanName, candidate)) {
|
||||
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
|
||||
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
|
||||
beanDefinitions.add(definitionHolder);
|
||||
registerBeanDefinition(definitionHolder, this.registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
return beanDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply further settings to the given bean definition,
|
||||
* beyond the contents retrieved from scanning the component class.
|
||||
* @param beanDefinition the scanned bean definition
|
||||
* @param beanName the generated bean name for the given bean
|
||||
*/
|
||||
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
|
||||
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
|
||||
if (this.autowireCandidatePatterns != null) {
|
||||
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the specified bean with the given registry.
|
||||
* <p>Can be overridden in subclasses, e.g. to adapt the registration
|
||||
* process or to register further bean definitions for each scanned bean.
|
||||
* @param definitionHolder the bean definition plus bean name for the bean
|
||||
* @param registry the BeanDefinitionRegistry to register the bean with
|
||||
*/
|
||||
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
|
||||
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check the given candidate's bean name, determining whether the corresponding
|
||||
* bean definition needs to be registered or conflicts with an existing definition.
|
||||
* @param beanName the suggested name for the bean
|
||||
* @param beanDefinition the corresponding bean definition
|
||||
* @return <code>true</code> if the bean can be registered as-is;
|
||||
* <code>false</code> if it should be skipped because there is an
|
||||
* existing, compatible bean definition for the specified name
|
||||
* @throws ConflictingBeanDefinitionException if an existing, incompatible
|
||||
* bean definition has been found for the specified name
|
||||
*/
|
||||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
|
||||
if (!this.registry.containsBeanDefinition(beanName)) {
|
||||
return true;
|
||||
}
|
||||
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
|
||||
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
|
||||
if (originatingDef != null) {
|
||||
existingDef = originatingDef;
|
||||
}
|
||||
if (isCompatible(beanDefinition, existingDef)) {
|
||||
return false;
|
||||
}
|
||||
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
|
||||
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
|
||||
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given new bean definition is compatible with
|
||||
* the given existing bean definition.
|
||||
* <p>The default implementation simply considers them as compatible
|
||||
* when the bean class name matches.
|
||||
* @param newDefinition the new bean definition, originated from scanning
|
||||
* @param existingDefinition the existing bean definition, potentially an
|
||||
* explicitly defined one or a previously generated one from scanning
|
||||
* @return whether the definitions are considered as compatible, with the
|
||||
* new definition to be skipped in favor of the existing definition
|
||||
*/
|
||||
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
|
||||
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
|
||||
newDefinition.getSource().equals(existingDefinition.getSource()) || // scanned same file twice
|
||||
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Environment from the given registry if possible, otherwise return a new
|
||||
* StandardEnvironment.
|
||||
*/
|
||||
private static Environment getOrCreateEnvironment(BeanDefinitionRegistry registry) {
|
||||
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
|
||||
if (registry instanceof EnvironmentCapable) {
|
||||
return ((EnvironmentCapable) registry).getEnvironment();
|
||||
}
|
||||
return new StandardEnvironment();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
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.context.ResourceLoaderAware;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.EnvironmentCapable;
|
||||
import org.springframework.core.io.Resource;
|
||||
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;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* A component provider that scans the classpath from a base package. It then
|
||||
* applies exclude and include filters to the resulting classes to find candidates.
|
||||
*
|
||||
* <p>This implementation is based on Spring's
|
||||
* {@link org.springframework.core.type.classreading.MetadataReader MetadataReader}
|
||||
* facility, backed by an ASM {@link org.springframework.asm.ClassReader ClassReader}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Juergen Hoeller
|
||||
* @author Ramnivas Laddad
|
||||
* @author Chris Beams
|
||||
* @since 2.5
|
||||
* @see org.springframework.core.type.classreading.MetadataReaderFactory
|
||||
* @see org.springframework.core.type.AnnotationMetadata
|
||||
* @see ScannedGenericBeanDefinition
|
||||
*/
|
||||
public class ClassPathScanningCandidateComponentProvider implements EnvironmentCapable, ResourceLoaderAware {
|
||||
|
||||
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
|
||||
|
||||
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
|
||||
|
||||
private final List<TypeFilter> includeFilters = new LinkedList<TypeFilter>();
|
||||
|
||||
private final List<TypeFilter> excludeFilters = new LinkedList<TypeFilter>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a ClassPathScanningCandidateComponentProvider.
|
||||
* @param useDefaultFilters whether to register the default filters for the
|
||||
* {@link Component @Component}, {@link Repository @Repository},
|
||||
* {@link Service @Service}, and {@link Controller @Controller}
|
||||
* stereotype annotations
|
||||
* @see #registerDefaultFilters()
|
||||
*/
|
||||
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters) {
|
||||
this(useDefaultFilters, new StandardEnvironment());
|
||||
}
|
||||
|
||||
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
|
||||
if (useDefaultFilters) {
|
||||
registerDefaultFilters();
|
||||
}
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the ResourceLoader to use for resource locations.
|
||||
* This will typically be a ResourcePatternResolver implementation.
|
||||
* <p>Default is PathMatchingResourcePatternResolver, also capable of
|
||||
* resource pattern resolving through the ResourcePatternResolver interface.
|
||||
* @see org.springframework.core.io.support.ResourcePatternResolver
|
||||
* @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
||||
*/
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
|
||||
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Environment to use when resolving placeholders and evaluating
|
||||
* {@link Profile @Profile}-annotated component classes.
|
||||
* <p>The default is a {@link StandardEnvironment}
|
||||
* @param environment the Environment to use
|
||||
*/
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this component provider uses.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourcePatternResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource pattern to use when scanning the classpath.
|
||||
* This value will be appended to each base package name.
|
||||
* @see #findCandidateComponents(String)
|
||||
* @see #DEFAULT_RESOURCE_PATTERN
|
||||
*/
|
||||
public void setResourcePattern(String resourcePattern) {
|
||||
Assert.notNull(resourcePattern, "'resourcePattern' must not be null");
|
||||
this.resourcePattern = resourcePattern;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an include type filter to the <i>end</i> of the inclusion list.
|
||||
*/
|
||||
public void addIncludeFilter(TypeFilter includeFilter) {
|
||||
this.includeFilters.add(includeFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an exclude type filter to the <i>front</i> of the exclusion list.
|
||||
*/
|
||||
public void addExcludeFilter(TypeFilter excludeFilter) {
|
||||
this.excludeFilters.add(0, excludeFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the configured type filters.
|
||||
* @param useDefaultFilters whether to re-register the default filters for
|
||||
* the {@link Component @Component}, {@link Repository @Repository},
|
||||
* {@link Service @Service}, and {@link Controller @Controller}
|
||||
* stereotype annotations
|
||||
* @see #registerDefaultFilters()
|
||||
*/
|
||||
public void resetFilters(boolean useDefaultFilters) {
|
||||
this.includeFilters.clear();
|
||||
this.excludeFilters.clear();
|
||||
if (useDefaultFilters) {
|
||||
registerDefaultFilters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the default filter for {@link Component @Component}.
|
||||
* <p>This will implicitly register all annotations that have the
|
||||
* {@link Component @Component} meta-annotation including the
|
||||
* {@link Repository @Repository}, {@link Service @Service}, and
|
||||
* {@link Controller @Controller} stereotype annotations.
|
||||
* <p>Also supports Java EE 6's {@link javax.annotation.ManagedBean} and
|
||||
* JSR-330's {@link javax.inject.Named} annotations, if available.
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void registerDefaultFilters() {
|
||||
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
|
||||
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
|
||||
try {
|
||||
this.includeFilters.add(new AnnotationTypeFilter(
|
||||
((Class<? extends Annotation>) cl.loadClass("javax.annotation.ManagedBean")), false));
|
||||
logger.info("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
|
||||
}
|
||||
try {
|
||||
this.includeFilters.add(new AnnotationTypeFilter(
|
||||
((Class<? extends Annotation>) cl.loadClass("javax.inject.Named")), false));
|
||||
logger.info("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// JSR-330 API not available - simply skip.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scan the class path for candidate components.
|
||||
* @param basePackage the package to check for annotated classes
|
||||
* @return a corresponding Set of autodetected bean definitions
|
||||
*/
|
||||
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
|
||||
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
|
||||
try {
|
||||
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
|
||||
resolveBasePackage(basePackage) + "/" + this.resourcePattern;
|
||||
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
|
||||
boolean traceEnabled = logger.isTraceEnabled();
|
||||
boolean debugEnabled = logger.isDebugEnabled();
|
||||
for (Resource resource : resources) {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Scanning " + resource);
|
||||
}
|
||||
if (resource.isReadable()) {
|
||||
try {
|
||||
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
|
||||
if (isCandidateComponent(metadataReader)) {
|
||||
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
|
||||
sbd.setResource(resource);
|
||||
sbd.setSource(resource);
|
||||
if (isCandidateComponent(sbd)) {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Identified candidate component class: " + resource);
|
||||
}
|
||||
candidates.add(sbd);
|
||||
}
|
||||
else {
|
||||
if (debugEnabled) {
|
||||
logger.debug("Ignored because not a concrete top-level class: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not matching any filter: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Failed to read candidate component class: " + resource, ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (traceEnabled) {
|
||||
logger.trace("Ignored because not readable: " + resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve the specified base package into a pattern specification for
|
||||
* the package search path.
|
||||
* <p>The default implementation resolves placeholders against system properties,
|
||||
* and converts a "."-based package path to a "/"-based resource path.
|
||||
* @param basePackage the base package as specified by the user
|
||||
* @return the pattern specification to be used for package searching
|
||||
*/
|
||||
protected String resolveBasePackage(String basePackage) {
|
||||
return ClassUtils.convertClassNameToResourcePath(environment.resolveRequiredPlaceholders(basePackage));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given class does not match any exclude filter
|
||||
* and does match at least one include filter.
|
||||
* @param metadataReader the ASM ClassReader for the class
|
||||
* @return whether the class qualifies as a candidate component
|
||||
*/
|
||||
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
|
||||
for (TypeFilter tf : this.excludeFilters) {
|
||||
if (tf.match(metadataReader, this.metadataReaderFactory)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (TypeFilter tf : this.includeFilters) {
|
||||
if (tf.match(metadataReader, this.metadataReaderFactory)) {
|
||||
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
|
||||
if (!ProfileHelper.isProfileAnnotationPresent(metadata)) {
|
||||
return true;
|
||||
}
|
||||
return this.environment.acceptsProfiles(ProfileHelper.getCandidateProfiles(metadata));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given bean definition qualifies as candidate.
|
||||
* <p>The default implementation checks whether the class is concrete
|
||||
* (i.e. not abstract and not an interface). Can be overridden in subclasses.
|
||||
* @param beanDefinition the bean definition to check
|
||||
* @return whether the bean definition qualifies as a candidate component
|
||||
*/
|
||||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
||||
return (beanDefinition.getMetadata().isConcrete() && beanDefinition.getMetadata().isIndependent());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.annotation.Resource;
|
||||
import javax.ejb.EJB;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.ws.Service;
|
||||
import javax.xml.ws.WebServiceClient;
|
||||
import javax.xml.ws.WebServiceRef;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.annotation.InjectionMetadata;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.jndi.support.SimpleJndiBeanFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
|
||||
* that supports common Java annotations out of the box, in particular the JSR-250
|
||||
* annotations in the <code>javax.annotation</code> package. These common Java
|
||||
* annotations are supported in many Java EE 5 technologies (e.g. JSF 1.2),
|
||||
* as well as in Java 6's JAX-WS.
|
||||
*
|
||||
* <p>This post-processor includes support for the {@link javax.annotation.PostConstruct}
|
||||
* and {@link javax.annotation.PreDestroy} annotations - as init annotation
|
||||
* and destroy annotation, respectively - through inheriting from
|
||||
* {@link InitDestroyAnnotationBeanPostProcessor} with pre-configured annotation types.
|
||||
*
|
||||
* <p>The central element is the {@link javax.annotation.Resource} annotation
|
||||
* for annotation-driven injection of named beans, by default from the containing
|
||||
* Spring BeanFactory, with only <code>mappedName</code> references resolved in JNDI.
|
||||
* The {@link #setAlwaysUseJndiLookup "alwaysUseJndiLookup" flag} enforces JNDI lookups
|
||||
* equivalent to standard Java EE 5 resource injection for <code>name</code> references
|
||||
* and default names as well. The target beans can be simple POJOs, with no special
|
||||
* requirements other than the type having to match.
|
||||
*
|
||||
* <p>The JAX-WS {@link javax.xml.ws.WebServiceRef} annotation is supported too,
|
||||
* analogous to {@link javax.annotation.Resource} but with the capability of creating
|
||||
* specific JAX-WS service endpoints. This may either point to an explicitly defined
|
||||
* resource by name or operate on a locally specified JAX-WS service class. Finally,
|
||||
* this post-processor also supports the EJB 3 {@link javax.ejb.EJB} annotation,
|
||||
* analogous to {@link javax.annotation.Resource} as well, with the capability to
|
||||
* specify both a local bean name and a global JNDI name for fallback retrieval.
|
||||
* The target beans can be plain POJOs as well as EJB 3 Session Beans in this case.
|
||||
*
|
||||
* <p>The common annotations supported by this post-processor are available in
|
||||
* Java 6 (JDK 1.6) as well as in Java EE 5/6 (which provides a standalone jar for
|
||||
* its common annotations as well, allowing for use in any Java 5 based application).
|
||||
* Hence, this post-processor works out of the box on JDK 1.6, and requires the
|
||||
* JSR-250 API jar (and optionally the JAX-WS API jar and/or the EJB 3 API jar)
|
||||
* to be added to the classpath on JDK 1.5 (when running outside of Java EE 5/6).
|
||||
*
|
||||
* <p>For default usage, resolving resource names as Spring bean names,
|
||||
* simply define the following in your application context:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/></pre>
|
||||
*
|
||||
* For direct JNDI access, resolving resource names as JNDI resource references
|
||||
* within the Java EE application's "java:comp/env/" namespace, use the following:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor">
|
||||
* <property name="alwaysUseJndiLookup" value="true"/>
|
||||
* </bean></pre>
|
||||
*
|
||||
* <code>mappedName</code> references will always be resolved in JNDI,
|
||||
* allowing for global JNDI names (including "java:" prefix) as well. The
|
||||
* "alwaysUseJndiLookup" flag just affects <code>name</code> references and
|
||||
* default names (inferred from the field name / property name).
|
||||
*
|
||||
* <p><b>NOTE:</b> A default CommonAnnotationBeanPostProcessor will be registered
|
||||
* by the "context:annotation-config" and "context:component-scan" XML tags.
|
||||
* Remove or turn off the default annotation configuration there if you intend
|
||||
* to specify a custom CommonAnnotationBeanPostProcessor bean definition!
|
||||
* <p><b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection; thus
|
||||
* the latter configuration will override the former for properties wired through
|
||||
* both approaches.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see #setAlwaysUseJndiLookup
|
||||
* @see #setResourceFactory
|
||||
* @see org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor
|
||||
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
|
||||
implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
|
||||
|
||||
private static Class<? extends Annotation> webServiceRefClass = null;
|
||||
|
||||
private static Class<? extends Annotation> ejbRefClass = null;
|
||||
|
||||
static {
|
||||
ClassLoader cl = CommonAnnotationBeanPostProcessor.class.getClassLoader();
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Annotation> clazz = (Class<? extends Annotation>) cl.loadClass("javax.xml.ws.WebServiceRef");
|
||||
webServiceRefClass = clazz;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
webServiceRefClass = null;
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Annotation> clazz = (Class<? extends Annotation>) cl.loadClass("javax.ejb.EJB");
|
||||
ejbRefClass = clazz;
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
ejbRefClass = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Set<String> ignoredResourceTypes = new HashSet<String>(1);
|
||||
|
||||
private boolean fallbackToDefaultTypeMatch = true;
|
||||
|
||||
private boolean alwaysUseJndiLookup = false;
|
||||
|
||||
private transient BeanFactory jndiFactory = new SimpleJndiBeanFactory();
|
||||
|
||||
private transient BeanFactory resourceFactory;
|
||||
|
||||
private transient BeanFactory beanFactory;
|
||||
|
||||
private transient final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
|
||||
new ConcurrentHashMap<Class<?>, InjectionMetadata>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new CommonAnnotationBeanPostProcessor,
|
||||
* with the init and destroy annotation types set to
|
||||
* {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy},
|
||||
* respectively.
|
||||
*/
|
||||
public CommonAnnotationBeanPostProcessor() {
|
||||
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
|
||||
setInitAnnotationType(PostConstruct.class);
|
||||
setDestroyAnnotationType(PreDestroy.class);
|
||||
ignoreResourceType("javax.xml.ws.WebServiceContext");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ignore the given resource type when resolving <code>@Resource</code>
|
||||
* annotations.
|
||||
* <p>By default, the <code>javax.xml.ws.WebServiceContext</code> interface
|
||||
* will be ignored, since it will be resolved by the JAX-WS runtime.
|
||||
* @param resourceType the resource type to ignore
|
||||
*/
|
||||
public void ignoreResourceType(String resourceType) {
|
||||
Assert.notNull(resourceType, "Ignored resource type must not be null");
|
||||
this.ignoredResourceTypes.add(resourceType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to allow a fallback to a type match if no explicit name has been
|
||||
* specified. The default name (i.e. the field name or bean property name) will
|
||||
* still be checked first; if a bean of that name exists, it will be taken.
|
||||
* However, if no bean of that name exists, a by-type resolution of the
|
||||
* dependency will be attempted if this flag is "true".
|
||||
* <p>Default is "true". Switch this flag to "false" in order to enforce a
|
||||
* by-name lookup in all cases, throwing an exception in case of no name match.
|
||||
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency
|
||||
*/
|
||||
public void setFallbackToDefaultTypeMatch(boolean fallbackToDefaultTypeMatch) {
|
||||
this.fallbackToDefaultTypeMatch = fallbackToDefaultTypeMatch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to always use JNDI lookups equivalent to standard Java EE 5 resource
|
||||
* injection, <b>even for <code>name</code> attributes and default names</b>.
|
||||
* <p>Default is "false": Resource names are used for Spring bean lookups in the
|
||||
* containing BeanFactory; only <code>mappedName</code> attributes point directly
|
||||
* into JNDI. Switch this flag to "true" for enforcing Java EE style JNDI lookups
|
||||
* in any case, even for <code>name</code> attributes and default names.
|
||||
* @see #setJndiFactory
|
||||
* @see #setResourceFactory
|
||||
*/
|
||||
public void setAlwaysUseJndiLookup(boolean alwaysUseJndiLookup) {
|
||||
this.alwaysUseJndiLookup = alwaysUseJndiLookup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the factory for objects to be injected into <code>@Resource</code> /
|
||||
* <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
|
||||
* <b>for <code>mappedName</code> attributes that point directly into JNDI</b>.
|
||||
* This factory will also be used if "alwaysUseJndiLookup" is set to "true" in order
|
||||
* to enforce JNDI lookups even for <code>name</code> attributes and default names.
|
||||
* <p>The default is a {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
|
||||
* for JNDI lookup behavior equivalent to standard Java EE 5 resource injection.
|
||||
* @see #setResourceFactory
|
||||
* @see #setAlwaysUseJndiLookup
|
||||
*/
|
||||
public void setJndiFactory(BeanFactory jndiFactory) {
|
||||
Assert.notNull(jndiFactory, "BeanFactory must not be null");
|
||||
this.jndiFactory = jndiFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the factory for objects to be injected into <code>@Resource</code> /
|
||||
* <code>@WebServiceRef</code> / <code>@EJB</code> annotated fields and setter methods,
|
||||
* <b>for <code>name</code> attributes and default names</b>.
|
||||
* <p>The default is the BeanFactory that this post-processor is defined in,
|
||||
* if any, looking up resource names as Spring bean names. Specify the resource
|
||||
* factory explicitly for programmatic usage of this post-processor.
|
||||
* <p>Specifying Spring's {@link org.springframework.jndi.support.SimpleJndiBeanFactory}
|
||||
* leads to JNDI lookup behavior equivalent to standard Java EE 5 resource injection,
|
||||
* even for <code>name</code> attributes and default names. This is the same behavior
|
||||
* that the "alwaysUseJndiLookup" flag enables.
|
||||
* @see #setAlwaysUseJndiLookup
|
||||
*/
|
||||
public void setResourceFactory(BeanFactory resourceFactory) {
|
||||
Assert.notNull(resourceFactory, "BeanFactory must not be null");
|
||||
this.resourceFactory = resourceFactory;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
this.beanFactory = beanFactory;
|
||||
if (this.resourceFactory == null) {
|
||||
this.resourceFactory = beanFactory;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
|
||||
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
|
||||
if (beanType != null) {
|
||||
InjectionMetadata metadata = findResourceMetadata(beanType);
|
||||
metadata.checkConfigMembers(beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
|
||||
return true;
|
||||
}
|
||||
|
||||
public PropertyValues postProcessPropertyValues(
|
||||
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
|
||||
|
||||
InjectionMetadata metadata = findResourceMetadata(bean.getClass());
|
||||
try {
|
||||
metadata.inject(bean, beanName, pvs);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
|
||||
}
|
||||
return pvs;
|
||||
}
|
||||
|
||||
|
||||
private InjectionMetadata findResourceMetadata(final Class<?> clazz) {
|
||||
// Quick check on the concurrent map first, with minimal locking.
|
||||
InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
|
||||
if (metadata == null) {
|
||||
synchronized (this.injectionMetadataCache) {
|
||||
metadata = this.injectionMetadataCache.get(clazz);
|
||||
if (metadata == null) {
|
||||
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||
Class<?> targetClass = clazz;
|
||||
|
||||
do {
|
||||
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
|
||||
for (Field field : targetClass.getDeclaredFields()) {
|
||||
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
|
||||
}
|
||||
currElements.add(new WebServiceRefElement(field, null));
|
||||
}
|
||||
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static fields");
|
||||
}
|
||||
currElements.add(new EjbRefElement(field, null));
|
||||
}
|
||||
else if (field.isAnnotationPresent(Resource.class)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static fields");
|
||||
}
|
||||
if (!ignoredResourceTypes.contains(field.getType().getName())) {
|
||||
currElements.add(new ResourceElement(field, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Method method : targetClass.getDeclaredMethods()) {
|
||||
method = BridgeMethodResolver.findBridgedMethod(method);
|
||||
Method mostSpecificMethod = BridgeMethodResolver.findBridgedMethod(ClassUtils.getMostSpecificMethod(method, clazz));
|
||||
if (method.equals(mostSpecificMethod)) {
|
||||
if (webServiceRefClass != null && method.isAnnotationPresent(webServiceRefClass)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
|
||||
}
|
||||
if (method.getParameterTypes().length != 1) {
|
||||
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
|
||||
}
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
||||
currElements.add(new WebServiceRefElement(method, pd));
|
||||
}
|
||||
else if (ejbRefClass != null && method.isAnnotationPresent(ejbRefClass)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@EJB annotation is not supported on static methods");
|
||||
}
|
||||
if (method.getParameterTypes().length != 1) {
|
||||
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
|
||||
}
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
||||
currElements.add(new EjbRefElement(method, pd));
|
||||
}
|
||||
else if (method.isAnnotationPresent(Resource.class)) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
throw new IllegalStateException("@Resource annotation is not supported on static methods");
|
||||
}
|
||||
Class<?>[] paramTypes = method.getParameterTypes();
|
||||
if (paramTypes.length != 1) {
|
||||
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
|
||||
}
|
||||
if (!ignoredResourceTypes.contains(paramTypes[0].getName())) {
|
||||
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
|
||||
currElements.add(new ResourceElement(method, pd));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elements.addAll(0, currElements);
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
|
||||
metadata = new InjectionMetadata(clazz, elements);
|
||||
this.injectionMetadataCache.put(clazz, metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the resource object for the given name and type.
|
||||
* @param element the descriptor for the annotated field/method
|
||||
* @param requestingBeanName the name of the requesting bean
|
||||
* @return the resource object (never <code>null</code>)
|
||||
* @throws BeansException if we failed to obtain the target resource
|
||||
*/
|
||||
protected Object getResource(LookupElement element, String requestingBeanName) throws BeansException {
|
||||
if (StringUtils.hasLength(element.mappedName)) {
|
||||
return this.jndiFactory.getBean(element.mappedName, element.lookupType);
|
||||
}
|
||||
if (this.alwaysUseJndiLookup) {
|
||||
return this.jndiFactory.getBean(element.name, element.lookupType);
|
||||
}
|
||||
if (this.resourceFactory == null) {
|
||||
throw new NoSuchBeanDefinitionException(element.lookupType,
|
||||
"No resource factory configured - specify the 'resourceFactory' property");
|
||||
}
|
||||
return autowireResource(this.resourceFactory, element, requestingBeanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a resource object for the given name and type through autowiring
|
||||
* based on the given factory.
|
||||
* @param factory the factory to autowire against
|
||||
* @param element the descriptor for the annotated field/method
|
||||
* @param requestingBeanName the name of the requesting bean
|
||||
* @return the resource object (never <code>null</code>)
|
||||
* @throws BeansException if we failed to obtain the target resource
|
||||
*/
|
||||
protected Object autowireResource(BeanFactory factory, LookupElement element, String requestingBeanName)
|
||||
throws BeansException {
|
||||
|
||||
Object resource;
|
||||
Set<String> autowiredBeanNames;
|
||||
String name = element.name;
|
||||
|
||||
if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
|
||||
factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
|
||||
autowiredBeanNames = new LinkedHashSet<String>();
|
||||
resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
|
||||
element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
|
||||
}
|
||||
else {
|
||||
resource = factory.getBean(name, element.lookupType);
|
||||
autowiredBeanNames = Collections.singleton(name);
|
||||
}
|
||||
|
||||
if (factory instanceof ConfigurableBeanFactory) {
|
||||
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing generic injection information about an annotated field
|
||||
* or setter method, supporting @Resource and related annotations.
|
||||
*/
|
||||
protected abstract class LookupElement extends InjectionMetadata.InjectedElement {
|
||||
|
||||
protected String name;
|
||||
|
||||
protected boolean isDefaultName = false;
|
||||
|
||||
protected Class<?> lookupType;
|
||||
|
||||
protected String mappedName;
|
||||
|
||||
public LookupElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
initAnnotation((AnnotatedElement) member);
|
||||
}
|
||||
|
||||
protected abstract void initAnnotation(AnnotatedElement ae);
|
||||
|
||||
/**
|
||||
* Return the resource name for the lookup.
|
||||
*/
|
||||
public final String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the desired type for the lookup.
|
||||
*/
|
||||
public final Class<?> getLookupType() {
|
||||
return this.lookupType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a DependencyDescriptor for the underlying field/method.
|
||||
*/
|
||||
public final DependencyDescriptor getDependencyDescriptor() {
|
||||
if (this.isField) {
|
||||
return new LookupDependencyDescriptor((Field) this.member, this.lookupType);
|
||||
}
|
||||
else {
|
||||
return new LookupDependencyDescriptor((Method) this.member, this.lookupType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field
|
||||
* or setter method, supporting the @Resource annotation.
|
||||
*/
|
||||
private class ResourceElement extends LookupElement {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected boolean shareable = true;
|
||||
|
||||
public ResourceElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initAnnotation(AnnotatedElement ae) {
|
||||
Resource resource = ae.getAnnotation(Resource.class);
|
||||
String resourceName = resource.name();
|
||||
Class<?> resourceType = resource.type();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
if (this.isDefaultName) {
|
||||
resourceName = this.member.getName();
|
||||
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
|
||||
resourceName = Introspector.decapitalize(resourceName.substring(3));
|
||||
}
|
||||
}
|
||||
else if (beanFactory instanceof ConfigurableBeanFactory){
|
||||
resourceName = ((ConfigurableBeanFactory) beanFactory).resolveEmbeddedValue(resourceName);
|
||||
}
|
||||
if (resourceType != null && !Object.class.equals(resourceType)) {
|
||||
checkResourceType(resourceType);
|
||||
}
|
||||
else {
|
||||
// No resource type specified... check field/method.
|
||||
resourceType = getResourceType();
|
||||
}
|
||||
this.name = resourceName;
|
||||
this.lookupType = resourceType;
|
||||
this.mappedName = resource.mappedName();
|
||||
this.shareable = resource.shareable();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, String requestingBeanName) {
|
||||
return getResource(this, requestingBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field
|
||||
* or setter method, supporting the @WebServiceRef annotation.
|
||||
*/
|
||||
private class WebServiceRefElement extends LookupElement {
|
||||
|
||||
private Class<?> elementType;
|
||||
|
||||
private String wsdlLocation;
|
||||
|
||||
public WebServiceRefElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initAnnotation(AnnotatedElement ae) {
|
||||
WebServiceRef resource = ae.getAnnotation(WebServiceRef.class);
|
||||
String resourceName = resource.name();
|
||||
Class<?> resourceType = resource.type();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
if (this.isDefaultName) {
|
||||
resourceName = this.member.getName();
|
||||
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
|
||||
resourceName = Introspector.decapitalize(resourceName.substring(3));
|
||||
}
|
||||
}
|
||||
if (resourceType != null && !Object.class.equals(resourceType)) {
|
||||
checkResourceType(resourceType);
|
||||
}
|
||||
else {
|
||||
// No resource type specified... check field/method.
|
||||
resourceType = getResourceType();
|
||||
}
|
||||
this.name = resourceName;
|
||||
this.elementType = resourceType;
|
||||
if (Service.class.isAssignableFrom(resourceType)) {
|
||||
this.lookupType = resourceType;
|
||||
}
|
||||
else {
|
||||
this.lookupType = (!Object.class.equals(resource.value()) ? resource.value() : Service.class);
|
||||
}
|
||||
this.mappedName = resource.mappedName();
|
||||
this.wsdlLocation = resource.wsdlLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, String requestingBeanName) {
|
||||
Service service;
|
||||
try {
|
||||
service = (Service) getResource(this, requestingBeanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException notFound) {
|
||||
// Service to be created through generated class.
|
||||
if (Service.class.equals(this.lookupType)) {
|
||||
throw new IllegalStateException("No resource with name '" + this.name + "' found in context, " +
|
||||
"and no specific JAX-WS Service subclass specified. The typical solution is to either specify " +
|
||||
"a LocalJaxWsServiceFactoryBean with the given name or to specify the (generated) Service " +
|
||||
"subclass as @WebServiceRef(...) value.");
|
||||
}
|
||||
if (StringUtils.hasLength(this.wsdlLocation)) {
|
||||
try {
|
||||
Constructor<?> ctor = this.lookupType.getConstructor(new Class[] {URL.class, QName.class});
|
||||
WebServiceClient clientAnn = this.lookupType.getAnnotation(WebServiceClient.class);
|
||||
if (clientAnn == null) {
|
||||
throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
|
||||
"] does not carry a WebServiceClient annotation");
|
||||
}
|
||||
service = (Service) BeanUtils.instantiateClass(ctor,
|
||||
new URL(this.wsdlLocation), new QName(clientAnn.targetNamespace(), clientAnn.name()));
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException("JAX-WS Service class [" + this.lookupType.getName() +
|
||||
"] does not have a (URL, QName) constructor. Cannot apply specified WSDL location [" +
|
||||
this.wsdlLocation + "].");
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Specified WSDL location [" + this.wsdlLocation + "] isn't a valid URL");
|
||||
}
|
||||
}
|
||||
else {
|
||||
service = (Service) BeanUtils.instantiateClass(this.lookupType);
|
||||
}
|
||||
}
|
||||
return service.getPort(this.elementType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class representing injection information about an annotated field
|
||||
* or setter method, supporting the @EJB annotation.
|
||||
*/
|
||||
private class EjbRefElement extends LookupElement {
|
||||
|
||||
private String beanName;
|
||||
|
||||
public EjbRefElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initAnnotation(AnnotatedElement ae) {
|
||||
EJB resource = ae.getAnnotation(EJB.class);
|
||||
String resourceBeanName = resource.beanName();
|
||||
String resourceName = resource.name();
|
||||
this.isDefaultName = !StringUtils.hasLength(resourceName);
|
||||
if (this.isDefaultName) {
|
||||
resourceName = this.member.getName();
|
||||
if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
|
||||
resourceName = Introspector.decapitalize(resourceName.substring(3));
|
||||
}
|
||||
}
|
||||
Class<?> resourceType = resource.beanInterface();
|
||||
if (resourceType != null && !Object.class.equals(resourceType)) {
|
||||
checkResourceType(resourceType);
|
||||
}
|
||||
else {
|
||||
// No resource type specified... check field/method.
|
||||
resourceType = getResourceType();
|
||||
}
|
||||
this.beanName = resourceBeanName;
|
||||
this.name = resourceName;
|
||||
this.lookupType = resourceType;
|
||||
this.mappedName = resource.mappedName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, String requestingBeanName) {
|
||||
if (StringUtils.hasLength(this.beanName)) {
|
||||
if (beanFactory != null && beanFactory.containsBean(this.beanName)) {
|
||||
// Local match found for explicitly specified local bean name.
|
||||
Object bean = beanFactory.getBean(this.beanName, this.lookupType);
|
||||
if (beanFactory instanceof ConfigurableBeanFactory) {
|
||||
((ConfigurableBeanFactory) beanFactory).registerDependentBean(this.beanName, requestingBeanName);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
else if (this.isDefaultName && !StringUtils.hasLength(this.mappedName)) {
|
||||
throw new NoSuchBeanDefinitionException(this.beanName,
|
||||
"Cannot resolve 'beanName' in local BeanFactory. Consider specifying a general 'name' value instead.");
|
||||
}
|
||||
}
|
||||
// JNDI name lookup - may still go to a local BeanFactory.
|
||||
return getResource(this, requestingBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extension of the DependencyDescriptor class,
|
||||
* overriding the dependency type with the specified resource type.
|
||||
*/
|
||||
private static class LookupDependencyDescriptor extends DependencyDescriptor {
|
||||
|
||||
private final Class<?> lookupType;
|
||||
|
||||
public LookupDependencyDescriptor(Field field, Class<?> lookupType) {
|
||||
super(field, true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
public LookupDependencyDescriptor(Method method, Class<?> lookupType) {
|
||||
super(new MethodParameter(method, 0), true);
|
||||
this.lookupType = lookupType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getDependencyType() {
|
||||
return this.lookupType;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* Configures component scanning directives for use with @{@link Configuration} classes.
|
||||
* Provides support parallel with Spring XML's {@code <context:component-scan>} element.
|
||||
*
|
||||
* <p>One of {@link #basePackageClasses()}, {@link #basePackages()} or its alias
|
||||
* {@link #value()} must be specified.
|
||||
*
|
||||
* <p>Note that the {@code <context:component-scan>} element has an
|
||||
* {@code annotation-config} attribute, however this annotation does not. This is because
|
||||
* in almost all cases when using {@code @ComponentScan}, default annotation config
|
||||
* processing (e.g. processing {@code @Autowired} and friends) is assumed. Furthermore,
|
||||
* when using {@link AnnotationConfigApplicationContext}, annotation config processors are
|
||||
* always registered, meaning that any attempt to disable them at the
|
||||
* {@code @ComponentScan} level would be ignored.
|
||||
*
|
||||
* <p>See @{@link Configuration} Javadoc for usage examples.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see Configuration
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
public @interface ComponentScan {
|
||||
|
||||
/**
|
||||
* Alias for the {@link #basePackages()} attribute.
|
||||
* Allows for more concise annotation declarations e.g.:
|
||||
* {@code @ComponentScan("org.my.pkg")} instead of
|
||||
* {@code @ComponentScan(basePackages="org.my.pkg")}.
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* Base packages to scan for annotated components.
|
||||
* <p>{@link #value()} is an alias for (and mutually exclusive with) this attribute.
|
||||
* <p>Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names.
|
||||
*/
|
||||
String[] basePackages() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #basePackages()} for specifying the packages
|
||||
* to scan for annotated components. The package of each class specified will be scanned.
|
||||
* <p>Consider creating a special no-op marker class or interface in each package
|
||||
* that serves no purpose other than being referenced by this attribute.
|
||||
*/
|
||||
Class<?>[] basePackageClasses() default {};
|
||||
|
||||
/**
|
||||
* The {@link BeanNameGenerator} class to be used for naming detected components
|
||||
* within the Spring container.
|
||||
*/
|
||||
Class<? extends BeanNameGenerator> nameGenerator() default AnnotationBeanNameGenerator.class;
|
||||
|
||||
/**
|
||||
* The {@link ScopeMetadataResolver} to be used for resolving the scope of detected components.
|
||||
*/
|
||||
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
|
||||
|
||||
/**
|
||||
* Indicates whether proxies should be generated for detected components, which may be
|
||||
* necessary when using scopes in a proxy-style fashion.
|
||||
* <p>The default is defer to the default behavior of the component scanner used to
|
||||
* execute the actual scan.
|
||||
* <p>Note that setting this attribute overrides any value set for {@link #scopeResolver()}.
|
||||
* @see ClassPathBeanDefinitionScanner#setScopedProxyMode(ScopedProxyMode)
|
||||
*/
|
||||
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
|
||||
|
||||
/**
|
||||
* Controls the class files eligible for component detection.
|
||||
* <p>Consider use of {@link #includeFilters()} and {@link #excludeFilters()}
|
||||
* for a more flexible approach.
|
||||
*/
|
||||
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
|
||||
|
||||
/**
|
||||
* Indicates whether automatic detection of classes annotated with {@code @Component}
|
||||
* {@code @Repository}, {@code @Service}, or {@code @Controller} should be enabled.
|
||||
*/
|
||||
boolean useDefaultFilters() default true;
|
||||
|
||||
/**
|
||||
* Specifies which types are eligible for component scanning.
|
||||
* <p>Further narrows the set of candidate components from everything in
|
||||
* {@link #basePackages()} to everything in the base packages that matches
|
||||
* the given filter or filters.
|
||||
* @see #resourcePattern()
|
||||
*/
|
||||
Filter[] includeFilters() default {};
|
||||
|
||||
/**
|
||||
* Specifies which types are not eligible for component scanning.
|
||||
* @see #resourcePattern()
|
||||
*/
|
||||
Filter[] excludeFilters() default {};
|
||||
|
||||
|
||||
/**
|
||||
* Declares the type filter to be used as an {@linkplain ComponentScan#includeFilters()
|
||||
* include filter} or {@linkplain ComponentScan#includeFilters() exclude filter}.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
@interface Filter {
|
||||
/**
|
||||
* The type of filter to use.
|
||||
* <p>Note that the filter types available are limited to those that may
|
||||
* be expressed as a {@code Class} in the {@link #value()} attribute. This is
|
||||
* in contrast to {@code <context:component-scan/>}, which allows for
|
||||
* expression-based (i.e., string-based) filters such as AspectJ pointcuts.
|
||||
* These filter types are intentionally not supported here, and not available
|
||||
* in the {@link FilterType} enum.
|
||||
* @see FilterType
|
||||
*/
|
||||
FilterType type() default FilterType.ANNOTATION;
|
||||
|
||||
/**
|
||||
* The class or classes to use as the filter. In the case of
|
||||
* {@link FilterType#ANNOTATION}, the class will be the annotation itself. In the
|
||||
* case of {@link FilterType#ASSIGNABLE_TYPE}, the class will be the type that
|
||||
* detected components should be assignable to. And in the case of
|
||||
* {@link FilterType#CUSTOM}, the class will be an implementation of
|
||||
* {@link TypeFilter}.
|
||||
* <p>When multiple classes are specified, OR logic is applied, e.g. "include
|
||||
* types annotated with {@code @Foo} OR {@code @Bar}".
|
||||
* <p>Specifying zero classes is permitted but will have no effect on component
|
||||
* scanning.
|
||||
*/
|
||||
Class<?>[] value(); //doco
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the @{@link ComponentScan} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see ClassPathBeanDefinitionScanner#scan(String...)
|
||||
* @see ComponentScanBeanDefinitionParser
|
||||
*/
|
||||
class ComponentScanAnnotationParser {
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
private final Environment environment;
|
||||
private final BeanDefinitionRegistry registry;
|
||||
|
||||
public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment, BeanDefinitionRegistry registry) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.environment = environment;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
public Set<BeanDefinitionHolder> parse(Map<String, Object> componentScanAttributes) {
|
||||
ClassPathBeanDefinitionScanner scanner =
|
||||
new ClassPathBeanDefinitionScanner(registry, (Boolean)componentScanAttributes.get("useDefaultFilters"));
|
||||
|
||||
Assert.notNull(this.environment, "Environment must not be null");
|
||||
scanner.setEnvironment(this.environment);
|
||||
|
||||
Assert.notNull(this.resourceLoader, "ResourceLoader must not be null");
|
||||
scanner.setResourceLoader(this.resourceLoader);
|
||||
|
||||
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(
|
||||
(Class<?>)componentScanAttributes.get("nameGenerator"), BeanNameGenerator.class));
|
||||
|
||||
ScopedProxyMode scopedProxyMode = (ScopedProxyMode) componentScanAttributes.get("scopedProxy");
|
||||
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
|
||||
scanner.setScopedProxyMode(scopedProxyMode);
|
||||
} else {
|
||||
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(
|
||||
(Class<?>)componentScanAttributes.get("scopeResolver"), ScopeMetadataResolver.class));
|
||||
}
|
||||
|
||||
scanner.setResourcePattern((String)componentScanAttributes.get("resourcePattern"));
|
||||
|
||||
for (Filter filterAnno : (Filter[])componentScanAttributes.get("includeFilters")) {
|
||||
for (TypeFilter typeFilter : typeFiltersFor(filterAnno)) {
|
||||
scanner.addIncludeFilter(typeFilter);
|
||||
}
|
||||
}
|
||||
for (Filter filterAnno : (Filter[])componentScanAttributes.get("excludeFilters")) {
|
||||
for (TypeFilter typeFilter : typeFiltersFor(filterAnno)) {
|
||||
scanner.addExcludeFilter(typeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> basePackages = new ArrayList<String>();
|
||||
for (String pkg : (String[])componentScanAttributes.get("value")) {
|
||||
if (StringUtils.hasText(pkg)) {
|
||||
basePackages.add(pkg);
|
||||
}
|
||||
}
|
||||
for (String pkg : (String[])componentScanAttributes.get("basePackages")) {
|
||||
if (StringUtils.hasText(pkg)) {
|
||||
basePackages.add(pkg);
|
||||
}
|
||||
}
|
||||
for (Class<?> clazz : (Class<?>[])componentScanAttributes.get("basePackageClasses")) {
|
||||
// TODO: loading user types directly here. implications on load-time
|
||||
// weaving may mean we need to revert to stringified class names in
|
||||
// annotation metadata
|
||||
basePackages.add(clazz.getPackage().getName());
|
||||
}
|
||||
|
||||
if (basePackages.isEmpty()) {
|
||||
throw new IllegalStateException("At least one base package must be specified");
|
||||
}
|
||||
|
||||
return scanner.doScan(basePackages.toArray(new String[]{}));
|
||||
}
|
||||
|
||||
private List<TypeFilter> typeFiltersFor(Filter filterAnno) {
|
||||
List<TypeFilter> typeFilters = new ArrayList<TypeFilter>();
|
||||
for (Class<?> filterClass : (Class<?>[])filterAnno.value()) {
|
||||
switch (filterAnno.type()) {
|
||||
case ANNOTATION:
|
||||
Assert.isAssignable(Annotation.class, filterClass,
|
||||
"An error occured when processing a @ComponentScan " +
|
||||
"ANNOTATION type filter: ");
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<Annotation> annoClass = (Class<Annotation>)filterClass;
|
||||
typeFilters.add(new AnnotationTypeFilter(annoClass));
|
||||
break;
|
||||
case ASSIGNABLE_TYPE:
|
||||
typeFilters.add(new AssignableTypeFilter(filterClass));
|
||||
break;
|
||||
case CUSTOM:
|
||||
Assert.isAssignable(TypeFilter.class, filterClass,
|
||||
"An error occured when processing a @ComponentScan " +
|
||||
"CUSTOM type filter: ");
|
||||
typeFilters.add(BeanUtils.instantiateClass(filterClass, TypeFilter.class));
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("unknown filter type " + filterAnno.type());
|
||||
}
|
||||
}
|
||||
return typeFilters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.lang.annotation.Annotation;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.beans.factory.xml.XmlReaderContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.type.filter.AnnotationTypeFilter;
|
||||
import org.springframework.core.type.filter.AspectJTypeFilter;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.core.type.filter.RegexPatternTypeFilter;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Parser for the {@code <context:component-scan/>} element.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public class ComponentScanBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String BASE_PACKAGE_ATTRIBUTE = "base-package";
|
||||
|
||||
private static final String RESOURCE_PATTERN_ATTRIBUTE = "resource-pattern";
|
||||
|
||||
private static final String USE_DEFAULT_FILTERS_ATTRIBUTE = "use-default-filters";
|
||||
|
||||
private static final String ANNOTATION_CONFIG_ATTRIBUTE = "annotation-config";
|
||||
|
||||
private static final String NAME_GENERATOR_ATTRIBUTE = "name-generator";
|
||||
|
||||
private static final String SCOPE_RESOLVER_ATTRIBUTE = "scope-resolver";
|
||||
|
||||
private static final String SCOPED_PROXY_ATTRIBUTE = "scoped-proxy";
|
||||
|
||||
private static final String EXCLUDE_FILTER_ELEMENT = "exclude-filter";
|
||||
|
||||
private static final String INCLUDE_FILTER_ELEMENT = "include-filter";
|
||||
|
||||
private static final String FILTER_TYPE_ATTRIBUTE = "type";
|
||||
|
||||
private static final String FILTER_EXPRESSION_ATTRIBUTE = "expression";
|
||||
|
||||
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
|
||||
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
|
||||
|
||||
// Actually scan for bean definitions and register them.
|
||||
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
|
||||
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
|
||||
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected ClassPathBeanDefinitionScanner configureScanner(ParserContext parserContext, Element element) {
|
||||
XmlReaderContext readerContext = parserContext.getReaderContext();
|
||||
|
||||
boolean useDefaultFilters = true;
|
||||
if (element.hasAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE)) {
|
||||
useDefaultFilters = Boolean.valueOf(element.getAttribute(USE_DEFAULT_FILTERS_ATTRIBUTE));
|
||||
}
|
||||
|
||||
// Delegate bean definition registration to scanner class.
|
||||
ClassPathBeanDefinitionScanner scanner = createScanner(readerContext, useDefaultFilters);
|
||||
scanner.setResourceLoader(readerContext.getResourceLoader());
|
||||
scanner.setEnvironment(parserContext.getDelegate().getEnvironment());
|
||||
scanner.setBeanDefinitionDefaults(parserContext.getDelegate().getBeanDefinitionDefaults());
|
||||
scanner.setAutowireCandidatePatterns(parserContext.getDelegate().getAutowireCandidatePatterns());
|
||||
|
||||
if (element.hasAttribute(RESOURCE_PATTERN_ATTRIBUTE)) {
|
||||
scanner.setResourcePattern(element.getAttribute(RESOURCE_PATTERN_ATTRIBUTE));
|
||||
}
|
||||
|
||||
try {
|
||||
parseBeanNameGenerator(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
try {
|
||||
parseScope(element, scanner);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
|
||||
parseTypeFilters(element, scanner, readerContext, parserContext);
|
||||
|
||||
return scanner;
|
||||
}
|
||||
|
||||
protected ClassPathBeanDefinitionScanner createScanner(XmlReaderContext readerContext, boolean useDefaultFilters) {
|
||||
return new ClassPathBeanDefinitionScanner(readerContext.getRegistry(), useDefaultFilters);
|
||||
}
|
||||
|
||||
protected void registerComponents(
|
||||
XmlReaderContext readerContext, Set<BeanDefinitionHolder> beanDefinitions, Element element) {
|
||||
|
||||
Object source = readerContext.extractSource(element);
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), source);
|
||||
|
||||
for (BeanDefinitionHolder beanDefHolder : beanDefinitions) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(beanDefHolder));
|
||||
}
|
||||
|
||||
// Register annotation config processors, if necessary.
|
||||
boolean annotationConfig = true;
|
||||
if (element.hasAttribute(ANNOTATION_CONFIG_ATTRIBUTE)) {
|
||||
annotationConfig = Boolean.valueOf(element.getAttribute(ANNOTATION_CONFIG_ATTRIBUTE));
|
||||
}
|
||||
if (annotationConfig) {
|
||||
Set<BeanDefinitionHolder> processorDefinitions =
|
||||
AnnotationConfigUtils.registerAnnotationConfigProcessors(readerContext.getRegistry(), source);
|
||||
for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(processorDefinition));
|
||||
}
|
||||
}
|
||||
|
||||
readerContext.fireComponentRegistered(compositeDef);
|
||||
}
|
||||
|
||||
protected void parseBeanNameGenerator(Element element, ClassPathBeanDefinitionScanner scanner) {
|
||||
if (element.hasAttribute(NAME_GENERATOR_ATTRIBUTE)) {
|
||||
BeanNameGenerator beanNameGenerator = (BeanNameGenerator) instantiateUserDefinedStrategy(
|
||||
element.getAttribute(NAME_GENERATOR_ATTRIBUTE), BeanNameGenerator.class,
|
||||
scanner.getResourceLoader().getClassLoader());
|
||||
scanner.setBeanNameGenerator(beanNameGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseScope(Element element, ClassPathBeanDefinitionScanner scanner) {
|
||||
// Register ScopeMetadataResolver if class name provided.
|
||||
if (element.hasAttribute(SCOPE_RESOLVER_ATTRIBUTE)) {
|
||||
if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot define both 'scope-resolver' and 'scoped-proxy' on <component-scan> tag");
|
||||
}
|
||||
ScopeMetadataResolver scopeMetadataResolver = (ScopeMetadataResolver) instantiateUserDefinedStrategy(
|
||||
element.getAttribute(SCOPE_RESOLVER_ATTRIBUTE), ScopeMetadataResolver.class,
|
||||
scanner.getResourceLoader().getClassLoader());
|
||||
scanner.setScopeMetadataResolver(scopeMetadataResolver);
|
||||
}
|
||||
|
||||
if (element.hasAttribute(SCOPED_PROXY_ATTRIBUTE)) {
|
||||
String mode = element.getAttribute(SCOPED_PROXY_ATTRIBUTE);
|
||||
if ("targetClass".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.TARGET_CLASS);
|
||||
}
|
||||
else if ("interfaces".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.INTERFACES);
|
||||
}
|
||||
else if ("no".equals(mode)) {
|
||||
scanner.setScopedProxyMode(ScopedProxyMode.NO);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("scoped-proxy only supports 'no', 'interfaces' and 'targetClass'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void parseTypeFilters(
|
||||
Element element, ClassPathBeanDefinitionScanner scanner, XmlReaderContext readerContext, ParserContext parserContext) {
|
||||
|
||||
// Parse exclude and include filter elements.
|
||||
ClassLoader classLoader = scanner.getResourceLoader().getClassLoader();
|
||||
NodeList nodeList = element.getChildNodes();
|
||||
for (int i = 0; i < nodeList.getLength(); i++) {
|
||||
Node node = nodeList.item(i);
|
||||
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||
String localName = parserContext.getDelegate().getLocalName(node);
|
||||
try {
|
||||
if (INCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
scanner.addIncludeFilter(typeFilter);
|
||||
}
|
||||
else if (EXCLUDE_FILTER_ELEMENT.equals(localName)) {
|
||||
TypeFilter typeFilter = createTypeFilter((Element) node, classLoader);
|
||||
scanner.addExcludeFilter(typeFilter);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
readerContext.error(ex.getMessage(), readerContext.extractSource(element), ex.getCause());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected TypeFilter createTypeFilter(Element element, ClassLoader classLoader) {
|
||||
String filterType = element.getAttribute(FILTER_TYPE_ATTRIBUTE);
|
||||
String expression = element.getAttribute(FILTER_EXPRESSION_ATTRIBUTE);
|
||||
try {
|
||||
if ("annotation".equals(filterType)) {
|
||||
return new AnnotationTypeFilter((Class<Annotation>) classLoader.loadClass(expression));
|
||||
}
|
||||
else if ("assignable".equals(filterType)) {
|
||||
return new AssignableTypeFilter(classLoader.loadClass(expression));
|
||||
}
|
||||
else if ("aspectj".equals(filterType)) {
|
||||
return new AspectJTypeFilter(expression, classLoader);
|
||||
}
|
||||
else if ("regex".equals(filterType)) {
|
||||
return new RegexPatternTypeFilter(Pattern.compile(expression));
|
||||
}
|
||||
else if ("custom".equals(filterType)) {
|
||||
Class filterClass = classLoader.loadClass(expression);
|
||||
if (!TypeFilter.class.isAssignableFrom(filterClass)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Class is not assignable to [" + TypeFilter.class.getName() + "]: " + expression);
|
||||
}
|
||||
return (TypeFilter) BeanUtils.instantiateClass(filterClass);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported filter type: " + filterType);
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new FatalBeanException("Type filter class not found: " + expression, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Object instantiateUserDefinedStrategy(String className, Class strategyType, ClassLoader classLoader) {
|
||||
Object result = null;
|
||||
try {
|
||||
result = classLoader.loadClass(className).newInstance();
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalArgumentException("Class [" + className + "] for strategy [" +
|
||||
strategyType.getName() + "] not found", ex);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Unable to instantiate class [" + className + "] for strategy [" +
|
||||
strategyType.getName() + "]. A zero-argument constructor is required", ex);
|
||||
}
|
||||
|
||||
if (!strategyType.isAssignableFrom(result.getClass())) {
|
||||
throw new IllegalArgumentException("Provided class name must be an implementation of " + strategyType);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2002-2011 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.lang.annotation.Documented;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Indicates that a class declares one or more @{@link Bean} methods and may be processed
|
||||
* by the Spring container to generate bean definitions and service requests for those
|
||||
* beans at runtime, for example:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class AppConfig {
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* // instantiate, configure and return bean ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <h2>Bootstrapping {@code @Configuration} classes</h2>
|
||||
* <h3>Via {@code AnnotationConfigApplicationContext}</h3>
|
||||
* {@code @Configuration} classes are typically bootstrapped using either
|
||||
* {@link AnnotationConfigApplicationContext} or its web-capable variant,
|
||||
* {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
* AnnotationConfigWebApplicationContext}.
|
||||
* A simple example with the former follows:
|
||||
* <pre class="code">
|
||||
* AnnotationConfigApplicationContext ctx =
|
||||
* new AnnotationConfigApplicationContext();
|
||||
* ctx.register(AppConfig.class);
|
||||
* ctx.refresh();
|
||||
* MyBean myBean = ctx.getBean(MyBean.class);
|
||||
* // use myBean ...</pre>
|
||||
*
|
||||
* See {@link AnnotationConfigApplicationContext} Javadoc for further details and see
|
||||
* {@link org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
* AnnotationConfigWebApplicationContext} for {@code web.xml} configuration instructions.
|
||||
*
|
||||
* <h3>Via Spring {@code <beans>} XML</h3>
|
||||
* <p>As an alternative to registering {@code @Configuration} classes directly against an
|
||||
* {@code AnnotationConfigApplicationContext}, {@code @Configuration} classes may be
|
||||
* declared as normal {@code <bean>} definitions within Spring XML files:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
* <beans>
|
||||
* <context:annotation-config/>
|
||||
* <bean class="com.acme.AppConfig"/>
|
||||
* </beans>
|
||||
* }</pre>
|
||||
*
|
||||
* In the example above, {@code <context:annotation-config/>} is required in order to
|
||||
* enable {@link ConfigurationClassPostProcessor} and other annotation-related
|
||||
* post processors that facilitate handling {@code @Configuration} classes.
|
||||
*
|
||||
* <h3>Via component scanning</h3>
|
||||
* <p>{@code @Configuration} is meta-annotated with @{@link Component}, therefore
|
||||
* {@code @Configuration} classes are candidates for component scanning (typically using
|
||||
* Spring XML's {@code <context:component-scan/>} element) and therefore may also take
|
||||
* advantage of @{@link Autowired}/@{@link Inject} at the field and method level (but not
|
||||
* at the constructor level).
|
||||
* <p>{@code @Configuration} classes may not only be bootstrapped using
|
||||
* component scanning, but may also themselves <em>configure</em> component scanning using
|
||||
* the @{@link ComponentScan} annotation:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @ComponentScan("com.acme.app.services")
|
||||
* public class AppConfig {
|
||||
* // various @Bean definitions ...
|
||||
* }</pre>
|
||||
*
|
||||
* See @{@link ComponentScan} Javadoc for details.
|
||||
*
|
||||
*
|
||||
* <h2>Working with externalized values</h2>
|
||||
* <h3>Using the {@code Environment} API</h3>
|
||||
* Externalized values may be looked up by injecting the Spring
|
||||
* {@link org.springframework.core.env.Environment Environment} into a
|
||||
* {@code @Configuration} class using the {@code @Autowired} or the {@code @Inject}
|
||||
* annotation:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class AppConfig {
|
||||
* @Inject Environment env;
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* MyBean myBean = new MyBean();
|
||||
* myBean.setName(env.getProperty("bean.name"));
|
||||
* return myBean;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Properties resolved through the {@code Environment} reside in one or more "property
|
||||
* source" objects, and {@code @Configuration} classes may contribute property sources to
|
||||
* the {@code Environment} object using the @{@link PropertySources} annotation:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @PropertySource("classpath:/com/acme/app.properties")
|
||||
* public class AppConfig {
|
||||
* @Inject Environment env;
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* return new MyBean(env.getProperty("bean.name"));
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* See {@link org.springframework.core.env.Environment Environment}
|
||||
* and @{@link PropertySource} Javadoc for further details.
|
||||
*
|
||||
* <h3>Using the {@code @Value} annotation</h3>
|
||||
* Externalized values may be 'wired into' {@code @Configuration} classes using
|
||||
* the @{@link Value} annotation:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @PropertySource("classpath:/com/acme/app.properties")
|
||||
* public class AppConfig {
|
||||
* @Value("${bean.name}") String beanName;
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* return new MyBean(beanName);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* This approach is most useful when using Spring's
|
||||
* {@link org.springframework.context.support.PropertySourcesPlaceholderConfigurer
|
||||
* PropertySourcesPlaceholderConfigurer}, usually enabled via XML with
|
||||
* {@code <context:property-placeholder/>}. See the section below on composing
|
||||
* {@code @Configuration} classes with Spring XML using {@code @ImportResource},
|
||||
* see @{@link Value} Javadoc, and see @{@link Bean} Javadoc for details on working with
|
||||
* {@code BeanFactoryPostProcessor} types such as
|
||||
* {@code PropertySourcesPlaceholderConfigurer}.
|
||||
*
|
||||
* <h2>Composing {@code @Configuration} classes</h2>
|
||||
* <h3>With the {@code @Import} annotation</h3>
|
||||
* <p>{@code @Configuration} classes may be composed using the @{@link Import} annotation,
|
||||
* not unlike the way that {@code <import>} works in Spring XML. Because
|
||||
* {@code @Configuration} objects are managed as Spring beans within the container,
|
||||
* imported configurations may be injected using {@code @Autowired} or {@code @Inject}:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class DatabaseConfig {
|
||||
* @Bean
|
||||
* public DataSource dataSource() {
|
||||
* // instantiate, configure and return DataSource
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Configuration
|
||||
* @Import(DatabaseConfig.class)
|
||||
* public class AppConfig {
|
||||
* @Inject DatabaseConfig dataConfig;
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* // reference the dataSource() bean method
|
||||
* return new MyBean(dataConfig.dataSource());
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Now both {@code AppConfig} and the imported {@code DatabaseConfig} can be bootstrapped
|
||||
* by registering only {@code AppConfig} against the Spring context:
|
||||
*
|
||||
* <pre class="code">
|
||||
* new AnnotationConfigApplicationContext(AppConfig.class);</pre>
|
||||
*
|
||||
* <h3>With the {@code @Profile} annotation</h3>
|
||||
* {@code @Configuration} classes may be marked with the @{@link Profile} annotation to
|
||||
* indicate they should be processed only if a given profile or profiles are
|
||||
* <em>active</em>:
|
||||
* <pre class="code">
|
||||
* @Profile("embedded")
|
||||
* @Configuration
|
||||
* public class EmbeddedDatabaseConfig {
|
||||
* @Bean
|
||||
* public DataSource dataSource() {
|
||||
* // instantiate, configure and return embedded DataSource
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Profile("production")
|
||||
* @Configuration
|
||||
* public class ProductionDatabaseConfig {
|
||||
* @Bean
|
||||
* public DataSource dataSource() {
|
||||
* // instantiate, configure and return production DataSource
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* See @{@link Profile} and {@link org.springframework.core.env.Environment Environment}
|
||||
* Javadoc for further details.
|
||||
*
|
||||
* <h3>With Spring XML using the {@code @ImportResource} annotation</h3>
|
||||
* As mentioned above, {@code @Configuration} classes may be declared as regular Spring
|
||||
* {@code <bean>} definitions within Spring XML files. It is also possible to
|
||||
* import Spring XML configuration files into {@code @Configuration} classes using
|
||||
* the @{@link ImportResource} annotation. Bean definitions imported from XML can be
|
||||
* injected using {@code @Autowired} or {@code @Import}:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @ImportResource("classpath:/com/acme/database-config.xml")
|
||||
* public class AppConfig {
|
||||
* @Inject DataSource dataSource; // from XML
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* // inject the XML-defined dataSource bean
|
||||
* return new MyBean(this.dataSource);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>With nested {@code @Configuration} classes</h3>
|
||||
* {@code @Configuration} classes may be nested within one another as follows:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class AppConfig {
|
||||
* @Inject DataSource dataSource;
|
||||
*
|
||||
* @Bean
|
||||
* public MyBean myBean() {
|
||||
* return new MyBean(dataSource);
|
||||
* }
|
||||
*
|
||||
* @Configuration
|
||||
* static class DatabaseConfig {
|
||||
* @Bean
|
||||
* DataSource dataSource() {
|
||||
* return new EmbeddedDatabaseBuilder().build();
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* When bootstrapping such an arrangement, only {@code AppConfig} need be registered
|
||||
* against the application context. By virtue of being a nested {@code @Configuration}
|
||||
* class, {@code DatabaseConfig} <em>will be registered automatically</em>. This avoids
|
||||
* the need to use an {@code @Import} annotation when the relationship between
|
||||
* {@code AppConfig} {@code DatabaseConfig} is already implicitly clear.
|
||||
*
|
||||
* <p>Note also that nested {@code @Configuration} classes can be used to good effect
|
||||
* with the {@code @Profile} annotation to provide two options of the same bean to the
|
||||
* enclosing {@code @Configuration} class.
|
||||
*
|
||||
* <h2>Configuring lazy initialization</h2>
|
||||
* <p>By default, {@code @Bean} methods will be <em>eagerly instantiated</em> at container
|
||||
* bootstrap time. To avoid this, {@code @Configuration} may be used in conjunction with
|
||||
* the @{@link Lazy} annotation to indicate that all {@code @Bean} methods declared within
|
||||
* the class are by default lazily initialized. Note that {@code @Lazy} may be used on
|
||||
* individual {@code @Bean} methods as well.
|
||||
*
|
||||
* <h2>Testing support for {@code @Configuration} classes</h2>
|
||||
* The Spring <em>TestContext framework</em> available in the {@code spring-test} module
|
||||
* provides the {@code @ContextConfiguration} annotation, which as of Spring 3.1 can
|
||||
* accept an array of {@code @Configuration} {@code Class} objects:
|
||||
* <pre class="code">
|
||||
* @RunWith(SpringJUnit4ClassRunner.class)
|
||||
* @ContextConfiguration(classes={AppConfig.class, DatabaseConfig.class})
|
||||
* public class MyTests {
|
||||
*
|
||||
* @Autowired MyBean myBean;
|
||||
*
|
||||
* @Autowired DataSource dataSource;
|
||||
*
|
||||
* @Test
|
||||
* public void test() {
|
||||
* // assertions against myBean ...
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* See TestContext framework reference documentation for details.
|
||||
*
|
||||
* <h2>Enabling built-in Spring features using {@code @Enable} annotations</h2>
|
||||
* Spring features such as asynchronous method execution, scheduled task execution,
|
||||
* annotation driven transaction management, and even Spring MVC can be enabled and
|
||||
* configured from {@code @Configuration}
|
||||
* classes using their respective "{@code @Enable}" annotations. See
|
||||
* {@link org.springframework.scheduling.annotation.EnableAsync @EnableAsync},
|
||||
* {@link org.springframework.scheduling.annotation.EnableScheduling @EnableScheduling},
|
||||
* {@link org.springframework.transaction.annotation.EnableTransactionManagement @EnableTransactionManagement},
|
||||
* {@link org.springframework.context.annotation.EnableAspectJAutoProxy @EnableAspectJAutoProxy},
|
||||
* and {@link org.springframework.web.servlet.config.annotation.EnableWebMvc @EnableWebMvc}
|
||||
* for details.
|
||||
*
|
||||
* <h2>Constraints when authoring {@code @Configuration} classes</h2>
|
||||
* <ul>
|
||||
* <li>@Configuration classes must be non-final
|
||||
* <li>@Configuration classes must be non-local (may not be declared within a method)
|
||||
* <li>@Configuration classes must have a default/no-arg constructor and may not
|
||||
* use @{@link Autowired} constructor parameters. Any nested configuration classes
|
||||
* must be {@code static}
|
||||
* </ul>
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see Bean
|
||||
* @see Profile
|
||||
* @see Import
|
||||
* @see ImportResource
|
||||
* @see ComponentScan
|
||||
* @see Lazy
|
||||
* @see PropertySource
|
||||
* @see AnnotationConfigApplicationContext
|
||||
* @see ConfigurationClassPostProcessor
|
||||
* @see org.springframework.core.env.Environment
|
||||
* @see org.springframework.test.context.ContextConfiguration
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Component
|
||||
public @interface Configuration {
|
||||
|
||||
/**
|
||||
* Explicitly specify the name of the Spring bean definition associated
|
||||
* with this Configuration class. If left unspecified (the common case),
|
||||
* a bean name will be automatically generated.
|
||||
*
|
||||
* <p>The custom name applies only if the Configuration class is picked up via
|
||||
* component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
|
||||
* If the Configuration class is registered as a traditional XML bean definition,
|
||||
* the name/id of the bean element will take precedence.
|
||||
*
|
||||
* @return the specified bean name, if any
|
||||
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user