mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
182 Commits
5.3.x
...
v3.1.3.RELEASE
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c36240055 | |||
| a844712908 | |||
| 03c3dc3dae | |||
| 9caf672f21 | |||
| 9d78f17176 | |||
| 245d3114a8 | |||
| 4169898842 | |||
| e491d990de | |||
| 6d6eaf8506 | |||
| a6bcda25c2 | |||
| 7702fb8513 | |||
| b17d53ffae | |||
| 7f081a5eb4 | |||
| 983e2ce64f | |||
| ea49c8b840 | |||
| 2cfd15899d | |||
| d8dc4a257a | |||
| 00ed17dca0 | |||
| 410b9f933e | |||
| a0745c0c0a | |||
| cfdd50d8ab | |||
| e3136e65f4 | |||
| ced6bb4569 | |||
| 4525527794 | |||
| 98075c33c6 | |||
| 4cee691d09 | |||
| 347737290e | |||
| a94677e7b6 | |||
| ca386c13fc | |||
| f9eacf9c1f | |||
| 88906d271b | |||
| d8166d6155 | |||
| 4fccd1799a | |||
| 9187eeff27 | |||
| 2ab55414e5 | |||
| c90e2c6f10 | |||
| 506e9f50d0 | |||
| 2564db2bfd | |||
| 13edc93f78 | |||
| 1d26c37b87 | |||
| ee8e178434 | |||
| 679e122326 | |||
| 7c1a2f37f2 | |||
| 5e318e73f8 | |||
| 682b0f2c63 | |||
| ad81ec922b | |||
| 9862fbfc01 | |||
| d7825586dc | |||
| 9338080916 | |||
| fd10e149f6 | |||
| 1dd01a7a82 | |||
| 3788635f51 | |||
| 86ef22db49 | |||
| b2fce2f254 | |||
| be2c2e9a86 | |||
| 0047200b98 | |||
| 1641e082eb | |||
| baeac9ea6b | |||
| bac05f568a | |||
| cb7d689f92 | |||
| da8e2d6736 | |||
| 6ed589d94b | |||
| e70119733a | |||
| 0824746255 | |||
| 30d0bd309c | |||
| ec2603de63 | |||
| 470c85ade0 | |||
| 01d8d64200 | |||
| 2c8b7fe093 | |||
| bbfc807b0c | |||
| c81543e1a4 | |||
| 437ce9bb0e | |||
| b79260a017 | |||
| 0f522cfe6a | |||
| da75eaedbf | |||
| 69f2496cd7 | |||
| e670975fde | |||
| 41d9d737ac | |||
| c25cbe5522 | |||
| 8c64b47473 | |||
| 229537288a | |||
| bec5463640 | |||
| 7ebfd9e6a8 | |||
| f9147d375f | |||
| 03dd7a57f0 | |||
| 43bc3f19d9 | |||
| 0cf4a2626b | |||
| a0d0b37d10 | |||
| 5a149e4600 | |||
| 25a806a54e | |||
| 33463a94e3 | |||
| a6f5d9eb93 | |||
| f11ef28a4d | |||
| 52c10c52cd | |||
| cb13353c1d | |||
| 8b09b52720 | |||
| d7fd70fa08 | |||
| fbe955ab28 | |||
| 26ee0c4842 | |||
| 19718700cf | |||
| 8599dfd74e | |||
| a3bfa754fd | |||
| 8e3a0070dc | |||
| f9cf5d1aa6 | |||
| f78bef906d | |||
| 7704d76144 | |||
| 3f48c71c77 | |||
| 576f6fd25c | |||
| 5204e11d9c | |||
| 30166981e2 | |||
| 617f0ef542 | |||
| dc400c7124 | |||
| a7e2297432 | |||
| 671f97721f | |||
| 49f728eae8 | |||
| 16da15eca1 | |||
| efabc6bf18 | |||
| 98b281ea45 | |||
| 00a69394e6 | |||
| 45f1438ef8 | |||
| e7b44e015a | |||
| 23fe1e2934 | |||
| 1f64b147b1 | |||
| 5e4f3b3108 | |||
| c87989477d | |||
| 3b4210fa6f | |||
| 79b86a7e3a | |||
| b488ae6009 | |||
| aeef8c87f6 | |||
| 556c6a79c5 | |||
| 0281158fcc | |||
| 100fa178dd | |||
| a387d13d5f | |||
| 841d9533db | |||
| 0d18deb6a4 | |||
| 42204e5098 | |||
| eed090e5f5 | |||
| 0320cc582b | |||
| a76c7ca299 | |||
| 8bab873107 | |||
| e8006bdf78 | |||
| 3f387eb9cf | |||
| 0769d53a6a | |||
| 6263c9abf9 | |||
| 1eb50297ad | |||
| 20f87ab98d | |||
| c25a1bcb60 | |||
| e8deba2915 | |||
| fa33c4b857 | |||
| 24c30eac49 | |||
| 1af7575bde | |||
| 8143d66e4c | |||
| 9e370208d0 | |||
| e66bc07c37 | |||
| c9a2dbdbfd | |||
| 2fa0e63e5a | |||
| 83ac44d1e4 | |||
| 0cf93711b9 | |||
| 8faa2e61ec | |||
| 71443efd90 | |||
| bc243b5aa7 | |||
| d36979bc80 | |||
| 5875695d99 | |||
| dcb44264ad | |||
| 9fcb578187 | |||
| 8cdfbfe864 | |||
| 9ca407d55e | |||
| 1380d053c4 | |||
| 6b76afd1ce | |||
| 2fe74a4ef0 | |||
| ee9b9d6a57 | |||
| bf8fa6e268 | |||
| c976fa329f | |||
| d22c8aca49 | |||
| 79d9f7a5f7 | |||
| 9d3cfcd8e6 | |||
| 7d72ab59ae | |||
| 40a6769309 | |||
| e30d6104f3 | |||
| 129b4b3136 | |||
| b32a365f14 | |||
| 79c9ca1a26 |
@@ -3,6 +3,83 @@ SPRING FRAMEWORK CHANGELOG
|
||||
http://www.springsource.org
|
||||
|
||||
|
||||
Changes in version 3.1.3 (2012-10-31)
|
||||
-------------------------------------
|
||||
|
||||
* fixed cache handling for JNLP connections
|
||||
* LinkedCaseInsensitiveMap checks for uniqueness of case-insensitive keys now
|
||||
* ResourceBundleMessageSource supports "defaultEncoding", "fallbackToSystemLocale", "cacheSeconds"
|
||||
* BeanWrapper does not fall back to String constructor if ConversionService attempt failed before
|
||||
* @Import'ed configuration classes get properly registered in case of same class name
|
||||
* @Autowired-driven ObjectFactory/Provider resolution works in non-singleton beans as well
|
||||
* @Resource processing properly works with scoped beans and prototypes again
|
||||
* allow replaced-method 'arg-type' matches against element body as well as 'match' attribute
|
||||
* fixed potential race condition in concurrent calling of autowired methods on a prototype bean
|
||||
* cancel on a Future returned by a TaskScheduler works reliably
|
||||
* LoadTimeWeaverAware beans are consistently being created early for JPA weaving to work reliably
|
||||
* Spring-backed DataSources consistently implement JDBC 4.0's Wrapper interface
|
||||
* optimized ResourceDatabasePopulator to work around JDK 1.7 substring performance regression
|
||||
* deprecated SimpleJdbcTestUtils in favor of JdbcTestUtils
|
||||
* introduced "countRowsInTableWhere()" and "dropTables()" in JdbcTestUtils
|
||||
* same-named unit from persistence.xml overrides in case of conflict with default unit
|
||||
* JPA persistence.xml files may use jar-file entries relative to the unit root (as per the JPA spec)
|
||||
* added "jtaDataSource" property to JPA LocalContainerEntityManagerFactoryBean (for default units)
|
||||
* Hibernate 4 LocalSessionFactoryBuilder sets thread context ClassLoader (for JBoss 7 compatibility)
|
||||
* Jaxb2Marshaller performs proper "supports" check for scanned packages
|
||||
* media types in HTTP Accept headers can be parsed with single quotes (-> Android 2.x)
|
||||
* MediaType's include method now recognizes wildcards in media types with a suffix
|
||||
* added support for recognizing wildcards in media types with a suffix
|
||||
* UriComponentsBuilder is capable of handling opaque URIs as well
|
||||
* CookieGenerator supports "cookieHttpOnly" flag for Servlet 3.0
|
||||
* HttpComponentsHttpInvokerRequestExecutor explicitly releases connection on HttpComponents 4.2
|
||||
* improved no-match handling for @RequestMapping methods
|
||||
* dynamic return value types for @MVC methods
|
||||
* fixed issue with resolution of WebDataBinder argument
|
||||
* fixed issue with resolving Errors controller argument
|
||||
* now decoding target parameters prior to saving a FlashMap
|
||||
* prevent response updates if @ResponseStatus has a reason
|
||||
* improved regex for parsing query params
|
||||
* optimized use of AntPathStringMatcher
|
||||
* optimized use of HandlerMethod and subclasses
|
||||
* fixed issue in message conversion where producible and acceptable headers are equally specific
|
||||
* fixed NullPointerException in AbstractMessageConverterMethodProcessor
|
||||
* fixed side effect from caching in AnnotationMethodHandlerExceptionResolver
|
||||
* DispatcherPortlet uses a forward for rendering a view as resource response
|
||||
* fixed Portlet request mapping priorities in cross-controller case
|
||||
|
||||
|
||||
Changes in version 3.1.2 (2012-07-09)
|
||||
-------------------------------------
|
||||
|
||||
* removed INDEX.LIST entries from Spring jar META-INF dirs
|
||||
* fixed OSGi metadata for optional "javax.inject" package import
|
||||
* fixed AbstractResource's InputStream-based "contentLength()" implementation
|
||||
* fixed MutablePropertySources "get" implementation to correctly return null if not found
|
||||
* fixed @PropertySource placeholder resolution to apply to all specified values
|
||||
* JDBC parameter binding uses JDBC 3.0 ParameterMetaData (if available) for type determination
|
||||
* added "jtaTransactionManager" property to Hibernate 4 LocalSessionFactoryBean/Builder
|
||||
* EhCacheFactoryBean applies listeners and enabled/disabled flags to existing cache regions as well
|
||||
* added "repeatCount" bean property to Quartz SimpleTriggerFactoryBean
|
||||
* support executor qualification with @Async#value
|
||||
* ServletContextResource's getFile implementation falls back to getRealPath for non-existent files
|
||||
* fixed StandardServletMultipartResolver compatibility with Resin (only deleting actual file parts)
|
||||
* fixed RequestConditionHolder to return null when no match is found
|
||||
* fixed concurrency issue in AnnotationMethodHandlerExceptionResolver
|
||||
* fixed issue with parsing invalid Content-Type or Accept headers
|
||||
* added "defaultCharset" property to StringHttpMessageConverter
|
||||
* added Jackson 2 HttpMessageConverter and View types
|
||||
* translate IOException on EOF from Jackson to HttpMessageNotReadableException
|
||||
* fix content negotiation issue when sorting selected media types by quality value
|
||||
* raise RestClientException instead of IllegalArgumentException for unknown status codes
|
||||
* discover controllers based on type-level @RequestMapping in new @MVC support classes
|
||||
* fixed issue with suffix pattern in AnnotationMethodHandlerAdapter
|
||||
* merge rather than add URI vars in ExtendedServletRequestDataBinder
|
||||
* raise RestClientException for unknown HTTP status codes
|
||||
* fixed issue with encoded params in UriComponentsBuilder
|
||||
* XStreamMarshaller converts StreamException to Spring exception in case of unmarshalling an empty stream
|
||||
* corrected DispatcherPortlet behavior to not forward event exceptions to the render phase by default
|
||||
|
||||
|
||||
Changes in version 3.1.1 (2012-02-16)
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SPRING FRAMEWORK 3.1.1 (February 2012)
|
||||
--------------------------------------
|
||||
SPRING FRAMEWORK 3.1.3 (October 2012)
|
||||
---------------------------------------
|
||||
http://www.springsource.org
|
||||
|
||||
1. INTRODUCTION
|
||||
|
||||
+8
-5
@@ -1,4 +1,4 @@
|
||||
version=3.1.1
|
||||
version=3.1.3
|
||||
|
||||
natural.name=spring-framework
|
||||
project.name=Spring Framework
|
||||
@@ -17,13 +17,16 @@ spring.osgi.range="${spring.osgi.range.nq}"
|
||||
aj.osgi.range="[1.6.8, 2.0.0)"
|
||||
|
||||
## For GA releases
|
||||
#release.type=release
|
||||
#build.stamp=RELEASE
|
||||
release.type=release
|
||||
build.stamp=RELEASE
|
||||
|
||||
## For milestone releases
|
||||
#release.type=milestone
|
||||
#build.stamp=M1
|
||||
|
||||
## For trunk development / ci builds
|
||||
release.type=integration
|
||||
build.stamp=BUILD-SNAPSHOT
|
||||
#release.type=integration
|
||||
#build.stamp=BUILD-SNAPSHOT
|
||||
|
||||
#bundlor
|
||||
fail.on.warnings=false
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.aop.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Base class for asynchronous method execution aspects, such as
|
||||
* {@link org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor}
|
||||
* or {@link org.springframework.scheduling.aspectj.AnnotationAsyncExecutionAspect}.
|
||||
*
|
||||
* <p>Provides support for <i>executor qualification</i> on a method-by-method basis.
|
||||
* {@code AsyncExecutionAspectSupport} objects must be constructed with a default {@code
|
||||
* Executor}, but each individual method may further qualify a specific {@code Executor}
|
||||
* bean to be used when executing it, e.g. through an annotation attribute.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
|
||||
private final Map<Method, AsyncTaskExecutor> executors = new HashMap<Method, AsyncTaskExecutor>();
|
||||
|
||||
private Executor defaultExecutor;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link AsyncExecutionAspectSupport}, using the provided default
|
||||
* executor unless individual async methods indicate via qualifier that a more
|
||||
* specific executor should be used.
|
||||
* @param defaultExecutor the executor to use when executing asynchronous methods
|
||||
*/
|
||||
public AsyncExecutionAspectSupport(Executor defaultExecutor) {
|
||||
this.setExecutor(defaultExecutor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Supply the executor to be used when executing async methods.
|
||||
* @param defaultExecutor the {@code Executor} (typically a Spring {@code
|
||||
* AsyncTaskExecutor} or {@link java.util.concurrent.ExecutorService}) to delegate to
|
||||
* unless a more specific executor has been requested via a qualifier on the async
|
||||
* method, in which case the executor will be looked up at invocation time against the
|
||||
* enclosing bean factory.
|
||||
* @see #getExecutorQualifier
|
||||
* @see #setBeanFactory(BeanFactory)
|
||||
*/
|
||||
public void setExecutor(Executor defaultExecutor) {
|
||||
this.defaultExecutor = defaultExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link BeanFactory} to be used when looking up executors by qualifier.
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine the specific executor to use when executing the given method.
|
||||
* @returns the executor to use (never {@code null})
|
||||
*/
|
||||
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
|
||||
if (!this.executors.containsKey(method)) {
|
||||
Executor executor = this.defaultExecutor;
|
||||
String qualifier = getExecutorQualifier(method);
|
||||
if (StringUtils.hasLength(qualifier)) {
|
||||
Assert.notNull(this.beanFactory,
|
||||
"BeanFactory must be set on " + this.getClass().getSimpleName() +
|
||||
" to access qualified executor [" + qualifier + "]");
|
||||
executor = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
this.beanFactory, Executor.class, qualifier);
|
||||
}
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
this.executors.put(method, (AsyncTaskExecutor) executor);
|
||||
}
|
||||
else if (executor != null) {
|
||||
this.executors.put(method, new TaskExecutorAdapter(executor));
|
||||
}
|
||||
}
|
||||
return this.executors.get(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualifier or bean name of the executor to be used when executing the
|
||||
* given async method, typically specified in the form of an annotation attribute.
|
||||
* Returning an empty string or {@code null} indicates that no specific executor has
|
||||
* been specified and that the {@linkplain #setExecutor(Executor) default executor}
|
||||
* should be used.
|
||||
* @param method the method to inspect for executor qualifier metadata
|
||||
* @return the qualifier if specified, otherwise empty string or {@code null}
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
protected abstract String getExecutorQualifier(Method method);
|
||||
|
||||
}
|
||||
+52
-35
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.aop.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -25,14 +27,12 @@ import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* AOP Alliance <code>MethodInterceptor</code> that processes method invocations
|
||||
* asynchronously, using a given {@link org.springframework.core.task.AsyncTaskExecutor}.
|
||||
* Typically used with the {@link org.springframework.context.task.Async} annotation.
|
||||
* Typically used with the {@link org.springframework.scheduling.annotation.Async} annotation.
|
||||
*
|
||||
* <p>In terms of target method signatures, any parameter types are supported.
|
||||
* However, the return type is constrained to either <code>void</code> or
|
||||
@@ -44,50 +44,53 @@ import org.springframework.util.ReflectionUtils;
|
||||
* (like Spring's {@link org.springframework.scheduling.annotation.AsyncResult}
|
||||
* or EJB 3.1's <code>javax.ejb.AsyncResult</code>).
|
||||
*
|
||||
* <p>As of Spring 3.1.2 the {@code AnnotationAsyncExecutionInterceptor} subclass is
|
||||
* preferred for use due to its support for executor qualification in conjunction with
|
||||
* Spring's {@code @Async} annotation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see org.springframework.scheduling.annotation.Async
|
||||
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
|
||||
* @see org.springframework.scheduling.annotation.AnnotationAsyncExecutionInterceptor
|
||||
*/
|
||||
public class AsyncExecutionInterceptor implements MethodInterceptor, Ordered {
|
||||
|
||||
private final AsyncTaskExecutor asyncExecutor;
|
||||
|
||||
public class AsyncExecutionInterceptor extends AsyncExecutionAspectSupport
|
||||
implements MethodInterceptor, Ordered {
|
||||
|
||||
/**
|
||||
* Create a new AsyncExecutionInterceptor.
|
||||
* @param asyncExecutor the Spring AsyncTaskExecutor to delegate to
|
||||
* Create a new {@code AsyncExecutionInterceptor}.
|
||||
* @param executor the {@link Executor} (typically a Spring {@link AsyncTaskExecutor}
|
||||
* or {@link java.util.concurrent.ExecutorService}) to delegate to.
|
||||
*/
|
||||
public AsyncExecutionInterceptor(AsyncTaskExecutor asyncExecutor) {
|
||||
Assert.notNull(asyncExecutor, "TaskExecutor must not be null");
|
||||
this.asyncExecutor = asyncExecutor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new AsyncExecutionInterceptor.
|
||||
* @param asyncExecutor the <code>java.util.concurrent</code> Executor
|
||||
* to delegate to (typically a {@link java.util.concurrent.ExecutorService}
|
||||
*/
|
||||
public AsyncExecutionInterceptor(Executor asyncExecutor) {
|
||||
this.asyncExecutor = new TaskExecutorAdapter(asyncExecutor);
|
||||
public AsyncExecutionInterceptor(Executor executor) {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Intercept the given method invocation, submit the actual calling of the method to
|
||||
* the correct task executor and return immediately to the caller.
|
||||
* @param invocation the method to intercept and make asynchronous
|
||||
* @return {@link Future} if the original method returns {@code Future}; {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
Future result = this.asyncExecutor.submit(new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
try {
|
||||
Object result = invocation.proceed();
|
||||
if (result instanceof Future) {
|
||||
return ((Future) result).get();
|
||||
Future<?> result = this.determineAsyncExecutor(invocation.getMethod()).submit(
|
||||
new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
try {
|
||||
Object result = invocation.proceed();
|
||||
if (result instanceof Future) {
|
||||
return ((Future<?>) result).get();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
ReflectionUtils.rethrowException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
ReflectionUtils.rethrowException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (Future.class.isAssignableFrom(invocation.getMethod().getReturnType())) {
|
||||
return result;
|
||||
}
|
||||
@@ -96,6 +99,20 @@ public class AsyncExecutionInterceptor implements MethodInterceptor, Ordered {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation is a no-op for compatibility in Spring 3.1.2. Subclasses may
|
||||
* override to provide support for extracting qualifier information, e.g. via an
|
||||
* annotation on the given method.
|
||||
* @return always {@code null}
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
* @since 3.1.2
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ public abstract class AopUtils {
|
||||
* @return whether the pointcut can apply on any method
|
||||
*/
|
||||
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
|
||||
Assert.notNull(pc, "Pointcut must not be null");
|
||||
if (!pc.getClassFilter().matches(targetClass)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-asm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-1.1.1.jar" sourcepath="/IVY_CACHE/org.apache.commons/com.springsource.org.apache.commons.logging/1.1.1/com.springsource.org.apache.commons.logging-sources-1.1.1.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar" sourcepath="/IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-sources-1.6.8.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.junit/com.springsource.org.junit/4.9.0/com.springsource.org.junit-4.9.0.jar" sourcepath="/IVY_CACHE/org.junit/com.springsource.org.junit/4.9.0/com.springsource.org.junit-sources-4.9.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-1.1.0.jar" sourcepath="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-sources-1.1.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-1.0.0.jar" sourcepath="/IVY_CACHE/javax.persistence/com.springsource.javax.persistence/1.0.0/com.springsource.javax.persistence-sources-1.0.0.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.orm"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/org.springframework.aop"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<dependency org="org.springframework" name="org.springframework.context.support" rev="latest.integration" conf="optional, cache->compile"/>
|
||||
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="optional, orm->compile"/>
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->compile"/>
|
||||
<dependency org="org.hamcrest" name="com.springsource.org.hamcrest" rev="1.1.0" conf="test->compile"/>
|
||||
<dependency org="javax.mail" name="com.springsource.javax.mail" rev="1.4.0" conf="optional, test->compile"/>
|
||||
</dependencies>
|
||||
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+10
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation.aspectj;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
@@ -25,14 +25,16 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Signals the current application context to apply dependency injection to non-managed
|
||||
* classes that are instantiated outside of the Spring bean factory (typically classes
|
||||
* annotated with the @{@link org.springframework.beans.factory.annotation.Configurable
|
||||
* Signals the current application context to apply dependency injection to
|
||||
* non-managed classes that are instantiated outside of the Spring bean factory
|
||||
* (typically classes annotated with the @
|
||||
* {@link org.springframework.beans.factory.annotation.Configurable
|
||||
* Configurable} annotation).
|
||||
*
|
||||
* <p>Similar to functionality found in Spring's {@code <context:spring-configured>} XML
|
||||
* element. Often used in conjunction with {@link
|
||||
* org.springframework.context.annotation.EnableLoadTimeWeaving @EnableLoadTimeWeaving}.
|
||||
* <p>Similar to functionality found in Spring's
|
||||
* {@code <context:spring-configured>} XML element. Often used in conjunction
|
||||
* with {@link org.springframework.context.annotation.EnableLoadTimeWeaving
|
||||
* @EnableLoadTimeWeaving}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -14,8 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation.aspectj;
|
||||
|
||||
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
+28
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -21,9 +21,9 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionAspectSupport;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
|
||||
/**
|
||||
* Abstract aspect that routes selected methods asynchronously.
|
||||
@@ -34,25 +34,33 @@ import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public abstract aspect AbstractAsyncExecutionAspect {
|
||||
public abstract aspect AbstractAsyncExecutionAspect extends AsyncExecutionAspectSupport {
|
||||
|
||||
private AsyncTaskExecutor asyncExecutor;
|
||||
|
||||
public void setExecutor(Executor executor) {
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
this.asyncExecutor = (AsyncTaskExecutor) executor;
|
||||
}
|
||||
else {
|
||||
this.asyncExecutor = new TaskExecutorAdapter(executor);
|
||||
}
|
||||
/**
|
||||
* Create an {@code AnnotationAsyncExecutionAspect} with a {@code null} default
|
||||
* executor, which should instead be set via {@code #aspectOf} and
|
||||
* {@link #setExecutor(Executor)}.
|
||||
*/
|
||||
public AbstractAsyncExecutionAspect() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply around advice to methods matching the {@link #asyncMethod()} pointcut,
|
||||
* submit the actual calling of the method to the correct task executor and return
|
||||
* immediately to the caller.
|
||||
* @return {@link Future} if the original method returns {@code Future}; {@code null}
|
||||
* otherwise.
|
||||
*/
|
||||
Object around() : asyncMethod() {
|
||||
if (this.asyncExecutor == null) {
|
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPointStaticPart.getSignature();
|
||||
AsyncTaskExecutor executor = determineAsyncExecutor(methodSignature.getMethod());
|
||||
if (executor == null) {
|
||||
return proceed();
|
||||
}
|
||||
}
|
||||
Callable<Object> callable = new Callable<Object>() {
|
||||
public Object call() throws Exception {
|
||||
Object result = proceed();
|
||||
@@ -61,8 +69,8 @@ public abstract aspect AbstractAsyncExecutionAspect {
|
||||
}
|
||||
return null;
|
||||
}};
|
||||
Future<?> result = this.asyncExecutor.submit(callable);
|
||||
if (Future.class.isAssignableFrom(((MethodSignature) thisJoinPointStaticPart.getSignature()).getReturnType())) {
|
||||
Future<?> result = executor.submit(callable);
|
||||
if (Future.class.isAssignableFrom(methodSignature.getReturnType())) {
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
@@ -70,6 +78,9 @@ public abstract aspect AbstractAsyncExecutionAspect {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of joinpoints at which async advice should be applied.
|
||||
*/
|
||||
public abstract pointcut asyncMethod();
|
||||
|
||||
}
|
||||
|
||||
+44
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.scheduling.aspectj;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
|
||||
/**
|
||||
@@ -24,31 +28,55 @@ import org.springframework.scheduling.annotation.Async;
|
||||
*
|
||||
* <p>This aspect routes methods marked with the {@link Async} annotation
|
||||
* as well as methods in classes marked with the same. Any method expected
|
||||
* to be routed asynchronously must return either void, {@link Future},
|
||||
* or a subtype of {@link Future}. This aspect, therefore, will produce
|
||||
* a compile-time error for methods that violate this constraint on the return type.
|
||||
* If, however, a class marked with <code>@Async</code> contains a method that
|
||||
* violates this constraint, it produces only a warning.
|
||||
*
|
||||
* to be routed asynchronously must return either {@code void}, {@link Future},
|
||||
* or a subtype of {@link Future}. This aspect, therefore, will produce
|
||||
* a compile-time error for methods that violate this constraint on the return type.
|
||||
* If, however, a class marked with {@code @Async} contains a method that violates this
|
||||
* constraint, it produces only a warning.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @author Chris Beams
|
||||
* @since 3.0.5
|
||||
*/
|
||||
public aspect AnnotationAsyncExecutionAspect extends AbstractAsyncExecutionAspect {
|
||||
|
||||
private pointcut asyncMarkedMethod()
|
||||
private pointcut asyncMarkedMethod()
|
||||
: execution(@Async (void || Future+) *(..));
|
||||
|
||||
private pointcut asyncTypeMarkedMethod()
|
||||
private pointcut asyncTypeMarkedMethod()
|
||||
: execution((void || Future+) (@Async *).*(..));
|
||||
|
||||
|
||||
public pointcut asyncMethod() : asyncMarkedMethod() || asyncTypeMarkedMethod();
|
||||
|
||||
declare error:
|
||||
execution(@Async !(void||Future) *(..)):
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This implementation inspects the given method and its declaring class for the
|
||||
* {@code @Async} annotation, returning the qualifier value expressed by
|
||||
* {@link Async#value()}. If {@code @Async} is specified at both the method and class level, the
|
||||
* method's {@code #value} takes precedence (even if empty string, indicating that
|
||||
* the default executor should be used preferentially).
|
||||
* @return the qualifier if specified, otherwise empty string indicating that the
|
||||
* {@linkplain #setExecutor(Executor) default executor} should be used
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
// maintainer's note: changes made here should also be made in
|
||||
// AnnotationAsyncExecutionInterceptor#getExecutorQualifier
|
||||
Async async = AnnotationUtils.findAnnotation(method, Async.class);
|
||||
if (async == null) {
|
||||
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
|
||||
}
|
||||
return async == null ? null : async.value();
|
||||
}
|
||||
|
||||
declare error:
|
||||
execution(@Async !(void||Future) *(..)):
|
||||
"Only methods that return void or Future may have an @Async annotation";
|
||||
|
||||
declare warning:
|
||||
execution(!(void||Future) (@Async *).*(..)):
|
||||
"Methods in a class marked with @Async that do not return void or Future will be routed synchronously";
|
||||
declare warning:
|
||||
execution(!(void||Future) (@Async *).*(..)):
|
||||
"Methods in a class marked with @Async that do not return void or Future will " +
|
||||
"be routed synchronously";
|
||||
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -14,16 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.aspectj;
|
||||
package org.springframework.context.annotation.aspectj;
|
||||
|
||||
import org.springframework.beans.factory.aspectj.AbstractBeanConfigurerTests;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
|
||||
|
||||
/**
|
||||
* Tests that @EnableSpringConfigured properly registers an
|
||||
* {@link AnnotationBeanConfigurerAspect}, just as does {@code <context:spring-configured>}
|
||||
* {@link org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect}, just
|
||||
* as does {@code <context:spring-configured>}
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
+70
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -20,31 +20,38 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationAsyncExecutionAspect}.
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
*/
|
||||
public class AnnotationAsyncExecutionAspectTests {
|
||||
|
||||
private static final long WAIT_TIME = 1000; //milli seconds
|
||||
private static final long WAIT_TIME = 1000; //milliseconds
|
||||
|
||||
private CountingExecutor executor;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
executor = new CountingExecutor();
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setExecutor(executor);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void asyncMethodGetsRoutedAsynchronously() {
|
||||
ClassWithoutAsyncAnnotation obj = new ClassWithoutAsyncAnnotation();
|
||||
@@ -54,7 +61,7 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(1, executor.submitStartCounter);
|
||||
assertEquals(1, executor.submitCompleteCounter);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void asyncMethodReturningFutureGetsRoutedAsynchronouslyAndReturnsAFuture() throws InterruptedException, ExecutionException {
|
||||
ClassWithoutAsyncAnnotation obj = new ClassWithoutAsyncAnnotation();
|
||||
@@ -73,8 +80,8 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(1, obj.counter);
|
||||
assertEquals(0, executor.submitStartCounter);
|
||||
assertEquals(0, executor.submitCompleteCounter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void voidMethodInAsyncClassGetsRoutedAsynchronously() {
|
||||
ClassWithAsyncAnnotation obj = new ClassWithAsyncAnnotation();
|
||||
@@ -102,13 +109,30 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
assertEquals(5, returnValue);
|
||||
assertEquals(0, executor.submitStartCounter);
|
||||
assertEquals(0, executor.submitCompleteCounter);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void qualifiedAsyncMethodsAreRoutedToCorrectExecutor() throws InterruptedException, ExecutionException {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
beanFactory.registerBeanDefinition("e1", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
AnnotationAsyncExecutionAspect.aspectOf().setBeanFactory(beanFactory);
|
||||
|
||||
ClassWithQualifiedAsyncMethods obj = new ClassWithQualifiedAsyncMethods();
|
||||
|
||||
Future<Thread> defaultThread = obj.defaultWork();
|
||||
assertThat(defaultThread.get(), not(Thread.currentThread()));
|
||||
assertThat(defaultThread.get().getName(), not(startsWith("e1-")));
|
||||
|
||||
Future<Thread> e1Thread = obj.e1Work();
|
||||
assertThat(e1Thread.get().getName(), startsWith("e1-"));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class CountingExecutor extends SimpleAsyncTaskExecutor {
|
||||
int submitStartCounter;
|
||||
int submitCompleteCounter;
|
||||
|
||||
|
||||
@Override
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
submitStartCounter++;
|
||||
@@ -119,52 +143,56 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
}
|
||||
return future;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void waitForCompletion() {
|
||||
try {
|
||||
wait(WAIT_TIME);
|
||||
} catch (InterruptedException e) {
|
||||
Assert.fail("Didn't finish the async job in " + WAIT_TIME + " milliseconds");
|
||||
fail("Didn't finish the async job in " + WAIT_TIME + " milliseconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static class ClassWithoutAsyncAnnotation {
|
||||
int counter;
|
||||
|
||||
|
||||
@Async public void incrementAsync() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
public void increment() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
|
||||
@Async public Future<Integer> incrementReturningAFuture() {
|
||||
counter++;
|
||||
return new AsyncResult<Integer>(5);
|
||||
}
|
||||
|
||||
// It should be an error to attach @Async to a method that returns a non-void
|
||||
// or non-Future.
|
||||
// We need to keep this commented out, otherwise there will be a compile-time error.
|
||||
// Please uncomment and re-comment this periodically to check that the compiler
|
||||
// produces an error message due to the 'declare error' statement
|
||||
// in AnnotationAsyncExecutionAspect
|
||||
|
||||
/**
|
||||
* It should raise an error to attach @Async to a method that returns a non-void
|
||||
* or non-Future. This method must remain commented-out, otherwise there will be a
|
||||
* compile-time error. Uncomment to manually verify that the compiler produces an
|
||||
* error message due to the 'declare error' statement in
|
||||
* {@link AnnotationAsyncExecutionAspect}.
|
||||
*/
|
||||
// @Async public int getInt() {
|
||||
// return 0;
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Async
|
||||
static class ClassWithAsyncAnnotation {
|
||||
int counter;
|
||||
|
||||
|
||||
public void increment() {
|
||||
counter++;
|
||||
}
|
||||
|
||||
// Manually check that there is a warning from the 'declare warning' statement in AnnotationAsynchExecutionAspect
|
||||
|
||||
// Manually check that there is a warning from the 'declare warning' statement in
|
||||
// AnnotationAsyncExecutionAspect
|
||||
public int return5() {
|
||||
return 5;
|
||||
}
|
||||
@@ -175,4 +203,16 @@ public class AnnotationAsyncExecutionAspectTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class ClassWithQualifiedAsyncMethods {
|
||||
@Async
|
||||
public Future<Thread> defaultWork() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public Future<Thread> e1Work() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+52
-27
@@ -34,6 +34,7 @@ import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
@@ -44,7 +45,7 @@ import org.springframework.util.StringUtils;
|
||||
* {@link Introspector#getBeanInfo(Class)}) by including non-void returning setter
|
||||
* methods in the collection of {@link #getPropertyDescriptors() property descriptors}.
|
||||
* Both regular and
|
||||
* <a href="http://download.oracle.com/javase/tutorial/javabeans/properties/indexed.html">
|
||||
* <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">
|
||||
* indexed properties</a> are fully supported.
|
||||
*
|
||||
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
|
||||
@@ -77,7 +78,7 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
|
||||
ALL_METHODS:
|
||||
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
|
||||
Method method = md.getMethod();
|
||||
Method method = resolveMethod(md.getMethod());
|
||||
|
||||
// bypass non-getter java.lang.Class methods for efficiency
|
||||
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
|
||||
@@ -91,8 +92,8 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
Method readMethod = pd.getReadMethod();
|
||||
Method writeMethod = pd.getWriteMethod();
|
||||
Method readMethod = readMethodFor(pd);
|
||||
Method writeMethod = writeMethodFor(pd);
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (writeMethod != null
|
||||
&& writeMethod.getName().equals(method.getName())) {
|
||||
@@ -126,10 +127,10 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
continue DELEGATE_PD;
|
||||
}
|
||||
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||
Method readMethod = ipd.getReadMethod();
|
||||
Method writeMethod = ipd.getWriteMethod();
|
||||
Method indexedReadMethod = ipd.getIndexedReadMethod();
|
||||
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
|
||||
Method readMethod = readMethodFor(ipd);
|
||||
Method writeMethod = writeMethodFor(ipd);
|
||||
Method indexedReadMethod = indexedReadMethodFor(ipd);
|
||||
Method indexedWriteMethod = indexedWriteMethodFor(ipd);
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (!(indexedWriteMethod != null
|
||||
&& indexedWriteMethod.getName().equals(method.getName()))) {
|
||||
@@ -149,26 +150,26 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
// have we already copied this read method to a property descriptor locally?
|
||||
String propertyName = pd.getName();
|
||||
Method readMethod = pd.getReadMethod();
|
||||
Method readMethod = readMethodFor(pd);
|
||||
Method mostSpecificReadMethod = ClassUtils.getMostSpecificMethod(readMethod, method.getDeclaringClass());
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
&& existingPD.getName().equals(propertyName)) {
|
||||
if (existingPD.getReadMethod() == null) {
|
||||
if (readMethodFor(existingPD) == null) {
|
||||
// no -> add it now
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, pd.getWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, writeMethodFor(pd));
|
||||
}
|
||||
// yes -> do not add a duplicate
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method.equals(((IndexedPropertyDescriptor) pd).getIndexedReadMethod()))) {
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method.equals(indexedReadMethodFor((IndexedPropertyDescriptor) pd)))) {
|
||||
// yes -> copy it, including corresponding setter method (if any -- may be null)
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd), indexedReadMethodFor((IndexedPropertyDescriptor)pd), indexedWriteMethodFor((IndexedPropertyDescriptor)pd));
|
||||
} else {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod());
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethodFor(pd));
|
||||
}
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
@@ -176,6 +177,27 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Method resolveMethod(Method method) {
|
||||
return BridgeMethodResolver.findBridgedMethod(method);
|
||||
}
|
||||
|
||||
private static Method readMethodFor(PropertyDescriptor pd) {
|
||||
return resolveMethod(pd.getReadMethod());
|
||||
}
|
||||
|
||||
private static Method writeMethodFor(PropertyDescriptor pd) {
|
||||
return resolveMethod(pd.getWriteMethod());
|
||||
}
|
||||
|
||||
private static Method indexedReadMethodFor(IndexedPropertyDescriptor ipd) {
|
||||
return resolveMethod(ipd.getIndexedReadMethod());
|
||||
}
|
||||
|
||||
private static Method indexedWriteMethodFor(IndexedPropertyDescriptor ipd) {
|
||||
return resolveMethod(ipd.getIndexedWriteMethod());
|
||||
}
|
||||
|
||||
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||
addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, null, null);
|
||||
}
|
||||
@@ -186,9 +208,9 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (existingPD.getName().equals(propertyName)) {
|
||||
// is there already a descriptor that captures this read method or its corresponding write method?
|
||||
if (existingPD.getReadMethod() != null) {
|
||||
if (readMethod != null && existingPD.getReadMethod().getReturnType() != readMethod.getReturnType()
|
||||
|| writeMethod != null && existingPD.getReadMethod().getReturnType() != writeMethod.getParameterTypes()[0]) {
|
||||
if (readMethodFor(existingPD) != null) {
|
||||
if (readMethod != null && readMethodFor(existingPD).getReturnType() != readMethod.getReturnType()
|
||||
|| writeMethod != null && readMethodFor(existingPD).getReturnType() != writeMethod.getParameterTypes()[0]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
@@ -205,9 +227,9 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
|
||||
// is there already a descriptor that captures this write method or its corresponding read method?
|
||||
if (existingPD.getWriteMethod() != null) {
|
||||
if (readMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != readMethod.getReturnType()
|
||||
|| writeMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
|
||||
if (writeMethodFor(existingPD) != null) {
|
||||
if (readMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != readMethod.getReturnType()
|
||||
|| writeMethod != null && writeMethodFor(existingPD).getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
@@ -224,9 +246,9 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD;
|
||||
|
||||
// is there already a descriptor that captures this indexed read method or its corresponding indexed write method?
|
||||
if (existingIPD.getIndexedReadMethod() != null) {
|
||||
if (indexedReadMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
if (indexedReadMethodFor(existingIPD) != null) {
|
||||
if (indexedReadMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && indexedReadMethodFor(existingIPD).getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
@@ -243,9 +265,9 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
|
||||
// is there already a descriptor that captures this indexed write method or its corresponding indexed read method?
|
||||
if (existingIPD.getIndexedWriteMethod() != null) {
|
||||
if (indexedReadMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
if (indexedWriteMethodFor(existingIPD) != null) {
|
||||
if (indexedReadMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedReadMethod.getReturnType()
|
||||
|| indexedWriteMethod != null && indexedWriteMethodFor(existingIPD).getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
@@ -279,6 +301,7 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
}
|
||||
}
|
||||
else {
|
||||
pd.setWriteMethod(null);
|
||||
pd.setReadMethod(readMethod);
|
||||
try {
|
||||
pd.setWriteMethod(writeMethod);
|
||||
@@ -288,6 +311,7 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(null);
|
||||
((IndexedPropertyDescriptor)pd).setIndexedReadMethod(indexedReadMethod);
|
||||
try {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(indexedWriteMethod);
|
||||
@@ -349,7 +373,8 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
|
||||
|
||||
/**
|
||||
* Sorts PropertyDescriptor instances alphanumerically to emulate the behavior of {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
|
||||
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
*
|
||||
* @see ExtendedBeanInfo#propertyDescriptors
|
||||
*/
|
||||
|
||||
+18
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -44,6 +44,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Dave Syer
|
||||
* @since 2.0
|
||||
* @see BeanWrapperImpl
|
||||
* @see SimpleTypeConverter
|
||||
@@ -158,11 +159,13 @@ class TypeConverterDelegate {
|
||||
}
|
||||
}
|
||||
if (editor == null) {
|
||||
editor = findDefaultEditor(requiredType, typeDescriptor);
|
||||
editor = findDefaultEditor(requiredType);
|
||||
}
|
||||
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
|
||||
}
|
||||
|
||||
boolean standardConversion = false;
|
||||
|
||||
if (requiredType != null) {
|
||||
// Try to apply some standard type conversion rules if appropriate.
|
||||
|
||||
@@ -178,21 +181,24 @@ class TypeConverterDelegate {
|
||||
// Convert elements to target type, if determined.
|
||||
convertedValue = convertToTypedCollection(
|
||||
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
|
||||
standardConversion = true;
|
||||
}
|
||||
else if (convertedValue instanceof Map) {
|
||||
// Convert keys and values to respective target type, if determined.
|
||||
convertedValue = convertToTypedMap(
|
||||
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
|
||||
standardConversion = true;
|
||||
}
|
||||
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
|
||||
convertedValue = Array.get(convertedValue, 0);
|
||||
standardConversion = true;
|
||||
}
|
||||
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
|
||||
// We can stringify any primitive value...
|
||||
return (T) convertedValue.toString();
|
||||
}
|
||||
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
|
||||
if (!requiredType.isInterface() && !requiredType.isEnum()) {
|
||||
if (firstAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
|
||||
try {
|
||||
Constructor strCtor = requiredType.getConstructor(String.class);
|
||||
return (T) BeanUtils.instantiateClass(strCtor, convertedValue);
|
||||
@@ -214,8 +220,8 @@ class TypeConverterDelegate {
|
||||
// It's an empty enum identifier: reset the enum value to null.
|
||||
return null;
|
||||
}
|
||||
|
||||
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
|
||||
standardConversion = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +250,7 @@ class TypeConverterDelegate {
|
||||
}
|
||||
|
||||
if (firstAttemptEx != null) {
|
||||
if (editor == null && convertedValue == newValue) {
|
||||
if (editor == null && !standardConversion && requiredType != null && !Object.class.equals(requiredType)) {
|
||||
throw firstAttemptEx;
|
||||
}
|
||||
logger.debug("Original ConversionService attempt failed - ignored since " +
|
||||
@@ -303,16 +309,11 @@ class TypeConverterDelegate {
|
||||
/**
|
||||
* Find a default editor for the given type.
|
||||
* @param requiredType the type to find an editor for
|
||||
* @param descriptor the JavaBeans descriptor for the property
|
||||
* @return the corresponding editor, or <code>null</code> if none
|
||||
*/
|
||||
protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) {
|
||||
private PropertyEditor findDefaultEditor(Class requiredType) {
|
||||
PropertyEditor editor = null;
|
||||
//if (typeDescriptor instanceof PropertyTypeDescriptor) {
|
||||
//PropertyDescriptor pd = ((PropertyTypeDescriptor) typeDescriptor).getPropertyDescriptor();
|
||||
//editor = pd.createPropertyEditor(this.targetObject);
|
||||
//}
|
||||
if (editor == null && requiredType != null) {
|
||||
if (requiredType != null) {
|
||||
// No custom editor -> check BeanWrapperImpl's default editors.
|
||||
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
|
||||
if (editor == null && !String.class.equals(requiredType)) {
|
||||
@@ -334,7 +335,7 @@ class TypeConverterDelegate {
|
||||
* @return the new value, possibly the result of type conversion
|
||||
* @throws IllegalArgumentException if type conversion failed
|
||||
*/
|
||||
protected Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
|
||||
private Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
|
||||
Object convertedValue = newValue;
|
||||
boolean sharedEditor = false;
|
||||
|
||||
@@ -421,7 +422,7 @@ class TypeConverterDelegate {
|
||||
* @param editor the PropertyEditor to use
|
||||
* @return the converted value
|
||||
*/
|
||||
protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
|
||||
private Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
|
||||
try {
|
||||
editor.setValue(oldValue);
|
||||
}
|
||||
@@ -435,7 +436,7 @@ class TypeConverterDelegate {
|
||||
return editor.getValue();
|
||||
}
|
||||
|
||||
protected Object convertToTypedArray(Object input, String propertyName, Class<?> componentType) {
|
||||
private Object convertToTypedArray(Object input, String propertyName, Class<?> componentType) {
|
||||
if (input instanceof Collection) {
|
||||
// Convert Collection elements to array elements.
|
||||
Collection coll = (Collection) input;
|
||||
@@ -474,7 +475,7 @@ class TypeConverterDelegate {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Collection convertToTypedCollection(
|
||||
private Collection convertToTypedCollection(
|
||||
Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
|
||||
|
||||
if (!Collection.class.isAssignableFrom(requiredType)) {
|
||||
@@ -556,7 +557,7 @@ class TypeConverterDelegate {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Map convertToTypedMap(
|
||||
private Map convertToTypedMap(
|
||||
Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
|
||||
|
||||
if (!Map.class.isAssignableFrom(requiredType)) {
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -37,6 +38,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 04.07.2003
|
||||
*/
|
||||
public abstract class BeanFactoryUtils {
|
||||
|
||||
+16
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -405,10 +405,16 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
protected boolean determineRequiredStatus(Annotation annotation) {
|
||||
try {
|
||||
Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
|
||||
if (method == null) {
|
||||
// annotations like @Inject and @Value don't have a method (attribute) named "required"
|
||||
// -> default to required status
|
||||
return true;
|
||||
}
|
||||
return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// required by default
|
||||
// an exception was thrown during reflective invocation of the required attribute
|
||||
// -> default to required status
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -419,11 +425,12 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
private void registerDependentBeans(String beanName, Set<String> autowiredBeanNames) {
|
||||
if (beanName != null) {
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
if (this.beanFactory.containsBean(autowiredBeanName)) {
|
||||
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName +
|
||||
"'");
|
||||
logger.debug("Autowiring by type from bean name '" + beanName +
|
||||
"' to bean named '" + autowiredBeanName + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,11 +442,11 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
|
||||
if (cachedArgument instanceof DependencyDescriptor) {
|
||||
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
|
||||
TypeConverter typeConverter = beanFactory.getTypeConverter();
|
||||
return beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
TypeConverter typeConverter = this.beanFactory.getTypeConverter();
|
||||
return this.beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
|
||||
}
|
||||
else if (cachedArgument instanceof RuntimeBeanReference) {
|
||||
return beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||
return this.beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
|
||||
}
|
||||
else {
|
||||
return cachedArgument;
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.beans.factory.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Convenience methods performing bean lookups related to annotations, for example
|
||||
* Spring's {@link Qualifier @Qualifier} annotation.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
* @see BeanFactoryUtils
|
||||
*/
|
||||
public class BeanFactoryAnnotationUtils {
|
||||
|
||||
/**
|
||||
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a
|
||||
* qualifier (e.g. via {@code <qualifier>} or {@code @Qualifier}) matching the given
|
||||
* qualifier, or having a bean name matching the given qualifier.
|
||||
* @param beanFactory the BeanFactory to get the target bean from
|
||||
* @param beanType the type of bean to retrieve
|
||||
* @param qualifier the qualifier for selecting between multiple bean matches
|
||||
* @return the matching bean of type {@code T} (never {@code null})
|
||||
* @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
|
||||
*/
|
||||
public static <T> T qualifiedBeanOfType(BeanFactory beanFactory, Class<T> beanType, String qualifier) {
|
||||
if (beanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
// Full qualifier matching supported.
|
||||
return qualifiedBeanOfType((ConfigurableListableBeanFactory) beanFactory, beanType, qualifier);
|
||||
}
|
||||
else if (beanFactory.containsBean(qualifier)) {
|
||||
// Fallback: target bean at least found by bean name.
|
||||
return beanFactory.getBean(qualifier, beanType);
|
||||
}
|
||||
else {
|
||||
throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() +
|
||||
" bean found for bean name '" + qualifier +
|
||||
"'! (Note: Qualifier matching not supported because given " +
|
||||
"BeanFactory does not implement ConfigurableListableBeanFactory.)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a bean of type {@code T} from the given {@code BeanFactory} declaring a qualifier
|
||||
* (e.g. {@code <qualifier>} or {@code @Qualifier}) matching the given qualifier).
|
||||
* @param bf the BeanFactory to get the target bean from
|
||||
* @param beanType the type of bean to retrieve
|
||||
* @param qualifier the qualifier for selecting between multiple bean matches
|
||||
* @return the matching bean of type {@code T} (never {@code null})
|
||||
* @throws NoSuchBeanDefinitionException if no matching bean of type {@code T} found
|
||||
*/
|
||||
private static <T> T qualifiedBeanOfType(ConfigurableListableBeanFactory bf, Class<T> beanType, String qualifier) {
|
||||
Map<String, T> candidateBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, beanType);
|
||||
T matchingBean = null;
|
||||
for (String beanName : candidateBeans.keySet()) {
|
||||
if (isQualifierMatch(qualifier, beanName, bf)) {
|
||||
if (matchingBean != null) {
|
||||
throw new NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "'");
|
||||
}
|
||||
matchingBean = candidateBeans.get(beanName);
|
||||
}
|
||||
}
|
||||
if (matchingBean != null) {
|
||||
return matchingBean;
|
||||
}
|
||||
else {
|
||||
throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "' - neither qualifier " + "match nor bean name match!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the named bean declares a qualifier of the given name.
|
||||
* @param qualifier the qualifier to match
|
||||
* @param beanName the name of the candidate bean
|
||||
* @param bf the {@code BeanFactory} from which to retrieve the named bean
|
||||
* @return {@code true} if either the bean definition (in the XML case)
|
||||
* or the bean's factory method (in the {@code @Bean} case) defines a matching
|
||||
* qualifier value (through {@code <qualifier>} or {@code @Qualifier})
|
||||
*/
|
||||
private static boolean isQualifierMatch(String qualifier, String beanName, ConfigurableListableBeanFactory bf) {
|
||||
if (bf.containsBean(beanName)) {
|
||||
try {
|
||||
BeanDefinition bd = bf.getMergedBeanDefinition(beanName);
|
||||
if (bd instanceof AbstractBeanDefinition) {
|
||||
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
|
||||
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
|
||||
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
|
||||
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (bd instanceof RootBeanDefinition) {
|
||||
Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod();
|
||||
if (factoryMethod != null) {
|
||||
Qualifier targetAnnotation = factoryMethod.getAnnotation(Qualifier.class);
|
||||
if (targetAnnotation != null && qualifier.equals(targetAnnotation.value())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
// ignore - can't compare qualifiers for a manually registered singleton object
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+32
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -54,17 +54,22 @@ public class InjectionMetadata {
|
||||
|
||||
|
||||
public InjectionMetadata(Class targetClass, Collection<InjectedElement> elements) {
|
||||
this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>());
|
||||
for (InjectedElement element : elements) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
|
||||
if (!elements.isEmpty()) {
|
||||
this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>(elements.size()));
|
||||
for (InjectedElement element : elements) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
|
||||
}
|
||||
this.injectedElements.add(element);
|
||||
}
|
||||
this.injectedElements.add(element);
|
||||
}
|
||||
else {
|
||||
this.injectedElements = Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
|
||||
synchronized(this.injectedElements) {
|
||||
synchronized (this.injectedElements) {
|
||||
for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) {
|
||||
Member member = it.next().getMember();
|
||||
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
|
||||
@@ -170,26 +175,30 @@ public class InjectionMetadata {
|
||||
* affected property as processed for other processors to ignore it.
|
||||
*/
|
||||
protected boolean checkPropertySkipping(PropertyValues pvs) {
|
||||
if (this.skip == null) {
|
||||
if (pvs != null) {
|
||||
synchronized (pvs) {
|
||||
if (this.skip == null) {
|
||||
if (this.pd != null) {
|
||||
if (pvs.contains(this.pd.getName())) {
|
||||
// Explicit value provided as part of the bean definition.
|
||||
this.skip = true;
|
||||
return true;
|
||||
}
|
||||
else if (pvs instanceof MutablePropertyValues) {
|
||||
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.skip != null) {
|
||||
return this.skip;
|
||||
}
|
||||
if (pvs == null) {
|
||||
this.skip = false;
|
||||
return false;
|
||||
}
|
||||
synchronized (pvs) {
|
||||
if (this.skip != null) {
|
||||
return this.skip;
|
||||
}
|
||||
if (this.pd != null) {
|
||||
if (pvs.contains(this.pd.getName())) {
|
||||
// Explicit value provided as part of the bean definition.
|
||||
this.skip = true;
|
||||
return true;
|
||||
}
|
||||
else if (pvs instanceof MutablePropertyValues) {
|
||||
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
|
||||
}
|
||||
}
|
||||
this.skip = false;
|
||||
return false;
|
||||
}
|
||||
return this.skip;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+22
@@ -121,6 +121,24 @@ public class DependencyDescriptor implements Serializable {
|
||||
this.eager = eager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
* @param original the original descriptor to create a copy from
|
||||
*/
|
||||
public DependencyDescriptor(DependencyDescriptor original) {
|
||||
this.methodParameter = (original.methodParameter != null ? new MethodParameter(original.methodParameter) : null);
|
||||
this.field = original.field;
|
||||
this.declaringClass = original.declaringClass;
|
||||
this.methodName = original.methodName;
|
||||
this.parameterTypes = original.parameterTypes;
|
||||
this.parameterIndex = original.parameterIndex;
|
||||
this.fieldName = original.fieldName;
|
||||
this.required = original.required;
|
||||
this.eager = original.eager;
|
||||
this.nestingLevel = original.nestingLevel;
|
||||
this.fieldAnnotations = original.fieldAnnotations;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the wrapped MethodParameter, if any.
|
||||
@@ -156,6 +174,10 @@ public class DependencyDescriptor implements Serializable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Increase this descriptor's nesting level.
|
||||
* @see MethodParameter#increaseNestingLevel()
|
||||
*/
|
||||
public void increaseNestingLevel() {
|
||||
this.nestingLevel++;
|
||||
if (this.methodParameter != null) {
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -672,12 +672,12 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
||||
// Try to obtain the FactoryBean's object type without instantiating it at all.
|
||||
BeanDefinition fbDef = getBeanDefinition(factoryBeanName);
|
||||
if (fbDef instanceof AbstractBeanDefinition) {
|
||||
Class<?> fbClass = ((AbstractBeanDefinition)fbDef).getBeanClass();
|
||||
Class<?> fbClass = ((AbstractBeanDefinition) fbDef).getBeanClass();
|
||||
if (ClassUtils.isCglibProxyClass(fbClass)) {
|
||||
// CGLIB subclass methods hide generic parameters. look at the superclass.
|
||||
fbClass = fbClass.getSuperclass();
|
||||
}
|
||||
// find the given factory method, taking into account that in the case of
|
||||
// Find the given factory method, taking into account that in the case of
|
||||
// @Bean methods, there may be parameters present.
|
||||
ReflectionUtils.doWithMethods(fbClass,
|
||||
new ReflectionUtils.MethodCallback() {
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -38,6 +38,7 @@ import org.springframework.beans.factory.config.RuntimeBeanNameReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -208,7 +209,7 @@ class BeanDefinitionValueResolver {
|
||||
*/
|
||||
protected Object evaluate(TypedStringValue value) {
|
||||
Object result = this.beanFactory.evaluateBeanDefinitionString(value.getValue(), this.beanDefinition);
|
||||
if (result != value.getValue()) {
|
||||
if (!ObjectUtils.nullSafeEquals(result, value.getValue())) {
|
||||
value.setDynamic();
|
||||
}
|
||||
return result;
|
||||
|
||||
+49
-22
@@ -24,8 +24,6 @@ import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
@@ -90,6 +88,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @since 16 April 2001
|
||||
* @see StaticListableBeanFactory
|
||||
* @see PropertiesBeanDefinitionReader
|
||||
@@ -98,7 +97,7 @@ import org.springframework.util.StringUtils;
|
||||
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
|
||||
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
|
||||
|
||||
private static Class javaxInjectProviderClass = null;
|
||||
private static Class<?> javaxInjectProviderClass = null;
|
||||
|
||||
static {
|
||||
ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
|
||||
@@ -128,11 +127,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();
|
||||
|
||||
/** Map from dependency type to corresponding autowired value */
|
||||
private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();
|
||||
private final Map<Class<?>, Object> resolvableDependencies = new HashMap<Class<?>, Object>();
|
||||
|
||||
/** Map of bean definition objects, keyed by bean name */
|
||||
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();
|
||||
|
||||
/** Map of singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> singletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** Map of non-singleton bean names keyed by bean class */
|
||||
private final Map<Class<?>, String[]> nonSingletonBeanNamesByType = new ConcurrentHashMap<Class<?>, String[]>();
|
||||
|
||||
/** List of bean definition names, in registration order */
|
||||
private final List<String> beanDefinitionNames = new ArrayList<String>();
|
||||
|
||||
@@ -294,11 +299,26 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class type) {
|
||||
public String[] getBeanNamesForType(Class<?> type) {
|
||||
return getBeanNamesForType(type, true, true);
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
|
||||
return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
Map<Class<?>, String[]> cache = includeNonSingletons ?
|
||||
this.nonSingletonBeanNamesByType : this.singletonBeanNamesByType;
|
||||
String[] resolvedBeanNames = cache.get(type);
|
||||
if (resolvedBeanNames != null) {
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
resolvedBeanNames = doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
cache.put(type, resolvedBeanNames);
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
|
||||
private String[] doGetBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
// Check all bean definitions.
|
||||
@@ -441,7 +461,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
*/
|
||||
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) {
|
||||
A ann = null;
|
||||
Class beanType = getType(beanName);
|
||||
Class<?> beanType = getType(beanName);
|
||||
if (beanType != null) {
|
||||
ann = AnnotationUtils.findAnnotation(beanType, annotationType);
|
||||
}
|
||||
@@ -564,18 +584,18 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(beanName)) {
|
||||
final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
|
||||
boolean isEagerInit;
|
||||
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
|
||||
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
return ((SmartFactoryBean) factory).isEagerInit();
|
||||
return ((SmartFactoryBean<?>) factory).isEagerInit();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean) factory).isEagerInit());
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
@@ -669,6 +689,10 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
destroySingleton(beanName);
|
||||
}
|
||||
|
||||
// Remove any assumptions about by-type mappings
|
||||
this.singletonBeanNamesByType.clear();
|
||||
this.nonSingletonBeanNamesByType.clear();
|
||||
|
||||
// Reset all bean definitions that have the given bean as parent (recursively).
|
||||
for (String bdName : this.beanDefinitionNames) {
|
||||
if (!beanName.equals(bdName)) {
|
||||
@@ -723,7 +747,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
Class componentType = type.getComponentType();
|
||||
Class<?> componentType = type.getComponentType();
|
||||
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor);
|
||||
if (matchingBeans.isEmpty()) {
|
||||
if (descriptor.isRequired()) {
|
||||
@@ -738,7 +762,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class elementType = descriptor.getCollectionType();
|
||||
Class<?> elementType = descriptor.getCollectionType();
|
||||
if (elementType == null) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("No element type declared for collection [" + type.getName() + "]");
|
||||
@@ -759,7 +783,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
return converter.convertIfNecessary(matchingBeans.values(), type);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(type) && type.isInterface()) {
|
||||
Class keyType = descriptor.getMapKeyType();
|
||||
Class<?> keyType = descriptor.getMapKeyType();
|
||||
if (keyType == null || !String.class.isAssignableFrom(keyType)) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("Key type [" + keyType + "] of map [" + type.getName() +
|
||||
@@ -767,7 +791,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Class valueType = descriptor.getMapValueType();
|
||||
Class<?> valueType = descriptor.getMapValueType();
|
||||
if (valueType == null) {
|
||||
if (descriptor.isRequired()) {
|
||||
throw new FatalBeanException("No value type declared for map [" + type.getName() + "]");
|
||||
@@ -828,12 +852,12 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* @see #autowireConstructor
|
||||
*/
|
||||
protected Map<String, Object> findAutowireCandidates(
|
||||
String beanName, Class requiredType, DependencyDescriptor descriptor) {
|
||||
String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
|
||||
|
||||
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
this, requiredType, true, descriptor.isEager());
|
||||
Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length);
|
||||
for (Class autowiringType : this.resolvableDependencies.keySet()) {
|
||||
for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
|
||||
if (autowiringType.isAssignableFrom(requiredType)) {
|
||||
Object autowiringValue = this.resolvableDependencies.get(autowiringType);
|
||||
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
|
||||
@@ -918,7 +942,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* Raise a NoSuchBeanDefinitionException for an unresolvable dependency.
|
||||
*/
|
||||
private void raiseNoSuchBeanDefinitionException(
|
||||
Class type, String dependencyDescription, DependencyDescriptor descriptor)
|
||||
Class<?> type, String dependencyDescription, DependencyDescriptor descriptor)
|
||||
throws NoSuchBeanDefinitionException {
|
||||
|
||||
throw new NoSuchBeanDefinitionException(type, dependencyDescription,
|
||||
@@ -967,6 +991,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
* Minimal id reference to the factory.
|
||||
* Resolved to the actual factory instance on deserialization.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class SerializedBeanFactoryReference implements Serializable {
|
||||
|
||||
private final String id;
|
||||
@@ -976,7 +1001,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
Reference ref = serializableFactories.get(this.id);
|
||||
Reference<?> ref = serializableFactories.get(this.id);
|
||||
if (ref == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot deserialize BeanFactory with id " + this.id + ": no factory registered for this id");
|
||||
@@ -994,16 +1019,17 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/**
|
||||
* Serializable ObjectFactory for lazy resolution of a dependency.
|
||||
*/
|
||||
private class DependencyObjectFactory implements ObjectFactory, Serializable {
|
||||
@SuppressWarnings("serial")
|
||||
private class DependencyObjectFactory implements ObjectFactory<Object>, Serializable {
|
||||
|
||||
private final DependencyDescriptor descriptor;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
public DependencyObjectFactory(DependencyDescriptor descriptor, String beanName) {
|
||||
this.descriptor = descriptor;
|
||||
this.beanName = beanName;
|
||||
this.descriptor = new DependencyDescriptor(descriptor);
|
||||
this.descriptor.increaseNestingLevel();
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
public Object getObject() throws BeansException {
|
||||
@@ -1015,7 +1041,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
/**
|
||||
* Serializable ObjectFactory for lazy resolution of a dependency.
|
||||
*/
|
||||
private class DependencyProvider extends DependencyObjectFactory implements Provider {
|
||||
@SuppressWarnings("serial")
|
||||
private class DependencyProvider extends DependencyObjectFactory implements Provider<Object> {
|
||||
|
||||
public DependencyProvider(DependencyDescriptor descriptor, String beanName) {
|
||||
super(descriptor, beanName);
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -303,7 +303,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
|
||||
/**
|
||||
* Callback after singleton creation.
|
||||
* <p>Default implementation marks the singleton as not in creation anymore.
|
||||
* <p>The default implementation marks the singleton as not in creation anymore.
|
||||
* @param beanName the name of the singleton that has been created
|
||||
* @see #isSingletonCurrentlyInCreation
|
||||
*/
|
||||
@@ -316,7 +316,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
public final void setCurrentlyInCreation(String beanName, boolean inCreation) {
|
||||
if (!inCreation) {
|
||||
this.inCreationCheckExclusions.add(beanName);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.inCreationCheckExclusions.remove(beanName);
|
||||
}
|
||||
}
|
||||
@@ -333,7 +334,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
|
||||
/**
|
||||
* Add the given bean to the list of disposable beans in this registry.
|
||||
* Disposable beans usually correspond to registered singletons,
|
||||
* <p>Disposable beans usually correspond to registered singletons,
|
||||
* matching the bean name but potentially being a different instance
|
||||
* (for example, a DisposableBean adapter for a singleton that does not
|
||||
* naturally implement Spring's DisposableBean interface).
|
||||
|
||||
+14
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -44,6 +44,17 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
|
||||
private static final ThreadLocal<Method> currentlyInvokedFactoryMethod = new ThreadLocal<Method>();
|
||||
|
||||
|
||||
/**
|
||||
* Return the factory method currently being invoked or {@code null} if none.
|
||||
* <p>Allows factory method implementations to determine whether the current
|
||||
* caller is the container itself as opposed to user code.
|
||||
*/
|
||||
public static Method getCurrentlyInvokedFactoryMethod() {
|
||||
return currentlyInvokedFactoryMethod.get();
|
||||
}
|
||||
|
||||
|
||||
public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
|
||||
// Don't override the class with CGLIB if no overrides.
|
||||
if (beanDefinition.getMethodOverrides().isEmpty()) {
|
||||
@@ -147,7 +158,8 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
try {
|
||||
currentlyInvokedFactoryMethod.set(factoryMethod);
|
||||
return factoryMethod.invoke(factoryBean, args);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (priorInvokedFactoryMethod != null) {
|
||||
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
|
||||
}
|
||||
@@ -171,12 +183,4 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory method currently being invoked or {@code null} if none.
|
||||
* Allows factory method implementations to determine whether the current
|
||||
* caller is the container itself as opposed to user code.
|
||||
*/
|
||||
public static Method getCurrentlyInvokedFactoryMethod() {
|
||||
return currentlyInvokedFactoryMethod.get();
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -826,7 +826,11 @@ public class BeanDefinitionParserDelegate {
|
||||
// Look for arg-type match elements.
|
||||
List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT);
|
||||
for (Element argTypeEle : argTypeEles) {
|
||||
replaceOverride.addTypeIdentifier(argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE));
|
||||
String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE);
|
||||
match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle));
|
||||
if (StringUtils.hasText(match)) {
|
||||
replaceOverride.addTypeIdentifier(match);
|
||||
}
|
||||
}
|
||||
replaceOverride.setSource(extractSource(replacedMethodEle));
|
||||
overrides.addOverride(replaceOverride);
|
||||
|
||||
+55
-27
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -43,14 +43,16 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.beans.propertyeditors.CustomNumberEditor;
|
||||
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
|
||||
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
|
||||
import org.springframework.beans.support.DerivedFromProtectedBaseBean;
|
||||
import org.springframework.core.convert.ConversionFailedException;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.convert.support.GenericConversionService;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -66,6 +68,7 @@ import test.beans.TestBean;
|
||||
* @author Alef Arendsen
|
||||
* @author Arjen Poutsma
|
||||
* @author Chris Beams
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public final class BeanWrapperTests {
|
||||
|
||||
@@ -78,7 +81,7 @@ public final class BeanWrapperTests {
|
||||
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
|
||||
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNullNestedTypeDescriptor2() {
|
||||
Foo foo = new Foo();
|
||||
@@ -90,31 +93,31 @@ public final class BeanWrapperTests {
|
||||
wrapper.setPropertyValue("list[0]", map);
|
||||
assertEquals(map, foo.list.get(0));
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
|
||||
private List list;
|
||||
|
||||
private List<Map> listOfMaps;
|
||||
|
||||
public List getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public List<Map> getListOfMaps() {
|
||||
return listOfMaps;
|
||||
}
|
||||
|
||||
public void setListOfMaps(List<Map> listOfMaps) {
|
||||
this.listOfMaps = listOfMaps;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullNestedTypeDescriptorWithNoConversionService() {
|
||||
Foo foo = new Foo();
|
||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
|
||||
wrapper.setAutoGrowNestedPaths(true);
|
||||
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
|
||||
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testNullNestedTypeDescriptorWithBadConversionService() {
|
||||
Foo foo = new Foo();
|
||||
BeanWrapperImpl wrapper = new BeanWrapperImpl(foo);
|
||||
wrapper.setConversionService(new GenericConversionService() {
|
||||
@Override
|
||||
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||
throw new ConversionFailedException(sourceType, targetType, source, null);
|
||||
}
|
||||
});
|
||||
wrapper.setAutoGrowNestedPaths(true);
|
||||
wrapper.setPropertyValue("listOfMaps[0]['luckyNumber']", "9");
|
||||
assertEquals("9", foo.listOfMaps.get(0).get("luckyNumber"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsReadablePropertyNotReadable() {
|
||||
NoRead nr = new NoRead();
|
||||
@@ -1448,7 +1451,7 @@ public final class BeanWrapperTests {
|
||||
|
||||
@Test
|
||||
public void testSetNumberProperties() {
|
||||
NumberPropertyBean bean = new NumberPropertyBean();
|
||||
NumberPropertyBean bean = new NumberPropertyBean();
|
||||
BeanWrapper bw = new BeanWrapperImpl(bean);
|
||||
|
||||
String byteValue = " " + Byte.MAX_VALUE + " ";
|
||||
@@ -1538,6 +1541,31 @@ public final class BeanWrapperTests {
|
||||
assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
|
||||
}
|
||||
|
||||
|
||||
private static class Foo {
|
||||
|
||||
private List list;
|
||||
|
||||
private List<Map> listOfMaps;
|
||||
|
||||
public List getList() {
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setList(List list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public List<Map> getListOfMaps() {
|
||||
return listOfMaps;
|
||||
}
|
||||
|
||||
public void setListOfMaps(List<Map> listOfMaps) {
|
||||
this.listOfMaps = listOfMaps;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class DifferentTestBean extends TestBean {
|
||||
// class to test naming of beans in a BeanWrapper error message
|
||||
}
|
||||
|
||||
+102
-10
@@ -16,29 +16,30 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IndexedPropertyDescriptor;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.ExtendedBeanInfo.PropertyDescriptorComparator;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import test.beans.TestBean;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ExtendedBeanInfo}.
|
||||
*
|
||||
@@ -149,7 +150,7 @@ public class ExtendedBeanInfoTests {
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -163,6 +164,50 @@ public class ExtendedBeanInfoTests {
|
||||
fail("never matched write method");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cornerSpr9414() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class Parent {
|
||||
public Number getProperty1() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
class Child extends Parent {
|
||||
@Override
|
||||
public Integer getProperty1() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
{ // always passes
|
||||
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Parent.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
|
||||
}
|
||||
{ // failed prior to fix for SPR-9414
|
||||
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(Child.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "property1"), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
interface Spr9453<T> {
|
||||
T getProp();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cornerSpr9453() throws IntrospectionException {
|
||||
final class Bean implements Spr9453<Class<?>> {
|
||||
public Class<?> getProp() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
{ // always passes
|
||||
BeanInfo info = Introspector.getBeanInfo(Bean.class);
|
||||
assertThat(info.getPropertyDescriptors().length, equalTo(2));
|
||||
}
|
||||
{ // failed prior to fix for SPR-9453
|
||||
BeanInfo info = new ExtendedBeanInfo(Introspector.getBeanInfo(Bean.class));
|
||||
assertThat(info.getPropertyDescriptors().length, equalTo(2));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardReadMethodInSuperclassAndNonStandardWriteMethodInSubclass() throws Exception {
|
||||
@SuppressWarnings("unused") class B {
|
||||
@@ -471,6 +516,53 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
}
|
||||
|
||||
@Ignore // see comments at SPR-9702
|
||||
@Test
|
||||
public void cornerSpr9702() throws IntrospectionException {
|
||||
{ // baseline with standard write method
|
||||
@SuppressWarnings("unused")
|
||||
class C {
|
||||
// VOID-RETURNING, NON-INDEXED write method
|
||||
public void setFoos(String[] foos) { }
|
||||
// indexed read method
|
||||
public String getFoos(int i) { return null; }
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
|
||||
BeanInfo ebi = Introspector.getBeanInfo(C.class);
|
||||
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
|
||||
}
|
||||
{ // variant with non-standard write method
|
||||
@SuppressWarnings("unused")
|
||||
class C {
|
||||
// NON-VOID-RETURNING, NON-INDEXED write method
|
||||
public C setFoos(String[] foos) { return this; }
|
||||
// indexed read method
|
||||
public String getFoos(int i) { return null; }
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "foos"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
assertThat(hasReadMethodForProperty(ebi, "foos"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class B {
|
||||
|
||||
+42
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,18 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.security.AccessControlContext;
|
||||
@@ -43,13 +31,19 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import test.beans.DerivedTestBean;
|
||||
import test.beans.DummyFactory;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.LifecycleBean;
|
||||
import test.beans.NestedTestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
@@ -85,12 +79,8 @@ import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.util.StopWatch;
|
||||
|
||||
import test.beans.DerivedTestBean;
|
||||
import test.beans.DummyFactory;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.LifecycleBean;
|
||||
import test.beans.NestedTestBean;
|
||||
import test.beans.TestBean;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests properties population and autowire behavior.
|
||||
@@ -2159,13 +2149,43 @@ public class DefaultListableBeanFactoryTests {
|
||||
@Test
|
||||
public void testContainsBeanReturnsTrueEvenForAbstractBeanDefinition() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("abs",
|
||||
rootBeanDefinition(TestBean.class).setAbstract(true).getBeanDefinition());
|
||||
bf.registerBeanDefinition("abs", BeanDefinitionBuilder
|
||||
.rootBeanDefinition(TestBean.class).setAbstract(true).getBeanDefinition());
|
||||
assertThat(bf.containsBean("abs"), is(true));
|
||||
assertThat(bf.containsBean("bogus"), is(false));
|
||||
}
|
||||
|
||||
|
||||
static class A { }
|
||||
static class B { }
|
||||
|
||||
/**
|
||||
* Test that by-type bean lookup caching is working effectively by searching for a
|
||||
* bean of type B 10K times within a container having 1K additional beans of type A.
|
||||
* Prior to by-type caching, each bean lookup would traverse the entire container
|
||||
* (all 1001 beans), performing expensive assignability checks, etc. Now these
|
||||
* operations are necessary only once, providing a dramatic performance improvement.
|
||||
* On load-free modern hardware (e.g. an 8-core MPB), this method should complete well
|
||||
* under the 1000 ms timeout, usually ~= 300ms. With caching removed and on the same
|
||||
* hardware the method will take ~13000 ms. See SPR-6870.
|
||||
*/
|
||||
@Test(timeout=1000)
|
||||
public void testByTypeLookupIsFastEnough() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
bf.registerBeanDefinition("a"+i, new RootBeanDefinition(A.class));
|
||||
}
|
||||
bf.registerBeanDefinition("b", new RootBeanDefinition(B.class));
|
||||
|
||||
bf.freezeConfiguration();
|
||||
|
||||
for (int i=0; i<10000; i++) {
|
||||
bf.getBean(B.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class NoDependencies {
|
||||
|
||||
private NoDependencies() {
|
||||
|
||||
+24
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -24,7 +24,6 @@ import java.lang.annotation.Target;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.IndexedTestBean;
|
||||
@@ -42,6 +41,8 @@ import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.util.SerializationTestUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AutowiredAnnotationBeanPostProcessor}.
|
||||
*
|
||||
@@ -118,6 +119,11 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
assertSame(tb, bean.getTestBean4());
|
||||
assertSame(ntb, bean.getNestedTestBean());
|
||||
assertSame(bf, bean.getBeanFactory());
|
||||
|
||||
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
|
||||
assertEquals(2, depBeans.length);
|
||||
assertEquals("testBean", depBeans[0]);
|
||||
assertEquals("nestedTestBean", depBeans[1]);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -596,6 +602,22 @@ public final class AutowiredAnnotationBeanPostProcessorTests {
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryInjectionIntoPrototypeBean() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class, false));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
ObjectFactoryInjectionBean anotherBean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNotSame(anotherBean, bean);
|
||||
assertSame(bf.getBean("testBean"), anotherBean.getTestBean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryQualifierInjection() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
+98
-10
@@ -372,13 +372,13 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
|
||||
bf.registerBeanDefinition("testBean", bd);
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
@@ -389,29 +389,86 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
|
||||
bf.registerBeanDefinition("testBean", bd);
|
||||
|
||||
ObjectFactoryQualifierInjectionBean bean = (ObjectFactoryQualifierInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactorySerialization() throws Exception {
|
||||
public void testObjectFactoryFieldInjectionIntoPrototypeBean() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierFieldInjectionBean.class, false));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
|
||||
bf.registerBeanDefinition("testBean", bd);
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
ObjectFactoryQualifierFieldInjectionBean bean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
ObjectFactoryQualifierFieldInjectionBean anotherBean = (ObjectFactoryQualifierFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNotSame(anotherBean, bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryMethodInjectionIntoPrototypeBean() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryQualifierMethodInjectionBean.class, false));
|
||||
RootBeanDefinition bd = new RootBeanDefinition(TestBean.class);
|
||||
bd.addQualifier(new AutowireCandidateQualifier(Qualifier.class, "testBean"));
|
||||
bf.registerBeanDefinition("testBean", bd);
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
ObjectFactoryQualifierMethodInjectionBean bean = (ObjectFactoryQualifierMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
ObjectFactoryQualifierMethodInjectionBean anotherBean = (ObjectFactoryQualifierMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNotSame(anotherBean, bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithBeanField() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryInjectionBean.class));
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryFieldInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryInjectionBean bean = (ObjectFactoryInjectionBean) bf.getBean("annotatedBean");
|
||||
ObjectFactoryFieldInjectionBean bean = (ObjectFactoryFieldInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
bean = (ObjectFactoryFieldInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObjectFactoryWithBeanMethod() throws Exception {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ObjectFactoryMethodInjectionBean.class));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class));
|
||||
bf.setSerializationId("test");
|
||||
|
||||
ObjectFactoryMethodInjectionBean bean = (ObjectFactoryMethodInjectionBean) bf.getBean("annotatedBean");
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bean = (ObjectFactoryMethodInjectionBean) SerializationTestUtils.serializeAndDeserialize(bean);
|
||||
assertSame(bf.getBean("testBean"), bean.getTestBean());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
@@ -881,7 +938,7 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryInjectionBean implements Serializable {
|
||||
public static class ObjectFactoryFieldInjectionBean implements Serializable {
|
||||
|
||||
@Inject
|
||||
private Provider<TestBean> testBeanFactory;
|
||||
@@ -892,7 +949,22 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryQualifierInjectionBean {
|
||||
public static class ObjectFactoryMethodInjectionBean implements Serializable {
|
||||
|
||||
private Provider<TestBean> testBeanFactory;
|
||||
|
||||
@Inject
|
||||
public void setTestBeanFactory(Provider<TestBean> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.testBeanFactory.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryQualifierFieldInjectionBean {
|
||||
|
||||
@Inject
|
||||
@Named("testBean")
|
||||
@@ -904,6 +976,22 @@ public class InjectAnnotationBeanPostProcessorTests {
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryQualifierMethodInjectionBean {
|
||||
|
||||
private Provider<?> testBeanFactory;
|
||||
|
||||
@Inject
|
||||
@Named("testBean")
|
||||
public void setTestBeanFactory(Provider<?> testBeanFactory) {
|
||||
this.testBeanFactory = testBeanFactory;
|
||||
}
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return (TestBean) this.testBeanFactory.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ObjectFactoryListFieldInjectionBean implements Serializable {
|
||||
|
||||
@Inject
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+3
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -56,9 +56,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setStartDelay
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
@@ -147,10 +145,9 @@ public class CronTriggerFactoryBean implements FactoryBean<CronTrigger>, BeanNam
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
Assert.isTrue(startDelay >= 0, "Start delay cannot be negative");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
|
||||
+15
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -56,9 +56,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @since 3.1
|
||||
* @see #setName
|
||||
* @see #setGroup
|
||||
* @see #setStartTime
|
||||
* @see #setJobName
|
||||
* @see #setJobGroup
|
||||
* @see #setStartDelay
|
||||
* @see #setJobDetail
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setTriggers
|
||||
* @see org.springframework.scheduling.quartz.SchedulerFactoryBean#setJobDetails
|
||||
@@ -84,6 +82,8 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
|
||||
private long repeatInterval;
|
||||
|
||||
private int repeatCount = -1;
|
||||
|
||||
private int priority;
|
||||
|
||||
private int misfireInstruction;
|
||||
@@ -145,10 +145,9 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
* Set the start delay in milliseconds.
|
||||
* <p>The start delay is added to the current system time (when the bean starts)
|
||||
* to control the start time of the trigger.
|
||||
* @param startDelay the start delay, in milliseconds
|
||||
*/
|
||||
public void setStartDelay(long startDelay) {
|
||||
Assert.state(startDelay >= 0, "Start delay cannot be negative.");
|
||||
Assert.isTrue(startDelay >= 0, "Start delay cannot be negative");
|
||||
this.startDelay = startDelay;
|
||||
}
|
||||
|
||||
@@ -159,6 +158,14 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
this.repeatInterval = repeatInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the number of times this trigger is supposed to fire.
|
||||
* <p>Default is to repeat indefinitely.
|
||||
*/
|
||||
public void setRepeatCount(int repeatCount) {
|
||||
this.repeatCount = repeatCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the priority of this trigger.
|
||||
*/
|
||||
@@ -218,6 +225,7 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
sti.setJobDataMap(this.jobDataMap);
|
||||
sti.setStartTime(this.startTime);
|
||||
sti.setRepeatInterval(this.repeatInterval);
|
||||
sti.setRepeatCount(this.repeatCount);
|
||||
sti.setPriority(this.priority);
|
||||
sti.setMisfireInstruction(this.misfireInstruction);
|
||||
this.simpleTrigger = sti;
|
||||
@@ -250,7 +258,7 @@ public class SimpleTriggerFactoryBean implements FactoryBean<SimpleTrigger>, Bea
|
||||
pvs.add("jobDataMap", this.jobDataMap);
|
||||
pvs.add("startTime", this.startTime);
|
||||
pvs.add("repeatInterval", this.repeatInterval);
|
||||
pvs.add("repeatCount", -1);
|
||||
pvs.add("repeatCount", this.repeatCount);
|
||||
pvs.add("priority", this.priority);
|
||||
pvs.add("misfireInstruction", this.misfireInstruction);
|
||||
bw.setPropertyValues(pvs);
|
||||
|
||||
@@ -15,14 +15,8 @@
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.springframework.ide.eclipse.core.springbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.springframework.ide.eclipse.core.springnature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
|
||||
</natures>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
Vendored
+2
-4
@@ -1,8 +1,6 @@
|
||||
|
||||
/**
|
||||
* Annotations and supporting classes for declarative cache management.
|
||||
* Hooked into Spring's caching interception infrastructure
|
||||
* via {@link org.springframework.cache.interceptor.CacheOperationSource
|
||||
* CacheOperationSource} implementation.
|
||||
* Hooked into Spring's cache interception infrastructure via
|
||||
* {@link org.springframework.cache.interceptor.CacheOperationSource}.
|
||||
*/
|
||||
package org.springframework.cache.annotation;
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -62,6 +62,7 @@ public class ConcurrentMapCache implements Cache {
|
||||
/**
|
||||
* Create a new ConcurrentMapCache with the specified name.
|
||||
* @param name the name of the cache
|
||||
* @param allowNullValues whether to accept and convert null values for this cache
|
||||
*/
|
||||
public ConcurrentMapCache(String name, boolean allowNullValues) {
|
||||
this(name, new ConcurrentHashMap<Object, Object>(), allowNullValues);
|
||||
@@ -115,7 +116,7 @@ public class ConcurrentMapCache implements Cache {
|
||||
/**
|
||||
* 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
|
||||
* @param storeValue the store value
|
||||
* @return the value to return to the user
|
||||
*/
|
||||
protected Object fromStoreValue(Object storeValue) {
|
||||
|
||||
Vendored
+1
-3
@@ -1,9 +1,7 @@
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* use in a Spring context, using a JDK based thread pool at runtime.
|
||||
*/
|
||||
package org.springframework.cache.concurrent;
|
||||
|
||||
|
||||
Vendored
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -57,10 +57,6 @@ public class EhCacheCache implements Cache {
|
||||
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);
|
||||
@@ -74,4 +70,8 @@ public class EhCacheCache implements Cache {
|
||||
this.cache.remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.cache.removeAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
org.springframework.context/src/main/java/org/springframework/cache/ehcache/EhCacheCacheManager.java
Vendored
+11
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -39,20 +39,19 @@ public class EhCacheCacheManager extends AbstractCacheManager {
|
||||
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
* Set the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*/
|
||||
public void setCacheManager(net.sf.ehcache.CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the backing EhCache {@link net.sf.ehcache.CacheManager}.
|
||||
*/
|
||||
public net.sf.ehcache.CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<Cache> loadCaches() {
|
||||
@@ -83,4 +82,5 @@ public class EhCacheCacheManager extends AbstractCacheManager {
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+17
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -332,6 +332,21 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
this.cacheManager.addCache(rawCache);
|
||||
}
|
||||
|
||||
if (this.cacheEventListeners != null) {
|
||||
for (CacheEventListener listener : this.cacheEventListeners) {
|
||||
rawCache.getCacheEventNotificationService().registerListener(listener);
|
||||
}
|
||||
}
|
||||
if (this.statisticsEnabled) {
|
||||
rawCache.setStatisticsEnabled(true);
|
||||
}
|
||||
if (this.sampledStatisticsEnabled) {
|
||||
rawCache.setSampledStatisticsEnabled(true);
|
||||
}
|
||||
if (this.disabled) {
|
||||
rawCache.setDisabled(true);
|
||||
}
|
||||
|
||||
// Decorate cache if necessary.
|
||||
Ehcache decoratedCache = decorateCache(rawCache);
|
||||
if (decoratedCache != rawCache) {
|
||||
@@ -345,7 +360,7 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
*/
|
||||
protected Cache createCache() {
|
||||
// Only call EHCache 1.6 constructor if actually necessary (for compatibility with EHCache 1.3+)
|
||||
Cache cache = (!this.clearOnFlush) ?
|
||||
return (!this.clearOnFlush) ?
|
||||
new Cache(this.cacheName, this.maxElementsInMemory, this.memoryStoreEvictionPolicy,
|
||||
this.overflowToDisk, null, this.eternal, this.timeToLive, this.timeToIdle,
|
||||
this.diskPersistent, this.diskExpiryThreadIntervalSeconds, null,
|
||||
@@ -355,22 +370,6 @@ public class EhCacheFactoryBean implements FactoryBean<Ehcache>, BeanNameAware,
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Vendored
+2
-6
@@ -1,11 +1,7 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Support classes for the open source cache
|
||||
* <a href="http://ehcache.sourceforge.net">Ehcache</a>,
|
||||
* allowing to set up an EHCache CacheManager and Caches
|
||||
* <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;
|
||||
|
||||
|
||||
+7
-7
@@ -52,6 +52,7 @@ public class AnnotatedBeanDefinitionReader {
|
||||
|
||||
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},
|
||||
@@ -78,10 +79,8 @@ public class AnnotatedBeanDefinitionReader {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -115,8 +114,8 @@ public class AnnotatedBeanDefinitionReader {
|
||||
* <p>The default is an {@link AnnotationScopeMetadataResolver}.
|
||||
*/
|
||||
public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
|
||||
this.scopeMetadataResolver = (scopeMetadataResolver != null ? scopeMetadataResolver
|
||||
: new AnnotationScopeMetadataResolver());
|
||||
this.scopeMetadataResolver =
|
||||
(scopeMetadataResolver != null ? scopeMetadataResolver : new AnnotationScopeMetadataResolver());
|
||||
}
|
||||
|
||||
public void register(Class<?>... annotatedClasses) {
|
||||
@@ -136,7 +135,6 @@ public class AnnotatedBeanDefinitionReader {
|
||||
public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
|
||||
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
|
||||
AnnotationMetadata metadata = abd.getMetadata();
|
||||
|
||||
if (metadata.isAnnotated(Profile.class.getName())) {
|
||||
AnnotationAttributes profile = MetadataUtils.attributesFor(metadata, Profile.class);
|
||||
if (!this.environment.acceptsProfiles(profile.getStringArray("value"))) {
|
||||
@@ -151,9 +149,11 @@ public class AnnotatedBeanDefinitionReader {
|
||||
for (Class<? extends Annotation> qualifier : qualifiers) {
|
||||
if (Primary.class.equals(qualifier)) {
|
||||
abd.setPrimary(true);
|
||||
} else if (Lazy.class.equals(qualifier)) {
|
||||
}
|
||||
else if (Lazy.class.equals(qualifier)) {
|
||||
abd.setLazyInit(true);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -73,7 +73,7 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
}
|
||||
}
|
||||
// Fallback: generate a unique default bean name.
|
||||
return buildDefaultBeanName(definition);
|
||||
return buildDefaultBeanName(definition, registry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,6 +119,17 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator {
|
||||
return (isStereotype && attributes != null && attributes.containsKey("value"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a default bean name from the given bean definition.
|
||||
* <p>The default implementation delegates to {@link #buildDefaultBeanName(BeanDefinition)}.
|
||||
* @param definition the bean definition to build a bean name for
|
||||
* @param registry the registry that the given bean definition is being registered with
|
||||
* @return the default bean name (never <code>null</code>)
|
||||
*/
|
||||
protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
|
||||
return buildDefaultBeanName(definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a default bean name from the given bean definition.
|
||||
* <p>The default implementation simply builds a decapitalized version
|
||||
|
||||
+9
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -446,7 +446,9 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
if (factory instanceof ConfigurableBeanFactory) {
|
||||
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
|
||||
for (String autowiredBeanName : autowiredBeanNames) {
|
||||
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
|
||||
if (beanFactory.containsBean(autowiredBeanName)) {
|
||||
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,9 +511,12 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
*/
|
||||
private class ResourceElement extends LookupElement {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected boolean shareable = true;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object cachedFieldValue;
|
||||
|
||||
public ResourceElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
@@ -724,4 +729,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+8
-15
@@ -49,7 +49,6 @@ import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
@@ -83,29 +82,25 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
private final BeanNameGenerator beanNameGenerator;
|
||||
private final BeanNameGenerator importBeanNameGenerator;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigurationClassBeanDefinitionReader} instance that will be used
|
||||
* to populate the given {@link BeanDefinitionRegistry}.
|
||||
* @param problemReporter
|
||||
* @param metadataReaderFactory
|
||||
*/
|
||||
public ConfigurationClassBeanDefinitionReader(
|
||||
BeanDefinitionRegistry registry, SourceExtractor sourceExtractor,
|
||||
ProblemReporter problemReporter, MetadataReaderFactory metadataReaderFactory,
|
||||
ResourceLoader resourceLoader, Environment environment,
|
||||
BeanNameGenerator beanNameGenerator) {
|
||||
ResourceLoader resourceLoader, Environment environment, BeanNameGenerator importBeanNameGenerator) {
|
||||
|
||||
Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null");
|
||||
this.registry = registry;
|
||||
this.sourceExtractor = sourceExtractor;
|
||||
this.problemReporter = problemReporter;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.environment = environment;
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
this.importBeanNameGenerator = importBeanNameGenerator;
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +119,9 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
* class itself, all its {@link Bean} methods
|
||||
*/
|
||||
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass) {
|
||||
doLoadBeanDefinitionForConfigurationClassIfNecessary(configClass);
|
||||
if (configClass.isImported()) {
|
||||
registerBeanDefinitionForImportedConfigurationClass(configClass);
|
||||
}
|
||||
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
|
||||
loadBeanDefinitionsForBeanMethod(beanMethod);
|
||||
}
|
||||
@@ -134,17 +131,13 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
/**
|
||||
* Register the {@link Configuration} class itself as a bean definition.
|
||||
*/
|
||||
private void doLoadBeanDefinitionForConfigurationClassIfNecessary(ConfigurationClass configClass) {
|
||||
if (!configClass.isImported()) {
|
||||
return;
|
||||
}
|
||||
|
||||
private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
|
||||
AnnotationMetadata metadata = configClass.getMetadata();
|
||||
BeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
|
||||
String className = metadata.getClassName();
|
||||
configBeanDef.setBeanClassName(className);
|
||||
if (ConfigurationClassUtils.checkConfigurationClassCandidate(configBeanDef, this.metadataReaderFactory)) {
|
||||
String configBeanName = this.beanNameGenerator.generateBeanName(configBeanDef, this.registry);
|
||||
String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
|
||||
this.registry.registerBeanDefinition(configBeanName, configBeanDef);
|
||||
configClass.setBeanName(configBeanName);
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
+53
-55
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -17,8 +17,6 @@
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
@@ -29,6 +27,7 @@ import net.sf.cglib.proxy.NoOp;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.scope.ScopedProxyFactoryBean;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
@@ -53,11 +52,28 @@ class ConfigurationClassEnhancer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ConfigurationClassEnhancer.class);
|
||||
|
||||
private final List<Callback> callbackInstances = new ArrayList<Callback>();
|
||||
private static final Class<?>[] CALLBACK_TYPES = { BeanMethodInterceptor.class,
|
||||
DisposableBeanMethodInterceptor.class, NoOp.class };
|
||||
|
||||
private final List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
||||
private static final CallbackFilter CALLBACK_FILTER = new CallbackFilter() {
|
||||
|
||||
private final CallbackFilter callbackFilter;
|
||||
public int accept(Method candidateMethod) {
|
||||
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
|
||||
return 0;
|
||||
}
|
||||
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
|
||||
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
|
||||
|
||||
|
||||
private final Callback[] callbackInstances;
|
||||
|
||||
|
||||
/**
|
||||
@@ -65,28 +81,11 @@ class ConfigurationClassEnhancer {
|
||||
*/
|
||||
public ConfigurationClassEnhancer(ConfigurableBeanFactory beanFactory) {
|
||||
Assert.notNull(beanFactory, "BeanFactory must not be null");
|
||||
|
||||
this.callbackInstances.add(new BeanMethodInterceptor(beanFactory));
|
||||
this.callbackInstances.add(new DisposableBeanMethodInterceptor());
|
||||
this.callbackInstances.add(NoOp.INSTANCE);
|
||||
|
||||
for (Callback callback : this.callbackInstances) {
|
||||
this.callbackTypes.add(callback.getClass());
|
||||
}
|
||||
|
||||
// Set up the callback filter to return the index of the BeanMethodInterceptor when
|
||||
// handling a @Bean-annotated method; otherwise, return index of the NoOp callback.
|
||||
callbackFilter = new CallbackFilter() {
|
||||
public int accept(Method candidateMethod) {
|
||||
if (BeanAnnotationHelper.isBeanAnnotated(candidateMethod)) {
|
||||
return 0;
|
||||
}
|
||||
if (DisposableBeanMethodInterceptor.isDestroyMethod(candidateMethod)) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
};
|
||||
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
|
||||
this.callbackInstances = new Callback[] {
|
||||
new BeanMethodInterceptor(beanFactory),
|
||||
DISPOSABLE_BEAN_METHOD_INTERCEPTOR,
|
||||
NoOp.INSTANCE };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,15 +133,11 @@ class ConfigurationClassEnhancer {
|
||||
*/
|
||||
private Enhancer newEnhancer(Class<?> superclass) {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
// Because callbackFilter and callbackTypes are dynamically populated
|
||||
// there's no opportunity for caching. This does not appear to be causing
|
||||
// any performance problem.
|
||||
enhancer.setUseCache(false);
|
||||
enhancer.setSuperclass(superclass);
|
||||
enhancer.setInterfaces(new Class[] {EnhancedConfiguration.class});
|
||||
enhancer.setUseFactory(false);
|
||||
enhancer.setCallbackFilter(this.callbackFilter);
|
||||
enhancer.setCallbackTypes(this.callbackTypes.toArray(new Class[this.callbackTypes.size()]));
|
||||
enhancer.setCallbackFilter(CALLBACK_FILTER);
|
||||
enhancer.setCallbackTypes(CALLBACK_TYPES);
|
||||
return enhancer;
|
||||
}
|
||||
|
||||
@@ -153,7 +148,7 @@ class ConfigurationClassEnhancer {
|
||||
private Class<?> createClass(Enhancer enhancer) {
|
||||
Class<?> subclass = enhancer.createClass();
|
||||
// registering callbacks statically (as opposed to threadlocal) is critical for usage in an OSGi env (SPR-5932)
|
||||
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances.toArray(new Callback[this.callbackInstances.size()]));
|
||||
Enhancer.registerStaticCallbacks(subclass, this.callbackInstances);
|
||||
return subclass;
|
||||
}
|
||||
|
||||
@@ -165,7 +160,7 @@ class ConfigurationClassEnhancer {
|
||||
* @see BeanMethodInterceptor#enhanceFactoryBean(Class, String)
|
||||
*/
|
||||
private static class GetObjectMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
private final String beanName;
|
||||
|
||||
@@ -177,7 +172,7 @@ class ConfigurationClassEnhancer {
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -215,8 +210,19 @@ class ConfigurationClassEnhancer {
|
||||
*/
|
||||
private static class BeanMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private static final Class<?>[] CALLBACK_TYPES = {
|
||||
GetObjectMethodInterceptor.class, NoOp.class };
|
||||
|
||||
private static final CallbackFilter CALLBACK_FITLER = new CallbackFilter() {
|
||||
public int accept(Method method) {
|
||||
return method.getName().equals("getObject") ? 0 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public BeanMethodInterceptor(ConfigurableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
@@ -245,7 +251,7 @@ class ConfigurationClassEnhancer {
|
||||
|
||||
// to handle the case of an inter-bean method reference, we must explicitly check the
|
||||
// container for already cached instances
|
||||
|
||||
|
||||
// first, check to see if the requested bean is a FactoryBean. If so, create a subclass
|
||||
// proxy that intercepts calls to getObject() and returns any cached bean instance.
|
||||
// this ensures that the semantics of calling a FactoryBean from within @Bean methods
|
||||
@@ -327,26 +333,18 @@ class ConfigurationClassEnhancer {
|
||||
*/
|
||||
private Object enhanceFactoryBean(Class<?> fbClass, String beanName) throws InstantiationException, IllegalAccessException {
|
||||
Enhancer enhancer = new Enhancer();
|
||||
enhancer.setUseCache(false);
|
||||
enhancer.setSuperclass(fbClass);
|
||||
enhancer.setUseFactory(false);
|
||||
enhancer.setCallbackFilter(new CallbackFilter() {
|
||||
public int accept(Method method) {
|
||||
return method.getName().equals("getObject") ? 0 : 1;
|
||||
}
|
||||
});
|
||||
List<Callback> callbackInstances = new ArrayList<Callback>();
|
||||
callbackInstances.add(new GetObjectMethodInterceptor(this.beanFactory, beanName));
|
||||
callbackInstances.add(NoOp.INSTANCE);
|
||||
|
||||
List<Class<? extends Callback>> callbackTypes = new ArrayList<Class<? extends Callback>>();
|
||||
for (Callback callback : callbackInstances) {
|
||||
callbackTypes.add(callback.getClass());
|
||||
}
|
||||
|
||||
enhancer.setCallbackTypes(callbackTypes.toArray(new Class[callbackTypes.size()]));
|
||||
enhancer.setCallbackFilter(CALLBACK_FITLER);
|
||||
// Callback instances must be ordered in the same way as CALLBACK_TYPES and CALLBACK_FILTER
|
||||
Callback[] callbackInstances = new Callback[] {
|
||||
new GetObjectMethodInterceptor(this.beanFactory, beanName),
|
||||
NoOp.INSTANCE
|
||||
};
|
||||
|
||||
enhancer.setCallbackTypes(CALLBACK_TYPES);
|
||||
Class<?> fbSubclass = enhancer.createClass();
|
||||
Enhancer.registerCallbacks(fbSubclass, callbackInstances.toArray(new Callback[callbackInstances.size()]));
|
||||
Enhancer.registerCallbacks(fbSubclass, callbackInstances);
|
||||
return fbSubclass.newInstance();
|
||||
}
|
||||
|
||||
|
||||
+16
-14
@@ -101,9 +101,8 @@ class ConfigurationClassParser {
|
||||
* to populate the set of configuration classes.
|
||||
*/
|
||||
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
|
||||
ProblemReporter problemReporter, Environment environment,
|
||||
ResourceLoader resourceLoader, BeanNameGenerator beanNameGenerator,
|
||||
BeanDefinitionRegistry registry) {
|
||||
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
|
||||
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {
|
||||
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.problemReporter = problemReporter;
|
||||
@@ -111,7 +110,7 @@ class ConfigurationClassParser {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.registry = registry;
|
||||
this.componentScanParser = new ComponentScanAnnotationParser(
|
||||
resourceLoader, environment, beanNameGenerator, registry);
|
||||
resourceLoader, environment, componentScanBeanNameGenerator, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +184,7 @@ class ConfigurationClassParser {
|
||||
throw new IllegalArgumentException("At least one @PropertySource(value) location is required");
|
||||
}
|
||||
for (int i = 0; i < nLocations; i++) {
|
||||
locations[0] = this.environment.resolveRequiredPlaceholders(locations[0]);
|
||||
locations[i] = this.environment.resolveRequiredPlaceholders(locations[i]);
|
||||
}
|
||||
ClassLoader classLoader = this.resourceLoader.getClassLoader();
|
||||
if (!StringUtils.hasText(name)) {
|
||||
@@ -315,12 +314,13 @@ class ConfigurationClassParser {
|
||||
AnnotationMetadata importingClassMetadata = configClass.getMetadata();
|
||||
for (String candidate : classesToImport) {
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(candidate);
|
||||
if (new AssignableTypeFilter(ImportSelector.class).match(reader, metadataReaderFactory)) {
|
||||
if (new AssignableTypeFilter(ImportSelector.class).match(reader, this.metadataReaderFactory)) {
|
||||
// the candidate class is an ImportSelector -> delegate to it to determine imports
|
||||
try {
|
||||
ImportSelector selector = BeanUtils.instantiateClass(Class.forName(candidate), ImportSelector.class);
|
||||
processImport(configClass, selector.selectImports(importingClassMetadata), false);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,8 @@ class ConfigurationClassParser {
|
||||
try {
|
||||
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(Class.forName(candidate), ImportBeanDefinitionRegistrar.class);
|
||||
registrar.registerBeanDefinitions(importingClassMetadata, registry);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
@@ -367,6 +368,7 @@ class ConfigurationClassParser {
|
||||
|
||||
|
||||
interface ImportRegistry {
|
||||
|
||||
String getImportingClassFor(String importedClass);
|
||||
}
|
||||
|
||||
@@ -374,14 +376,14 @@ class ConfigurationClassParser {
|
||||
@SuppressWarnings("serial")
|
||||
private static class ImportStack extends Stack<ConfigurationClass> implements ImportRegistry {
|
||||
|
||||
private Map<String, String> imports = new HashMap<String, String>();
|
||||
|
||||
public String getImportingClassFor(String importedClass) {
|
||||
return imports.get(importedClass);
|
||||
}
|
||||
private final Map<String, String> imports = new HashMap<String, String>();
|
||||
|
||||
public void registerImport(String importingClass, String importedClass) {
|
||||
imports.put(importedClass, importingClass);
|
||||
this.imports.put(importedClass, importingClass);
|
||||
}
|
||||
|
||||
public String getImportingClassFor(String importedClass) {
|
||||
return this.imports.get(importedClass);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+42
-17
@@ -43,7 +43,6 @@ import org.springframework.beans.factory.parsing.PassThroughSourceExtractor;
|
||||
import org.springframework.beans.factory.parsing.ProblemReporter;
|
||||
import org.springframework.beans.factory.parsing.SourceExtractor;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
@@ -92,6 +91,12 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
private static final boolean cglibAvailable = ClassUtils.isPresent(
|
||||
"net.sf.cglib.proxy.Enhancer", ConfigurationClassPostProcessor.class.getClassLoader());
|
||||
|
||||
private static final String IMPORT_AWARE_PROCESSOR_BEAN_NAME =
|
||||
ConfigurationClassPostProcessor.class.getName() + ".importAwareProcessor";
|
||||
|
||||
private static final String IMPORT_REGISTRY_BEAN_NAME =
|
||||
ConfigurationClassPostProcessor.class.getName() + ".importRegistry";
|
||||
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -115,7 +120,18 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
private ConfigurationClassBeanDefinitionReader reader;
|
||||
|
||||
private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
private boolean localBeanNameGeneratorSet = false;
|
||||
|
||||
/* using short class names as default bean names */
|
||||
private BeanNameGenerator componentScanBeanNameGenerator = new AnnotationBeanNameGenerator();
|
||||
|
||||
/* using fully qualified class names as default bean names */
|
||||
private BeanNameGenerator importBeanNameGenerator = new AnnotationBeanNameGenerator() {
|
||||
@Override
|
||||
protected String buildDefaultBeanName(BeanDefinition definition) {
|
||||
return definition.getBeanClassName();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -148,23 +164,27 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link BeanNameGenerator} to be used when registering imported and nested
|
||||
* {@link Configuration} classes. The default is {@link AnnotationBeanNameGenerator}.
|
||||
* Set the {@link BeanNameGenerator} to be used when triggering component scanning
|
||||
* from {@link Configuration} classes and when registering {@link Import}'ed
|
||||
* configuration classes. The default is a standard {@link AnnotationBeanNameGenerator}
|
||||
* for scanned components (compatible with the default in {@link ClassPathBeanDefinitionScanner})
|
||||
* and a variant thereof for imported configuration classes (using unique fully-qualified
|
||||
* class names instead of standard component overriding).
|
||||
* <p>Note that this strategy does <em>not</em> apply to {@link Bean} methods.
|
||||
* <p>This setter is typically only appropriate when configuring the post-processor as
|
||||
* a standalone bean definition in XML, e.g. not using the dedicated
|
||||
* {@code AnnotationConfig*} application contexts or the {@code
|
||||
* <context:annotation-config>} element. Any bean name generator specified against
|
||||
* the application context will take precedence over any value set here.
|
||||
* @param beanNameGenerator the strategy to use when generating configuration class
|
||||
* bean names
|
||||
* @since 3.1.1
|
||||
* @see AnnotationConfigApplicationContext#setBeanNameGenerator(BeanNameGenerator)
|
||||
* @see AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
|
||||
*/
|
||||
public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
|
||||
Assert.notNull(beanNameGenerator, "BeanNameGenerator must not be null");
|
||||
this.beanNameGenerator = beanNameGenerator;
|
||||
this.localBeanNameGeneratorSet = true;
|
||||
this.componentScanBeanNameGenerator = beanNameGenerator;
|
||||
this.importBeanNameGenerator = beanNameGenerator;
|
||||
}
|
||||
|
||||
public void setEnvironment(Environment environment) {
|
||||
@@ -189,7 +209,10 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
* Derive further bean definitions from the configuration classes in the registry.
|
||||
*/
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
|
||||
BeanDefinitionReaderUtils.registerWithGeneratedName(new RootBeanDefinition(ImportAwareBeanPostProcessor.class), registry);
|
||||
RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
|
||||
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
|
||||
|
||||
int registryId = System.identityHashCode(registry);
|
||||
if (this.registriesPostProcessed.contains(registryId)) {
|
||||
throw new IllegalStateException(
|
||||
@@ -200,6 +223,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
"postProcessBeanFactory already called for this post-processor against " + registry);
|
||||
}
|
||||
this.registriesPostProcessed.add(registryId);
|
||||
|
||||
processConfigBeanDefinitions(registry);
|
||||
}
|
||||
|
||||
@@ -244,15 +268,17 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
SingletonBeanRegistry singletonRegistry = null;
|
||||
if (registry instanceof SingletonBeanRegistry) {
|
||||
singletonRegistry = (SingletonBeanRegistry) registry;
|
||||
if (singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
|
||||
this.beanNameGenerator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
|
||||
if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
|
||||
BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
|
||||
this.componentScanBeanNameGenerator = generator;
|
||||
this.importBeanNameGenerator = generator;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse each @Configuration class
|
||||
ConfigurationClassParser parser = new ConfigurationClassParser(
|
||||
this.metadataReaderFactory, this.problemReporter, this.environment,
|
||||
this.resourceLoader, this.beanNameGenerator, registry);
|
||||
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
|
||||
for (BeanDefinitionHolder holder : configCandidates) {
|
||||
BeanDefinition bd = holder.getBeanDefinition();
|
||||
try {
|
||||
@@ -287,16 +313,15 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
// Read the model and create bean definitions based on its content
|
||||
if (this.reader == null) {
|
||||
this.reader = new ConfigurationClassBeanDefinitionReader(
|
||||
registry, this.sourceExtractor, this.problemReporter,
|
||||
this.metadataReaderFactory, this.resourceLoader, this.environment,
|
||||
this.beanNameGenerator);
|
||||
registry, this.sourceExtractor, this.problemReporter, this.metadataReaderFactory,
|
||||
this.resourceLoader, this.environment, this.importBeanNameGenerator);
|
||||
}
|
||||
this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
|
||||
|
||||
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
|
||||
if (singletonRegistry != null) {
|
||||
if (!singletonRegistry.containsSingleton("importRegistry")) {
|
||||
singletonRegistry.registerSingleton("importRegistry", parser.getImportRegistry());
|
||||
if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
|
||||
singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -359,7 +384,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ImportAware) {
|
||||
ImportRegistry importRegistry = beanFactory.getBean(ImportRegistry.class);
|
||||
ImportRegistry importRegistry = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
|
||||
String importingClass = importRegistry.getImportingClassFor(bean.getClass().getSuperclass().getName());
|
||||
if (importingClass != null) {
|
||||
try {
|
||||
|
||||
+27
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
@@ -67,12 +68,14 @@ import org.springframework.context.event.ContextStartedEvent;
|
||||
import org.springframework.context.event.ContextStoppedEvent;
|
||||
import org.springframework.context.event.SimpleApplicationEventMulticaster;
|
||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||
import org.springframework.context.weaving.LoadTimeWeaverAware;
|
||||
import org.springframework.context.weaving.LoadTimeWeaverAwareProcessor;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.StandardEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -224,7 +227,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
public AbstractApplicationContext(ApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
this.resourcePatternResolver = getResourcePatternResolver();
|
||||
this.environment = this.createEnvironment();
|
||||
this.environment = createEnvironment();
|
||||
}
|
||||
|
||||
|
||||
@@ -378,14 +381,19 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>The parent {@linkplain #getEnvironment() environment} is
|
||||
* delegated to this (child) context if the parent is a
|
||||
* {@link ConfigurableApplicationContext} implementation.
|
||||
* <p>The parent {@linkplain ApplicationContext#getEnvironment() environment} is
|
||||
* {@linkplain ConfigurableEnvironment#merge(ConfigurableEnvironment) merged} with
|
||||
* this (child) application context environment if the parent is non-{@code null} and
|
||||
* its environment is an instance of {@link ConfigurableEnvironment}.
|
||||
* @see ConfigurableEnvironment#merge(ConfigurableEnvironment)
|
||||
*/
|
||||
public void setParent(ApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
if (parent instanceof ConfigurableApplicationContext) {
|
||||
this.setEnvironment(((ConfigurableApplicationContext)parent).getEnvironment());
|
||||
if (parent != null) {
|
||||
Environment parentEnvironment = parent.getEnvironment();
|
||||
if (parentEnvironment instanceof ConfigurableEnvironment) {
|
||||
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -500,13 +508,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
// Validate that all properties marked as required are resolvable
|
||||
// see ConfigurablePropertyResolver#setRequiredProperties
|
||||
this.environment.validateRequiredProperties();
|
||||
getEnvironment().validateRequiredProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replace any stub property sources with actual instances.
|
||||
* @see org.springframework.core.env.PropertySource.StubPropertySource
|
||||
* @see org.springframework.web.context.support.WebApplicationContextUtils#initSerlvetPropertySources
|
||||
* @see org.springframework.web.context.support.WebApplicationContextUtils#initServletPropertySources
|
||||
*/
|
||||
protected void initPropertySources() {
|
||||
// For subclasses: do nothing by default.
|
||||
@@ -536,7 +544,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
// Tell the internal bean factory to use the context's class loader etc.
|
||||
beanFactory.setBeanClassLoader(getClassLoader());
|
||||
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
|
||||
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));
|
||||
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
|
||||
|
||||
// Configure the bean factory with context callbacks.
|
||||
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
|
||||
@@ -561,15 +569,13 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
}
|
||||
|
||||
// Register default environment beans.
|
||||
if (!beanFactory.containsBean(ENVIRONMENT_BEAN_NAME)) {
|
||||
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
|
||||
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
|
||||
}
|
||||
|
||||
if (!beanFactory.containsBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
|
||||
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
|
||||
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
|
||||
}
|
||||
|
||||
if (!beanFactory.containsBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
|
||||
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
|
||||
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
|
||||
}
|
||||
}
|
||||
@@ -903,6 +909,12 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
|
||||
}
|
||||
|
||||
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
|
||||
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
|
||||
for (String weaverAwareName : weaverAwareNames) {
|
||||
getBean(weaverAwareName);
|
||||
}
|
||||
|
||||
// Stop using the temporary ClassLoader for type matching.
|
||||
beanFactory.setTempClassLoader(null);
|
||||
|
||||
|
||||
+16
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -52,7 +52,8 @@ public abstract class MessageSourceSupport {
|
||||
* Used for passed-in default messages. MessageFormats for resolved
|
||||
* codes are cached on a specific basis in subclasses.
|
||||
*/
|
||||
private final Map<String, MessageFormat> cachedMessageFormats = new HashMap<String, MessageFormat>();
|
||||
private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage =
|
||||
new HashMap<String, Map<Locale, MessageFormat>>();
|
||||
|
||||
|
||||
/**
|
||||
@@ -114,9 +115,16 @@ public abstract class MessageSourceSupport {
|
||||
if (msg == null || (!this.alwaysUseMessageFormat && ObjectUtils.isEmpty(args))) {
|
||||
return msg;
|
||||
}
|
||||
MessageFormat messageFormat;
|
||||
synchronized (this.cachedMessageFormats) {
|
||||
messageFormat = this.cachedMessageFormats.get(msg);
|
||||
MessageFormat messageFormat = null;
|
||||
synchronized (this.messageFormatsPerMessage) {
|
||||
Map<Locale, MessageFormat> messageFormatsPerLocale = this.messageFormatsPerMessage.get(msg);
|
||||
if (messageFormatsPerLocale != null) {
|
||||
messageFormat = messageFormatsPerLocale.get(locale);
|
||||
}
|
||||
else {
|
||||
messageFormatsPerLocale = new HashMap<Locale, MessageFormat>();
|
||||
this.messageFormatsPerMessage.put(msg, messageFormatsPerLocale);
|
||||
}
|
||||
if (messageFormat == null) {
|
||||
try {
|
||||
messageFormat = createMessageFormat(msg, locale);
|
||||
@@ -130,7 +138,7 @@ public abstract class MessageSourceSupport {
|
||||
// silently proceed with raw message if format not enforced
|
||||
messageFormat = INVALID_MESSAGE_FORMAT;
|
||||
}
|
||||
this.cachedMessageFormats.put(msg, messageFormat);
|
||||
messageFormatsPerLocale.put(locale, messageFormat);
|
||||
}
|
||||
}
|
||||
if (messageFormat == INVALID_MESSAGE_FORMAT) {
|
||||
@@ -153,9 +161,8 @@ public abstract class MessageSourceSupport {
|
||||
|
||||
/**
|
||||
* Template method for resolving argument objects.
|
||||
* <p>The default implementation simply returns the given argument
|
||||
* array as-is. Can be overridden in subclasses in order to resolve
|
||||
* special argument types.
|
||||
* <p>The default implementation simply returns the given argument array as-is.
|
||||
* Can be overridden in subclasses in order to resolve special argument types.
|
||||
* @param args the original argument array
|
||||
* @param locale the Locale to resolve against
|
||||
* @return the resolved argument array
|
||||
|
||||
+15
-12
@@ -37,12 +37,16 @@ import org.springframework.util.PropertiesPersister;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.context.MessageSource} implementation that
|
||||
* accesses resource bundles using specified basenames. This class uses
|
||||
* {@link java.util.Properties} instances as its custom data structure for
|
||||
* messages, loading them via a {@link org.springframework.util.PropertiesPersister}
|
||||
* strategy: The default strategy is capable of loading properties files
|
||||
* with a specific character encoding, if desired.
|
||||
* Spring-specific {@link org.springframework.context.MessageSource} implementation
|
||||
* that accesses resource bundles using specified basenames, participating in the
|
||||
* Spring {@link org.springframework.context.ApplicationContext}'s resource loading.
|
||||
*
|
||||
* <p>In contrast to the JDK-based {@link ResourceBundleMessageSource}, this class uses
|
||||
* {@link java.util.Properties} instances as its custom data structure for messages,
|
||||
* loading them via a {@link org.springframework.util.PropertiesPersister} strategy
|
||||
* from Spring {@link Resource} handles. This strategy is not only capable of
|
||||
* reloading files based on timestamp changes, but also of loading properties files
|
||||
* with a specific character encoding. It will detect XML property files as well.
|
||||
*
|
||||
* <p>In contrast to {@link ResourceBundleMessageSource}, this class supports
|
||||
* reloading of properties files through the {@link #setCacheSeconds "cacheSeconds"}
|
||||
@@ -171,7 +175,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
|
||||
* Set the default charset to use for parsing properties files.
|
||||
* Used if no file-specific charset is specified for a file.
|
||||
* <p>Default is none, using the <code>java.util.Properties</code>
|
||||
* default encoding.
|
||||
* default encoding: ISO-8859-1.
|
||||
* <p>Only applies to classic properties files, not to XML files.
|
||||
* @param defaultEncoding the default charset
|
||||
* @see #setFileEncodings
|
||||
@@ -201,10 +205,9 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
|
||||
* fallback will be the default file (e.g. "messages.properties" for
|
||||
* basename "messages").
|
||||
* <p>Falling back to the system Locale is the default behavior of
|
||||
* <code>java.util.ResourceBundle</code>. However, this is often not
|
||||
* desirable in an application server environment, where the system Locale
|
||||
* is not relevant to the application at all: Set this flag to "false"
|
||||
* in such a scenario.
|
||||
* <code>java.util.ResourceBundle</code>. However, this is often not desirable
|
||||
* in an application server environment, where the system Locale is not relevant
|
||||
* to the application at all: Set this flag to "false" in such a scenario.
|
||||
*/
|
||||
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
|
||||
this.fallbackToSystemLocale = fallbackToSystemLocale;
|
||||
@@ -448,7 +451,7 @@ public class ReloadableResourceBundleMessageSource extends AbstractMessageSource
|
||||
* @param propHolder the current PropertiesHolder for the bundle
|
||||
*/
|
||||
protected PropertiesHolder refreshProperties(String filename, PropertiesHolder propHolder) {
|
||||
long refreshTimestamp = (this.cacheMillis < 0) ? -1 : System.currentTimeMillis();
|
||||
long refreshTimestamp = (this.cacheMillis < 0 ? -1 : System.currentTimeMillis());
|
||||
|
||||
Resource resource = this.resourceLoader.getResource(filename + PROPERTIES_SUFFIX);
|
||||
if (!resource.exists()) {
|
||||
|
||||
+209
-23
@@ -16,14 +16,24 @@
|
||||
|
||||
package org.springframework.context.support;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.PropertyResourceBundle;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.core.JdkVersion;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -58,6 +68,12 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
|
||||
private String[] basenames = new String[0];
|
||||
|
||||
private String defaultEncoding;
|
||||
|
||||
private boolean fallbackToSystemLocale = true;
|
||||
|
||||
private long cacheMillis = -1;
|
||||
|
||||
private ClassLoader bundleClassLoader;
|
||||
|
||||
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
@@ -133,6 +149,59 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default charset to use for parsing resource bundle files.
|
||||
* <p>Default is none, using the <code>java.util.ResourceBundle</code>
|
||||
* default encoding: ISO-8859-1.
|
||||
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
|
||||
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support
|
||||
* and more flexibility in setting of an encoding per file.
|
||||
*/
|
||||
public void setDefaultEncoding(String defaultEncoding) {
|
||||
this.defaultEncoding = defaultEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to fall back to the system Locale if no files for a specific
|
||||
* Locale have been found. Default is "true"; if this is turned off, the only
|
||||
* fallback will be the default file (e.g. "messages.properties" for
|
||||
* basename "messages").
|
||||
* <p>Falling back to the system Locale is the default behavior of
|
||||
* <code>java.util.ResourceBundle</code>. However, this is often not desirable
|
||||
* in an application server environment, where the system Locale is not relevant
|
||||
* to the application at all: Set this flag to "false" in such a scenario.
|
||||
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
|
||||
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support.
|
||||
*/
|
||||
public void setFallbackToSystemLocale(boolean fallbackToSystemLocale) {
|
||||
this.fallbackToSystemLocale = fallbackToSystemLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of seconds to cache loaded resource bundle files.
|
||||
* <ul>
|
||||
* <li>Default is "-1", indicating to cache forever.
|
||||
* <li>A positive number will expire resource bundles after the given
|
||||
* number of seconds. This is essentially the interval between refresh checks.
|
||||
* Note that a refresh attempt will first check the last-modified timestamp
|
||||
* of the file before actually reloading it; so if files don't change, this
|
||||
* interval can be set rather low, as refresh attempts will not actually reload.
|
||||
* <li>A value of "0" will check the last-modified timestamp of the file on
|
||||
* every message access. <b>Do not use this in a production environment!</b>
|
||||
* <li><b>Note that depending on your ClassLoader, expiration might not work reliably
|
||||
* since the ClassLoader may hold on to a cached version of the bundle file.</b>
|
||||
* Consider {@link ReloadableResourceBundleMessageSource} in combination
|
||||
* with resource bundle files in a non-classpath location.
|
||||
* </ul>
|
||||
* <p><b>NOTE: Only works on JDK 1.6 and higher.</b> Consider using
|
||||
* {@link ReloadableResourceBundleMessageSource} for JDK 1.5 support
|
||||
* and more flexibility in terms of the kinds of resources to load from
|
||||
* (in particular from outside of the classpath where expiration works reliably).
|
||||
*/
|
||||
public void setCacheSeconds(int cacheSeconds) {
|
||||
this.cacheMillis = (cacheSeconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ClassLoader to load resource bundles with.
|
||||
* <p>Default is the containing BeanFactory's
|
||||
@@ -201,30 +270,38 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
* found for the given basename and Locale
|
||||
*/
|
||||
protected ResourceBundle getResourceBundle(String basename, Locale locale) {
|
||||
synchronized (this.cachedResourceBundles) {
|
||||
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
|
||||
if (localeMap != null) {
|
||||
ResourceBundle bundle = localeMap.get(locale);
|
||||
if (bundle != null) {
|
||||
if (this.cacheMillis >= 0) {
|
||||
// Fresh ResourceBundle.getBundle call in order to let ResourceBundle
|
||||
// do its native caching, at the expense of more extensive lookup steps.
|
||||
return doGetBundle(basename, locale);
|
||||
}
|
||||
else {
|
||||
// Cache forever: prefer locale cache over repeated getBundle calls.
|
||||
synchronized (this.cachedResourceBundles) {
|
||||
Map<Locale, ResourceBundle> localeMap = this.cachedResourceBundles.get(basename);
|
||||
if (localeMap != null) {
|
||||
ResourceBundle bundle = localeMap.get(locale);
|
||||
if (bundle != null) {
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ResourceBundle bundle = doGetBundle(basename, locale);
|
||||
if (localeMap == null) {
|
||||
localeMap = new HashMap<Locale, ResourceBundle>();
|
||||
this.cachedResourceBundles.put(basename, localeMap);
|
||||
}
|
||||
localeMap.put(locale, bundle);
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
try {
|
||||
ResourceBundle bundle = doGetBundle(basename, locale);
|
||||
if (localeMap == null) {
|
||||
localeMap = new HashMap<Locale, ResourceBundle>();
|
||||
this.cachedResourceBundles.put(basename, localeMap);
|
||||
catch (MissingResourceException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
|
||||
}
|
||||
// Assume bundle not found
|
||||
// -> do NOT throw the exception to allow for checking parent message source.
|
||||
return null;
|
||||
}
|
||||
localeMap.put(locale, bundle);
|
||||
return bundle;
|
||||
}
|
||||
catch (MissingResourceException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("ResourceBundle [" + basename + "] not found for MessageSource: " + ex.getMessage());
|
||||
}
|
||||
// Assume bundle not found
|
||||
// -> do NOT throw the exception to allow for checking parent message source.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,7 +316,20 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
* @see #getBundleClassLoader()
|
||||
*/
|
||||
protected ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
|
||||
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
|
||||
if ((this.defaultEncoding != null && !"ISO-8859-1".equals(this.defaultEncoding)) ||
|
||||
!this.fallbackToSystemLocale || this.cacheMillis >= 0) {
|
||||
// Custom Control required...
|
||||
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
|
||||
throw new IllegalStateException("Cannot use 'defaultEncoding', 'fallbackToSystemLocale' and " +
|
||||
"'cacheSeconds' on the standard ResourceBundleMessageSource when running on Java 5. " +
|
||||
"Consider using ReloadableResourceBundleMessageSource instead.");
|
||||
}
|
||||
return new ControlBasedResourceBundleFactory().getBundle(basename, locale);
|
||||
}
|
||||
else {
|
||||
// Good old standard call...
|
||||
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,7 +388,6 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the configuration of this MessageSource.
|
||||
*/
|
||||
@@ -308,4 +397,101 @@ public class ResourceBundleMessageSource extends AbstractMessageSource implement
|
||||
StringUtils.arrayToCommaDelimitedString(this.basenames) + "]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Factory indirection for runtime isolation of the optional dependencv on
|
||||
* Java 6's Control class.
|
||||
* @see ResourceBundle#getBundle(String, java.util.Locale, ClassLoader, java.util.ResourceBundle.Control)
|
||||
* @see MessageSourceControl
|
||||
*/
|
||||
private class ControlBasedResourceBundleFactory {
|
||||
|
||||
public ResourceBundle getBundle(String basename, Locale locale) {
|
||||
return ResourceBundle.getBundle(basename, locale, getBundleClassLoader(), new MessageSourceControl());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Custom implementation of Java 6's <code>ResourceBundle.Control</code>,
|
||||
* adding support for custom file encodings, deactivating the fallback to the
|
||||
* system locale and activating ResourceBundle's native cache, if desired.
|
||||
*/
|
||||
private class MessageSourceControl extends ResourceBundle.Control {
|
||||
|
||||
@Override
|
||||
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
|
||||
throws IllegalAccessException, InstantiationException, IOException {
|
||||
if (format.equals("java.properties")) {
|
||||
String bundleName = toBundleName(baseName, locale);
|
||||
final String resourceName = toResourceName(bundleName, "properties");
|
||||
final ClassLoader classLoader = loader;
|
||||
final boolean reloadFlag = reload;
|
||||
InputStream stream;
|
||||
try {
|
||||
stream = AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<InputStream>() {
|
||||
public InputStream run() throws IOException {
|
||||
InputStream is = null;
|
||||
if (reloadFlag) {
|
||||
URL url = classLoader.getResource(resourceName);
|
||||
if (url != null) {
|
||||
URLConnection connection = url.openConnection();
|
||||
if (connection != null) {
|
||||
connection.setUseCaches(false);
|
||||
is = connection.getInputStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
is = classLoader.getResourceAsStream(resourceName);
|
||||
}
|
||||
return is;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (PrivilegedActionException ex) {
|
||||
throw (IOException) ex.getException();
|
||||
}
|
||||
if (stream != null) {
|
||||
try {
|
||||
return (defaultEncoding != null ?
|
||||
new PropertyResourceBundle(new InputStreamReader(stream, defaultEncoding)) :
|
||||
new PropertyResourceBundle(stream));
|
||||
}
|
||||
finally {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return super.newBundle(baseName, locale, format, loader, reload);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getFallbackLocale(String baseName, Locale locale) {
|
||||
return (fallbackToSystemLocale ? super.getFallbackLocale(baseName, locale) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeToLive(String baseName, Locale locale) {
|
||||
return (cacheMillis >= 0 ? cacheMillis : super.getTimeToLive(baseName, locale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
|
||||
if (super.needsReload(baseName, locale, format, loader, bundle, loadTime)) {
|
||||
cachedBundleMessageFormats.remove(bundle);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -164,6 +164,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
|
||||
*/
|
||||
protected static final String FIELD_METRIC_CATEGORY = "metricCategory";
|
||||
|
||||
|
||||
/**
|
||||
* Default value for the JMX field "currencyTimeLimit".
|
||||
*/
|
||||
@@ -223,7 +224,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
|
||||
* Return whether strict casing for attributes is enabled.
|
||||
*/
|
||||
protected boolean isUseStrictCasing() {
|
||||
return useStrictCasing;
|
||||
return this.useStrictCasing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +251,7 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
|
||||
* Return whether to expose the JMX descriptor field "class" for managed operations.
|
||||
*/
|
||||
protected boolean isExposeClassDescriptor() {
|
||||
return exposeClassDescriptor;
|
||||
return this.exposeClassDescriptor;
|
||||
}
|
||||
|
||||
|
||||
@@ -447,7 +448,6 @@ public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBean
|
||||
*/
|
||||
protected abstract boolean includeOperation(Method method, String beanKey);
|
||||
|
||||
|
||||
/**
|
||||
* Get the description for a particular attribute.
|
||||
* <p>The default implementation returns a description for the operation
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.scheduling.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
|
||||
/**
|
||||
* Specialization of {@link AsyncExecutionInterceptor} that delegates method execution to
|
||||
* an {@code Executor} based on the {@link Async} annotation. Specifically designed to
|
||||
* support use of {@link Async#value()} executor qualification mechanism introduced in
|
||||
* Spring 3.1.2. Supports detecting qualifier metadata via {@code @Async} at the method or
|
||||
* declaring class level. See {@link #getExecutorQualifier(Method)} for details.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
* @see org.springframework.scheduling.annotation.Async
|
||||
* @see org.springframework.scheduling.annotation.AsyncAnnotationAdvisor
|
||||
*/
|
||||
public class AnnotationAsyncExecutionInterceptor extends AsyncExecutionInterceptor {
|
||||
|
||||
/**
|
||||
* Create a new {@code AnnotationAsyncExecutionInterceptor} with the given executor.
|
||||
* @param defaultExecutor the executor to be used by default if no more specific
|
||||
* executor has been qualified at the method level using {@link Async#value()}.
|
||||
*/
|
||||
public AnnotationAsyncExecutionInterceptor(Executor defaultExecutor) {
|
||||
super(defaultExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the qualifier or bean name of the executor to be used when executing the
|
||||
* given method, specified via {@link Async#value} at the method or declaring
|
||||
* class level. If {@code @Async} is specified at both the method and class level, the
|
||||
* method's {@code #value} takes precedence (even if empty string, indicating that
|
||||
* the default executor should be used preferentially).
|
||||
* @param method the method to inspect for executor qualifier metadata
|
||||
* @return the qualifier if specified, otherwise empty string indicating that the
|
||||
* {@linkplain #setExecutor(Executor) default executor} should be used
|
||||
* @see #determineAsyncExecutor(Method)
|
||||
*/
|
||||
@Override
|
||||
protected String getExecutorQualifier(Method method) {
|
||||
// maintainer's note: changes made here should also be made in
|
||||
// AnnotationAsyncExecutionAspect#getExecutorQualifier
|
||||
Async async = AnnotationUtils.findAnnotation(method, Async.class);
|
||||
if (async == null) {
|
||||
async = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Async.class);
|
||||
}
|
||||
return async == null ? null : async.value();
|
||||
}
|
||||
|
||||
}
|
||||
+23
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -28,22 +28,37 @@ import java.lang.annotation.Target;
|
||||
* considered as asynchronous.
|
||||
*
|
||||
* <p>In terms of target method signatures, any parameter types are supported.
|
||||
* However, the return type is constrained to either <code>void</code> or
|
||||
* <code>java.util.concurrent.Future</code>. In the latter case, the Future handle
|
||||
* returned from the proxy will be an actual asynchronous Future that can be used
|
||||
* However, the return type is constrained to either {@code void} or
|
||||
* {@link java.util.concurrent.Future}. In the latter case, the {@code Future} handle
|
||||
* returned from the proxy will be an actual asynchronous {@code Future} that can be used
|
||||
* to track the result of the asynchronous method execution. However, since the
|
||||
* target method needs to implement the same signature, it will have to return
|
||||
* a temporary Future handle that just passes the return value through: e.g.
|
||||
* Spring's {@link AsyncResult} or EJB 3.1's <code>javax.ejb.AsyncResult</code>.
|
||||
* a temporary {@code Future} handle that just passes the return value through: e.g.
|
||||
* Spring's {@link AsyncResult} or EJB 3.1's {@link javax.ejb.AsyncResult}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 3.0
|
||||
* @see org.springframework.aop.interceptor.AsyncExecutionInterceptor
|
||||
* @see AnnotationAsyncExecutionInterceptor
|
||||
* @see AsyncAnnotationAdvisor
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Async {
|
||||
|
||||
/**
|
||||
* A qualifier value for the specified asynchronous operation(s).
|
||||
* <p>May be used to determine the target executor to be used when executing this
|
||||
* method, matching the qualifier value (or the bean name) of a specific
|
||||
* {@link java.util.concurrent.Executor Executor} or
|
||||
* {@link org.springframework.core.task.TaskExecutor TaskExecutor}
|
||||
* bean definition.
|
||||
* <p>When specified on a class level {@code @Async} annotation, indicates that the
|
||||
* given executor should be used for all methods within the class. Method level use
|
||||
* of {@link Async#value} always overrides any value set at the class level.
|
||||
* @since 3.1.2
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
|
||||
+31
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -25,11 +25,12 @@ import java.util.concurrent.Executor;
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.interceptor.AsyncExecutionInterceptor;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -45,27 +46,30 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see PersistenceExceptionTranslationAdvisor
|
||||
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisor
|
||||
* @see org.springframework.stereotype.Repository
|
||||
* @see org.springframework.dao.DataAccessException
|
||||
* @see org.springframework.dao.support.PersistenceExceptionTranslator
|
||||
*/
|
||||
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
@SuppressWarnings("serial")
|
||||
public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private Advice advice;
|
||||
|
||||
private Pointcut pointcut;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ConcurrencyAnnotationBeanPostProcessor for bean-style configuration.
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for bean-style configuration.
|
||||
*/
|
||||
public AsyncAnnotationAdvisor() {
|
||||
this(new SimpleAsyncTaskExecutor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ConcurrencyAnnotationBeanPostProcessor for the given task executor.
|
||||
* Create a new {@code AsyncAnnotationAdvisor} for the given task executor.
|
||||
* @param executor the task executor to use for asynchronous methods
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -74,20 +78,36 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
asyncAnnotationTypes.add(Async.class);
|
||||
ClassLoader cl = AsyncAnnotationAdvisor.class.getClassLoader();
|
||||
try {
|
||||
asyncAnnotationTypes.add((Class) cl.loadClass("javax.ejb.Asynchronous"));
|
||||
asyncAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.ejb.Asynchronous"));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// If EJB 3.1 API not present, simply ignore.
|
||||
}
|
||||
this.advice = buildAdvice(executor);
|
||||
this.setTaskExecutor(executor);
|
||||
this.pointcut = buildPointcut(asyncAnnotationTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code BeanFactory} to be used when looking up executors by qualifier.
|
||||
*/
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
delegateBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
public void delegateBeanFactory(BeanFactory beanFactory) {
|
||||
if (this.advice instanceof AnnotationAsyncExecutionInterceptor) {
|
||||
((AnnotationAsyncExecutionInterceptor)this.advice).setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the task executor to use for asynchronous methods.
|
||||
*/
|
||||
public void setTaskExecutor(Executor executor) {
|
||||
this.advice = buildAdvice(executor);
|
||||
delegateBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,17 +137,12 @@ public class AsyncAnnotationAdvisor extends AbstractPointcutAdvisor {
|
||||
|
||||
|
||||
protected Advice buildAdvice(Executor executor) {
|
||||
if (executor instanceof AsyncTaskExecutor) {
|
||||
return new AsyncExecutionInterceptor((AsyncTaskExecutor) executor);
|
||||
}
|
||||
else {
|
||||
return new AsyncExecutionInterceptor(executor);
|
||||
}
|
||||
return new AnnotationAsyncExecutionInterceptor(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate a pointcut for the given target class, if any.
|
||||
* @param targetClass the class to introspect
|
||||
* Calculate a pointcut for the given async annotation types, if any.
|
||||
* @param asyncAnnotationTypes the async annotation types to introspect
|
||||
* @return the applicable Pointcut object, or <code>null</code> if none
|
||||
*/
|
||||
protected Pointcut buildPointcut(Set<Class<? extends Annotation>> asyncAnnotationTypes) {
|
||||
|
||||
+13
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -24,7 +24,10 @@ import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.aop.framework.ProxyConfig;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.core.Ordered;
|
||||
@@ -53,7 +56,8 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
implements BeanPostProcessor, BeanClassLoaderAware, InitializingBean, Ordered {
|
||||
implements BeanPostProcessor, BeanClassLoaderAware, BeanFactoryAware,
|
||||
InitializingBean, Ordered {
|
||||
|
||||
private Class<? extends Annotation> asyncAnnotationType;
|
||||
|
||||
@@ -69,6 +73,8 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
*/
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Set the 'async' annotation type to be detected at either class or method
|
||||
@@ -95,12 +101,17 @@ public class AsyncAnnotationBeanPostProcessor extends ProxyConfig
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
this.asyncAnnotationAdvisor = (this.executor != null ?
|
||||
new AsyncAnnotationAdvisor(this.executor) : new AsyncAnnotationAdvisor());
|
||||
if (this.asyncAnnotationType != null) {
|
||||
this.asyncAnnotationAdvisor.setAsyncAnnotationType(this.asyncAnnotationType);
|
||||
}
|
||||
this.asyncAnnotationAdvisor.setBeanFactory(this.beanFactory);
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -105,12 +105,16 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
* taskRegistrar.setScheduler(taskExecutor());
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Bean(destroyMethod="shutdown")
|
||||
* public Executor taskExecutor() {
|
||||
* return Executors.newScheduledThreadPool(100);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Note in the example above the use of {@code @Bean(destroyMethod="shutdown")}. This
|
||||
* ensures that the task executor is properly shut down when the Spring application
|
||||
* context itself is closed.
|
||||
*
|
||||
* Implementing {@code SchedulingConfigurer} also allows for fine-grained
|
||||
* control over task registration via the {@code ScheduledTaskRegistrar}.
|
||||
* For example, the following configures the execution of a particular bean
|
||||
@@ -133,7 +137,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
* );
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Bean(destroyMethod="shutdown")
|
||||
* public Executor taskScheduler() {
|
||||
* return Executors.newScheduledThreadPool(42);
|
||||
* }
|
||||
|
||||
+30
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -49,9 +49,9 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
|
||||
|
||||
private final ScheduledExecutorService executor;
|
||||
|
||||
private volatile ScheduledFuture currentFuture;
|
||||
private ScheduledFuture currentFuture;
|
||||
|
||||
private volatile Date scheduledExecutionTime;
|
||||
private Date scheduledExecutionTime;
|
||||
|
||||
private final Object triggerContextMonitor = new Object();
|
||||
|
||||
@@ -82,35 +82,53 @@ class ReschedulingRunnable extends DelegatingErrorHandlingRunnable implements Sc
|
||||
Date completionTime = new Date();
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
|
||||
}
|
||||
if (!this.currentFuture.isCancelled()) {
|
||||
schedule();
|
||||
if (!this.currentFuture.isCancelled()) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return this.currentFuture.cancel(mayInterruptIfRunning);
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
return this.currentFuture.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCancelled() {
|
||||
return this.currentFuture.isCancelled();
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
return this.currentFuture.isCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return this.currentFuture.isDone();
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
return this.currentFuture.isDone();
|
||||
}
|
||||
}
|
||||
|
||||
public Object get() throws InterruptedException, ExecutionException {
|
||||
return this.currentFuture.get();
|
||||
ScheduledFuture curr;
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
curr = this.currentFuture;
|
||||
}
|
||||
return curr.get();
|
||||
}
|
||||
|
||||
public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return this.currentFuture.get(timeout, unit);
|
||||
ScheduledFuture curr;
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
curr = this.currentFuture;
|
||||
}
|
||||
return curr.get(timeout, unit);
|
||||
}
|
||||
|
||||
public long getDelay(TimeUnit unit) {
|
||||
return this.currentFuture.getDelay(unit);
|
||||
ScheduledFuture curr;
|
||||
synchronized (this.triggerContextMonitor) {
|
||||
curr = this.currentFuture;
|
||||
}
|
||||
return curr.getDelay(unit);
|
||||
}
|
||||
|
||||
public int compareTo(Delayed other) {
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* of that class. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
|
||||
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* In Spring 3.1.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* and automatically use the standard method validation support there (once available).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
* as well. By default, JSR-303 will validate against its default group only.
|
||||
*
|
||||
* <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
|
||||
* In Spring 3.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* In Spring 3.1.2, this class will autodetect a Bean Validation 1.1 compliant provider
|
||||
* and automatically use the standard method validation support there (once available).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
|
||||
+7
-1
@@ -35,7 +35,10 @@
|
||||
<xsd:documentation><![CDATA[
|
||||
Specifies the java.util.Executor instance to use when invoking asynchronous methods.
|
||||
If not provided, an instance of org.springframework.core.task.SimpleAsyncTaskExecutor
|
||||
will be used by default
|
||||
will be used by default.
|
||||
Note that as of Spring 3.1.2, individual @Async methods may qualify which executor to
|
||||
use, meaning that the executor specified here acts as a default for all non-qualified
|
||||
@Async methods.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
@@ -98,6 +101,9 @@
|
||||
required even when defining the executor as an inner bean: The executor
|
||||
won't be directly accessible then but will nevertheless use the specified
|
||||
id as the thread name prefix of the threads that it manages.
|
||||
In the case of multiple task:executors, as of Spring 3.1.2 this value may be used to
|
||||
qualify which executor should handle a given @Async method, e.g. @Async("executorId").
|
||||
See the Javadoc for the #value attribute of Spring's @Async annotation for details.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
+8
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -20,7 +20,6 @@ import java.lang.reflect.Proxy;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
@@ -37,10 +36,12 @@ import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.context.support.StaticMessageSource;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @since 09.12.2003
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @since 09.12.2003
|
||||
*/
|
||||
public final class AutoProxyCreatorTests {
|
||||
|
||||
@@ -113,20 +114,18 @@ public final class AutoProxyCreatorTests {
|
||||
ITestBean singletonToBeProxied = (ITestBean) sac.getBean("singletonToBeProxied");
|
||||
assertTrue(Proxy.isProxyClass(singletonToBeProxied.getClass()));
|
||||
|
||||
// 2 invocations coming from FactoryBean inspection during lifecycle startup
|
||||
|
||||
TestInterceptor ti = (TestInterceptor) sac.getBean("testInterceptor");
|
||||
assertEquals(2, ti.nrOfInvocations);
|
||||
int initialNr = ti.nrOfInvocations;
|
||||
singletonToBeProxied.getName();
|
||||
assertEquals(3, ti.nrOfInvocations);
|
||||
assertEquals(initialNr + 1, ti.nrOfInvocations);
|
||||
|
||||
FactoryBean<?> factory = (FactoryBean<?>) sac.getBean("&singletonFactoryToBeProxied");
|
||||
assertTrue(Proxy.isProxyClass(factory.getClass()));
|
||||
TestBean tb = (TestBean) sac.getBean("singletonFactoryToBeProxied");
|
||||
assertFalse(AopUtils.isAopProxy(tb));
|
||||
assertEquals(5, ti.nrOfInvocations);
|
||||
assertEquals(initialNr + 3, ti.nrOfInvocations);
|
||||
tb.getAge();
|
||||
assertEquals(5, ti.nrOfInvocations);
|
||||
assertEquals(initialNr + 3, ti.nrOfInvocations);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+32
-29
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
*
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -41,7 +41,7 @@ import org.springframework.beans.factory.support.MethodReplacer;
|
||||
|
||||
/**
|
||||
* Types used by {@link XmlBeanFactoryTests} and its attendant XML config files.
|
||||
*
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
final class XmlBeanFactoryTestTypes { }
|
||||
@@ -161,20 +161,20 @@ class SimpleConstructorArgBean {
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
abstract class ConstructorInjectedOverrides {
|
||||
|
||||
|
||||
private ITestBean tb;
|
||||
|
||||
|
||||
private String setterString;
|
||||
|
||||
|
||||
public ConstructorInjectedOverrides(ITestBean tb) {
|
||||
this.tb = tb;
|
||||
}
|
||||
|
||||
|
||||
public ITestBean getTestBean() {
|
||||
return this.tb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected abstract FactoryMethods createFactoryMethods();
|
||||
|
||||
/**
|
||||
@@ -234,7 +234,7 @@ class DerivedConstructorDependenciesBean extends ConstructorDependenciesBean {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
interface DummyBo {
|
||||
@@ -245,19 +245,19 @@ interface DummyBo {
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
class DummyBoImpl implements DummyBo {
|
||||
|
||||
|
||||
DummyDao dao;
|
||||
|
||||
public DummyBoImpl(DummyDao dao) {
|
||||
this.dao = dao;
|
||||
}
|
||||
|
||||
|
||||
public void something() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -267,7 +267,7 @@ class DummyBoImpl implements DummyBo {
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
class DummyDao {
|
||||
|
||||
|
||||
DataSource ds;
|
||||
|
||||
public DummyDao(DataSource ds) {
|
||||
@@ -332,7 +332,7 @@ class DummyReferencer {
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
class FactoryMethods {
|
||||
|
||||
|
||||
public static FactoryMethods nullInstance() {
|
||||
return null;
|
||||
}
|
||||
@@ -342,21 +342,21 @@ class FactoryMethods {
|
||||
tb.setName("defaultInstance");
|
||||
return new FactoryMethods(tb, "default", 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note that overloaded methods are supported.
|
||||
*/
|
||||
public static FactoryMethods newInstance(TestBean tb) {
|
||||
return new FactoryMethods(tb, "default", 0);
|
||||
}
|
||||
|
||||
|
||||
protected static FactoryMethods newInstance(TestBean tb, int num, String name) {
|
||||
if (name == null) {
|
||||
throw new IllegalStateException("Should never be called with null value");
|
||||
}
|
||||
return new FactoryMethods(tb, name, num);
|
||||
}
|
||||
|
||||
|
||||
static FactoryMethods newInstance(TestBean tb, int num, Integer something) {
|
||||
if (something != null) {
|
||||
throw new IllegalStateException("Should never be called with non-null value");
|
||||
@@ -384,35 +384,35 @@ class FactoryMethods {
|
||||
this.name = name;
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
|
||||
public void setStringValue(String stringValue) {
|
||||
this.stringValue = stringValue;
|
||||
}
|
||||
|
||||
|
||||
public String getStringValue() {
|
||||
return this.stringValue;
|
||||
}
|
||||
|
||||
|
||||
public TestBean getTestBean() {
|
||||
return this.tb;
|
||||
}
|
||||
|
||||
|
||||
protected TestBean protectedGetTestBean() {
|
||||
return this.tb;
|
||||
}
|
||||
|
||||
|
||||
private TestBean privateGetTestBean() {
|
||||
return this.tb;
|
||||
}
|
||||
|
||||
|
||||
public int getNum() {
|
||||
return num;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set via Setter Injection once instance is created.
|
||||
*/
|
||||
@@ -540,6 +540,9 @@ abstract class OverrideOneMethod extends MethodReplaceCandidate implements Overr
|
||||
return "replaceMe:" + someParam;
|
||||
}
|
||||
|
||||
public String replaceMe(String someParam) {
|
||||
return "replaceMe:" + someParam;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -550,7 +553,7 @@ abstract class OverrideOneMethod extends MethodReplaceCandidate implements Overr
|
||||
* @author Rod Johnson
|
||||
*/
|
||||
abstract class OverrideOneMethodSubclass extends OverrideOneMethod {
|
||||
|
||||
|
||||
protected void doSomething(String arg) {
|
||||
// This implementation does nothing!
|
||||
// It's not overloaded
|
||||
|
||||
+30
-18
@@ -1,22 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<beans>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
Not yet in use: illustration of possible approach
|
||||
-->
|
||||
<bean id="overrideOneMethod" class="org.springframework.beans.factory.xml.OverrideOneMethod">
|
||||
|
||||
|
||||
<lookup-method name="getPrototypeDependency" bean="jenny"/>
|
||||
|
||||
|
||||
<lookup-method name="protectedOverrideSingleton" bean="david"/>
|
||||
|
||||
|
||||
<!-- Arbitrary method replacer -->
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type>String</arg-type>
|
||||
</replaced-method>
|
||||
|
||||
|
||||
<replaced-method name="replaceMe" replacer="fixedReplacer"/>
|
||||
|
||||
</bean>
|
||||
@@ -39,25 +39,25 @@
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="reverseReplacer"
|
||||
<bean id="reverseReplacer"
|
||||
class="org.springframework.beans.factory.xml.ReverseMethodReplacer"/>
|
||||
|
||||
<bean id="fixedReplacer"
|
||||
|
||||
<bean id="fixedReplacer"
|
||||
class="org.springframework.beans.factory.xml.FixedMethodReplacer"/>
|
||||
|
||||
<bean id="doSomethingReplacer"
|
||||
|
||||
<bean id="doSomethingReplacer"
|
||||
class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoSomethingReplacer"/>
|
||||
|
||||
<bean id="serializableReplacer"
|
||||
<bean id="serializableReplacer"
|
||||
class="org.springframework.beans.factory.xml.SerializableMethodReplacerCandidate">
|
||||
|
||||
|
||||
<!-- Arbitrary method replacer -->
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type>String</arg-type>
|
||||
</replaced-method>
|
||||
|
||||
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="jenny" class="org.springframework.beans.TestBean"
|
||||
scope="prototype">
|
||||
<property name="name"><value>Jenny</value></property>
|
||||
@@ -67,7 +67,7 @@
|
||||
<ref local="david"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="david" class="org.springframework.beans.TestBean"
|
||||
scope="singleton">
|
||||
<description>
|
||||
@@ -80,4 +80,16 @@
|
||||
<property name="age"><value>27</value></property>
|
||||
</bean>
|
||||
|
||||
<bean id="overrideOneMethodByAttribute" class="org.springframework.beans.factory.xml.OverrideOneMethod">
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type match="String"/>
|
||||
</replaced-method>
|
||||
</bean>
|
||||
|
||||
<bean id="overrideOneMethodByElement" class="org.springframework.beans.factory.xml.OverrideOneMethod">
|
||||
<replaced-method name="replaceMe" replacer="reverseReplacer">
|
||||
<arg-type>String</arg-type>
|
||||
</replaced-method>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
||||
+22
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -83,10 +83,10 @@ import org.xml.sax.InputSource;
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public final class XmlBeanFactoryTests {
|
||||
|
||||
|
||||
private static final Class<?> CLASS = XmlBeanFactoryTests.class;
|
||||
private static final String CLASSNAME = CLASS.getSimpleName();
|
||||
|
||||
|
||||
private static final ClassPathResource AUTOWIRE_CONTEXT = classPathResource("-autowire.xml");
|
||||
private static final ClassPathResource CHILD_CONTEXT = classPathResource("-child.xml");
|
||||
private static final ClassPathResource CLASS_NOT_FOUND_CONTEXT = classPathResource("-classNotFound.xml");
|
||||
@@ -128,7 +128,7 @@ public final class XmlBeanFactoryTests {
|
||||
private static final ClassPathResource REFTYPES_CONTEXT = classPathResource("-reftypes.xml");
|
||||
private static final ClassPathResource DEFAULT_LAZY_CONTEXT = classPathResource("-defaultLazyInit.xml");
|
||||
private static final ClassPathResource DEFAULT_AUTOWIRE_CONTEXT = classPathResource("-defaultAutowire.xml");
|
||||
|
||||
|
||||
private static ClassPathResource classPathResource(String suffix) {
|
||||
return new ClassPathResource(CLASSNAME + suffix, CLASS);
|
||||
}
|
||||
@@ -1489,6 +1489,24 @@ public final class XmlBeanFactoryTests {
|
||||
}
|
||||
}
|
||||
|
||||
public @Test void testOverrideMethodByArgTypeAttribute() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
|
||||
reader.loadBeanDefinitions(DELEGATION_OVERRIDES_CONTEXT);
|
||||
OverrideOneMethod oom = (OverrideOneMethod) xbf.getBean("overrideOneMethodByAttribute");
|
||||
assertEquals("should not replace", "replaceMe:1", oom.replaceMe(1));
|
||||
assertEquals("should replace", "cba", oom.replaceMe("abc"));
|
||||
}
|
||||
|
||||
public @Test void testOverrideMethodByArgTypeElement() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
|
||||
reader.loadBeanDefinitions(DELEGATION_OVERRIDES_CONTEXT);
|
||||
OverrideOneMethod oom = (OverrideOneMethod) xbf.getBean("overrideOneMethodByElement");
|
||||
assertEquals("should not replace", "replaceMe:1", oom.replaceMe(1));
|
||||
assertEquals("should replace", "cba", oom.replaceMe("abc"));
|
||||
}
|
||||
|
||||
public static class DoSomethingReplacer implements MethodReplacer {
|
||||
|
||||
public Object lastArg;
|
||||
|
||||
+9
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,17 +16,17 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import example.scannable.DefaultNamedComponent;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import example.scannable.DefaultNamedComponent;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Rick Evans
|
||||
@@ -41,64 +41,44 @@ public class AnnotationBeanNameGeneratorTests {
|
||||
|
||||
@Test
|
||||
public void testGenerateBeanNameWithNamedComponent() {
|
||||
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
|
||||
replay(registry);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentWithName.class);
|
||||
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
|
||||
assertNotNull("The generated beanName must *never* be null.", beanName);
|
||||
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
|
||||
assertEquals("walden", beanName);
|
||||
|
||||
verify(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBeanNameWithDefaultNamedComponent() {
|
||||
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
|
||||
replay(registry);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(DefaultNamedComponent.class);
|
||||
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
|
||||
assertNotNull("The generated beanName must *never* be null.", beanName);
|
||||
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
|
||||
assertEquals("thoreau", beanName);
|
||||
|
||||
verify(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBeanNameWithNamedComponentWhereTheNameIsBlank() {
|
||||
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
|
||||
replay(registry);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(ComponentWithBlankName.class);
|
||||
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
|
||||
assertNotNull("The generated beanName must *never* be null.", beanName);
|
||||
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
|
||||
|
||||
String expectedGeneratedBeanName = this.beanNameGenerator.buildDefaultBeanName(bd);
|
||||
|
||||
assertEquals(expectedGeneratedBeanName, beanName);
|
||||
|
||||
verify(registry);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateBeanNameWithAnonymousComponentYieldsGeneratedBeanName() {
|
||||
BeanDefinitionRegistry registry = createMock(BeanDefinitionRegistry.class);
|
||||
replay(registry);
|
||||
|
||||
BeanDefinitionRegistry registry = new SimpleBeanDefinitionRegistry();
|
||||
AnnotatedBeanDefinition bd = new AnnotatedGenericBeanDefinition(AnonymousComponent.class);
|
||||
String beanName = this.beanNameGenerator.generateBeanName(bd, registry);
|
||||
assertNotNull("The generated beanName must *never* be null.", beanName);
|
||||
assertTrue("The generated beanName must *never* be blank.", StringUtils.hasText(beanName));
|
||||
|
||||
String expectedGeneratedBeanName = this.beanNameGenerator.buildDefaultBeanName(bd);
|
||||
|
||||
assertEquals(expectedGeneratedBeanName, beanName);
|
||||
|
||||
verify(registry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+76
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -32,6 +32,7 @@ import org.springframework.beans.TestBean;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
@@ -164,6 +165,72 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
assertTrue(bean.destroy3Called);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceInjectionWithPrototypes() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
|
||||
bpp.setResourceFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ResourceInjectionBean.class, false));
|
||||
bf.registerBeanDefinition("testBean", new RootBeanDefinition(TestBean.class, false));
|
||||
bf.registerBeanDefinition("testBean2", new RootBeanDefinition(TestBean.class, false));
|
||||
|
||||
ResourceInjectionBean bean = (ResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertTrue(bean.initCalled);
|
||||
assertTrue(bean.init2Called);
|
||||
assertTrue(bean.init3Called);
|
||||
|
||||
TestBean tb = bean.getTestBean();
|
||||
TestBean tb2 = bean.getTestBean2();
|
||||
assertNotNull(tb);
|
||||
assertNotNull(tb2);
|
||||
|
||||
ResourceInjectionBean anotherBean = (ResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNotSame(anotherBean, bean);
|
||||
assertNotSame(anotherBean.getTestBean(), tb);
|
||||
assertNotSame(anotherBean.getTestBean2(), tb2);
|
||||
|
||||
bf.destroyBean("annotatedBean", bean);
|
||||
assertTrue(bean.destroyCalled);
|
||||
assertTrue(bean.destroy2Called);
|
||||
assertTrue(bean.destroy3Called);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceInjectionWithResolvableDependencyType() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
CommonAnnotationBeanPostProcessor bpp = new CommonAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(ExtendedResourceInjectionBean.class, false));
|
||||
bf.registerBeanDefinition("testBean4", new RootBeanDefinition(TestBean.class, false));
|
||||
|
||||
bf.registerResolvableDependency(BeanFactory.class, bf);
|
||||
bf.registerResolvableDependency(INestedTestBean.class, new ObjectFactory() {
|
||||
public Object getObject() throws BeansException {
|
||||
return new NestedTestBean();
|
||||
}
|
||||
});
|
||||
|
||||
PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
|
||||
Properties props = new Properties();
|
||||
props.setProperty("tb", "testBean4");
|
||||
ppc.setProperties(props);
|
||||
ppc.postProcessBeanFactory(bf);
|
||||
|
||||
ExtendedResourceInjectionBean bean = (ExtendedResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
INestedTestBean tb = bean.getTestBean6();
|
||||
assertNotNull(tb);
|
||||
|
||||
ExtendedResourceInjectionBean anotherBean = (ExtendedResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNotSame(anotherBean, bean);
|
||||
assertNotSame(anotherBean.getTestBean6(), tb);
|
||||
|
||||
String[] depBeans = bf.getDependenciesForBean("annotatedBean");
|
||||
assertEquals(1, depBeans.length);
|
||||
assertEquals("testBean4", depBeans[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceInjectionWithTwoProcessors() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
@@ -522,6 +589,14 @@ public class CommonAnnotationBeanPostProcessorTests {
|
||||
return testBean4;
|
||||
}
|
||||
|
||||
public INestedTestBean getTestBean5() {
|
||||
return testBean5;
|
||||
}
|
||||
|
||||
public INestedTestBean getTestBean6() {
|
||||
return testBean6;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void init2() {
|
||||
if (this.testBean3 == null || this.testBean4 == null) {
|
||||
|
||||
+61
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package org.springframework.context.annotation.configuration;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor;
|
||||
@@ -32,6 +35,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.ListFactoryBean;
|
||||
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
@@ -46,8 +50,7 @@ import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
|
||||
import test.beans.ITestBean;
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Miscellaneous system tests covering {@link Bean} naming, aliases, scoping and error
|
||||
@@ -65,7 +68,7 @@ public class ConfigurationClassProcessingTests {
|
||||
* When complete, the factory is ready to service requests for any {@link Bean} methods
|
||||
* declared by <var>configClasses</var>.
|
||||
*/
|
||||
private BeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
private ListableBeanFactory initBeanFactory(Class<?>... configClasses) {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
for (Class<?> configClass : configClasses) {
|
||||
String configBeanName = configClass.getName();
|
||||
@@ -126,6 +129,42 @@ public class ConfigurationClassProcessingTests {
|
||||
assertEquals(stringBean, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithObjectReturnType() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
assertEquals(Object.class, factory.getType("stringBean"));
|
||||
assertFalse(factory.isTypeMatch("stringBean", String.class));
|
||||
String stringBean = factory.getBean("stringBean", String.class);
|
||||
assertEquals(stringBean, "foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithFactoryBeanReturnType() {
|
||||
ListableBeanFactory factory = initBeanFactory(ConfigWithNonSpecificReturnTypes.class);
|
||||
assertEquals(List.class, factory.getType("factoryBean"));
|
||||
assertTrue(factory.isTypeMatch("factoryBean", List.class));
|
||||
assertEquals(FactoryBean.class, factory.getType("&factoryBean"));
|
||||
assertTrue(factory.isTypeMatch("&factoryBean", FactoryBean.class));
|
||||
assertFalse(factory.isTypeMatch("&factoryBean", BeanClassLoaderAware.class));
|
||||
assertFalse(factory.isTypeMatch("&factoryBean", ListFactoryBean.class));
|
||||
assertTrue(factory.getBean("factoryBean") instanceof List);
|
||||
|
||||
String[] beanNames = factory.getBeanNamesForType(FactoryBean.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(BeanClassLoaderAware.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(ListFactoryBean.class);
|
||||
assertEquals(1, beanNames.length);
|
||||
assertEquals("&factoryBean", beanNames[0]);
|
||||
|
||||
beanNames = factory.getBeanNamesForType(List.class);
|
||||
assertEquals("factoryBean", beanNames[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationWithPrototypeScopedBeans() {
|
||||
BeanFactory factory = initBeanFactory(ConfigWithPrototypeBean.class);
|
||||
@@ -186,6 +225,19 @@ public class ConfigurationClassProcessingTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithNonSpecificReturnTypes {
|
||||
public @Bean Object stringBean() {
|
||||
return "foo";
|
||||
}
|
||||
public @Bean FactoryBean factoryBean() {
|
||||
ListFactoryBean fb = new ListFactoryBean();
|
||||
fb.setSourceList(Arrays.asList("element1", "element2"));
|
||||
return fb;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class ConfigWithBeanWithAliases {
|
||||
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.support;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests covering the integration of {@link Environment} into {@link ApplicationContext} hierarchies.
|
||||
*
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class EnvironmentIntegrationTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void repro() {
|
||||
ConfigurableApplicationContext parent = new GenericApplicationContext();
|
||||
parent.refresh();
|
||||
|
||||
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
|
||||
child.setParent(parent);
|
||||
child.refresh();
|
||||
|
||||
ConfigurableEnvironment env = child.getBean(ConfigurableEnvironment.class);
|
||||
assertThat("unknown env", env, anyOf(
|
||||
sameInstance(parent.getEnvironment()),
|
||||
sameInstance(child.getEnvironment())));
|
||||
assertThat("expected child ctx env", env, sameInstance(child.getEnvironment()));
|
||||
}
|
||||
|
||||
}
|
||||
+52
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -19,6 +19,7 @@ package org.springframework.context.support;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@@ -46,6 +47,20 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
doTestMessageAccess(false, true, true, true, false);
|
||||
}
|
||||
|
||||
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOff() {
|
||||
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
|
||||
return;
|
||||
}
|
||||
doTestMessageAccess(false, false, false, false, false);
|
||||
}
|
||||
|
||||
public void testMessageAccessWithDefaultMessageSourceAndFallbackTurnedOffAndFallbackToGerman() {
|
||||
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
|
||||
return;
|
||||
}
|
||||
doTestMessageAccess(false, false, true, true, false);
|
||||
}
|
||||
|
||||
public void testMessageAccessWithReloadableMessageSource() {
|
||||
doTestMessageAccess(true, true, false, false, false);
|
||||
}
|
||||
@@ -82,13 +97,13 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
String[] basenames = null;
|
||||
if (reloadable) {
|
||||
basenames = new String[] {
|
||||
"classpath:" + basepath + "messages",
|
||||
"classpath:" + basepath + "more-messages"};
|
||||
"classpath:" + basepath + "messages",
|
||||
"classpath:" + basepath + "more-messages"};
|
||||
}
|
||||
else {
|
||||
basenames = new String[] {
|
||||
basepath + "messages",
|
||||
basepath + "more-messages"};
|
||||
basepath + "messages",
|
||||
basepath + "more-messages"};
|
||||
}
|
||||
pvs.add("basenames", basenames);
|
||||
if (!fallbackToSystemLocale) {
|
||||
@@ -202,6 +217,31 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
|
||||
}
|
||||
|
||||
public void testResourceBundleMessageSourceWithDefaultCharset() {
|
||||
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
|
||||
ms.setBasename("org/springframework/context/support/messages");
|
||||
ms.setDefaultEncoding("ISO-8859-1");
|
||||
assertEquals("message1", ms.getMessage("code1", null, Locale.ENGLISH));
|
||||
assertEquals("nachricht2", ms.getMessage("code2", null, Locale.GERMAN));
|
||||
}
|
||||
|
||||
public void testResourceBundleMessageSourceWithInappropriateDefaultCharset() {
|
||||
if (JdkVersion.getMajorJavaVersion() < JdkVersion.JAVA_16) {
|
||||
return;
|
||||
}
|
||||
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
|
||||
ms.setBasename("org/springframework/context/support/messages");
|
||||
ms.setDefaultEncoding("argh");
|
||||
ms.setFallbackToSystemLocale(false);
|
||||
try {
|
||||
ms.getMessage("code1", null, Locale.ENGLISH);
|
||||
fail("Should have thrown NoSuchMessageException");
|
||||
}
|
||||
catch (NoSuchMessageException ex) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
public void testReloadableResourceBundleMessageSourceStandalone() {
|
||||
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
|
||||
ms.setBasename("org/springframework/context/support/messages");
|
||||
@@ -300,4 +340,11 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
assertEquals(0, filenames.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_16) {
|
||||
ResourceBundle.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+23
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,8 +16,6 @@
|
||||
|
||||
package org.springframework.format.datetime.joda;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@@ -34,6 +32,7 @@ import org.joda.time.MutableDateTime;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
@@ -42,6 +41,8 @@ import org.springframework.format.annotation.DateTimeFormat.ISO;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.validation.DataBinder;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Keith Donald
|
||||
* @author Juergen Hoeller
|
||||
@@ -96,7 +97,7 @@ public class JodaTimeFormattingTests {
|
||||
@Test
|
||||
public void testBindLocalDateArray() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("localDate", new String[] {"10/31/09"});
|
||||
propertyValues.add("localDate", new String[]{"10/31/09"});
|
||||
binder.bind(propertyValues);
|
||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||
}
|
||||
@@ -240,6 +241,24 @@ public class JodaTimeFormattingTests {
|
||||
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("date"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateWithErrorAvoidingDateConstructor() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("date", "Sat, 12 Aug 1995 13:30:00 GMT");
|
||||
binder.bind(propertyValues);
|
||||
assertEquals(1, binder.getBindingResult().getErrorCount());
|
||||
assertEquals("Sat, 12 Aug 1995 13:30:00 GMT", binder.getBindingResult().getFieldValue("date"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateWithoutErrorFallingBackToDateConstructor() {
|
||||
DataBinder binder = new DataBinder(new JodaTimeBean());
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
propertyValues.add("date", "Sat, 12 Aug 1995 13:30:00 GMT");
|
||||
binder.bind(propertyValues);
|
||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBindDateAnnotated() {
|
||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.scheduling.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AnnotationAsyncExecutionInterceptor}.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public class AnnotationAsyncExecutionInterceptorTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unused")
|
||||
public void testGetExecutorQualifier() throws SecurityException, NoSuchMethodException {
|
||||
AnnotationAsyncExecutionInterceptor i = new AnnotationAsyncExecutionInterceptor(null);
|
||||
{ // method level
|
||||
class C { @Async("qMethod") void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMethod"));
|
||||
}
|
||||
{ // class level
|
||||
@Async("qClass") class C { void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qClass"));
|
||||
}
|
||||
{ // method and class level -> method value overrides
|
||||
@Async("qClass") class C { @Async("qMethod") void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMethod"));
|
||||
}
|
||||
{ // method and class level -> method value, even if empty, overrides
|
||||
@Async("qClass") class C { @Async void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is(""));
|
||||
}
|
||||
{ // meta annotation with qualifier
|
||||
@MyAsync class C { void m() { } }
|
||||
assertThat(i.getExecutorQualifier(C.class.getDeclaredMethod("m")), is("qMeta"));
|
||||
}
|
||||
}
|
||||
|
||||
@Async("qMeta")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface MyAsync { }
|
||||
}
|
||||
+63
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,10 +16,10 @@
|
||||
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
|
||||
@@ -27,10 +27,13 @@ import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
*/
|
||||
public class AsyncExecutionTests {
|
||||
|
||||
@@ -56,6 +59,26 @@ public class AsyncExecutionTests {
|
||||
assertEquals("20", future.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncMethodsWithQualifier() throws Exception {
|
||||
originalThreadName = Thread.currentThread().getName();
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.registerBeanDefinition("asyncTest", new RootBeanDefinition(AsyncMethodWithQualifierBean.class));
|
||||
context.registerBeanDefinition("autoProxyCreator", new RootBeanDefinition(DefaultAdvisorAutoProxyCreator.class));
|
||||
context.registerBeanDefinition("asyncAdvisor", new RootBeanDefinition(AsyncAnnotationAdvisor.class));
|
||||
context.registerBeanDefinition("e0", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.registerBeanDefinition("e1", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.registerBeanDefinition("e2", new RootBeanDefinition(ThreadPoolTaskExecutor.class));
|
||||
context.refresh();
|
||||
AsyncMethodWithQualifierBean asyncTest = context.getBean("asyncTest", AsyncMethodWithQualifierBean.class);
|
||||
asyncTest.doNothing(5);
|
||||
asyncTest.doSomething(10);
|
||||
Future<String> future = asyncTest.returnSomething(20);
|
||||
assertEquals("20", future.get());
|
||||
Future<String> future2 = asyncTest.returnSomething2(30);
|
||||
assertEquals("30", future2.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncClass() throws Exception {
|
||||
originalThreadName = Thread.currentThread().getName();
|
||||
@@ -155,7 +178,6 @@ public class AsyncExecutionTests {
|
||||
|
||||
@Async
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -167,11 +189,44 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
|
||||
@Async("e0")
|
||||
public static class AsyncMethodWithQualifierBean {
|
||||
|
||||
public void doNothing(int i) {
|
||||
assertTrue(Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public void doSomething(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e1-"));
|
||||
}
|
||||
|
||||
@MyAsync
|
||||
public Future<String> returnSomething(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e2-"));
|
||||
return new AsyncResult<String>(Integer.toString(i));
|
||||
}
|
||||
|
||||
public Future<String> returnSomething2(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e0-"));
|
||||
return new AsyncResult<String>(Integer.toString(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Async("e2")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MyAsync {
|
||||
}
|
||||
|
||||
|
||||
@Async
|
||||
public static class AsyncClassBean {
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -194,7 +249,6 @@ public class AsyncExecutionTests {
|
||||
public static class AsyncInterfaceBean implements AsyncInterface {
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -224,7 +278,6 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
public void doSomething(int i) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + i);
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
}
|
||||
|
||||
@@ -235,7 +288,7 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
|
||||
public static class AsyncMethodListener implements ApplicationListener {
|
||||
public static class AsyncMethodListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
@Async
|
||||
public void onApplicationEvent(ApplicationEvent event) {
|
||||
@@ -246,7 +299,7 @@ public class AsyncExecutionTests {
|
||||
|
||||
|
||||
@Async
|
||||
public static class AsyncClassListener implements ApplicationListener {
|
||||
public static class AsyncClassListener implements ApplicationListener<ApplicationEvent> {
|
||||
|
||||
public AsyncClassListener() {
|
||||
listenerConstructed++;
|
||||
|
||||
+75
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,22 +16,22 @@
|
||||
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -39,6 +39,11 @@ import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Tests use of @EnableAsync on @Configuration classes.
|
||||
*
|
||||
@@ -69,6 +74,48 @@ public class EnableAsyncTests {
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void withAsyncBeanWithExecutorQualifiedByName() throws ExecutionException, InterruptedException {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(AsyncWithExecutorQualifiedByNameConfig.class);
|
||||
ctx.refresh();
|
||||
|
||||
AsyncBeanWithExecutorQualifiedByName asyncBean = ctx.getBean(AsyncBeanWithExecutorQualifiedByName.class);
|
||||
Future<Thread> workerThread0 = asyncBean.work0();
|
||||
assertThat(workerThread0.get().getName(), not(anyOf(startsWith("e1-"), startsWith("otherExecutor-"))));
|
||||
Future<Thread> workerThread = asyncBean.work();
|
||||
assertThat(workerThread.get().getName(), startsWith("e1-"));
|
||||
Future<Thread> workerThread2 = asyncBean.work2();
|
||||
assertThat(workerThread2.get().getName(), startsWith("otherExecutor-"));
|
||||
Future<Thread> workerThread3 = asyncBean.work3();
|
||||
assertThat(workerThread3.get().getName(), startsWith("otherExecutor-"));
|
||||
}
|
||||
|
||||
|
||||
static class AsyncBeanWithExecutorQualifiedByName {
|
||||
@Async
|
||||
public Future<Thread> work0() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e1")
|
||||
public Future<Thread> work() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("otherExecutor")
|
||||
public Future<Thread> work2() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
|
||||
@Async("e2")
|
||||
public Future<Thread> work3() {
|
||||
return new AsyncResult<Thread>(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static class AsyncBean {
|
||||
private Thread threadOfExecution;
|
||||
|
||||
@@ -206,6 +253,28 @@ public class EnableAsyncTests {
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
static class AsyncWithExecutorQualifiedByNameConfig {
|
||||
@Bean
|
||||
public AsyncBeanWithExecutorQualifiedByName asyncBean() {
|
||||
return new AsyncBeanWithExecutorQualifiedByName();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Executor e1() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
return executor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Qualifier("e2")
|
||||
public Executor otherExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ Bundle-Name: Spring Context
|
||||
Bundle-Vendor: SpringSource
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package:
|
||||
javax.inject;version="[1.0.0, 2.0.0)";resolution:=optional,
|
||||
javax.persistence;version="[1.0.0, 3.0.0)";resolution:=optional,
|
||||
org.springframework.orm.jpa.support;version="${spring.osgi.range.nq}";resolution:=optional,
|
||||
com.ibm.websphere.management;version="0";resolution:=optional
|
||||
@@ -13,7 +14,6 @@ Import-Template:
|
||||
groovy.*;version="[1.5.0, 2.0.0)";resolution:=optional,
|
||||
javax.annotation.*;version="0";resolution:=optional,
|
||||
javax.ejb.*;version="[2.1.0, 4.0.0)";resolution:=optional,
|
||||
javax.inject.*;version="[1.0.0, 2.0.0)";resolution:=optional,
|
||||
javax.interceptor.*;version="[3.0.0, 4.0.0)";resolution:=optional,
|
||||
javax.jms.*;version="[1.1.0, 2.0.0)";resolution:=optional,
|
||||
javax.management.*;version="0";resolution:=optional,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.aspectj/com.springsource.org.aspectj.weaver/1.6.8.RELEASE/com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-1.2.0.jar" sourcepath="/IVY_CACHE/org.custommonkey.xmlunit/com.springsource.org.custommonkey.xmlunit/1.2.0/com.springsource.org.custommonkey.xmlunit-sources-1.2.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-2.5.1.jar" sourcepath="/IVY_CACHE/org.easymock/com.springsource.org.easymock/2.5.1/com.springsource.org.easymock-sources-2.5.1.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-1.1.0.jar" sourcepath="IVY_CACHE/org.hamcrest/com.springsource.org.hamcrest/1.1.0/com.springsource.org.hamcrest-sources-1.1.0.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-3.2.7.jar" sourcepath="/IVY_CACHE/org.codehaus.woodstox/com.springsource.com.ctc.wstx/3.2.7/com.springsource.com.ctc.wstx-sources-3.2.7.jar"/>
|
||||
<classpathentry kind="var" path="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-3.0.0.jar" sourcepath="IVY_CACHE/net.sourceforge.jopt-simple/com.springsource.joptsimple/3.0.0/com.springsource.joptsimple-sources-3.0.0.jar"/>
|
||||
<classpathentry kind="lib" path="/org.springframework.asm/target/artifacts/org.springframework.asm.jar" sourcepath="/org.springframework.asm/target/artifacts/org.springframework.asm-sources.jar"/>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<orderEntry type="library" name="AspectJ" level="project" />
|
||||
<orderEntry type="library" name="Commons Logging" level="project" />
|
||||
<orderEntry type="library" name="EasyMock" level="project" />
|
||||
<orderEntry type="library" name="Hamcrest" level="project" />
|
||||
<orderEntry type="library" name="JUnit" level="project" />
|
||||
<orderEntry type="library" name="Log4j" level="project" />
|
||||
<orderEntry type="module-library">
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<dependency org="net.sourceforge.jopt-simple" name="com.springsource.joptsimple" rev="3.0.0" conf="optional->compile"/>
|
||||
<!-- test dependencies -->
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="${junit.version}" conf="test->runtime"/>
|
||||
<dependency org="org.hamcrest" name="com.springsource.org.hamcrest" rev="1.1.0" conf="test->compile"/>
|
||||
<dependency org="org.easymock" name="com.springsource.org.easymock" rev="2.5.1" conf="test->compile"/>
|
||||
<dependency org="org.custommonkey.xmlunit" name="com.springsource.org.custommonkey.xmlunit" rev="1.2.0" conf="test->compile"/>
|
||||
<dependency org="org.codehaus.woodstox" name="com.springsource.com.ctc.wstx" rev="3.2.7" conf="test->compile"/>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.1.BUILD-SNAPSHOT</version>
|
||||
<version>3.1.3.RELEASE</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+10
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -35,6 +35,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Factory for collections, being aware of Java 5 and Java 6 collections.
|
||||
@@ -253,7 +255,8 @@ public abstract class CollectionFactory {
|
||||
return (Collection) collectionType.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Could not instantiate Collection type: " + collectionType.getName());
|
||||
throw new IllegalArgumentException("Could not instantiate Collection type: " +
|
||||
collectionType.getName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,6 +308,9 @@ public abstract class CollectionFactory {
|
||||
else if (SortedMap.class.equals(mapType) || mapType.equals(navigableMapClass)) {
|
||||
return new TreeMap();
|
||||
}
|
||||
else if (MultiValueMap.class.equals(mapType)) {
|
||||
return new LinkedMultiValueMap();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
|
||||
}
|
||||
@@ -317,7 +323,8 @@ public abstract class CollectionFactory {
|
||||
return (Map) mapType.newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalArgumentException("Could not instantiate Map type: " + mapType.getName());
|
||||
throw new IllegalArgumentException("Could not instantiate Map type: " +
|
||||
mapType.getName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -58,7 +58,7 @@ public class Constants {
|
||||
* @param clazz the class to analyze
|
||||
* @throws IllegalArgumentException if the supplied <code>clazz</code> is <code>null</code>
|
||||
*/
|
||||
public Constants(Class clazz) {
|
||||
public Constants(Class<?> clazz) {
|
||||
Assert.notNull(clazz);
|
||||
this.className = clazz.getName();
|
||||
Field[] fields = clazz.getFields();
|
||||
@@ -189,7 +189,7 @@ public class Constants {
|
||||
* @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
|
||||
* @return the set of constant names
|
||||
*/
|
||||
public Set getNamesForSuffix(String nameSuffix) {
|
||||
public Set<String> getNamesForSuffix(String nameSuffix) {
|
||||
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (String code : this.fieldCache.keySet()) {
|
||||
@@ -264,7 +264,7 @@ public class Constants {
|
||||
* @throws ConstantException if the value wasn't found
|
||||
*/
|
||||
public String toCode(Object value, String namePrefix) throws ConstantException {
|
||||
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : null);
|
||||
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
|
||||
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
|
||||
if (entry.getKey().startsWith(prefixToUse) && entry.getValue().equals(value)) {
|
||||
return entry.getKey();
|
||||
@@ -295,7 +295,7 @@ public class Constants {
|
||||
* @throws ConstantException if the value wasn't found
|
||||
*/
|
||||
public String toCodeForSuffix(Object value, String nameSuffix) throws ConstantException {
|
||||
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : null);
|
||||
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
|
||||
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
|
||||
if (entry.getKey().endsWith(suffixToUse) && entry.getValue().equals(value)) {
|
||||
return entry.getKey();
|
||||
@@ -319,17 +319,17 @@ public class Constants {
|
||||
* @see #toCodeForProperty
|
||||
*/
|
||||
public String propertyToConstantNamePrefix(String propertyName) {
|
||||
StringBuilder parsedPrefix = new StringBuilder();
|
||||
for(int i = 0; i < propertyName.length(); i++) {
|
||||
char c = propertyName.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
parsedPrefix.append("_");
|
||||
parsedPrefix.append(c);
|
||||
}
|
||||
else {
|
||||
parsedPrefix.append(Character.toUpperCase(c));
|
||||
}
|
||||
}
|
||||
StringBuilder parsedPrefix = new StringBuilder();
|
||||
for(int i = 0; i < propertyName.length(); i++) {
|
||||
char c = propertyName.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
parsedPrefix.append("_");
|
||||
parsedPrefix.append(c);
|
||||
}
|
||||
else {
|
||||
parsedPrefix.append(Character.toUpperCase(c));
|
||||
}
|
||||
}
|
||||
return parsedPrefix.toString();
|
||||
}
|
||||
|
||||
|
||||
+28
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -62,18 +64,25 @@ final class MapToMapConverter implements ConditionalGenericConverter {
|
||||
if (!copyRequired && sourceMap.isEmpty()) {
|
||||
return sourceMap;
|
||||
}
|
||||
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
|
||||
List<MapEntry> targetEntries = new ArrayList<MapEntry>(sourceMap.size());
|
||||
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
|
||||
Object sourceKey = entry.getKey();
|
||||
Object sourceValue = entry.getValue();
|
||||
Object targetKey = convertKey(sourceKey, sourceType, targetType.getMapKeyTypeDescriptor());
|
||||
Object targetValue = convertValue(sourceValue, sourceType, targetType.getMapValueTypeDescriptor());
|
||||
targetMap.put(targetKey, targetValue);
|
||||
targetEntries.add(new MapEntry(targetKey, targetValue));
|
||||
if (sourceKey != targetKey || sourceValue != targetValue) {
|
||||
copyRequired = true;
|
||||
}
|
||||
}
|
||||
return (copyRequired ? targetMap : sourceMap);
|
||||
if(!copyRequired) {
|
||||
return sourceMap;
|
||||
}
|
||||
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
|
||||
for (MapEntry entry : targetEntries) {
|
||||
entry.addToMap(targetMap);
|
||||
}
|
||||
return targetMap;
|
||||
}
|
||||
|
||||
// internal helpers
|
||||
@@ -102,4 +111,19 @@ final class MapToMapConverter implements ConditionalGenericConverter {
|
||||
return this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(sourceValue), targetType);
|
||||
}
|
||||
|
||||
private static class MapEntry {
|
||||
|
||||
private Object key;
|
||||
private Object value;
|
||||
|
||||
public MapEntry(Object key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public void addToMap(Map<Object, Object> map) {
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+51
-29
@@ -17,7 +17,6 @@
|
||||
package org.springframework.core.env;
|
||||
|
||||
import java.security.AccessControlException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
@@ -41,9 +40,9 @@ import static org.springframework.util.StringUtils.*;
|
||||
*
|
||||
* <p>Concrete subclasses differ primarily on which {@link PropertySource} objects they
|
||||
* add by default. {@code AbstractEnvironment} adds none. Subclasses should contribute
|
||||
* property sources through the protected {@link #customizePropertySources()} hook, while
|
||||
* clients should customize using {@link ConfigurableEnvironment#getPropertySources()} and
|
||||
* working against the {@link MutablePropertySources} API. See
|
||||
* property sources through the protected {@link #customizePropertySources(MutablePropertySources)}
|
||||
* hook, while clients should customize using {@link ConfigurableEnvironment#getPropertySources()}
|
||||
* and working against the {@link MutablePropertySources} API. See
|
||||
* {@link ConfigurableEnvironment} Javadoc for usage examples.
|
||||
*
|
||||
* @author Chris Beams
|
||||
@@ -87,15 +86,14 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
*/
|
||||
protected static final String RESERVED_DEFAULT_PROFILE_NAME = "default";
|
||||
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private Set<String> activeProfiles = new LinkedHashSet<String>();
|
||||
|
||||
private Set<String> defaultProfiles =
|
||||
new LinkedHashSet<String>(this.getReservedDefaultProfiles());
|
||||
private Set<String> defaultProfiles = new LinkedHashSet<String>(getReservedDefaultProfiles());
|
||||
|
||||
private final MutablePropertySources propertySources =
|
||||
new MutablePropertySources(this.logger);
|
||||
private final MutablePropertySources propertySources = new MutablePropertySources(this.logger);
|
||||
|
||||
private final ConfigurablePropertyResolver propertyResolver =
|
||||
new PropertySourcesPropertyResolver(this.propertySources);
|
||||
@@ -109,13 +107,11 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
* @see #customizePropertySources(MutablePropertySources)
|
||||
*/
|
||||
public AbstractEnvironment() {
|
||||
String name = this.getClass().getSimpleName();
|
||||
String name = getClass().getSimpleName();
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(format("Initializing new %s", name));
|
||||
}
|
||||
|
||||
this.customizePropertySources(this.propertySources);
|
||||
|
||||
customizePropertySources(this.propertySources);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(format(
|
||||
"Initialized %s with PropertySources %s", name, this.propertySources));
|
||||
@@ -244,7 +240,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
Assert.notNull(profiles, "Profile array must not be null");
|
||||
this.activeProfiles.clear();
|
||||
for (String profile : profiles) {
|
||||
this.addActiveProfile(profile);
|
||||
addActiveProfile(profile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +248,7 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(format("Activating profile '%s'", profile));
|
||||
}
|
||||
this.validateProfile(profile);
|
||||
validateProfile(profile);
|
||||
this.activeProfiles.add(profile);
|
||||
}
|
||||
|
||||
@@ -273,10 +269,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
* @see #getReservedDefaultProfiles()
|
||||
*/
|
||||
protected Set<String> doGetDefaultProfiles() {
|
||||
if (this.defaultProfiles.equals(this.getReservedDefaultProfiles())) {
|
||||
if (this.defaultProfiles.equals(getReservedDefaultProfiles())) {
|
||||
String profiles = this.getProperty(DEFAULT_PROFILES_PROPERTY_NAME);
|
||||
if (StringUtils.hasText(profiles)) {
|
||||
this.setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
|
||||
setDefaultProfiles(commaDelimitedListToStringArray(trimAllWhitespace(profiles)));
|
||||
}
|
||||
}
|
||||
return this.defaultProfiles;
|
||||
@@ -293,38 +289,49 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
Assert.notNull(profiles, "Profile array must not be null");
|
||||
this.defaultProfiles.clear();
|
||||
for (String profile : profiles) {
|
||||
this.validateProfile(profile);
|
||||
validateProfile(profile);
|
||||
this.defaultProfiles.add(profile);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean acceptsProfiles(String... profiles) {
|
||||
Assert.notEmpty(profiles, "Must specify at least one profile");
|
||||
boolean activeProfileFound = false;
|
||||
Set<String> activeProfiles = this.doGetActiveProfiles();
|
||||
Set<String> defaultProfiles = this.doGetDefaultProfiles();
|
||||
for (String profile : profiles) {
|
||||
this.validateProfile(profile);
|
||||
if (activeProfiles.contains(profile)
|
||||
|| (activeProfiles.isEmpty() && defaultProfiles.contains(profile))) {
|
||||
activeProfileFound = true;
|
||||
break;
|
||||
if (profile != null && profile.length() > 0 && profile.charAt(0) == '!') {
|
||||
return !isProfileActive(profile.substring(1));
|
||||
}
|
||||
if (isProfileActive(profile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return activeProfileFound;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the given profile is active, or if active profiles are empty
|
||||
* whether the profile should be active by default.
|
||||
* @throws IllegalArgumentException per {@link #validateProfile(String)}
|
||||
*/
|
||||
protected boolean isProfileActive(String profile) {
|
||||
validateProfile(profile);
|
||||
return doGetActiveProfiles().contains(profile) ||
|
||||
(doGetActiveProfiles().isEmpty() && doGetDefaultProfiles().contains(profile));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the given profile, called internally prior to adding to the set of
|
||||
* active or default profiles.
|
||||
* <p>Subclasses may override to impose further restrictions on profile syntax.
|
||||
* @throws IllegalArgumentException if the profile is null, empty or whitespace-only
|
||||
* @throws IllegalArgumentException if the profile is null, empty, whitespace-only or
|
||||
* begins with the profile NOT operator (!).
|
||||
* @see #acceptsProfiles
|
||||
* @see #addActiveProfile
|
||||
* @see #setDefaultProfiles
|
||||
*/
|
||||
protected void validateProfile(String profile) {
|
||||
Assert.hasText(profile, "Invalid profile [" + profile + "]: must contain text");
|
||||
Assert.isTrue(profile.charAt(0) != '!',
|
||||
"Invalid profile [" + profile + "]: must not begin with the ! operator");
|
||||
}
|
||||
|
||||
public MutablePropertySources getPropertySources() {
|
||||
@@ -387,6 +394,23 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
return systemProperties;
|
||||
}
|
||||
|
||||
public void merge(ConfigurableEnvironment parent) {
|
||||
for (PropertySource<?> ps : parent.getPropertySources()) {
|
||||
if (!this.propertySources.contains(ps.getName())) {
|
||||
this.propertySources.addLast(ps);
|
||||
}
|
||||
}
|
||||
for (String profile : parent.getActiveProfiles()) {
|
||||
this.activeProfiles.add(profile);
|
||||
}
|
||||
if (parent.getDefaultProfiles().length > 0) {
|
||||
this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);
|
||||
for (String profile : parent.getDefaultProfiles()) {
|
||||
this.defaultProfiles.add(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// Implementation of ConfigurablePropertyResolver interface
|
||||
@@ -452,12 +476,10 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
this.propertyResolver.setPlaceholderPrefix(placeholderPrefix);
|
||||
}
|
||||
|
||||
|
||||
public void setPlaceholderSuffix(String placeholderSuffix) {
|
||||
this.propertyResolver.setPlaceholderSuffix(placeholderSuffix);
|
||||
}
|
||||
|
||||
|
||||
public void setValueSeparator(String valueSeparator) {
|
||||
this.propertyResolver.setValueSeparator(valueSeparator);
|
||||
}
|
||||
|
||||
Vendored
+29
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -55,13 +55,14 @@ import java.util.Map;
|
||||
* propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);
|
||||
* </pre>
|
||||
*
|
||||
* When an {@link Environment} is being used by an ApplicationContext, it is important
|
||||
* that any such PropertySource manipulations be performed <em>before</em> the context's
|
||||
* {@link org.springframework.context.support.AbstractApplicationContext#refresh()
|
||||
* refresh()} method is called. This ensures that all property sources are available
|
||||
* during the container bootstrap process, including use by
|
||||
* {@linkplain org.springframework.context.support.PropertySourcesPlaceholderConfigurer
|
||||
* property placeholder configurers}.
|
||||
* When an {@link Environment} is being used by an {@code ApplicationContext}, it is
|
||||
* important that any such {@code PropertySource} manipulations be performed
|
||||
* <em>before</em> the context's {@link
|
||||
* org.springframework.context.support.AbstractApplicationContext#refresh() refresh()}
|
||||
* method is called. This ensures that all property sources are available during the
|
||||
* container bootstrap process, including use by {@linkplain
|
||||
* org.springframework.context.support.PropertySourcesPlaceholderConfigurer property
|
||||
* placeholder configurers}.
|
||||
*
|
||||
*
|
||||
* @author Chris Beams
|
||||
@@ -78,7 +79,6 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* <p>Any existing active profiles will be replaced with the given arguments; call
|
||||
* with zero arguments to clear the current set of active profiles. Use
|
||||
* {@link #addActiveProfile} to add a profile while preserving the existing set.
|
||||
*
|
||||
* @see #addActiveProfile
|
||||
* @see #setDefaultProfiles
|
||||
* @see org.springframework.context.annotation.Profile
|
||||
@@ -123,12 +123,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* Return the value of {@link System#getenv()} if allowed by the current
|
||||
* {@link SecurityManager}, otherwise return a map implementation that will attempt
|
||||
* to access individual keys using calls to {@link System#getenv(String)}.
|
||||
*
|
||||
* <p>Note that most {@link Environment} implementations will include this system
|
||||
* environment map as a default {@link PropertySource} to be searched. Therefore, it
|
||||
* is recommended that this method not be used directly unless bypassing other
|
||||
* property sources is expressly intended.
|
||||
*
|
||||
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
|
||||
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
|
||||
* to a property, {@code null} will be returned and an INFO-level log message will be
|
||||
@@ -140,12 +138,10 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
* Return the value of {@link System#getProperties()} if allowed by the current
|
||||
* {@link SecurityManager}, otherwise return a map implementation that will attempt
|
||||
* to access individual keys using calls to {@link System#getProperty(String)}.
|
||||
*
|
||||
* <p>Note that most {@code Environment} implementations will include this system
|
||||
* properties map as a default {@link PropertySource} to be searched. Therefore, it is
|
||||
* recommended that this method not be used directly unless bypassing other property
|
||||
* sources is expressly intended.
|
||||
*
|
||||
* <p>Calls to {@link Map#get(Object)} on the Map returned will never throw
|
||||
* {@link IllegalAccessException}; in cases where the SecurityManager forbids access
|
||||
* to a property, {@code null} will be returned and an INFO-level log message will be
|
||||
@@ -153,4 +149,24 @@ public interface ConfigurableEnvironment extends Environment, ConfigurableProper
|
||||
*/
|
||||
Map<String, Object> getSystemProperties();
|
||||
|
||||
/**
|
||||
* Append the given parent environment's active profiles, default profiles and
|
||||
* property sources to this (child) environment's respective collections of each.
|
||||
* <p>For any identically-named {@code PropertySource} instance existing in both
|
||||
* parent and child, the child instance is to be preserved and the parent instance
|
||||
* discarded. This has the effect of allowing overriding of property sources by the
|
||||
* child as well as avoiding redundant searches through common property source types,
|
||||
* e.g. system environment and system properties.
|
||||
* <p>Active and default profile names are also filtered for duplicates, to avoid
|
||||
* confusion and redundant storage.
|
||||
* <p>The parent environment remains unmodified in any case. Note that any changes to
|
||||
* the parent environment occurring after the call to {@code merge} will not be
|
||||
* reflected in the child. Therefore, care should be taken to configure parent
|
||||
* property sources and profile information prior to calling {@code merge}.
|
||||
* @param parent the environment to merge with
|
||||
* @since 3.1.2
|
||||
* @see org.springframework.context.support.AbstractApplicationContext#setParent
|
||||
*/
|
||||
void merge(ConfigurableEnvironment parent);
|
||||
|
||||
}
|
||||
|
||||
Vendored
+32
-20
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -79,7 +79,8 @@ public class MutablePropertySources implements PropertySources {
|
||||
}
|
||||
|
||||
public PropertySource<?> get(String name) {
|
||||
return this.propertySourceList.get(this.propertySourceList.indexOf(PropertySource.named(name)));
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
return index == -1 ? null : this.propertySourceList.get(index);
|
||||
}
|
||||
|
||||
public Iterator<PropertySource<?>> iterator() {
|
||||
@@ -90,8 +91,10 @@ public class MutablePropertySources implements PropertySources {
|
||||
* Add the given property source object with highest precedence.
|
||||
*/
|
||||
public void addFirst(PropertySource<?> propertySource) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
|
||||
propertySource.getName()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with highest search precedence",
|
||||
propertySource.getName()));
|
||||
}
|
||||
removeIfPresent(propertySource);
|
||||
this.propertySourceList.addFirst(propertySource);
|
||||
}
|
||||
@@ -100,8 +103,10 @@ public class MutablePropertySources implements PropertySources {
|
||||
* Add the given property source object with lowest precedence.
|
||||
*/
|
||||
public void addLast(PropertySource<?> propertySource) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
|
||||
propertySource.getName()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with lowest search precedence",
|
||||
propertySource.getName()));
|
||||
}
|
||||
removeIfPresent(propertySource);
|
||||
this.propertySourceList.addLast(propertySource);
|
||||
}
|
||||
@@ -111,8 +116,10 @@ public class MutablePropertySources implements PropertySources {
|
||||
* than the named relative property source.
|
||||
*/
|
||||
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
|
||||
propertySource.getName(), relativePropertySourceName));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately higher than [%s]",
|
||||
propertySource.getName(), relativePropertySourceName));
|
||||
}
|
||||
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
|
||||
removeIfPresent(propertySource);
|
||||
int index = assertPresentAndGetIndex(relativePropertySourceName);
|
||||
@@ -124,8 +131,10 @@ public class MutablePropertySources implements PropertySources {
|
||||
* than the named relative property source.
|
||||
*/
|
||||
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
|
||||
propertySource.getName(), relativePropertySourceName));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Adding [%s] PropertySource with search precedence immediately lower than [%s]",
|
||||
propertySource.getName(), relativePropertySourceName));
|
||||
}
|
||||
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
|
||||
removeIfPresent(propertySource);
|
||||
int index = assertPresentAndGetIndex(relativePropertySourceName);
|
||||
@@ -144,12 +153,11 @@ public class MutablePropertySources implements PropertySources {
|
||||
* @param name the name of the property source to find and remove
|
||||
*/
|
||||
public PropertySource<?> remove(String name) {
|
||||
logger.debug(String.format("Removing [%s] PropertySource", name));
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
if (index >= 0) {
|
||||
return this.propertySourceList.remove(index);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Removing [%s] PropertySource", name));
|
||||
}
|
||||
return null;
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
return index == -1 ? null : this.propertySourceList.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,8 +168,10 @@ public class MutablePropertySources implements PropertySources {
|
||||
* @see #contains
|
||||
*/
|
||||
public void replace(String name, PropertySource<?> propertySource) {
|
||||
logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
|
||||
name, propertySource.getName()));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Replacing [%s] PropertySource with [%s]",
|
||||
name, propertySource.getName()));
|
||||
}
|
||||
int index = assertPresentAndGetIndex(name);
|
||||
this.propertySourceList.set(index, propertySource);
|
||||
}
|
||||
@@ -210,11 +220,13 @@ public class MutablePropertySources implements PropertySources {
|
||||
|
||||
/**
|
||||
* Assert that the named property source is present and return its index.
|
||||
* @param name the {@linkplain PropertySource#getName() name of the property source}
|
||||
* to find
|
||||
* @throws IllegalArgumentException if the named property source is not present
|
||||
*/
|
||||
private int assertPresentAndGetIndex(String propertySourceName) {
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(propertySourceName));
|
||||
Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, propertySourceName));
|
||||
private int assertPresentAndGetIndex(String name) {
|
||||
int index = this.propertySourceList.indexOf(PropertySource.named(name));
|
||||
Assert.isTrue(index >= 0, String.format(NON_EXISTENT_PROPERTY_SOURCE_MESSAGE, name));
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2012 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
|
||||
* 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,
|
||||
@@ -72,6 +72,9 @@ public class PropertySourcesPropertyResolver extends AbstractPropertyResolver {
|
||||
Object value;
|
||||
if ((value = propertySource.getProperty(key)) != null) {
|
||||
Class<?> valueType = value.getClass();
|
||||
if (String.class.equals(valueType)) {
|
||||
value = this.resolveRequiredPlaceholders((String) value);
|
||||
}
|
||||
if (debugEnabled) {
|
||||
logger.debug(
|
||||
format("Found key '%s' in [%s] with type [%s] and value '%s'",
|
||||
|
||||
+3
-2
@@ -118,7 +118,8 @@ public abstract class AbstractResource implements Resource {
|
||||
try {
|
||||
long size = 0;
|
||||
byte[] buf = new byte[255];
|
||||
for (int read = is.read(buf); read != -1;) {
|
||||
int read;
|
||||
while ((read = is.read(buf)) != -1) {
|
||||
size += read;
|
||||
}
|
||||
return size;
|
||||
@@ -169,7 +170,7 @@ public abstract class AbstractResource implements Resource {
|
||||
* This implementation always returns <code>null</code>,
|
||||
* assuming that this resource type does not have a filename.
|
||||
*/
|
||||
public String getFilename() throws IllegalStateException {
|
||||
public String getFilename() {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+15
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -35,6 +35,7 @@ import org.springframework.util.StringUtils;
|
||||
* Always supports resolution as URL.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 28.12.2003
|
||||
* @see java.lang.ClassLoader#getResourceAsStream(String)
|
||||
* @see java.lang.Class#getResourceAsStream(String)
|
||||
@@ -108,7 +109,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the path for this resource (as resource path within the class path).
|
||||
*/
|
||||
@@ -123,7 +123,6 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation checks for the resolution of a resource URL.
|
||||
* @see java.lang.ClassLoader#getResource(String)
|
||||
@@ -155,8 +154,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
is = this.classLoader.getResourceAsStream(this.path);
|
||||
}
|
||||
if (is == null) {
|
||||
throw new FileNotFoundException(
|
||||
getDescription() + " cannot be opened because it does not exist");
|
||||
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
|
||||
}
|
||||
return is;
|
||||
}
|
||||
@@ -176,8 +174,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
url = this.classLoader.getResource(this.path);
|
||||
}
|
||||
if (url == null) {
|
||||
throw new FileNotFoundException(
|
||||
getDescription() + " cannot be resolved to URL because it does not exist");
|
||||
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -209,17 +206,22 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
public String getDescription() {
|
||||
StringBuilder builder = new StringBuilder("class path resource [");
|
||||
|
||||
if (this.clazz != null) {
|
||||
String pathToUse = path;
|
||||
|
||||
if (this.clazz != null && !pathToUse.startsWith("/")) {
|
||||
builder.append(ClassUtils.classPackageAsResourcePath(this.clazz));
|
||||
builder.append('/');
|
||||
}
|
||||
|
||||
builder.append(this.path);
|
||||
if (pathToUse.startsWith("/")) {
|
||||
pathToUse = pathToUse.substring(1);
|
||||
}
|
||||
|
||||
builder.append(pathToUse);
|
||||
builder.append(']');
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation compares the underlying class path locations.
|
||||
*/
|
||||
@@ -230,9 +232,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
||||
}
|
||||
if (obj instanceof ClassPathResource) {
|
||||
ClassPathResource otherRes = (ClassPathResource) obj;
|
||||
return (this.path.equals(otherRes.path) &&
|
||||
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
|
||||
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
|
||||
return (this.path.equals(otherRes.path)
|
||||
&& ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && ObjectUtils.nullSafeEquals(
|
||||
this.clazz, otherRes.clazz));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -47,6 +47,7 @@ public interface InputStreamSource {
|
||||
* as JavaMail, which needs to be able to read the stream multiple times when
|
||||
* creating mail attachments. For such a use case, it is <i>required</i>
|
||||
* that each <code>getInputStream()</code> call returns a fresh stream.
|
||||
* @return the input stream for the underlying resource (must not be {@code null})
|
||||
* @throws IOException if the stream could not be opened
|
||||
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.core.io;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
|
||||
+8
-10
@@ -80,18 +80,14 @@ abstract class AbstractRecursiveAnnotationVisitor implements AnnotationVisitor {
|
||||
valueToUse = enumConstant.get(null);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logNonFatalException(ex);
|
||||
catch (ClassNotFoundException ex) {
|
||||
this.logger.debug("Failed to classload enum type while reading annotation metadata", ex);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
this.logger.warn("Could not access enum value while reading annotation metadata", ex);
|
||||
}
|
||||
this.attributes.put(attributeName, valueToUse);
|
||||
}
|
||||
|
||||
|
||||
protected void logNonFatalException(Exception ex) {
|
||||
this.logger.warn("Failed to classload type while reading annotation metadata. " +
|
||||
"This is a non-fatal error, but certain annotation metadata may be " +
|
||||
"unavailable.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -168,7 +164,9 @@ class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVi
|
||||
this.doVisitEnd(annotationClass);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
logNonFatalException(ex);
|
||||
this.logger.debug("Failed to classload type while reading annotation " +
|
||||
"metadata. This is a non-fatal error, but certain annotation " +
|
||||
"metadata may be unavailable.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.core.type.classreading;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -38,15 +39,19 @@ import org.springframework.core.type.ClassMetadata;
|
||||
final class SimpleMetadataReader implements MetadataReader {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
private final ClassMetadata classMetadata;
|
||||
|
||||
private final AnnotationMetadata annotationMetadata;
|
||||
|
||||
|
||||
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
|
||||
InputStream is = resource.getInputStream();
|
||||
InputStream is = new BufferedInputStream(resource.getInputStream());
|
||||
ClassReader classReader = null;
|
||||
try {
|
||||
classReader = new ClassReader(is);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
is.close();
|
||||
}
|
||||
|
||||
@@ -70,4 +75,5 @@ final class SimpleMetadataReader implements MetadataReader {
|
||||
public AnnotationMetadata getAnnotationMetadata() {
|
||||
return this.annotationMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user