mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
207 Commits
v3.1.2.RELEASE
...
3.1.x
| Author | SHA1 | Date | |
|---|---|---|---|
| 7a7df66374 | |||
| 63bff1f068 | |||
| e7d2ac4427 | |||
| ae1b36dc4f | |||
| 75a84b50e3 | |||
| 72013f975c | |||
| 3a6e7b8020 | |||
| 8a93309c22 | |||
| 50ed863279 | |||
| f9bb4b3b80 | |||
| 01f31027d7 | |||
| a52396b342 | |||
| d547570bcd | |||
| a9290d80c4 | |||
| 1f52f09089 | |||
| e9616b7204 | |||
| bbd5fc6f35 | |||
| a420e84591 | |||
| b1267b95b9 | |||
| a3161632dd | |||
| e731746bb0 | |||
| f2d7d663b4 | |||
| acd268e48f | |||
| 9d77afc218 | |||
| f3d0beb955 | |||
| 245c61e278 | |||
| c374389a59 | |||
| b720804c6d | |||
| 1e888706d3 | |||
| 1bca2c01fd | |||
| 0c45f179b3 | |||
| eb492ed583 | |||
| cd8d72e3f8 | |||
| 8cf61c595e | |||
| 8422da4d3f | |||
| b93d266c75 | |||
| 57bf64364f | |||
| ebbcc4d9fb | |||
| 27f0142d9e | |||
| 8135442336 | |||
| aa388bc094 | |||
| e5783b1bfa | |||
| 7f7fbe8172 | |||
| 42ab04a299 | |||
| 5ae706ee10 | |||
| f449b5a5b3 | |||
| bdc8d798e2 | |||
| 30ad07a4e2 | |||
| 0ed1a21073 | |||
| bad2b900e9 | |||
| 9a04191c2b | |||
| a7889c9df4 | |||
| 1cd7600d8a | |||
| 418c2a8641 | |||
| 377f3eecb4 | |||
| d1efd4c3a1 | |||
| aba4be83a7 | |||
| 5bc58773fb | |||
| 39fe8eebb1 | |||
| 778693f9f8 | |||
| aa3e0be1a0 | |||
| 28c7760c79 | |||
| cc11b811ea | |||
| 8abe1f055e | |||
| 12c4f8e7f9 | |||
| b9ff29ec7e | |||
| a551cbcf9d | |||
| c207882fdc | |||
| ab21bd4266 | |||
| 2ec41af799 | |||
| eb24365556 | |||
| 3a31efaf31 | |||
| 751c429897 | |||
| f19bc572da | |||
| 81348516ff | |||
| a00e00bd47 | |||
| b0fce78abf | |||
| 90fbeb1643 | |||
| 7384c3d82f | |||
| b016c8db91 | |||
| ceb8ce0a76 | |||
| d34c392c53 | |||
| 2b4e25b6f3 | |||
| 9929810610 | |||
| 4058f693e5 | |||
| 959ab7a5db | |||
| bed2134736 | |||
| dd68fecbf2 | |||
| 667438ddb0 | |||
| f4532ad704 | |||
| 0deacb9fa1 | |||
| 0ee12563b0 | |||
| e0b29b708c | |||
| 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 |
@@ -3,6 +3,88 @@ SPRING FRAMEWORK CHANGELOG
|
||||
http://www.springsource.org
|
||||
|
||||
|
||||
Changes in version 3.1.4 (2013-01-23)
|
||||
-------------------------------------
|
||||
|
||||
* overhauled non-void JavaBean write method support (SPR-10029)
|
||||
* SpEL support for static finals on interfaces (SPR-10125)
|
||||
* BridgeMethodResolver properly handles bridge methods in interfaces (SPR-9330)
|
||||
* LocalVariableTableParameterNameDiscoverer works for bridge methods as well (SPR-9429)
|
||||
* CachedIntrospectionResults uses full WeakReference for any non-safe ClassLoader arrangement (SPR-10028)
|
||||
* CachedIntrospectionResults.clearClassLoader(null) removes cached classes for the system class loader (SPR-9189)
|
||||
* DefaultListableBeanFactory avoids wide/interleaved metadata locks to avoid deadlock potential (SPR-10020)
|
||||
* fixed CGLIB proxy class leaks through further equals/hashCode implementations in Spring AOP pointcuts (SPR-8008)
|
||||
* ConfigurationClassPostProcessor consistently uses ClassLoader, not loading core JDK classes via ASM (SPR-10058)
|
||||
* MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136)
|
||||
* SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013)
|
||||
* added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428)
|
||||
* MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451)
|
||||
* AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955)
|
||||
* reintroduced "mode" and "proxy-target-class" attributes in spring-task-3.1.xsd (SPR-10177)
|
||||
* improved defensiveness of JDBC 3.0 ParameterMetaData calls for parameter type determination (SPR-10084)
|
||||
* JpaTransactionManager etc finds default EntityManagerFactory in parent context as well (SPR-10160)
|
||||
* tx timeouts for JPA translate to "javax.persistence.query.timeout" only (for EclipseLink compatibility; SPR-10068)
|
||||
* DefaultMessageListenerContainer allows for concurrent subscription consumers on WebLogic/ActiveMQ (SPR-10037)
|
||||
* DefaultMessageListenerContainer clears resources of paused tasks when shutting down after stop (SPR-10092)
|
||||
* JmsTemplate uses configured receiveTimeout if shorter than remaining transaction timeout (SPR-10109)
|
||||
* added MappingJackson2MessageConverter for JMS (SPR-10099)
|
||||
* ResourceDatabasePopulator explicitly closes its LineNumberReader (SPR-9912)
|
||||
* MimeMessageHelper encodes attachment filename if not ASCII compliant (SPR-9258)
|
||||
* FreeMarkerConfigurationFactory properly supports TemplateLoaders when recreating Configurations (SPR-9389)
|
||||
* SpringContextResourceAdapter implements equals/hashCode according to the JCA 1.5 contract (SPR-9162)
|
||||
* ContextLoader properly detects pre-refreshed WebApplicationContext (SPR-9996)
|
||||
* AbstractCachingViewResolver uses a cache limit of 1024 by default, avoiding overflow for redirect URLs (SPR-10065)
|
||||
* fixed HierarchicalUriComponents equals implementation (SPR-10088)
|
||||
* MockHttpServletRequest's getParameter(Values) returns null for null parameter name (SPR-10192)
|
||||
* MockHttpServletResponse's getHeaderNames declares Collection instead of Set for Servlet 3.0 compatibility (SPR-9885)
|
||||
* ResourcePropertySource supports JDK 5-style Properties XML (SPR-9896)
|
||||
|
||||
|
||||
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)
|
||||
-------------------------------------
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SPRING FRAMEWORK 3.1.2 (July 2012)
|
||||
----------------------------------
|
||||
SPRING FRAMEWORK 3.1.4 (January 2013)
|
||||
-------------------------------------
|
||||
http://www.springsource.org
|
||||
|
||||
1. INTRODUCTION
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
version=3.1.2
|
||||
version=3.1.5
|
||||
|
||||
natural.name=spring-framework
|
||||
project.name=Spring Framework
|
||||
@@ -17,16 +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.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+18
-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.
|
||||
@@ -681,6 +681,23 @@ public abstract class AbstractAspectJAdvice implements Advice, AspectJPrecedence
|
||||
public boolean matches(Method method, Class targetClass) {
|
||||
return !this.adviceMethod.equals(method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AdviceExcludingMethodMatcher)) {
|
||||
return false;
|
||||
}
|
||||
AdviceExcludingMethodMatcher otherMm = (AdviceExcludingMethodMatcher) other;
|
||||
return this.adviceMethod.equals(otherMm.adviceMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.adviceMethod.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-7
@@ -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.
|
||||
@@ -271,7 +271,7 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
|
||||
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
|
||||
Object cacheKey = getCacheKey(beanClass, beanName);
|
||||
|
||||
if (!this.targetSourcedBeans.contains(cacheKey)) {
|
||||
if (!this.targetSourcedBeans.contains(beanName)) {
|
||||
if (this.advisedBeans.contains(cacheKey) || this.nonAdvisedBeans.contains(cacheKey)) {
|
||||
return null;
|
||||
}
|
||||
@@ -371,17 +371,18 @@ public abstract class AbstractAutoProxyCreator extends ProxyConfig
|
||||
/**
|
||||
* Return whether the given bean class represents an infrastructure class
|
||||
* that should never be proxied.
|
||||
* <p>Default implementation considers Advisors, Advices and
|
||||
* AbstractAutoProxyCreators as infrastructure classes.
|
||||
* <p>The default implementation considers Advices, Advisors and
|
||||
* AopInfrastructureBeans as infrastructure classes.
|
||||
* @param beanClass the class of the bean
|
||||
* @return whether the bean represents an infrastructure class
|
||||
* @see org.aopalliance.aop.Advice
|
||||
* @see org.springframework.aop.Advisor
|
||||
* @see org.aopalliance.intercept.MethodInterceptor
|
||||
* @see org.springframework.aop.framework.AopInfrastructureBean
|
||||
* @see #shouldSkip
|
||||
*/
|
||||
protected boolean isInfrastructureClass(Class<?> beanClass) {
|
||||
boolean retVal = Advisor.class.isAssignableFrom(beanClass) ||
|
||||
Advice.class.isAssignableFrom(beanClass) ||
|
||||
boolean retVal = Advice.class.isAssignableFrom(beanClass) ||
|
||||
Advisor.class.isAssignableFrom(beanClass) ||
|
||||
AopInfrastructureBean.class.isAssignableFrom(beanClass);
|
||||
if (retVal && logger.isTraceEnabled()) {
|
||||
logger.trace("Did not attempt to auto-proxy infrastructure class [" + beanClass.getName() + "]");
|
||||
|
||||
+26
-29
@@ -17,7 +17,6 @@
|
||||
package org.springframework.aop.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -85,6 +84,32 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
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.
|
||||
@@ -97,32 +122,4 @@ public abstract class AsyncExecutionAspectSupport implements BeanFactoryAware {
|
||||
*/
|
||||
protected abstract String getExecutorQualifier(Method method);
|
||||
|
||||
/**
|
||||
* 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 instanceof Executor) {
|
||||
this.executors.put(method, new TaskExecutorAdapter(executor));
|
||||
}
|
||||
}
|
||||
|
||||
return this.executors.get(method);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-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.
|
||||
@@ -56,4 +56,21 @@ public class AnnotationMethodMatcher extends StaticMethodMatcher {
|
||||
return (specificMethod != method && specificMethod.isAnnotationPresent(this.annotationType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AnnotationMethodMatcher)) {
|
||||
return false;
|
||||
}
|
||||
AnnotationMethodMatcher otherMm = (AnnotationMethodMatcher) other;
|
||||
return this.annotationType.equals(otherMm.annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.annotationType.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-asm</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aspects</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
package org.springframework.context.annotation.aspectj;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
package org.springframework.context.annotation.aspectj;
|
||||
|
||||
import org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
+5
-1
@@ -14,10 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
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
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+20
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +22,7 @@ import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -32,7 +33,6 @@ import java.util.WeakHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -100,9 +100,6 @@ public class CachedIntrospectionResults {
|
||||
* @param classLoader the ClassLoader to clear the cache for
|
||||
*/
|
||||
public static void clearClassLoader(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (classCache) {
|
||||
for (Iterator<Class> it = classCache.keySet().iterator(); it.hasNext();) {
|
||||
Class beanClass = it.next();
|
||||
@@ -140,20 +137,16 @@ public class CachedIntrospectionResults {
|
||||
results = (CachedIntrospectionResults) value;
|
||||
}
|
||||
if (results == null) {
|
||||
// On JDK 1.5 and higher, it is almost always safe to cache the bean class...
|
||||
// The sole exception is a custom BeanInfo class being provided in a non-safe ClassLoader.
|
||||
boolean fullyCacheable =
|
||||
ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
|
||||
isClassLoaderAccepted(beanClass.getClassLoader());
|
||||
if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
|
||||
results = new CachedIntrospectionResults(beanClass, fullyCacheable);
|
||||
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
|
||||
isClassLoaderAccepted(beanClass.getClassLoader())) {
|
||||
results = new CachedIntrospectionResults(beanClass);
|
||||
classCache.put(beanClass, results);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
|
||||
}
|
||||
results = new CachedIntrospectionResults(beanClass, true);
|
||||
results = new CachedIntrospectionResults(beanClass);
|
||||
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
|
||||
}
|
||||
}
|
||||
@@ -187,12 +180,12 @@ public class CachedIntrospectionResults {
|
||||
* @param parent the parent ClassLoader to check for
|
||||
*/
|
||||
private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) {
|
||||
if (candidate == null) {
|
||||
return false;
|
||||
}
|
||||
if (candidate == parent) {
|
||||
return true;
|
||||
}
|
||||
if (candidate == null) {
|
||||
return false;
|
||||
}
|
||||
ClassLoader classLoaderToCheck = candidate;
|
||||
while (classLoaderToCheck != null) {
|
||||
classLoaderToCheck = classLoaderToCheck.getParent();
|
||||
@@ -216,12 +209,20 @@ public class CachedIntrospectionResults {
|
||||
* @param beanClass the bean class to analyze
|
||||
* @throws BeansException in case of introspection failure
|
||||
*/
|
||||
private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) throws BeansException {
|
||||
private CachedIntrospectionResults(Class beanClass) throws BeansException {
|
||||
try {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
|
||||
}
|
||||
this.beanInfo = new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass));
|
||||
BeanInfo originalBeanInfo = Introspector.getBeanInfo(beanClass);
|
||||
BeanInfo extendedBeanInfo = null;
|
||||
for (Method method : beanClass.getMethods()) {
|
||||
if (ExtendedBeanInfo.isCandidateWriteMethod(method)) {
|
||||
extendedBeanInfo = new ExtendedBeanInfo(originalBeanInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.beanInfo = extendedBeanInfo != null ? extendedBeanInfo : originalBeanInfo;
|
||||
|
||||
// Immediately remove class from Introspector cache, to allow for proper
|
||||
// garbage collection on class loader shutdown - we cache it here anyway,
|
||||
@@ -252,9 +253,7 @@ public class CachedIntrospectionResults {
|
||||
(pd.getPropertyEditorClass() != null ?
|
||||
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
|
||||
}
|
||||
if (cacheFullMetadata) {
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
}
|
||||
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
|
||||
this.propertyDescriptorCache.put(pd.getName(), pd);
|
||||
}
|
||||
}
|
||||
|
||||
+574
-272
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
import java.awt.Image;
|
||||
|
||||
import java.beans.BeanDescriptor;
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.EventSetDescriptor;
|
||||
@@ -27,286 +26,202 @@ import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.MethodDescriptor;
|
||||
import java.beans.PropertyDescriptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.SortedSet;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import static org.springframework.beans.PropertyDescriptorUtils.*;
|
||||
|
||||
/**
|
||||
* Decorates a standard {@link BeanInfo} object (likely created created by
|
||||
* {@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">
|
||||
* indexed properties</a> are fully supported.
|
||||
* Decorator for a standard {@link BeanInfo} object, e.g. as created by
|
||||
* {@link Introspector#getBeanInfo(Class)}, designed to discover and register static
|
||||
* and/or non-void returning setter methods. For example:
|
||||
* <pre>{@code
|
||||
* public class Bean {
|
||||
* private Foo foo;
|
||||
*
|
||||
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
|
||||
* public Foo getFoo() {
|
||||
* return this.foo;
|
||||
* }
|
||||
*
|
||||
* public Bean setFoo(Foo foo) {
|
||||
* this.foo = foo;
|
||||
* return this;
|
||||
* }
|
||||
* }</pre>
|
||||
* The standard JavaBeans {@code Introspector} will discover the {@code getFoo} read
|
||||
* method, but will bypass the {@code #setFoo(Foo)} write method, because its non-void
|
||||
* returning signature does not comply with the JavaBeans specification.
|
||||
* {@code ExtendedBeanInfo}, on the other hand, will recognize and include it. This is
|
||||
* designed to allow APIs with "builder" or method-chaining style setter signatures to be
|
||||
* used within Spring {@code <beans>} XML. {@link #getPropertyDescriptors()} returns all
|
||||
* existing property descriptors from the wrapped {@code BeanInfo} as well any added for
|
||||
* non-void returning setters. Both standard ("non-indexed") and
|
||||
* <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">
|
||||
* indexed properties</a> are fully supported.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
* @see CachedIntrospectionResults
|
||||
*/
|
||||
class ExtendedBeanInfo implements BeanInfo {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final BeanInfo delegate;
|
||||
|
||||
private final SortedSet<PropertyDescriptor> propertyDescriptors =
|
||||
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
|
||||
private final Set<PropertyDescriptor> propertyDescriptors =
|
||||
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
|
||||
|
||||
|
||||
/**
|
||||
* Wrap the given delegate {@link BeanInfo} instance and find any non-void returning
|
||||
* setter methods, creating and adding a {@link PropertyDescriptor} for each.
|
||||
*
|
||||
* <p>Note that the wrapped {@code BeanInfo} is modified by this process.
|
||||
*
|
||||
* Wrap the given {@link BeanInfo} instance; copy all its existing property descriptors
|
||||
* locally, wrapping each in a custom {@link SimpleIndexedPropertyDescriptor indexed} or
|
||||
* {@link SimpleNonIndexedPropertyDescriptor non-indexed} {@code PropertyDescriptor}
|
||||
* variant that bypasses default JDK weak/soft reference management; then search
|
||||
* through its method descriptors to find any non-void returning write methods and
|
||||
* update or create the corresponding {@link PropertyDescriptor} for each one found.
|
||||
* @param delegate the wrapped {@code BeanInfo}, which is never modified
|
||||
* @throws IntrospectionException if any problems occur creating and adding new
|
||||
* property descriptors
|
||||
* @see #getPropertyDescriptors()
|
||||
* @throws IntrospectionException if any problems occur creating and adding new {@code PropertyDescriptors}
|
||||
*/
|
||||
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
|
||||
this.delegate = delegate;
|
||||
|
||||
ALL_METHODS:
|
||||
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
|
||||
Method method = md.getMethod();
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
this.propertyDescriptors.add(pd instanceof IndexedPropertyDescriptor ?
|
||||
new SimpleIndexedPropertyDescriptor((IndexedPropertyDescriptor) pd) :
|
||||
new SimpleNonIndexedPropertyDescriptor(pd));
|
||||
}
|
||||
|
||||
// bypass non-getter java.lang.Class methods for efficiency
|
||||
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// is the method a NON-INDEXED setter? ignore return type in order to capture non-void signatures
|
||||
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
|
||||
String propertyName = propertyNameFor(method);
|
||||
if(propertyName.length() == 0) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
Method readMethod = pd.getReadMethod();
|
||||
Method writeMethod = pd.getWriteMethod();
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (writeMethod != null
|
||||
&& writeMethod.getName().equals(method.getName())) {
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
|
||||
if (readMethod != null
|
||||
&& readMethod.getName().equals(getterMethodNameFor(propertyName))
|
||||
&& readMethod.getReturnType().equals(method.getParameterTypes()[0])) {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
// the setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor for it
|
||||
// no corresponding getter was detected, so the 'read method' parameter is null.
|
||||
this.addOrUpdatePropertyDescriptor(null, propertyName, null, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// is the method an INDEXED setter? ignore return type in order to capture non-void signatures
|
||||
if (method.getName().startsWith("set") && method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(int.class)) {
|
||||
String propertyName = propertyNameFor(method);
|
||||
if(propertyName.length() == 0) {
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
DELEGATE_PD:
|
||||
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
continue DELEGATE_PD;
|
||||
}
|
||||
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||
Method readMethod = ipd.getReadMethod();
|
||||
Method writeMethod = ipd.getWriteMethod();
|
||||
Method indexedReadMethod = ipd.getIndexedReadMethod();
|
||||
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
|
||||
// has the setter already been found by the wrapped BeanInfo?
|
||||
if (!(indexedWriteMethod != null
|
||||
&& indexedWriteMethod.getName().equals(method.getName()))) {
|
||||
indexedWriteMethod = method;
|
||||
}
|
||||
// yes -> copy it, including corresponding getter method (if any -- may be null)
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
// the INDEXED setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor
|
||||
// for it. no corresponding INDEXED getter was detected, so the 'indexed read method' parameter is null.
|
||||
this.addOrUpdatePropertyDescriptor(null, propertyName, null, null, null, method);
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
|
||||
// the method is not a setter, but is it a getter?
|
||||
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 mostSpecificReadMethod = ClassUtils.getMostSpecificMethod(readMethod, method.getDeclaringClass());
|
||||
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
&& existingPD.getName().equals(propertyName)) {
|
||||
if (existingPD.getReadMethod() == null) {
|
||||
// no -> add it now
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, method, pd.getWriteMethod());
|
||||
}
|
||||
// yes -> do not add a duplicate
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
if (method.equals(mostSpecificReadMethod)
|
||||
|| (pd instanceof IndexedPropertyDescriptor && method.equals(((IndexedPropertyDescriptor) pd).getIndexedReadMethod()))) {
|
||||
// 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());
|
||||
} else {
|
||||
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, pd.getWriteMethod());
|
||||
}
|
||||
continue ALL_METHODS;
|
||||
}
|
||||
}
|
||||
for (Method method : findCandidateWriteMethods(delegate.getMethodDescriptors())) {
|
||||
handleCandidateWriteMethod(method);
|
||||
}
|
||||
}
|
||||
|
||||
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||
addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, null, null);
|
||||
}
|
||||
|
||||
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
|
||||
Assert.notNull(propertyName, "propertyName may not be null");
|
||||
propertyName = pd == null ? propertyName : pd.getName();
|
||||
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]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's read method
|
||||
if (readMethod != null) {
|
||||
try {
|
||||
existingPD.setReadMethod(readMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// there is a conflicting setter method present -> null it out and try again
|
||||
existingPD.setWriteMethod(null);
|
||||
existingPD.setReadMethod(readMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// 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]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's write method
|
||||
if (writeMethod != null
|
||||
&& !(existingPD instanceof IndexedPropertyDescriptor &&
|
||||
!writeMethod.getParameterTypes()[0].isArray())) {
|
||||
existingPD.setWriteMethod(writeMethod);
|
||||
}
|
||||
|
||||
// is this descriptor indexed?
|
||||
if (existingPD instanceof IndexedPropertyDescriptor) {
|
||||
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]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's indexed read method
|
||||
try {
|
||||
if (indexedReadMethod != null) {
|
||||
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||
}
|
||||
} catch (IntrospectionException ex) {
|
||||
// there is a conflicting indexed setter method present -> null it out and try again
|
||||
existingIPD.setIndexedWriteMethod(null);
|
||||
existingIPD.setIndexedReadMethod(indexedReadMethod);
|
||||
}
|
||||
|
||||
// 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]) {
|
||||
// no -> add a new descriptor for it below
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update the existing descriptor's indexed write method
|
||||
if (indexedWriteMethod != null) {
|
||||
existingIPD.setIndexedWriteMethod(indexedWriteMethod);
|
||||
}
|
||||
}
|
||||
|
||||
// the descriptor has been updated -> return immediately
|
||||
return;
|
||||
private List<Method> findCandidateWriteMethods(MethodDescriptor[] methodDescriptors) {
|
||||
List<Method> matches = new ArrayList<Method>();
|
||||
for (MethodDescriptor methodDescriptor : methodDescriptors) {
|
||||
Method method = methodDescriptor.getMethod();
|
||||
if (isCandidateWriteMethod(method)) {
|
||||
matches.add(method);
|
||||
}
|
||||
}
|
||||
// sort non-void returning write methods to guard against the ill effects of
|
||||
// non-deterministic sorting of methods returned from Class#getDeclaredMethods
|
||||
// under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180
|
||||
Collections.sort(matches, new Comparator<Method>() {
|
||||
public int compare(Method m1, Method m2) {
|
||||
return m2.toString().compareTo(m1.toString());
|
||||
}
|
||||
});
|
||||
return matches;
|
||||
}
|
||||
|
||||
// we haven't yet seen read or write methods for this property -> add a new descriptor
|
||||
if (pd == null) {
|
||||
try {
|
||||
if (indexedReadMethod == null && indexedWriteMethod == null) {
|
||||
pd = new PropertyDescriptor(propertyName, readMethod, writeMethod);
|
||||
}
|
||||
else {
|
||||
pd = new IndexedPropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not create new PropertyDescriptor for readMethod [%s] writeMethod [%s] " +
|
||||
"indexedReadMethod [%s] indexedWriteMethod [%s] for property [%s]. Reason: %s",
|
||||
readMethod, writeMethod, indexedReadMethod, indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// suppress exception and attempt to continue
|
||||
public static boolean isCandidateWriteMethod(Method method) {
|
||||
String methodName = method.getName();
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
int nParams = parameterTypes.length;
|
||||
if (methodName.length() > 3 && methodName.startsWith("set") &&
|
||||
Modifier.isPublic(method.getModifiers()) &&
|
||||
(
|
||||
!void.class.isAssignableFrom(method.getReturnType()) ||
|
||||
Modifier.isStatic(method.getModifiers())
|
||||
) &&
|
||||
(nParams == 1 || (nParams == 2 && parameterTypes[0].equals(int.class)))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleCandidateWriteMethod(Method method) throws IntrospectionException {
|
||||
int nParams = method.getParameterTypes().length;
|
||||
String propertyName = propertyNameFor(method);
|
||||
Class<?> propertyType = method.getParameterTypes()[nParams-1];
|
||||
PropertyDescriptor existingPD = findExistingPropertyDescriptor(propertyName, propertyType);
|
||||
if (nParams == 1) {
|
||||
if (existingPD == null) {
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleNonIndexedPropertyDescriptor(propertyName, null, method));
|
||||
}
|
||||
else {
|
||||
existingPD.setWriteMethod(method);
|
||||
}
|
||||
}
|
||||
else if (nParams == 2) {
|
||||
if (existingPD == null) {
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleIndexedPropertyDescriptor(
|
||||
propertyName, null, null, null, method));
|
||||
}
|
||||
else if (existingPD instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)existingPD).setIndexedWriteMethod(method);
|
||||
}
|
||||
else {
|
||||
this.propertyDescriptors.remove(existingPD);
|
||||
this.propertyDescriptors.add(
|
||||
new SimpleIndexedPropertyDescriptor(
|
||||
propertyName, existingPD.getReadMethod(),
|
||||
existingPD.getWriteMethod(), null, method));
|
||||
}
|
||||
}
|
||||
else {
|
||||
pd.setReadMethod(readMethod);
|
||||
try {
|
||||
pd.setWriteMethod(writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not add write method [%s] for property [%s]. Reason: %s",
|
||||
writeMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedReadMethod(indexedReadMethod);
|
||||
try {
|
||||
((IndexedPropertyDescriptor)pd).setIndexedWriteMethod(indexedWriteMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
logger.debug(format("Could not add indexed write method [%s] for property [%s]. Reason: %s",
|
||||
indexedWriteMethod, propertyName, ex.getMessage()));
|
||||
// fall through -> add property descriptor as best we can
|
||||
}
|
||||
}
|
||||
this.propertyDescriptors.add(pd);
|
||||
throw new IllegalArgumentException(
|
||||
"write method must have exactly 1 or 2 parameters: " + method);
|
||||
}
|
||||
}
|
||||
|
||||
private String propertyNameFor(Method method) {
|
||||
return Introspector.decapitalize(method.getName().substring(3,method.getName().length()));
|
||||
private PropertyDescriptor findExistingPropertyDescriptor(
|
||||
String propertyName, Class<?> propertyType) {
|
||||
|
||||
for (PropertyDescriptor pd : this.propertyDescriptors) {
|
||||
final Class<?> candidateType;
|
||||
final String candidateName = pd.getName();
|
||||
if (pd instanceof IndexedPropertyDescriptor) {
|
||||
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
|
||||
candidateType = ipd.getIndexedPropertyType();
|
||||
if (candidateName.equals(propertyName) &&
|
||||
(candidateType.equals(propertyType) ||
|
||||
candidateType.equals(propertyType.getComponentType()))) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
else {
|
||||
candidateType = pd.getPropertyType();
|
||||
if (candidateName.equals(propertyName) &&
|
||||
(candidateType.equals(propertyType) ||
|
||||
propertyType.equals(candidateType.getComponentType()))) {
|
||||
return pd;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Object getterMethodNameFor(String name) {
|
||||
return "get" + StringUtils.capitalize(name);
|
||||
private String propertyNameFor(Method method) {
|
||||
return Introspector.decapitalize(
|
||||
method.getName().substring(3, method.getName().length()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
|
||||
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
|
||||
* method found during construction.
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
*/
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.toArray(
|
||||
new PropertyDescriptor[this.propertyDescriptors.size()]);
|
||||
}
|
||||
|
||||
public BeanInfo[] getAdditionalBeanInfo() {
|
||||
@@ -329,44 +244,431 @@ class ExtendedBeanInfo implements BeanInfo {
|
||||
return delegate.getEventSetDescriptors();
|
||||
}
|
||||
|
||||
public Image getIcon(int arg0) {
|
||||
return delegate.getIcon(arg0);
|
||||
public Image getIcon(int iconKind) {
|
||||
return delegate.getIcon(iconKind);
|
||||
}
|
||||
|
||||
public MethodDescriptor[] getMethodDescriptors() {
|
||||
return delegate.getMethodDescriptors();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
|
||||
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
|
||||
* method found during construction.
|
||||
* @see #ExtendedBeanInfo(BeanInfo)
|
||||
*/
|
||||
public PropertyDescriptor[] getPropertyDescriptors() {
|
||||
return this.propertyDescriptors.toArray(new PropertyDescriptor[this.propertyDescriptors.size()]);
|
||||
|
||||
class SimpleNonIndexedPropertyDescriptor extends PropertyDescriptor {
|
||||
|
||||
private Method readMethod;
|
||||
private Method writeMethod;
|
||||
private Class<?> propertyType;
|
||||
private Class<?> propertyEditorClass;
|
||||
|
||||
|
||||
public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
|
||||
throws IntrospectionException {
|
||||
|
||||
this(original.getName(), original.getReadMethod(), original.getWriteMethod());
|
||||
copyNonMethodProperties(original, this);
|
||||
}
|
||||
|
||||
public SimpleNonIndexedPropertyDescriptor(String propertyName,
|
||||
Method readMethod, Method writeMethod) throws IntrospectionException {
|
||||
|
||||
super(propertyName, null, null);
|
||||
this.setReadMethod(readMethod);
|
||||
this.setWriteMethod(writeMethod);
|
||||
this.propertyType = findPropertyType(readMethod, writeMethod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts PropertyDescriptor instances alphanumerically to emulate the behavior of {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
*
|
||||
* @see ExtendedBeanInfo#propertyDescriptors
|
||||
*/
|
||||
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
|
||||
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
|
||||
String left = desc1.getName();
|
||||
String right = desc2.getName();
|
||||
for (int i = 0; i < left.length(); i++) {
|
||||
if (right.length() == i) {
|
||||
return 1;
|
||||
}
|
||||
int result = left.getBytes()[i] - right.getBytes()[i];
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getWriteMethod() {
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (this.propertyType == null) {
|
||||
try {
|
||||
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does PropertyDescriptor#getPropertyType
|
||||
}
|
||||
return left.length() - right.length();
|
||||
}
|
||||
return this.propertyType;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyEditorClass() {
|
||||
return this.propertyEditorClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyEditorClass(Class<?> propertyEditorClass) {
|
||||
this.propertyEditorClass = propertyEditorClass;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return PropertyDescriptorUtils.equals(this, obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[name=%s, propertyType=%s, readMethod=%s, writeMethod=%s]",
|
||||
this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
|
||||
this.readMethod, this.writeMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor {
|
||||
|
||||
private Method readMethod;
|
||||
private Method writeMethod;
|
||||
private Class<?> propertyType;
|
||||
private Class<?> propertyEditorClass;
|
||||
|
||||
private Method indexedReadMethod;
|
||||
private Method indexedWriteMethod;
|
||||
private Class<?> indexedPropertyType;
|
||||
|
||||
|
||||
public SimpleIndexedPropertyDescriptor(IndexedPropertyDescriptor original)
|
||||
throws IntrospectionException {
|
||||
|
||||
this(original.getName(), original.getReadMethod(), original.getWriteMethod(),
|
||||
original.getIndexedReadMethod(), original.getIndexedWriteMethod());
|
||||
copyNonMethodProperties(original, this);
|
||||
}
|
||||
|
||||
public SimpleIndexedPropertyDescriptor(String propertyName,
|
||||
Method readMethod, Method writeMethod,
|
||||
Method indexedReadMethod, Method indexedWriteMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
super(propertyName, null, null, null, null);
|
||||
this.setReadMethod(readMethod);
|
||||
this.setWriteMethod(writeMethod);
|
||||
this.propertyType = findPropertyType(readMethod, writeMethod);
|
||||
|
||||
this.setIndexedReadMethod(indexedReadMethod);
|
||||
this.setIndexedWriteMethod(indexedWriteMethod);
|
||||
this.indexedPropertyType = findIndexedPropertyType(
|
||||
this.getName(), this.propertyType, indexedReadMethod, indexedWriteMethod);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Method getReadMethod() {
|
||||
return this.readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadMethod(Method readMethod) {
|
||||
this.readMethod = readMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getWriteMethod() {
|
||||
return this.writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWriteMethod(Method writeMethod) {
|
||||
this.writeMethod = writeMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyType() {
|
||||
if (this.propertyType == null) {
|
||||
try {
|
||||
this.propertyType = findPropertyType(this.readMethod, this.writeMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does IndexedPropertyDescriptor#getPropertyType
|
||||
}
|
||||
}
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getIndexedReadMethod() {
|
||||
return this.indexedReadMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndexedReadMethod(Method indexedReadMethod) throws IntrospectionException {
|
||||
this.indexedReadMethod = indexedReadMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getIndexedWriteMethod() {
|
||||
return this.indexedWriteMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndexedWriteMethod(Method indexedWriteMethod) throws IntrospectionException {
|
||||
this.indexedWriteMethod = indexedWriteMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getIndexedPropertyType() {
|
||||
if (this.indexedPropertyType == null) {
|
||||
try {
|
||||
this.indexedPropertyType = findIndexedPropertyType(
|
||||
this.getName(), this.getPropertyType(),
|
||||
this.indexedReadMethod, this.indexedWriteMethod);
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignore, as does IndexedPropertyDescriptor#getIndexedPropertyType
|
||||
}
|
||||
}
|
||||
return this.indexedPropertyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getPropertyEditorClass() {
|
||||
return this.propertyEditorClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPropertyEditorClass(Class<?> propertyEditorClass) {
|
||||
this.propertyEditorClass = propertyEditorClass;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @see java.beans.IndexedPropertyDescriptor#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj != null && obj instanceof IndexedPropertyDescriptor) {
|
||||
IndexedPropertyDescriptor other = (IndexedPropertyDescriptor) obj;
|
||||
if (!compareMethods(getIndexedReadMethod(), other.getIndexedReadMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compareMethods(getIndexedWriteMethod(), other.getIndexedWriteMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
|
||||
return false;
|
||||
}
|
||||
return PropertyDescriptorUtils.equals(this, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("%s[name=%s, propertyType=%s, indexedPropertyType=%s, " +
|
||||
"readMethod=%s, writeMethod=%s, indexedReadMethod=%s, indexedWriteMethod=%s]",
|
||||
this.getClass().getSimpleName(), this.getName(), this.getPropertyType(),
|
||||
this.getIndexedPropertyType(), this.readMethod, this.writeMethod,
|
||||
this.indexedReadMethod, this.indexedWriteMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PropertyDescriptorUtils {
|
||||
|
||||
/*
|
||||
* see java.beans.FeatureDescriptor#FeatureDescriptor(FeatureDescriptor)
|
||||
*/
|
||||
public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDescriptor target)
|
||||
throws IntrospectionException {
|
||||
|
||||
target.setExpert(source.isExpert());
|
||||
target.setHidden(source.isHidden());
|
||||
target.setPreferred(source.isPreferred());
|
||||
target.setName(source.getName());
|
||||
target.setShortDescription(source.getShortDescription());
|
||||
target.setDisplayName(source.getDisplayName());
|
||||
|
||||
// copy all attributes (emulating behavior of private FeatureDescriptor#addTable)
|
||||
Enumeration<String> keys = source.attributeNames();
|
||||
while (keys.hasMoreElements()) {
|
||||
String key = keys.nextElement();
|
||||
target.setValue(key, source.getValue(key));
|
||||
}
|
||||
|
||||
// see java.beans.PropertyDescriptor#PropertyDescriptor(PropertyDescriptor)
|
||||
target.setPropertyEditorClass(source.getPropertyEditorClass());
|
||||
target.setBound(source.isBound());
|
||||
target.setConstrained(source.isConstrained());
|
||||
}
|
||||
|
||||
/*
|
||||
* See PropertyDescriptor#findPropertyType
|
||||
*/
|
||||
public static Class<?> findPropertyType(Method readMethod, Method writeMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
Class<?> propertyType = null;
|
||||
|
||||
if (readMethod != null) {
|
||||
Class<?>[] params = readMethod.getParameterTypes();
|
||||
if (params.length != 0) {
|
||||
throw new IntrospectionException("bad read method arg count: " + readMethod);
|
||||
}
|
||||
propertyType = readMethod.getReturnType();
|
||||
if (propertyType == Void.TYPE) {
|
||||
throw new IntrospectionException("read method "
|
||||
+ readMethod.getName() + " returns void");
|
||||
}
|
||||
}
|
||||
if (writeMethod != null) {
|
||||
Class<?> params[] = writeMethod.getParameterTypes();
|
||||
if (params.length != 1) {
|
||||
throw new IntrospectionException("bad write method arg count: " + writeMethod);
|
||||
}
|
||||
if (propertyType != null
|
||||
&& !params[0].isAssignableFrom(propertyType)) {
|
||||
throw new IntrospectionException("type mismatch between read and write methods");
|
||||
}
|
||||
propertyType = params[0];
|
||||
}
|
||||
return propertyType;
|
||||
}
|
||||
|
||||
/*
|
||||
* See IndexedPropertyDescriptor#findIndexedPropertyType
|
||||
*/
|
||||
public static Class<?> findIndexedPropertyType(String name, Class<?> propertyType,
|
||||
Method indexedReadMethod, Method indexedWriteMethod)
|
||||
throws IntrospectionException {
|
||||
|
||||
Class<?> indexedPropertyType = null;
|
||||
|
||||
if (indexedReadMethod != null) {
|
||||
Class<?> params[] = indexedReadMethod.getParameterTypes();
|
||||
if (params.length != 1) {
|
||||
throw new IntrospectionException(
|
||||
"bad indexed read method arg count");
|
||||
}
|
||||
if (params[0] != Integer.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"non int index to indexed read method");
|
||||
}
|
||||
indexedPropertyType = indexedReadMethod.getReturnType();
|
||||
if (indexedPropertyType == Void.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"indexed read method returns void");
|
||||
}
|
||||
}
|
||||
if (indexedWriteMethod != null) {
|
||||
Class<?> params[] = indexedWriteMethod.getParameterTypes();
|
||||
if (params.length != 2) {
|
||||
throw new IntrospectionException(
|
||||
"bad indexed write method arg count");
|
||||
}
|
||||
if (params[0] != Integer.TYPE) {
|
||||
throw new IntrospectionException(
|
||||
"non int index to indexed write method");
|
||||
}
|
||||
if (indexedPropertyType != null && indexedPropertyType != params[1]) {
|
||||
throw new IntrospectionException(
|
||||
"type mismatch between indexed read and indexed write methods: " + name);
|
||||
}
|
||||
indexedPropertyType = params[1];
|
||||
}
|
||||
if (propertyType != null
|
||||
&& (!propertyType.isArray() ||
|
||||
propertyType.getComponentType() != indexedPropertyType)) {
|
||||
throw new IntrospectionException(
|
||||
"type mismatch between indexed and non-indexed methods: " + name);
|
||||
}
|
||||
return indexedPropertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the given {@link PropertyDescriptor} against the given {@link Object} and
|
||||
* return {@code true} if they are objects are equivalent, i.e. both are {@code
|
||||
* PropertyDescriptor}s whose read method, write method, property types, property
|
||||
* editor and flags are equivalent.
|
||||
*
|
||||
* @see PropertyDescriptor#equals(Object)
|
||||
*/
|
||||
public static boolean equals(PropertyDescriptor pd1, Object obj) {
|
||||
if (pd1 == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof PropertyDescriptor) {
|
||||
PropertyDescriptor pd2 = (PropertyDescriptor) obj;
|
||||
if (!compareMethods(pd1.getReadMethod(), pd2.getReadMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!compareMethods(pd1.getWriteMethod(), pd2.getWriteMethod())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pd1.getPropertyType() == pd2.getPropertyType()
|
||||
&& pd1.getPropertyEditorClass() == pd2.getPropertyEditorClass()
|
||||
&& pd1.isBound() == pd2.isBound()
|
||||
&& pd1.isConstrained() == pd2.isConstrained()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* see PropertyDescriptor#compareMethods
|
||||
*/
|
||||
public static boolean compareMethods(Method a, Method b) {
|
||||
if ((a == null) != (b == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a != null && b != null) {
|
||||
if (!a.equals(b)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sorts PropertyDescriptor instances alpha-numerically to emulate the behavior of
|
||||
* {@link java.beans.BeanInfo#getPropertyDescriptors()}.
|
||||
*
|
||||
* @see ExtendedBeanInfo#propertyDescriptors
|
||||
*/
|
||||
class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
|
||||
|
||||
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
|
||||
String left = desc1.getName();
|
||||
String right = desc2.getName();
|
||||
for (int i = 0; i < left.length(); i++) {
|
||||
if (right.length() == i) {
|
||||
return 1;
|
||||
}
|
||||
int result = left.getBytes()[i] - right.getBytes()[i];
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return left.length() - right.length();
|
||||
}
|
||||
}
|
||||
|
||||
+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)) {
|
||||
|
||||
+6
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.beans.factory.access;
|
||||
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ public interface BeanFactoryReference {
|
||||
|
||||
/**
|
||||
* Return the {@link BeanFactory} instance held by this reference.
|
||||
* @throws IllegalStateException if invoked after <code>release()</code> has been called
|
||||
* @throws IllegalStateException if invoked after {@code release()} has been called
|
||||
*/
|
||||
BeanFactory getFactory();
|
||||
|
||||
@@ -42,18 +41,17 @@ public interface BeanFactoryReference {
|
||||
* Indicate that the {@link BeanFactory} instance referred to by this object is not
|
||||
* needed any longer by the client code which obtained the {@link BeanFactoryReference}.
|
||||
* <p>Depending on the actual implementation of {@link BeanFactoryLocator}, and
|
||||
* the actual type of <code>BeanFactory</code>, this may possibly not actually
|
||||
* do anything; alternately in the case of a 'closeable' <code>BeanFactory</code>
|
||||
* the actual type of {@code BeanFactory}, this may possibly not actually
|
||||
* do anything; alternately in the case of a 'closeable' {@code BeanFactory}
|
||||
* or derived class (such as {@link org.springframework.context.ApplicationContext})
|
||||
* may 'close' it, or may 'close' it once no more references remain.
|
||||
* <p>In an EJB usage scenario this would normally be called from
|
||||
* <code>ejbRemove()</code> and <code>ejbPassivate()</code>.
|
||||
* {@code ejbRemove()} and {@code ejbPassivate()}.
|
||||
* <p>This is safe to call multiple times.
|
||||
* @throws FatalBeanException if the <code>BeanFactory</code> cannot be released
|
||||
* @see BeanFactoryLocator
|
||||
* @see org.springframework.context.access.ContextBeanFactoryReference
|
||||
* @see org.springframework.context.ConfigurableApplicationContext#close()
|
||||
*/
|
||||
void release() throws FatalBeanException;
|
||||
void release();
|
||||
|
||||
}
|
||||
|
||||
+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;
|
||||
|
||||
+9
-12
@@ -17,7 +17,6 @@
|
||||
package org.springframework.beans.factory.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -44,11 +43,11 @@ 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 bf the BeanFactory to get the target bean from
|
||||
* @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 IllegalStateException if no matching bean of type {@code T} found
|
||||
* @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) {
|
||||
@@ -60,7 +59,7 @@ public class BeanFactoryAnnotationUtils {
|
||||
return beanFactory.getBean(qualifier, beanType);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
|
||||
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.)");
|
||||
@@ -68,14 +67,13 @@ public class BeanFactoryAnnotationUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 IllegalStateException if no matching bean of type {@code T} found
|
||||
* @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);
|
||||
@@ -83,7 +81,7 @@ public class BeanFactoryAnnotationUtils {
|
||||
for (String beanName : candidateBeans.keySet()) {
|
||||
if (isQualifierMatch(qualifier, beanName, bf)) {
|
||||
if (matchingBean != null) {
|
||||
throw new IllegalStateException("No unique " + beanType.getSimpleName() +
|
||||
throw new NoSuchBeanDefinitionException(qualifier, "No unique " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "'");
|
||||
}
|
||||
matchingBean = candidateBeans.get(beanName);
|
||||
@@ -93,9 +91,8 @@ public class BeanFactoryAnnotationUtils {
|
||||
return matchingBean;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("No matching " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "' - neither qualifier " +
|
||||
"match nor bean name match!");
|
||||
throw new NoSuchBeanDefinitionException(qualifier, "No matching " + beanType.getSimpleName() +
|
||||
" bean found for qualifier '" + qualifier + "' - neither qualifier " + "match nor bean name match!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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.
|
||||
@@ -265,7 +265,7 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||
}
|
||||
|
||||
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
|
||||
synchronized(this.initMethods) {
|
||||
synchronized (this.initMethods) {
|
||||
for (Iterator<LifecycleElement> it = this.initMethods.iterator(); it.hasNext();) {
|
||||
String methodIdentifier = it.next().getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
|
||||
@@ -276,7 +276,7 @@ public class InitDestroyAnnotationBeanPostProcessor
|
||||
}
|
||||
}
|
||||
}
|
||||
synchronized(this.destroyMethods) {
|
||||
synchronized (this.destroyMethods) {
|
||||
for (Iterator<LifecycleElement> it = this.destroyMethods.iterator(); it.hasNext();) {
|
||||
String methodIdentifier = it.next().getIdentifier();
|
||||
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
|
||||
|
||||
+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) {
|
||||
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.beans.factory.config;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A container object holding a map of attributes and optionally destruction callbacks. The callbacks will be invoked,
|
||||
* if an attribute is being removed or if the holder is cleaned out.
|
||||
*
|
||||
* @author Micha Kiener
|
||||
* @since 3.1
|
||||
*/
|
||||
public class DestructionAwareAttributeHolder implements Serializable {
|
||||
|
||||
/** The map containing the registered attributes. */
|
||||
private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
/**
|
||||
* The optional map having any destruction callbacks registered using the
|
||||
* name of the bean as the key.
|
||||
*/
|
||||
private Map<String, Runnable> registeredDestructionCallbacks;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the map representation of the registered attributes directly. Be
|
||||
* aware to synchronize any invocations to it on the map object itself to
|
||||
* avoid concurrent modification exceptions.
|
||||
*
|
||||
* @return the attributes as a map representation
|
||||
*/
|
||||
public Map<String, Object> getAttributeMap() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the attribute having the specified name, if available,
|
||||
* <code>null</code> otherwise.
|
||||
*
|
||||
* @param name
|
||||
* the name of the attribute to be returned
|
||||
* @return the attribute value or <code>null</code> if not available
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object getAttribute(String name) {
|
||||
return attributes.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given object with the specified name as an attribute to the
|
||||
* underlying map.
|
||||
*
|
||||
* @param name
|
||||
* the name of the attribute
|
||||
* @param value
|
||||
* the value to be stored
|
||||
* @return any previously object stored under the same name, if any,
|
||||
* <code>null</code> otherwise
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object setAttribute(String name, Object value) {
|
||||
return attributes.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the object with the given <code>name</code> from the underlying
|
||||
* scope.
|
||||
* <p>
|
||||
* Returns <code>null</code> if no object was found; otherwise returns the
|
||||
* removed <code>Object</code>.
|
||||
* <p>
|
||||
* Note that an implementation should also remove a registered destruction
|
||||
* callback for the specified object, if any. It does, however, <i>not</i>
|
||||
* need to <i>execute</i> a registered destruction callback in this case,
|
||||
* since the object will be destroyed by the caller (if appropriate).
|
||||
* <p>
|
||||
* <b>Note: This is an optional operation.</b> Implementations may throw
|
||||
* {@link UnsupportedOperationException} if they do not support explicitly
|
||||
* removing an object.
|
||||
*
|
||||
* @param name
|
||||
* the name of the object to remove
|
||||
* @return the removed object, or <code>null</code> if no object was present
|
||||
* @see #registerDestructionCallback
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object removeAttribute(String name) {
|
||||
Object value = attributes.remove(name);
|
||||
|
||||
// check for a destruction callback to be invoked
|
||||
Runnable callback = getDestructionCallback(name, true);
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the map by removing all registered attribute values and invokes
|
||||
* every destruction callback registered.
|
||||
*/
|
||||
public void clear() {
|
||||
synchronized (this) {
|
||||
// step through the attribute map and invoke destruction callbacks,
|
||||
// if any
|
||||
if (registeredDestructionCallbacks != null) {
|
||||
for (Runnable runnable : registeredDestructionCallbacks.values()) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
registeredDestructionCallbacks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// clear out the registered attribute map
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be executed on destruction of the specified object
|
||||
* in the scope (or at destruction of the entire scope, if the scope does
|
||||
* not destroy individual objects but rather only terminates in its
|
||||
* entirety).
|
||||
* <p>
|
||||
* <b>Note: This is an optional operation.</b> This method will only be
|
||||
* called for scoped beans with actual destruction configuration
|
||||
* (DisposableBean, destroy-method, DestructionAwareBeanPostProcessor).
|
||||
* Implementations should do their best to execute a given callback at the
|
||||
* appropriate time. If such a callback is not supported by the underlying
|
||||
* runtime environment at all, the callback <i>must be ignored and a
|
||||
* corresponding warning should be logged</i>.
|
||||
* <p>
|
||||
* Note that 'destruction' refers to to automatic destruction of the object
|
||||
* as part of the scope's own lifecycle, not to the individual scoped object
|
||||
* having been explicitly removed by the application. If a scoped object
|
||||
* gets removed via this facade's {@link #removeAttribute(String)} method,
|
||||
* any registered destruction callback should be removed as well, assuming
|
||||
* that the removed object will be reused or manually destroyed.
|
||||
*
|
||||
* @param name
|
||||
* the name of the object to execute the destruction callback for
|
||||
* @param callback
|
||||
* the destruction callback to be executed. Note that the
|
||||
* passed-in Runnable will never throw an exception, so it can
|
||||
* safely be executed without an enclosing try-catch block.
|
||||
* Furthermore, the Runnable will usually be serializable,
|
||||
* provided that its target object is serializable as well.
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#getDestroyMethodName()
|
||||
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
|
||||
*/
|
||||
public void registerDestructionCallback(String name, Runnable callback) {
|
||||
if (registeredDestructionCallbacks == null) {
|
||||
registeredDestructionCallbacks = new ConcurrentHashMap<String, Runnable>();
|
||||
}
|
||||
|
||||
registeredDestructionCallbacks.put(name, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the destruction callback, if any registered for the attribute
|
||||
* with the given name or <code>null</code> if no such callback was
|
||||
* registered.
|
||||
*
|
||||
* @param name
|
||||
* the name of the registered callback requested
|
||||
* @param remove
|
||||
* <code>true</code>, if the callback should be removed after
|
||||
* this call, <code>false</code>, if it stays
|
||||
* @return the callback, if found, <code>null</code> otherwise
|
||||
*/
|
||||
public Runnable getDestructionCallback(String name, boolean remove) {
|
||||
if (registeredDestructionCallbacks == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
return registeredDestructionCallbacks.remove(name);
|
||||
}
|
||||
|
||||
return registeredDestructionCallbacks.get(name);
|
||||
}
|
||||
}
|
||||
+2
-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.
|
||||
@@ -101,7 +101,7 @@ public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean<Object
|
||||
|
||||
/**
|
||||
* Set the name of the target bean.
|
||||
* <p>The target does not <i>have</> to be a non-singleton bean, but realisticially
|
||||
* <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
|
||||
* always will be (because if the target bean were a singleton, then said singleton
|
||||
* bean could simply be injected straight into the dependent object, thus obviating
|
||||
* the need for the extra level of indirection afforded by this factory approach).
|
||||
|
||||
+2
-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.
|
||||
@@ -46,7 +46,7 @@ public class ProviderCreatingFactoryBean extends AbstractFactoryBean<Provider> {
|
||||
|
||||
/**
|
||||
* Set the name of the target bean.
|
||||
* <p>The target does not <i>have</> to be a non-singleton bean, but realisticially
|
||||
* <p>The target does not <i>have</i> to be a non-singleton bean, but realistically
|
||||
* always will be (because if the target bean were a singleton, then said singleton
|
||||
* bean could simply be injected straight into the dependent object, thus obviating
|
||||
* the need for the extra level of indirection afforded by this factory approach).
|
||||
|
||||
+17
-18
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,6 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.FatalBeanException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
@@ -36,8 +35,8 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link FactoryBean} implementation that takes an interface which must have one or more
|
||||
* methods with the signatures <code>MyType xxx()</code> or <code>MyType xxx(MyIdType id)</code>
|
||||
* (typically, <code>MyService getService()</code> or <code>MyService getService(String id)</code>)
|
||||
* methods with the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
|
||||
* (typically, {@code MyService getService()} or {@code MyService getService(String id)})
|
||||
* and creates a dynamic proxy which implements that interface, delegating to an
|
||||
* underlying {@link org.springframework.beans.factory.BeanFactory}.
|
||||
*
|
||||
@@ -51,7 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
* setter or constructor injection of the target bean is preferable.</b>
|
||||
*
|
||||
* <p>On invocation of the no-arg factory method, or the single-arg factory
|
||||
* method with a String id of <code>null</code> or empty String, if exactly
|
||||
* method with a String id of {@code null} or empty String, if exactly
|
||||
* <b>one</b> bean in the factory matches the return type of the factory
|
||||
* method, that bean is returned, otherwise a
|
||||
* {@link org.springframework.beans.factory.NoSuchBeanDefinitionException}
|
||||
@@ -64,7 +63,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* <p>A factory method argument will usually be a String, but can also be an
|
||||
* int or a custom enumeration type, for example, stringified via
|
||||
* <code>toString</code>. The resulting String can be used as bean name as-is,
|
||||
* {@code toString}. The resulting String can be used as bean name as-is,
|
||||
* provided that corresponding beans are defined in the bean factory.
|
||||
* Alternatively, {@link #setServiceMappings(java.util.Properties) a custom mapping}
|
||||
* between service ids and bean names can be defined.
|
||||
@@ -99,7 +98,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
*</beans></pre>
|
||||
*
|
||||
* <p>The attendant <code>MyClientBean</code> class implementation might then
|
||||
* <p>The attendant {@code MyClientBean} class implementation might then
|
||||
* look something like this:
|
||||
*
|
||||
* <pre class="code">package a.b.c;
|
||||
@@ -151,7 +150,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
*</beans></pre>
|
||||
*
|
||||
* <p>The attendant <code>MyClientBean</code> class implementation might then
|
||||
* <p>The attendant {@code MyClientBean} class implementation might then
|
||||
* look something like this:
|
||||
*
|
||||
* <pre class="code">package a.b.c;
|
||||
@@ -202,8 +201,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
|
||||
/**
|
||||
* Set the service locator interface to use, which must have one or more methods with
|
||||
* the signatures <code>MyType xxx()</code> or <code>MyType xxx(MyIdType id)</code>
|
||||
* (typically, <code>MyService getService()</code> or <code>MyService getService(String id)</code>).
|
||||
* the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
|
||||
* (typically, {@code MyService getService()} or {@code MyService getService(String id)}).
|
||||
* See the {@link ServiceLocatorFactoryBean class-level Javadoc} for
|
||||
* information on the semantics of such methods.
|
||||
*/
|
||||
@@ -214,8 +213,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
/**
|
||||
* Set the exception class that the service locator should throw if service
|
||||
* lookup failed. The specified exception class must have a constructor
|
||||
* with one of the following parameter types: <code>(String, Throwable)</code>
|
||||
* or <code>(Throwable)</code> or <code>(String)</code>.
|
||||
* with one of the following parameter types: {@code (String, Throwable)}
|
||||
* or {@code (Throwable)} or {@code (String)}.
|
||||
* <p>If not specified, subclasses of Spring's BeansException will be thrown,
|
||||
* for example NoSuchBeanDefinitionException. As those are unchecked, the
|
||||
* caller does not need to handle them, so it might be acceptable that
|
||||
@@ -236,7 +235,7 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
* Set mappings between service ids (passed into the service locator)
|
||||
* and bean names (in the bean factory). Service ids that are not defined
|
||||
* here will be treated as bean names as-is.
|
||||
* <p>The empty string as service id key defines the mapping for <code>null</code> and
|
||||
* <p>The empty string as service id key defines the mapping for {@code null} and
|
||||
* empty string, and for factory methods without parameter. If not defined,
|
||||
* a single matching bean will be retrieved from the bean factory.
|
||||
* @param serviceMappings mappings between service ids and bean names,
|
||||
@@ -271,8 +270,8 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
* Determine the constructor to use for the given service locator exception
|
||||
* class. Only called in case of a custom service locator exception.
|
||||
* <p>The default implementation looks for a constructor with one of the
|
||||
* following parameter types: <code>(String, Throwable)</code>
|
||||
* or <code>(Throwable)</code> or <code>(String)</code>.
|
||||
* following parameter types: {@code (String, Throwable)}
|
||||
* or {@code (Throwable)} or {@code (String)}.
|
||||
* @param exceptionClass the exception class
|
||||
* @return the constructor to use
|
||||
* @see #setServiceLocatorExceptionClass
|
||||
@@ -364,12 +363,12 @@ public class ServiceLocatorFactoryBean implements FactoryBean<Object>, BeanFacto
|
||||
try {
|
||||
String beanName = tryGetBeanName(args);
|
||||
if (StringUtils.hasLength(beanName)) {
|
||||
// Service locator for a specific bean name.
|
||||
// Service locator for a specific bean name
|
||||
return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
|
||||
}
|
||||
else {
|
||||
// Service locator for a bean type.
|
||||
return BeanFactoryUtils.beanOfTypeIncludingAncestors(beanFactory, serviceLocatorMethodReturnType);
|
||||
// Service locator for a bean type
|
||||
return beanFactory.getBean(serviceLocatorMethodReturnType);
|
||||
}
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
|
||||
+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;
|
||||
|
||||
+32
-37
@@ -21,14 +21,11 @@ import java.io.NotSerializableException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -39,7 +36,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.inject.Provider;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
@@ -308,8 +304,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
|
||||
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
|
||||
if (type == null || !allowEagerInit) {
|
||||
return this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
|
||||
return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
}
|
||||
Map<Class<?>, String[]> cache = includeNonSingletons ?
|
||||
this.nonSingletonBeanNamesByType : this.singletonBeanNamesByType;
|
||||
@@ -317,7 +313,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (resolvedBeanNames != null) {
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
resolvedBeanNames = this.doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
resolvedBeanNames = doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
|
||||
cache.put(type, resolvedBeanNames);
|
||||
return resolvedBeanNames;
|
||||
}
|
||||
@@ -580,35 +576,36 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
if (this.logger.isInfoEnabled()) {
|
||||
this.logger.info("Pre-instantiating singletons in " + this);
|
||||
}
|
||||
List<String> beanNames;
|
||||
synchronized (this.beanDefinitionMap) {
|
||||
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
|
||||
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
|
||||
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
|
||||
for (String beanName : beanNames) {
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(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();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
}
|
||||
beanNames = new ArrayList<String>(this.beanDefinitionNames);
|
||||
}
|
||||
for (String beanName : beanNames) {
|
||||
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
|
||||
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
|
||||
if (isFactoryBean(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();
|
||||
}
|
||||
}, getAccessControlContext());
|
||||
}
|
||||
else {
|
||||
isEagerInit = (factory instanceof SmartFactoryBean &&
|
||||
((SmartFactoryBean<?>) factory).isEagerInit());
|
||||
}
|
||||
if (isEagerInit) {
|
||||
getBean(beanName);
|
||||
}
|
||||
}
|
||||
else {
|
||||
getBean(beanName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -654,9 +651,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
this.frozenBeanDefinitionNames = null;
|
||||
}
|
||||
this.beanDefinitionMap.put(beanName, beanDefinition);
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
|
||||
@@ -672,9 +669,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
this.beanDefinitionNames.remove(beanName);
|
||||
this.frozenBeanDefinitionNames = null;
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
resetBeanDefinition(beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,9 +686,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
// Remove corresponding bean from singleton cache, if any. Shouldn't usually
|
||||
// be necessary, rather just meant for overriding a context's default beans
|
||||
// (e.g. the default StaticMessageSource in a StaticApplicationContext).
|
||||
synchronized (getSingletonMutex()) {
|
||||
destroySingleton(beanName);
|
||||
}
|
||||
destroySingleton(beanName);
|
||||
|
||||
// Remove any assumptions about by-type mappings
|
||||
this.singletonBeanNamesByType.clear();
|
||||
@@ -1031,9 +1026,9 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
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 {
|
||||
|
||||
+11
-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.
|
||||
@@ -98,7 +98,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
private final Set<String> singletonsCurrentlyInCreation = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
/** Names of beans currently excluded from in creation checks */
|
||||
private final Set<String> inCreationCheckExclusions = new HashSet<String>();
|
||||
private final Set<String> inCreationCheckExclusions = Collections.synchronizedSet(new HashSet<String>());
|
||||
|
||||
/** List of suppressed Exceptions, available for associating related causes */
|
||||
private Set<Exception> suppressedExceptions;
|
||||
@@ -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).
|
||||
@@ -436,11 +437,12 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
||||
this.singletonsCurrentlyInDestruction = true;
|
||||
}
|
||||
|
||||
String[] disposableBeanNames;
|
||||
synchronized (this.disposableBeans) {
|
||||
String[] disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
|
||||
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
|
||||
destroySingleton(disposableBeanNames[i]);
|
||||
}
|
||||
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
|
||||
}
|
||||
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
|
||||
destroySingleton(disposableBeanNames[i]);
|
||||
}
|
||||
|
||||
this.containedBeanMap.clear();
|
||||
|
||||
+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);
|
||||
|
||||
+73
-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,49 @@ public final class BeanWrapperTests {
|
||||
assertEquals(TestEnum.TEST_VALUE, consumer.getEnumValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cornerSpr10115() {
|
||||
Spr10115Bean foo = new Spr10115Bean();
|
||||
BeanWrapperImpl bwi = new BeanWrapperImpl();
|
||||
bwi.setWrappedInstance(foo);
|
||||
bwi.setPropertyValue("prop1", "val1");
|
||||
assertEquals("val1", Spr10115Bean.prop1);
|
||||
}
|
||||
|
||||
|
||||
static class Spr10115Bean {
|
||||
private static String prop1;
|
||||
|
||||
public static void setProp1(String prop1) {
|
||||
Spr10115Bean.prop1 = prop1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+14
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.beans;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.junit.Test;
|
||||
import test.beans.TestBean;
|
||||
|
||||
import org.springframework.core.OverridingClassLoader;
|
||||
|
||||
import test.beans.TestBean;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Juergen Hoeller
|
||||
@@ -30,7 +32,7 @@ import test.beans.TestBean;
|
||||
public final class CachedIntrospectionResultsTests {
|
||||
|
||||
@Test
|
||||
public void testAcceptClassLoader() throws Exception {
|
||||
public void acceptAndClearClassLoader() throws Exception {
|
||||
BeanWrapper bw = new BeanWrapperImpl(TestBean.class);
|
||||
assertTrue(bw.isWritableProperty("name"));
|
||||
assertTrue(bw.isWritableProperty("age"));
|
||||
@@ -50,4 +52,12 @@ public final class CachedIntrospectionResultsTests {
|
||||
assertTrue(CachedIntrospectionResults.classCache.containsKey(TestBean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearClassLoaderForSystemClassLoader() throws Exception {
|
||||
BeanUtils.getPropertyDescriptors(ArrayList.class);
|
||||
assertTrue(CachedIntrospectionResults.classCache.containsKey(ArrayList.class));
|
||||
CachedIntrospectionResults.clearClassLoader(ArrayList.class.getClassLoader());
|
||||
assertFalse(CachedIntrospectionResults.classCache.containsKey(ArrayList.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+249
-38
@@ -16,29 +16,31 @@
|
||||
|
||||
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 java.math.BigDecimal;
|
||||
|
||||
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.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ExtendedBeanInfo}.
|
||||
*
|
||||
@@ -127,12 +129,32 @@ 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(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardReadAndNonStandardIndexedWriteMethod() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public String[] getFoo() { return null; }
|
||||
public C setFoo(int i, String foo) { return this; }
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foo"), is(trueUntilJdk17()));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardReadMethodsAndOverloadedNonStandardWriteMethods() throws Exception {
|
||||
@SuppressWarnings("unused") class C {
|
||||
@@ -149,7 +171,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(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -163,6 +185,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 {
|
||||
@@ -180,7 +246,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(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -223,13 +289,13 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "bar"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -246,7 +312,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -267,7 +333,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -284,7 +350,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
@@ -305,7 +371,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
@@ -322,7 +388,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(false));
|
||||
@@ -463,14 +529,74 @@ public class ExtendedBeanInfoTests {
|
||||
BeanInfo ebi = new ExtendedBeanInfo(Introspector.getBeanInfo(C.class));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foos"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "foos"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "foos"), is(true));
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
|
||||
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
|
||||
* methods" intermittently (approximately one out of every four times) under JDK 7
|
||||
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
|
||||
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
|
||||
* @see #cornerSpr9702()
|
||||
*/
|
||||
@Test
|
||||
public void cornerSpr10111() throws Exception {
|
||||
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
|
||||
@SuppressWarnings("unused") class B {
|
||||
@@ -487,10 +613,10 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
@@ -506,7 +632,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(true));
|
||||
@@ -527,9 +653,67 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(ebi.getPropertyDescriptors(), equalTo(bi.getPropertyDescriptors()));
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(1));
|
||||
assertThat(ebi.getPropertyDescriptors()[0].getName(), equalTo("class"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overloadedNonStandardWriteMethodsOnly_orderA() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public Object setFoo(String p) { return new Object(); }
|
||||
public Object setFoo(int p) { return new Object(); }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals("foo")) {
|
||||
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("never matched write method");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionException, SecurityException, NoSuchMethodException {
|
||||
@SuppressWarnings("unused") class C {
|
||||
public Object setFoo(int p) { return new Object(); }
|
||||
public Object setFoo(String p) { return new Object(); }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(C.class);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "foo"), is(false));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "foo"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "foo"), is(true));
|
||||
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals("foo")) {
|
||||
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("never matched write method");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,7 +737,7 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "dateFormat"), is(false));
|
||||
@@ -561,15 +745,15 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "dateFormat"), is(trueUntilJdk17()));
|
||||
|
||||
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
|
||||
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyCountsMatch() throws IntrospectionException {
|
||||
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(ebi.getPropertyDescriptors().length, equalTo(bi.getPropertyDescriptors().length));
|
||||
}
|
||||
@@ -581,7 +765,7 @@ public class ExtendedBeanInfoTests {
|
||||
public ExtendedTestBean setFoo(String s) { return this; }
|
||||
}
|
||||
BeanInfo bi = Introspector.getBeanInfo(ExtendedTestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
boolean found = false;
|
||||
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
|
||||
@@ -600,7 +784,7 @@ public class ExtendedBeanInfoTests {
|
||||
@Test
|
||||
public void propertyDescriptorOrderIsEqual() throws IntrospectionException {
|
||||
BeanInfo bi = Introspector.getBeanInfo(TestBean.class);
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
for (int i = 0; i < bi.getPropertyDescriptors().length; i++) {
|
||||
assertThat("element " + i + " in BeanInfo and ExtendedBeanInfo propertyDescriptor arrays do not match",
|
||||
@@ -647,7 +831,9 @@ public class ExtendedBeanInfoTests {
|
||||
private boolean hasIndexedWriteMethodForProperty(BeanInfo beanInfo, String propertyName) {
|
||||
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
return ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod() != null;
|
||||
}
|
||||
}
|
||||
@@ -657,7 +843,9 @@ public class ExtendedBeanInfoTests {
|
||||
private boolean hasIndexedReadMethodForProperty(BeanInfo beanInfo, String propertyName) {
|
||||
for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
|
||||
if (pd.getName().equals(propertyName)) {
|
||||
assertThat(propertyName + " property is not indexed", pd, instanceOf(IndexedPropertyDescriptor.class));
|
||||
if (!(pd instanceof IndexedPropertyDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
return ((IndexedPropertyDescriptor)pd).getIndexedReadMethod() != null;
|
||||
}
|
||||
}
|
||||
@@ -738,7 +926,7 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
|
||||
// and now demonstrate that we've indeed fixed the problem
|
||||
ExtendedBeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
BeanInfo ebi = new ExtendedBeanInfo(bi);
|
||||
|
||||
assertThat(hasReadMethodForProperty(bi, "targetMethod"), is(true));
|
||||
assertThat(hasWriteMethodForProperty(bi, "targetMethod"), is(false));
|
||||
@@ -763,7 +951,7 @@ public class ExtendedBeanInfoTests {
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
|
||||
}
|
||||
{
|
||||
ExtendedBeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(A.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "address"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "address"), is(true));
|
||||
@@ -771,4 +959,27 @@ public class ExtendedBeanInfoTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportStaticWriteMethod() throws IntrospectionException {
|
||||
{
|
||||
BeanInfo bi = Introspector.getBeanInfo(WithStaticWriteMethod.class);
|
||||
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
}
|
||||
{
|
||||
BeanInfo bi = new ExtendedBeanInfo(Introspector.getBeanInfo(WithStaticWriteMethod.class));
|
||||
assertThat(hasReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasWriteMethodForProperty(bi, "prop1"), is(true));
|
||||
assertThat(hasIndexedReadMethodForProperty(bi, "prop1"), is(false));
|
||||
assertThat(hasIndexedWriteMethodForProperty(bi, "prop1"), is(false));
|
||||
}
|
||||
}
|
||||
|
||||
static class WithStaticWriteMethod {
|
||||
@SuppressWarnings("unused")
|
||||
public static void setProp1(String prop1) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-15
@@ -17,17 +17,13 @@
|
||||
package org.springframework.beans.factory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Principal;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -35,14 +31,18 @@ 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;
|
||||
@@ -79,15 +79,7 @@ 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.*;
|
||||
|
||||
/**
|
||||
@@ -2181,11 +2173,13 @@ public class DefaultListableBeanFactoryTests {
|
||||
public void testByTypeLookupIsFastEnough() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
for (int i=0; i<1000; i++) {
|
||||
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);
|
||||
}
|
||||
|
||||
+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.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+59
-53
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,6 +35,7 @@ import javax.mail.internet.MimeBodyPart;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import javax.mail.internet.MimeMultipart;
|
||||
import javax.mail.internet.MimePart;
|
||||
import javax.mail.internet.MimeUtility;
|
||||
|
||||
import org.springframework.core.io.InputStreamSource;
|
||||
import org.springframework.core.io.Resource;
|
||||
@@ -241,7 +242,7 @@ public class MimeMessageHelper {
|
||||
* @see #MimeMessageHelper(javax.mail.internet.MimeMessage, int, String)
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, boolean multipart, String encoding)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
this(mimeMessage, (multipart ? MULTIPART_MODE_MIXED_RELATED : MULTIPART_MODE_NO), encoding);
|
||||
}
|
||||
@@ -283,7 +284,7 @@ public class MimeMessageHelper {
|
||||
* @see #MULTIPART_MODE_MIXED_RELATED
|
||||
*/
|
||||
public MimeMessageHelper(MimeMessage mimeMessage, int multipartMode, String encoding)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
this.mimeMessage = mimeMessage;
|
||||
createMimeMultiparts(mimeMessage, multipartMode);
|
||||
@@ -355,7 +356,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Set the given MimeMultipart objects for use by this MimeMessageHelper.
|
||||
* @param root the root MimeMultipart object, which attachments will be added to;
|
||||
* or <code>null</code> to indicate no multipart at all
|
||||
* or {@code null} to indicate no multipart at all
|
||||
* @param main the main MimeMultipart object, which text(s) and inline elements
|
||||
* will be added to (can be the same as the root multipart object, or an element
|
||||
* nested underneath the root multipart element)
|
||||
@@ -380,8 +381,8 @@ public class MimeMessageHelper {
|
||||
private void checkMultipart() throws IllegalStateException {
|
||||
if (!isMultipart()) {
|
||||
throw new IllegalStateException("Not in multipart mode - " +
|
||||
"create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
|
||||
"if you need to set alternative texts or add inline elements or attachments.");
|
||||
"create an appropriate MimeMessageHelper via a constructor that takes a 'multipart' flag " +
|
||||
"if you need to set alternative texts or add inline elements or attachments.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +421,7 @@ public class MimeMessageHelper {
|
||||
* Determine the default encoding for the given MimeMessage.
|
||||
* @param mimeMessage the passed-in MimeMessage
|
||||
* @return the default encoding associated with the MimeMessage,
|
||||
* or <code>null</code> if none found
|
||||
* or {@code null} if none found
|
||||
*/
|
||||
protected String getDefaultEncoding(MimeMessage mimeMessage) {
|
||||
if (mimeMessage instanceof SmartMimeMessage) {
|
||||
@@ -456,12 +457,12 @@ public class MimeMessageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Java Activation Framework <code>FileTypeMap</code> to use
|
||||
* Set the Java Activation Framework {@code FileTypeMap} to use
|
||||
* for determining the content type of inline content and attachments
|
||||
* that get added to the message.
|
||||
* <p>Default is the <code>FileTypeMap</code> that the underlying
|
||||
* <p>Default is the {@code FileTypeMap} that the underlying
|
||||
* MimeMessage carries, if any, or the Activation Framework's default
|
||||
* <code>FileTypeMap</code> instance else.
|
||||
* {@code FileTypeMap} instance else.
|
||||
* @see #addInline
|
||||
* @see #addAttachment
|
||||
* @see #getDefaultFileTypeMap(javax.mail.internet.MimeMessage)
|
||||
@@ -474,7 +475,7 @@ public class MimeMessageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>FileTypeMap</code> used by this MimeMessageHelper.
|
||||
* Return the {@code FileTypeMap} used by this MimeMessageHelper.
|
||||
*/
|
||||
public FileTypeMap getFileTypeMap() {
|
||||
return this.fileTypeMap;
|
||||
@@ -485,7 +486,7 @@ public class MimeMessageHelper {
|
||||
* Set whether to validate all addresses which get passed to this helper.
|
||||
* Default is "false".
|
||||
* <p>Note that this is by default just available for JavaMail >= 1.3.
|
||||
* You can override the default <code>validateAddress method</code> for
|
||||
* You can override the default {@code validateAddress method} for
|
||||
* validation on older JavaMail versions (or for custom validation).
|
||||
* @see #validateAddress
|
||||
*/
|
||||
@@ -503,7 +504,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Validate the given mail address.
|
||||
* Called by all of MimeMessageHelper's address setters and adders.
|
||||
* <p>Default implementation invokes <code>InternetAddress.validate()</code>,
|
||||
* <p>Default implementation invokes {@code InternetAddress.validate()},
|
||||
* provided that address validation is activated for the helper instance.
|
||||
* <p>Note that this method will just work on JavaMail >= 1.3. You can override
|
||||
* it for validation on older JavaMail versions or for custom validation.
|
||||
@@ -546,7 +547,7 @@ public class MimeMessageHelper {
|
||||
public void setFrom(String from, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(from, "From address must not be null");
|
||||
setFrom(getEncoding() != null ?
|
||||
new InternetAddress(from, personal, getEncoding()) : new InternetAddress(from, personal));
|
||||
new InternetAddress(from, personal, getEncoding()) : new InternetAddress(from, personal));
|
||||
}
|
||||
|
||||
public void setReplyTo(InternetAddress replyTo) throws MessagingException {
|
||||
@@ -608,8 +609,8 @@ public class MimeMessageHelper {
|
||||
public void addTo(String to, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(to, "To address must not be null");
|
||||
addTo(getEncoding() != null ?
|
||||
new InternetAddress(to, personal, getEncoding()) :
|
||||
new InternetAddress(to, personal));
|
||||
new InternetAddress(to, personal, getEncoding()) :
|
||||
new InternetAddress(to, personal));
|
||||
}
|
||||
|
||||
|
||||
@@ -653,8 +654,8 @@ public class MimeMessageHelper {
|
||||
public void addCc(String cc, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(cc, "Cc address must not be null");
|
||||
addCc(getEncoding() != null ?
|
||||
new InternetAddress(cc, personal, getEncoding()) :
|
||||
new InternetAddress(cc, personal));
|
||||
new InternetAddress(cc, personal, getEncoding()) :
|
||||
new InternetAddress(cc, personal));
|
||||
}
|
||||
|
||||
|
||||
@@ -698,8 +699,8 @@ public class MimeMessageHelper {
|
||||
public void addBcc(String bcc, String personal) throws MessagingException, UnsupportedEncodingException {
|
||||
Assert.notNull(bcc, "Bcc address must not be null");
|
||||
addBcc(getEncoding() != null ?
|
||||
new InternetAddress(bcc, personal, getEncoding()) :
|
||||
new InternetAddress(bcc, personal));
|
||||
new InternetAddress(bcc, personal, getEncoding()) :
|
||||
new InternetAddress(bcc, personal));
|
||||
}
|
||||
|
||||
private InternetAddress parseAddress(String address) throws MessagingException {
|
||||
@@ -730,7 +731,7 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Set the sent-date of the message.
|
||||
* @param sentDate the date to set (never <code>null</code>)
|
||||
* @param sentDate the date to set (never {@code null})
|
||||
* @throws MessagingException in case of errors
|
||||
*/
|
||||
public void setSentDate(Date sentDate) throws MessagingException {
|
||||
@@ -758,7 +759,7 @@ public class MimeMessageHelper {
|
||||
* Set the given text directly as content in non-multipart mode
|
||||
* or as default body part in multipart mode.
|
||||
* Always applies the default content type "text/plain".
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param text the text for the message
|
||||
* @throws MessagingException in case of errors
|
||||
@@ -771,7 +772,7 @@ public class MimeMessageHelper {
|
||||
* Set the given text directly as content in non-multipart mode
|
||||
* or as default body part in multipart mode.
|
||||
* The "html" flag determines the content type to apply.
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param text the text for the message
|
||||
* @param html whether to apply content type "text/html" for an
|
||||
@@ -798,7 +799,7 @@ public class MimeMessageHelper {
|
||||
/**
|
||||
* Set the given plain text and HTML text as alternatives, offering
|
||||
* both options to the email client. Requires multipart mode.
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> <code>setText</code>;
|
||||
* <p><b>NOTE:</b> Invoke {@link #addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param plainText the plain text for the message
|
||||
* @param htmlText the HTML text for the message
|
||||
@@ -860,16 +861,16 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>javax.activation.DataSource</code>.
|
||||
* {@code javax.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
* Can be referenced in HTML source via src="cid:myId" expressions.
|
||||
* @param dataSource the <code>javax.activation.DataSource</code> to take
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addInline(String, java.io.File)
|
||||
@@ -889,11 +890,11 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>java.io.File</code>.
|
||||
* {@code java.io.File}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -913,14 +914,14 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from a
|
||||
* <code>org.springframework.core.io.Resource</code>.
|
||||
* {@code org.springframework.core.io.Resource}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
* <p>Note that the InputStream returned by the Resource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> {@link #setText};
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@link #setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -939,14 +940,14 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an inline element to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.InputStreamResource</code>, and
|
||||
* {@code org.springframework.core.InputStreamResource}, and
|
||||
* specifying the content type explicitly.
|
||||
* <p>You can determine the content type for any given filename via a Java
|
||||
* Activation Framework's FileTypeMap, for example the one held by this helper.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* <p><b>NOTE:</b> Invoke <code>addInline</code> <i>after</i> <code>setText</code>;
|
||||
* {@code getInputStream()} multiple times.
|
||||
* <p><b>NOTE:</b> Invoke {@code addInline} <i>after</i> {@code setText};
|
||||
* else, mail readers might not be able to resolve inline references correctly.
|
||||
* @param contentId the content ID to use. Will end up as "Content-ID" header
|
||||
* in the body part, surrounded by angle brackets: e.g. "myId" -> "<myId>".
|
||||
@@ -960,7 +961,7 @@ public class MimeMessageHelper {
|
||||
* @see #addInline(String, javax.activation.DataSource)
|
||||
*/
|
||||
public void addInline(String contentId, InputStreamSource inputStreamSource, String contentType)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
|
||||
if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
|
||||
@@ -974,13 +975,13 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from a
|
||||
* <code>javax.activation.DataSource</code>.
|
||||
* {@code javax.activation.DataSource}.
|
||||
* <p>Note that the InputStream returned by the DataSource implementation
|
||||
* needs to be a <i>fresh one on each call</i>, as JavaMail will invoke
|
||||
* <code>getInputStream()</code> multiple times.
|
||||
* {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail (the content type will be determined by this)
|
||||
* @param dataSource the <code>javax.activation.DataSource</code> to take
|
||||
* @param dataSource the {@code javax.activation.DataSource} to take
|
||||
* the content from, determining the InputStream and the content type
|
||||
* @throws MessagingException in case of errors
|
||||
* @see #addAttachment(String, org.springframework.core.io.InputStreamSource)
|
||||
@@ -989,16 +990,21 @@ public class MimeMessageHelper {
|
||||
public void addAttachment(String attachmentFilename, DataSource dataSource) throws MessagingException {
|
||||
Assert.notNull(attachmentFilename, "Attachment filename must not be null");
|
||||
Assert.notNull(dataSource, "DataSource must not be null");
|
||||
MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
|
||||
mimeBodyPart.setFileName(attachmentFilename);
|
||||
mimeBodyPart.setDataHandler(new DataHandler(dataSource));
|
||||
getRootMimeMultipart().addBodyPart(mimeBodyPart);
|
||||
try {
|
||||
MimeBodyPart mimeBodyPart = new MimeBodyPart();
|
||||
mimeBodyPart.setDisposition(MimeBodyPart.ATTACHMENT);
|
||||
mimeBodyPart.setFileName(MimeUtility.encodeText(attachmentFilename));
|
||||
mimeBodyPart.setDataHandler(new DataHandler(dataSource));
|
||||
getRootMimeMultipart().addBodyPart(mimeBodyPart);
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
throw new MessagingException("Failed to encode attachment filename", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from a
|
||||
* <code>java.io.File</code>.
|
||||
* {@code java.io.File}.
|
||||
* <p>The content type will be determined by the name of the given
|
||||
* content file. Do not use this for temporary files with arbitrary
|
||||
* filenames (possibly ending in ".tmp" or the like)!
|
||||
@@ -1018,13 +1024,13 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.io.InputStreamResource</code>.
|
||||
* {@code org.springframework.core.io.InputStreamResource}.
|
||||
* <p>The content type will be determined by the given filename for
|
||||
* the attachment. Thus, any content source will be fine, including
|
||||
* temporary files with arbitrary filenames.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource
|
||||
* implementation needs to be a <i>fresh one on each call</i>, as
|
||||
* JavaMail will invoke <code>getInputStream()</code> multiple times.
|
||||
* JavaMail will invoke {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail
|
||||
* @param inputStreamSource the resource to take the content from
|
||||
@@ -1035,7 +1041,7 @@ public class MimeMessageHelper {
|
||||
* @see org.springframework.core.io.Resource
|
||||
*/
|
||||
public void addAttachment(String attachmentFilename, InputStreamSource inputStreamSource)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
String contentType = getFileTypeMap().getContentType(attachmentFilename);
|
||||
addAttachment(attachmentFilename, inputStreamSource, contentType);
|
||||
@@ -1043,10 +1049,10 @@ public class MimeMessageHelper {
|
||||
|
||||
/**
|
||||
* Add an attachment to the MimeMessage, taking the content from an
|
||||
* <code>org.springframework.core.io.InputStreamResource</code>.
|
||||
* {@code org.springframework.core.io.InputStreamResource}.
|
||||
* <p>Note that the InputStream returned by the InputStreamSource
|
||||
* implementation needs to be a <i>fresh one on each call</i>, as
|
||||
* JavaMail will invoke <code>getInputStream()</code> multiple times.
|
||||
* JavaMail will invoke {@code getInputStream()} multiple times.
|
||||
* @param attachmentFilename the name of the attachment as it will
|
||||
* appear in the mail
|
||||
* @param inputStreamSource the resource to take the content from
|
||||
@@ -1059,7 +1065,7 @@ public class MimeMessageHelper {
|
||||
*/
|
||||
public void addAttachment(
|
||||
String attachmentFilename, InputStreamSource inputStreamSource, String contentType)
|
||||
throws MessagingException {
|
||||
throws MessagingException {
|
||||
|
||||
Assert.notNull(inputStreamSource, "InputStreamSource must not be null");
|
||||
if (inputStreamSource instanceof Resource && ((Resource) inputStreamSource).isOpen()) {
|
||||
@@ -1079,7 +1085,7 @@ public class MimeMessageHelper {
|
||||
* @return the Activation Framework DataSource
|
||||
*/
|
||||
protected DataSource createDataSource(
|
||||
final InputStreamSource inputStreamSource, final String contentType, final String name) {
|
||||
final InputStreamSource inputStreamSource, final String contentType, final String name) {
|
||||
|
||||
return new DataSource() {
|
||||
public InputStream getInputStream() throws IOException {
|
||||
|
||||
+6
-1
@@ -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.
|
||||
@@ -43,10 +43,15 @@ public class TimerManagerTaskScheduler extends TimerManagerAccessor implements T
|
||||
|
||||
private volatile ErrorHandler errorHandler;
|
||||
|
||||
|
||||
/**
|
||||
* Provide an {@link ErrorHandler} strategy.
|
||||
*/
|
||||
public void setErrorHandler(ErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
|
||||
public ScheduledFuture schedule(Runnable task, Trigger trigger) {
|
||||
return new ReschedulingTimerListener(errorHandlingTask(task, true), trigger).schedule();
|
||||
}
|
||||
|
||||
+1
-21
@@ -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.
|
||||
@@ -65,26 +65,6 @@ import org.springframework.util.Assert;
|
||||
* {@link org.springframework.jca.work.glassfish.GlassFishWorkManagerTaskExecutor}
|
||||
* classes which are the direct equivalent of this CommonJ adapter class.
|
||||
*
|
||||
* <p>A similar facility is available on WebSphere 5.1, under the name
|
||||
* "Asynch Beans". Its central interface is called WorkManager too and is
|
||||
* also obtained from JNDI, just like a standard CommonJ WorkManager.
|
||||
* However, this WorkManager variant is notably different: The central
|
||||
* execution method is called "startWork" instead of "schedule",
|
||||
* and takes a slightly different Work interface as parameter.
|
||||
*
|
||||
* <p>Support for this WebSphere 5.1 variant can be built with this class
|
||||
* and its helper DelegatingWork as template: Call the WorkManager's
|
||||
* <code>startWork(Work)</code> instead of <code>schedule(Work)</code>
|
||||
* in the <code>execute(Runnable)</code> implementation. Furthermore,
|
||||
* for simplicity's sake, drop the entire "Implementation of the CommonJ
|
||||
* WorkManager interface" section (and the corresponding
|
||||
* <code>implements WorkManager</code> clause at the class level).
|
||||
* Of course, you also need to change all <code>commonj.work</code> imports in
|
||||
* your WorkManagerTaskExecutor and DelegatingWork variants to the corresponding
|
||||
* WebSphere API imports (<code>com.ibm.websphere.asynchbeans.WorkManager</code>
|
||||
* and <code>com.ibm.websphere.asynchbeans.Work</code>, respectively).
|
||||
* This should be sufficient to get a TaskExecutor adapter for WebSphere 5.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
|
||||
+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.
|
||||
@@ -95,8 +95,8 @@ public class SchedulerAccessorBean extends SchedulerAccessor implements BeanFact
|
||||
if (this.beanFactory instanceof ListableBeanFactory) {
|
||||
ListableBeanFactory lbf = (ListableBeanFactory) this.beanFactory;
|
||||
String[] beanNames = lbf.getBeanNamesForType(Scheduler.class);
|
||||
for (int i = 0; i < beanNames.length; i++) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanNames[i]);
|
||||
for (String beanName : beanNames) {
|
||||
Scheduler schedulerBean = (Scheduler) lbf.getBean(beanName);
|
||||
if (schedulerName.equals(schedulerBean.getSchedulerName())) {
|
||||
return schedulerBean;
|
||||
}
|
||||
|
||||
+27
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -20,6 +20,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -47,12 +48,12 @@ import org.springframework.util.CollectionUtils;
|
||||
* <p>The optional "configLocation" property sets the location of a FreeMarker
|
||||
* properties file, within the current application. FreeMarker properties can be
|
||||
* overridden via "freemarkerSettings". All of these properties will be set by
|
||||
* calling FreeMarker's <code>Configuration.setSettings()</code> method and are
|
||||
* calling FreeMarker's {@code Configuration.setSettings()} method and are
|
||||
* subject to constraints set by FreeMarker.
|
||||
*
|
||||
* <p>The "freemarkerVariables" property can be used to specify a Map of
|
||||
* shared variables that will be applied to the Configuration via the
|
||||
* <code>setAllSharedVariables()</code> method. Like <code>setSettings()</code>,
|
||||
* {@code setAllSharedVariables()} method. Like {@code setSettings()},
|
||||
* these entries are subject to FreeMarker constraints.
|
||||
*
|
||||
* <p>The simplest way to use this class is to specify a "templateLoaderPath";
|
||||
@@ -109,7 +110,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
|
||||
/**
|
||||
* Set properties that contain well-known FreeMarker keys which will be
|
||||
* passed to FreeMarker's <code>Configuration.setSettings</code> method.
|
||||
* passed to FreeMarker's {@code Configuration.setSettings} method.
|
||||
* @see freemarker.template.Configuration#setSettings
|
||||
*/
|
||||
public void setFreemarkerSettings(Properties settings) {
|
||||
@@ -118,7 +119,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
|
||||
/**
|
||||
* Set a Map that contains well-known FreeMarker objects which will be passed
|
||||
* to FreeMarker's <code>Configuration.setAllSharedVariables()</code> method.
|
||||
* to FreeMarker's {@code Configuration.setAllSharedVariables()} method.
|
||||
* @see freemarker.template.Configuration#setAllSharedVariables
|
||||
*/
|
||||
public void setFreemarkerVariables(Map<String, Object> variables) {
|
||||
@@ -138,7 +139,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* @deprecated as of Spring 2.0.1, in favor of the "preTemplateLoaders"
|
||||
@@ -147,14 +148,14 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setPostTemplateLoaders
|
||||
*/
|
||||
@Deprecated
|
||||
public void setTemplateLoaders(TemplateLoader[] templateLoaders) {
|
||||
public void setTemplateLoaders(TemplateLoader... templateLoaders) {
|
||||
if (templateLoaders != null) {
|
||||
this.templateLoaders.addAll(Arrays.asList(templateLoaders));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders could be configured and injected here.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
@@ -164,12 +165,12 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPreTemplateLoaders(TemplateLoader[] preTemplateLoaders) {
|
||||
public void setPreTemplateLoaders(TemplateLoader... preTemplateLoaders) {
|
||||
this.preTemplateLoaders = Arrays.asList(preTemplateLoaders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a List of <code>TemplateLoader<code>s that will be used to search
|
||||
* Set a List of {@code TemplateLoader}s that will be used to search
|
||||
* for templates. For example, one or more custom loaders such as database
|
||||
* loaders can be configured.
|
||||
* <p>The {@link TemplateLoader TemplateLoaders} specified here will be
|
||||
@@ -179,7 +180,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* @see #setTemplateLoaderPaths
|
||||
* @see #postProcessTemplateLoaders
|
||||
*/
|
||||
public void setPostTemplateLoaders(TemplateLoader[] postTemplateLoaders) {
|
||||
public void setPostTemplateLoaders(TemplateLoader... postTemplateLoaders) {
|
||||
this.postTemplateLoaders = Arrays.asList(postTemplateLoaders);
|
||||
}
|
||||
|
||||
@@ -198,20 +199,20 @@ public class FreeMarkerConfigurationFactory {
|
||||
* pseudo URLs are supported, as understood by ResourceEditor. Allows for
|
||||
* relative paths when running in an ApplicationContext.
|
||||
* <p>Will define a path for the default FreeMarker template loader.
|
||||
* If a specified resource cannot be resolved to a <code>java.io.File</code>,
|
||||
* If a specified resource cannot be resolved to a {@code java.io.File},
|
||||
* a generic SpringTemplateLoader will be used, without modification detection.
|
||||
* <p>To enforce the use of SpringTemplateLoader, i.e. to not resolve a path
|
||||
* as file system resource in any case, turn off the "preferFileSystemAccess"
|
||||
* flag. See the latter's javadoc for details.
|
||||
* <p>If you wish to specify your own list of TemplateLoaders, do not set this
|
||||
* property and instead use <code>setTemplateLoaders(List templateLoaders)</code>
|
||||
* property and instead use {@code setTemplateLoaders(List templateLoaders)}
|
||||
* @see org.springframework.core.io.ResourceEditor
|
||||
* @see org.springframework.context.ApplicationContext#getResource
|
||||
* @see freemarker.template.Configuration#setDirectoryForTemplateLoading
|
||||
* @see SpringTemplateLoader
|
||||
* @see #setTemplateLoaders
|
||||
*/
|
||||
public void setTemplateLoaderPaths(String[] templateLoaderPaths) {
|
||||
public void setTemplateLoaderPaths(String... templateLoaderPaths) {
|
||||
this.templateLoaderPaths = templateLoaderPaths;
|
||||
}
|
||||
|
||||
@@ -229,7 +230,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* Return the Spring ResourceLoader to use for loading FreeMarker template files.
|
||||
*/
|
||||
protected ResourceLoader getResourceLoader() {
|
||||
return resourceLoader;
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +253,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* Return whether to prefer file system access for template loading.
|
||||
*/
|
||||
protected boolean isPreferFileSystemAccess() {
|
||||
return preferFileSystemAccess;
|
||||
return this.preferFileSystemAccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -293,25 +294,27 @@ public class FreeMarkerConfigurationFactory {
|
||||
config.setDefaultEncoding(this.defaultEncoding);
|
||||
}
|
||||
|
||||
List<TemplateLoader> templateLoaders = new LinkedList<TemplateLoader>(this.templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in early.
|
||||
if (this.preTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.preTemplateLoaders);
|
||||
templateLoaders.addAll(this.preTemplateLoaders);
|
||||
}
|
||||
|
||||
// Register default template loaders.
|
||||
if (this.templateLoaderPaths != null) {
|
||||
for (String path : this.templateLoaderPaths) {
|
||||
this.templateLoaders.add(getTemplateLoaderForPath(path));
|
||||
templateLoaders.add(getTemplateLoaderForPath(path));
|
||||
}
|
||||
}
|
||||
postProcessTemplateLoaders(this.templateLoaders);
|
||||
postProcessTemplateLoaders(templateLoaders);
|
||||
|
||||
// Register template loaders that are supposed to kick in late.
|
||||
if (this.postTemplateLoaders != null) {
|
||||
this.templateLoaders.addAll(this.postTemplateLoaders);
|
||||
templateLoaders.addAll(this.postTemplateLoaders);
|
||||
}
|
||||
|
||||
TemplateLoader loader = getAggregateTemplateLoader(this.templateLoaders);
|
||||
TemplateLoader loader = getAggregateTemplateLoader(templateLoaders);
|
||||
if (loader != null) {
|
||||
config.setTemplateLoader(loader);
|
||||
}
|
||||
@@ -323,7 +326,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
/**
|
||||
* Return a new Configuration object. Subclasses can override this for
|
||||
* custom initialization, or for using a mock object for testing.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* <p>Called by {@code createConfiguration()}.
|
||||
* @return the Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
@@ -374,7 +377,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* To be overridden by subclasses that want to to register custom
|
||||
* TemplateLoader instances after this factory created its default
|
||||
* template loaders.
|
||||
* <p>Called by <code>createConfiguration()</code>. Note that specified
|
||||
* <p>Called by {@code createConfiguration()}. Note that specified
|
||||
* "postTemplateLoaders" will be registered <i>after</i> any loaders
|
||||
* registered by this callback; as a consequence, they are are <i>not</i>
|
||||
* included in the given List.
|
||||
@@ -411,7 +414,7 @@ public class FreeMarkerConfigurationFactory {
|
||||
* To be overridden by subclasses that want to to perform custom
|
||||
* post-processing of the Configuration object after this factory
|
||||
* performed its default initialization.
|
||||
* <p>Called by <code>createConfiguration()</code>.
|
||||
* <p>Called by {@code createConfiguration()}.
|
||||
* @param config the current Configuration object
|
||||
* @throws IOException if a config file wasn't found
|
||||
* @throws TemplateException on FreeMarker initialization failure
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+42
-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.
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.Assert;
|
||||
* {@code CacheOperationSource}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
@@ -71,6 +72,16 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
this.annotationParsers.add(new SpringCacheAnnotationParser());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParser the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheOperationSource(CacheAnnotationParser annotationParser) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notNull(annotationParser, "CacheAnnotationParser must not be null");
|
||||
this.annotationParsers = Collections.singleton(annotationParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
@@ -83,6 +94,16 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
this.annotationParsers = parsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a custom AnnotationCacheOperationSource.
|
||||
* @param annotationParsers the CacheAnnotationParser to use
|
||||
*/
|
||||
public AnnotationCacheOperationSource(Set<CacheAnnotationParser> annotationParsers) {
|
||||
this.publicMethodsOnly = true;
|
||||
Assert.notEmpty(annotationParsers, "At least one CacheAnnotationParser needs to be specified");
|
||||
this.annotationParsers = annotationParsers;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Collection<CacheOperation> findCacheOperations(Class<?> clazz) {
|
||||
@@ -106,7 +127,6 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
*/
|
||||
protected Collection<CacheOperation> determineCacheOperations(AnnotatedElement ae) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
for (CacheAnnotationParser annotationParser : this.annotationParsers) {
|
||||
Collection<CacheOperation> annOps = annotationParser.parseCacheAnnotations(ae);
|
||||
if (annOps != null) {
|
||||
@@ -126,4 +146,24 @@ public class AnnotationCacheOperationSource extends AbstractFallbackCacheOperati
|
||||
protected boolean allowPublicMethodsOnly() {
|
||||
return this.publicMethodsOnly;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof AnnotationCacheOperationSource)) {
|
||||
return false;
|
||||
}
|
||||
AnnotationCacheOperationSource otherCos = (AnnotationCacheOperationSource) other;
|
||||
return (this.annotationParsers.equals(otherCos.annotationParsers) &&
|
||||
this.publicMethodsOnly == otherCos.publicMethodsOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.annotationParsers.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-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.
|
||||
@@ -135,7 +135,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||
return ops;
|
||||
}
|
||||
|
||||
private static <T extends Annotation> Collection<T> getAnnotations(AnnotatedElement ae, Class<T> annotationType) {
|
||||
private <T extends Annotation> Collection<T> getAnnotations(AnnotatedElement ae, Class<T> annotationType) {
|
||||
Collection<T> anns = new ArrayList<T>(2);
|
||||
|
||||
// look at raw annotation
|
||||
@@ -154,4 +154,15 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||
|
||||
return (anns.isEmpty() ? null : anns);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other || other instanceof SpringCacheAnnotationParser);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return SpringCacheAnnotationParser.class.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-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.
|
||||
@@ -47,8 +47,7 @@ abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut
|
||||
return false;
|
||||
}
|
||||
CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(),
|
||||
otherPc.getCacheOperationSource());
|
||||
return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-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.
|
||||
@@ -39,16 +39,16 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
*/
|
||||
class ExpressionEvaluator {
|
||||
|
||||
private SpelExpressionParser parser = new SpelExpressionParser();
|
||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// shared param discoverer since it caches data internally
|
||||
private ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
private final ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>();
|
||||
private final Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>();
|
||||
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>();
|
||||
|
||||
private Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>();
|
||||
private final Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>();
|
||||
|
||||
|
||||
public EvaluationContext createEvaluationContext(
|
||||
|
||||
+5
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,12 +26,10 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract base class implementing the common {@link CacheManager}
|
||||
* methods. Useful for 'static' environments where the backing caches do
|
||||
* not change.
|
||||
* Abstract base class implementing the common {@link CacheManager} methods.
|
||||
* Useful for 'static' environments where the backing caches do not change.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
@@ -46,10 +44,10 @@ public abstract class AbstractCacheManager implements CacheManager, Initializing
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Collection<? extends Cache> caches = loadCaches();
|
||||
Assert.notEmpty(caches, "loadCaches must not return an empty Collection");
|
||||
this.cacheMap.clear();
|
||||
|
||||
// preserve the initial order of the cache names
|
||||
this.cacheMap.clear();
|
||||
this.cacheNames.clear();
|
||||
for (Cache cache : caches) {
|
||||
this.cacheMap.put(cache.getName(), cache);
|
||||
this.cacheNames.add(cache.getName());
|
||||
|
||||
Vendored
+31
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
@@ -40,7 +40,35 @@ import org.springframework.cache.CacheManager;
|
||||
public class NoOpCacheManager implements CacheManager {
|
||||
|
||||
private final ConcurrentMap<String, Cache> caches = new ConcurrentHashMap<String, Cache>();
|
||||
private Set<String> names = new LinkedHashSet<String>();
|
||||
|
||||
private final Set<String> cacheNames = new LinkedHashSet<String>();
|
||||
|
||||
|
||||
/**
|
||||
* This implementation always returns a {@link Cache} implementation that will not store items.
|
||||
* Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = this.caches.get(name);
|
||||
if (cache == null) {
|
||||
this.caches.putIfAbsent(name, new NoOpCache(name));
|
||||
synchronized (this.cacheNames) {
|
||||
this.cacheNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return this.caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
public Collection<String> getCacheNames() {
|
||||
synchronized (this.cacheNames) {
|
||||
return Collections.unmodifiableSet(this.cacheNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class NoOpCache implements Cache {
|
||||
|
||||
@@ -61,7 +89,7 @@ public class NoOpCacheManager implements CacheManager {
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public Object getNativeCache() {
|
||||
@@ -72,30 +100,4 @@ public class NoOpCacheManager implements CacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation always returns a {@link Cache} implementation that will not
|
||||
* store items. Additionally, the request cache will be remembered by the manager for consistency.
|
||||
*/
|
||||
public Cache getCache(String name) {
|
||||
Cache cache = caches.get(name);
|
||||
if (cache == null) {
|
||||
caches.putIfAbsent(name, new NoOpCache(name));
|
||||
synchronized (names) {
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
return caches.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation returns the name of the caches previously requested.
|
||||
*/
|
||||
public Collection<String> getCacheNames() {
|
||||
return Collections.unmodifiableSet(names);
|
||||
}
|
||||
}
|
||||
|
||||
+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
|
||||
|
||||
+7
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,12 +39,13 @@ import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
*/
|
||||
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
private final ScopedProxyMode defaultProxyMode;
|
||||
|
||||
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* Create a new instance of the {@code AnnotationScopeMetadataResolver} class.
|
||||
* @see #AnnotationScopeMetadataResolver(ScopedProxyMode)
|
||||
* @see ScopedProxyMode#NO
|
||||
*/
|
||||
@@ -53,7 +54,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the <code>AnnotationScopeMetadataResolver</code> class.
|
||||
* Create a new instance of the {@code AnnotationScopeMetadataResolver} class.
|
||||
* @param defaultProxyMode the desired scoped-proxy mode
|
||||
*/
|
||||
public AnnotationScopeMetadataResolver(ScopedProxyMode defaultProxyMode) {
|
||||
@@ -77,8 +78,7 @@ public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {
|
||||
ScopeMetadata metadata = new ScopeMetadata();
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
|
||||
AnnotationAttributes attributes =
|
||||
attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
|
||||
AnnotationAttributes attributes = attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
|
||||
if (attributes != null) {
|
||||
metadata.setScopeName(attributes.getString("value"));
|
||||
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
|
||||
|
||||
+8
-7
@@ -25,6 +25,7 @@ import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -120,6 +121,13 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this component provider uses.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourcePatternResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Environment to use when resolving placeholders and evaluating
|
||||
* {@link Profile @Profile}-annotated component classes.
|
||||
@@ -134,13 +142,6 @@ public class ClassPathScanningCandidateComponentProvider implements EnvironmentC
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ResourceLoader that this component provider uses.
|
||||
*/
|
||||
public final ResourceLoader getResourceLoader() {
|
||||
return this.resourcePatternResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource pattern to use when scanning the classpath.
|
||||
* This value will be appended to each base package name.
|
||||
|
||||
+5
-21
@@ -35,7 +35,6 @@ import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.annotation.Resource;
|
||||
@@ -447,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,10 +513,6 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
protected boolean shareable = true;
|
||||
|
||||
private volatile boolean cached = false;
|
||||
|
||||
private volatile Object cachedFieldValue;
|
||||
|
||||
public ResourceElement(Member member, PropertyDescriptor pd) {
|
||||
super(member, pd);
|
||||
}
|
||||
@@ -550,20 +547,7 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
|
||||
@Override
|
||||
protected Object getResourceToInject(Object target, String requestingBeanName) {
|
||||
Object value = null;
|
||||
if (this.cached && this.shareable) {
|
||||
value = this.cachedFieldValue;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (!this.cached) {
|
||||
value = getResource(this, requestingBeanName);
|
||||
if (value != null && this.shareable) {
|
||||
this.cachedFieldValue = value;
|
||||
this.cached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
return getResource(this, requestingBeanName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,4 +725,4 @@ public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBean
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -54,8 +54,7 @@ class ComponentScanAnnotationParser {
|
||||
private final BeanNameGenerator beanNameGenerator;
|
||||
|
||||
|
||||
public ComponentScanAnnotationParser(
|
||||
ResourceLoader resourceLoader, Environment environment,
|
||||
public ComponentScanAnnotationParser(ResourceLoader resourceLoader, Environment environment,
|
||||
BeanNameGenerator beanNameGenerator, BeanDefinitionRegistry registry) {
|
||||
|
||||
this.resourceLoader = resourceLoader;
|
||||
@@ -67,7 +66,7 @@ class ComponentScanAnnotationParser {
|
||||
|
||||
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan) {
|
||||
ClassPathBeanDefinitionScanner scanner =
|
||||
new ClassPathBeanDefinitionScanner(registry, componentScan.getBoolean("useDefaultFilters"));
|
||||
new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"));
|
||||
|
||||
Assert.notNull(this.environment, "Environment must not be null");
|
||||
scanner.setEnvironment(this.environment);
|
||||
@@ -77,14 +76,14 @@ class ComponentScanAnnotationParser {
|
||||
|
||||
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
|
||||
boolean useInheritedGenerator = BeanNameGenerator.class.equals(generatorClass);
|
||||
scanner.setBeanNameGenerator(useInheritedGenerator
|
||||
? this.beanNameGenerator
|
||||
: BeanUtils.instantiateClass(generatorClass));
|
||||
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
|
||||
BeanUtils.instantiateClass(generatorClass));
|
||||
|
||||
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
|
||||
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
|
||||
scanner.setScopedProxyMode(scopedProxyMode);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
|
||||
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
|
||||
}
|
||||
@@ -121,7 +120,7 @@ class ComponentScanAnnotationParser {
|
||||
throw new IllegalStateException("At least one base package must be specified");
|
||||
}
|
||||
|
||||
return scanner.doScan(basePackages.toArray(new String[]{}));
|
||||
return scanner.doScan(StringUtils.toStringArray(basePackages));
|
||||
}
|
||||
|
||||
private List<TypeFilter> typeFiltersFor(AnnotationAttributes filterAttributes) {
|
||||
@@ -153,4 +152,5 @@ class ComponentScanAnnotationParser {
|
||||
}
|
||||
return typeFilters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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()) {
|
||||
|
||||
+55
-57
@@ -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,26 @@ 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() {
|
||||
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 final CallbackFilter callbackFilter;
|
||||
private static final Callback DISPOSABLE_BEAN_METHOD_INTERCEPTOR = new DisposableBeanMethodInterceptor();
|
||||
|
||||
private final Callback[] callbackInstances;
|
||||
|
||||
|
||||
/**
|
||||
@@ -65,28 +79,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 +131,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 +146,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,8 +158,9 @@ class ConfigurationClassEnhancer {
|
||||
* @see BeanMethodInterceptor#enhanceFactoryBean(Class, String)
|
||||
*/
|
||||
private static class GetObjectMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
|
||||
private final ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private final String beanName;
|
||||
|
||||
public GetObjectMethodInterceptor(ConfigurableBeanFactory beanFactory, String beanName) {
|
||||
@@ -177,7 +171,7 @@ class ConfigurationClassEnhancer {
|
||||
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
|
||||
return beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -215,8 +209,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 +250,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
|
||||
@@ -290,7 +295,8 @@ class ConfigurationClassEnhancer {
|
||||
this.beanFactory.setCurrentlyInCreation(beanName, false);
|
||||
}
|
||||
return this.beanFactory.getBean(beanName);
|
||||
} finally {
|
||||
}
|
||||
finally {
|
||||
if (alreadyInCreation) {
|
||||
this.beanFactory.setCurrentlyInCreation(beanName, true);
|
||||
}
|
||||
@@ -327,28 +333,20 @@ 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+62
-60
@@ -16,15 +16,16 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.attributesFor;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
@@ -51,8 +52,6 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.AssignableTypeFilter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.context.annotation.MetadataUtils.*;
|
||||
|
||||
/**
|
||||
* Parses a {@link Configuration} class definition, populating a collection of
|
||||
* {@link ConfigurationClass} objects (parsing a single Configuration class may result in
|
||||
@@ -101,9 +100,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 +109,7 @@ class ConfigurationClassParser {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.registry = registry;
|
||||
this.componentScanParser = new ComponentScanAnnotationParser(
|
||||
resourceLoader, environment, beanNameGenerator, registry);
|
||||
resourceLoader, environment, componentScanBeanNameGenerator, registry);
|
||||
}
|
||||
|
||||
|
||||
@@ -222,10 +220,9 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
// process any @Import annotations
|
||||
List<AnnotationAttributes> imports =
|
||||
findAllAnnotationAttributes(Import.class, metadata.getClassName(), true);
|
||||
for (AnnotationAttributes importAnno : imports) {
|
||||
processImport(configClass, importAnno.getStringArray("value"), true);
|
||||
Set<String> imports = getImports(metadata.getClassName(), null, new HashSet<String>());
|
||||
if (imports != null && !imports.isEmpty()) {
|
||||
processImport(configClass, imports.toArray(new String[imports.size()]), true);
|
||||
}
|
||||
|
||||
// process any @ImportResource annotations
|
||||
@@ -253,6 +250,16 @@ class ConfigurationClassParser {
|
||||
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
|
||||
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
|
||||
}
|
||||
else if (superclass.startsWith("java")) {
|
||||
// never load core JDK classes via ASM, in particular not java.lang.Object!
|
||||
try {
|
||||
return new StandardAnnotationMetadata(
|
||||
this.resourceLoader.getClassLoader().loadClass(superclass), true);
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
|
||||
return reader.getAnnotationMetadata();
|
||||
@@ -265,45 +272,36 @@ class ConfigurationClassParser {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of attribute maps for all declarations of the given annotation
|
||||
* on the given annotated class using the given MetadataReaderFactory to introspect
|
||||
* annotation metadata. Meta-annotations are ordered first in the list, and if the
|
||||
* target annotation is declared directly on the class, its map of attributes will be
|
||||
* ordered last in the list.
|
||||
* @param targetAnnotation the annotation to search for, both locally and as a meta-annotation
|
||||
* @param annotatedClassName the class to inspect
|
||||
* @param classValuesAsString whether class attributes should be returned as strings
|
||||
* Recursively collect all declared {@code @Import} values. Unlike most
|
||||
* meta-annotations it is valid to have several {@code @Import}s declared with
|
||||
* different values, the usual process or returning values from the first
|
||||
* meta-annotation on a class is not sufficient.
|
||||
* <p>For example, it is common for a {@code @Configuration} class to declare direct
|
||||
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
|
||||
* annotation.
|
||||
* @param className the class name to search
|
||||
* @param imports the imports collected so far or {@code null}
|
||||
* @param visited used to track visited classes to prevent infinite recursion (must not be null)
|
||||
* @return a set of all {@link Import#value() import values} or {@code null}
|
||||
* @throws IOException if there is any problem reading metadata from the named class
|
||||
*/
|
||||
private List<AnnotationAttributes> findAllAnnotationAttributes(
|
||||
Class<? extends Annotation> targetAnnotation, String annotatedClassName,
|
||||
boolean classValuesAsString) throws IOException {
|
||||
|
||||
List<AnnotationAttributes> allAttribs = new ArrayList<AnnotationAttributes>();
|
||||
|
||||
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(annotatedClassName);
|
||||
AnnotationMetadata metadata = reader.getAnnotationMetadata();
|
||||
String targetAnnotationType = targetAnnotation.getName();
|
||||
|
||||
for (String annotationType : metadata.getAnnotationTypes()) {
|
||||
if (annotationType.equals(targetAnnotationType)) {
|
||||
continue;
|
||||
private Set<String> getImports(String className, Set<String> imports,
|
||||
Set<String> visited) throws IOException {
|
||||
if (visited.add(className)) {
|
||||
AnnotationMetadata metadata = metadataReaderFactory.getMetadataReader(className).getAnnotationMetadata();
|
||||
for (String annotationType : metadata.getAnnotationTypes()) {
|
||||
imports = getImports(annotationType, imports, visited);
|
||||
}
|
||||
AnnotationMetadata metaAnnotations =
|
||||
this.metadataReaderFactory.getMetadataReader(annotationType).getAnnotationMetadata();
|
||||
AnnotationAttributes targetAttribs =
|
||||
AnnotationAttributes.fromMap(metaAnnotations.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
|
||||
if (targetAttribs != null) {
|
||||
allAttribs.add(targetAttribs);
|
||||
Map<String, Object> attributes = metadata.getAnnotationAttributes(Import.class.getName(), true);
|
||||
if (attributes != null) {
|
||||
String[] value = (String[]) attributes.get("value");
|
||||
if (value != null && value.length > 0) {
|
||||
imports = (imports == null ? new LinkedHashSet<String>() : imports);
|
||||
imports.addAll(Arrays.asList(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationAttributes localAttribs =
|
||||
AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(targetAnnotationType, classValuesAsString));
|
||||
if (localAttribs != null) {
|
||||
allAttribs.add(localAttribs);
|
||||
}
|
||||
|
||||
return allAttribs;
|
||||
return imports;
|
||||
}
|
||||
|
||||
private void processImport(ConfigurationClass configClass, String[] classesToImport, boolean checkForCircularImports) throws IOException {
|
||||
@@ -315,21 +313,25 @@ 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);
|
||||
ImportSelector selector = BeanUtils.instantiateClass(
|
||||
this.resourceLoader.getClassLoader().loadClass(candidate), ImportSelector.class);
|
||||
processImport(configClass, selector.selectImports(importingClassMetadata), false);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
else if (new AssignableTypeFilter(ImportBeanDefinitionRegistrar.class).match(reader, metadataReaderFactory)) {
|
||||
// the candidate class is an ImportBeanDefinitionRegistrar -> delegate to it to register additional bean definitions
|
||||
try {
|
||||
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(Class.forName(candidate), ImportBeanDefinitionRegistrar.class);
|
||||
ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(
|
||||
this.resourceLoader.getClassLoader().loadClass(candidate), ImportBeanDefinitionRegistrar.class);
|
||||
registrar.registerBeanDefinitions(importingClassMetadata, registry);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
@@ -367,6 +369,7 @@ class ConfigurationClassParser {
|
||||
|
||||
|
||||
interface ImportRegistry {
|
||||
|
||||
String getImportingClassFor(String importedClass);
|
||||
}
|
||||
|
||||
@@ -374,14 +377,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,5 +440,4 @@ class ConfigurationClassParser {
|
||||
new Location(importStack.peek().getResource(), metadata));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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 {
|
||||
|
||||
+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.
|
||||
@@ -53,7 +53,8 @@ import java.lang.annotation.Target;
|
||||
public @interface Import {
|
||||
|
||||
/**
|
||||
* The @{@link Configuration} and/or {@link ImportSelector} classes to import.
|
||||
* The @{@link Configuration}, {@link ImportSelector} and/or
|
||||
* {@link ImportBeanDefinitionRegistrar} classes to import.
|
||||
*/
|
||||
Class<?>[] value();
|
||||
}
|
||||
|
||||
+23
-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.
|
||||
@@ -113,7 +113,9 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||
* @see org.springframework.context.ApplicationListener
|
||||
*/
|
||||
protected Collection<ApplicationListener> getApplicationListeners() {
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
synchronized (this.defaultRetriever) {
|
||||
return this.defaultRetriever.getApplicationListeners();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,26 +137,30 @@ public abstract class AbstractApplicationEventMulticaster implements Application
|
||||
else {
|
||||
retriever = new ListenerRetriever(true);
|
||||
LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
|
||||
Set<ApplicationListener> listeners;
|
||||
Set<String> listenerBeans;
|
||||
synchronized (this.defaultRetriever) {
|
||||
for (ApplicationListener listener : this.defaultRetriever.applicationListeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
listeners = new LinkedHashSet<ApplicationListener>(this.defaultRetriever.applicationListeners);
|
||||
listenerBeans = new LinkedHashSet<String>(this.defaultRetriever.applicationListenerBeans);
|
||||
}
|
||||
for (ApplicationListener listener : listeners) {
|
||||
if (supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListeners.add(listener);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
if (!listenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : listenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
if (!this.defaultRetriever.applicationListenerBeans.isEmpty()) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
for (String listenerBeanName : this.defaultRetriever.applicationListenerBeans) {
|
||||
ApplicationListener listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
|
||||
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
|
||||
retriever.applicationListenerBeans.add(listenerBeanName);
|
||||
allListeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
}
|
||||
OrderComparator.sort(allListeners);
|
||||
this.retrieverCache.put(cacheKey, retriever);
|
||||
return allListeners;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-10
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -387,9 +390,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
public void setParent(ApplicationContext parent) {
|
||||
this.parent = parent;
|
||||
if (parent != null) {
|
||||
Object parentEnvironment = parent.getEnvironment();
|
||||
Environment parentEnvironment = parent.getEnvironment();
|
||||
if (parentEnvironment instanceof ConfigurableEnvironment) {
|
||||
this.environment.merge((ConfigurableEnvironment)parentEnvironment);
|
||||
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -505,7 +508,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
|
||||
|
||||
// Validate that all properties marked as required are resolvable
|
||||
// see ConfigurablePropertyResolver#setRequiredProperties
|
||||
this.environment.validateRequiredProperties();
|
||||
getEnvironment().validateRequiredProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,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));
|
||||
@@ -566,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());
|
||||
}
|
||||
}
|
||||
@@ -908,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);
|
||||
|
||||
|
||||
+25
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -67,12 +67,12 @@ public class MessageSourceResourceBundle extends ResourceBundle {
|
||||
|
||||
/**
|
||||
* This implementation resolves the code in the MessageSource.
|
||||
* Returns <code>null</code> if the message could not be resolved.
|
||||
* Returns {@code null} if the message could not be resolved.
|
||||
*/
|
||||
@Override
|
||||
protected Object handleGetObject(String code) {
|
||||
protected Object handleGetObject(String key) {
|
||||
try {
|
||||
return this.messageSource.getMessage(code, null, this.locale);
|
||||
return this.messageSource.getMessage(key, null, this.locale);
|
||||
}
|
||||
catch (NoSuchMessageException ex) {
|
||||
return null;
|
||||
@@ -80,17 +80,34 @@ public class MessageSourceResourceBundle extends ResourceBundle {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns <code>null</code>, as a MessageSource does
|
||||
* not allow for enumerating the defined message codes.
|
||||
* This implementation checks whether the target MessageSource can resolve
|
||||
* a message for the given key, translating {@code NoSuchMessageException}
|
||||
* accordingly. In contrast to ResourceBundle's default implementation in
|
||||
* JDK 1.6, this does not rely on the capability to enumerate message keys.
|
||||
*/
|
||||
@Override
|
||||
public boolean containsKey(String key) {
|
||||
try {
|
||||
this.messageSource.getMessage(key, null, this.locale);
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchMessageException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation throws {@code UnsupportedOperationException},
|
||||
* as a MessageSource does not allow for enumerating the defined message codes.
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<String> getKeys() {
|
||||
return null;
|
||||
throw new UnsupportedOperationException("MessageSourceResourceBundle does not support enumerating its keys");
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation exposes the specified Locale for introspection
|
||||
* through the standard <code>ResourceBundle.getLocale()</code> method.
|
||||
* through the standard {@code ResourceBundle.getLocale()} method.
|
||||
*/
|
||||
@Override
|
||||
public Locale getLocale() {
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+16
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,13 +34,13 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
|
||||
/**
|
||||
* EJB3-compliant interceptor class that injects Spring beans into
|
||||
* fields and methods which are annotated with <code>@Autowired</code>.
|
||||
* fields and methods which are annotated with {@code @Autowired}.
|
||||
* Performs injection after construction as well as after activation
|
||||
* of a passivated bean.
|
||||
*
|
||||
* <p>To be applied through an <code>@Interceptors</code> annotation in
|
||||
* <p>To be applied through an {@code @Interceptors} annotation in
|
||||
* the EJB Session Bean or Message-Driven Bean class, or through an
|
||||
* <code>interceptor-binding</code> XML element in the EJB deployment descriptor.
|
||||
* {@code interceptor-binding} XML element in the EJB deployment descriptor.
|
||||
*
|
||||
* <p>Delegates to Spring's {@link AutowiredAnnotationBeanPostProcessor}
|
||||
* underneath, allowing for customization of its specific settings through
|
||||
@@ -60,7 +60,7 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
*
|
||||
* <p><b>WARNING: Do not define the same bean as Spring-managed bean and as
|
||||
* EJB3 session bean in the same deployment unit.</b> In particular, be
|
||||
* careful when using the <code><context:component-scan></code> feature
|
||||
* careful when using the {@code <context:component-scan>} feature
|
||||
* in combination with the deployment of Spring-based EJB3 session beans:
|
||||
* Make sure that the EJB3 session beans are <i>not</i> autodetected as
|
||||
* Spring-managed beans as well, using appropriate package restrictions.
|
||||
@@ -71,8 +71,6 @@ import org.springframework.context.access.ContextSingletonBeanFactoryLocator;
|
||||
* @see org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
|
||||
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
|
||||
* @see #getBeanFactoryLocatorKey
|
||||
* @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocator
|
||||
* @see org.springframework.ejb.support.AbstractEnterpriseBean#setBeanFactoryLocatorKey
|
||||
*/
|
||||
public class SpringBeanAutowiringInterceptor {
|
||||
|
||||
@@ -99,9 +97,15 @@ public class SpringBeanAutowiringInterceptor {
|
||||
invocationContext.proceed();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
throw ex;
|
||||
}
|
||||
catch (Error err) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
throw err;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
doReleaseBean(invocationContext.getTarget());
|
||||
// Cannot declare a checked exception on WebSphere here - so we need to wrap.
|
||||
throw new EJBException(ex);
|
||||
}
|
||||
@@ -130,7 +134,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
/**
|
||||
* Determine the BeanFactory for autowiring the given target bean.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactory to use (never <code>null</code>)
|
||||
* @return the BeanFactory to use (never {@code null})
|
||||
* @see #getBeanFactoryReference
|
||||
*/
|
||||
protected BeanFactory getBeanFactory(Object target) {
|
||||
@@ -146,7 +150,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* <p>The default implementation delegates to {@link #getBeanFactoryLocator}
|
||||
* and {@link #getBeanFactoryLocatorKey}.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryReference to use (never <code>null</code>)
|
||||
* @return the BeanFactoryReference to use (never {@code null})
|
||||
* @see #getBeanFactoryLocator
|
||||
* @see #getBeanFactoryLocatorKey
|
||||
* @see org.springframework.beans.factory.access.BeanFactoryLocator#useBeanFactory(String)
|
||||
@@ -163,7 +167,7 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* <p>The default implementation exposes Spring's default
|
||||
* {@link ContextSingletonBeanFactoryLocator}.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryLocator to use (never <code>null</code>)
|
||||
* @return the BeanFactoryLocator to use (never {@code null})
|
||||
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator#getInstance()
|
||||
*/
|
||||
protected BeanFactoryLocator getBeanFactoryLocator(Object target) {
|
||||
@@ -174,11 +178,11 @@ public class SpringBeanAutowiringInterceptor {
|
||||
* Determine the BeanFactoryLocator key to use. This typically indicates
|
||||
* the bean name of the ApplicationContext definition in
|
||||
* <strong>classpath*:beanRefContext.xml</strong> resource files.
|
||||
* <p>The default is <code>null</code>, indicating the single
|
||||
* <p>The default is {@code null}, indicating the single
|
||||
* ApplicationContext defined in the locator. This must be overridden
|
||||
* if more than one shared ApplicationContext definition is available.
|
||||
* @param target the target bean to autowire
|
||||
* @return the BeanFactoryLocator key to use (or <code>null</code> for
|
||||
* @return the BeanFactoryLocator key to use (or {@code null} for
|
||||
* referring to the single ApplicationContext defined in the locator)
|
||||
*/
|
||||
protected String getBeanFactoryLocatorKey(Object target) {
|
||||
|
||||
+5
-5
@@ -3,20 +3,20 @@
|
||||
*
|
||||
* <p>Base classes to make implementing EJB 2.x beans simpler and less error-prone,
|
||||
* as well as guaranteeing a Spring BeanFactory is available to such EJBs.
|
||||
* This promotes good practice, with EJB services used for transaction
|
||||
* This promotes good EJB practice, with EJB services used for transaction
|
||||
* management, thread management, and (possibly) remoting, while
|
||||
* business logic is implemented in easily testable POJOs.</p>
|
||||
*
|
||||
*
|
||||
* <p>In this model, the EJB is a facade, with as many POJO helpers
|
||||
* behind the BeanFactory as required.</p>
|
||||
*
|
||||
* <p>Note that the default behavior is to look for an EJB enviroment variable
|
||||
*
|
||||
* <p>Note that the default behavior is to look for an EJB environment variable
|
||||
* with name <code>ejb/BeanFactoryPath</code> that specifies the
|
||||
* location <i>on the classpath</i> of an XML bean factory definition
|
||||
* file (such as <code>/com/mycom/mypackage/mybeans.xml</code>).
|
||||
* If this JNDI key is missing, your EJB subclass won't successfully
|
||||
* initialize in the container.</p>
|
||||
*
|
||||
*
|
||||
* <p><b>Check out the <code>org.springframework.ejb.interceptor</code>
|
||||
* package for equivalent support for the EJB 3 component model</b>,
|
||||
* providing annotation-based autowiring using an EJB 3 interceptor.</p>
|
||||
|
||||
+14
-8
@@ -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.
|
||||
@@ -24,12 +24,15 @@ import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link LoadTimeWeaver} implementation for OC4J's instrumentable ClassLoader.
|
||||
*
|
||||
* <p><b>NOTE:</b> Requires Oracle OC4J version 10.1.3.1 or higher.
|
||||
* Requires Oracle OC4J version 10.1.3.1 or higher.
|
||||
*
|
||||
* <p>Many thanks to <a href="mailto:mike.keith@oracle.com">Mike Keith</a>
|
||||
* for his assistance.
|
||||
*
|
||||
* <p><b>NOTE:</b> This class will be deprecated as of Spring 3.2, in favor of
|
||||
* {@link org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver}
|
||||
* since Oracle end-of-lifed OC4J in favor of WebLogic.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
@@ -38,10 +41,11 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
|
||||
|
||||
private final OC4JClassLoaderAdapter classLoader;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance of thie {@link OC4JLoadTimeWeaver} class
|
||||
* using the default {@link ClassLoader class loader}.
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
public OC4JLoadTimeWeaver() {
|
||||
this(ClassUtils.getDefaultClassLoader());
|
||||
@@ -57,18 +61,20 @@ public class OC4JLoadTimeWeaver implements LoadTimeWeaver {
|
||||
this.classLoader = new OC4JClassLoaderAdapter(classLoader);
|
||||
}
|
||||
|
||||
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
Assert.notNull(transformer, "Transformer must not be null");
|
||||
// Since OC4J 10.1.3's PolicyClassLoader is going to be removed,
|
||||
// we rely on the ClassLoaderUtilities API instead.
|
||||
classLoader.addTransformer(transformer);
|
||||
this.classLoader.addTransformer(transformer);
|
||||
}
|
||||
|
||||
public ClassLoader getInstrumentableClassLoader() {
|
||||
return classLoader.getClassLoader();
|
||||
return this.classLoader.getClassLoader();
|
||||
}
|
||||
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
return classLoader.getThrowawayClassLoader();
|
||||
return this.classLoader.getThrowawayClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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
|
||||
|
||||
+11
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.jmx.support;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
@@ -92,19 +93,19 @@ public class MBeanRegistrationSupport {
|
||||
private static final Constants constants = new Constants(MBeanRegistrationSupport.class);
|
||||
|
||||
/**
|
||||
* <code>Log</code> instance for this class.
|
||||
* {@code Log} instance for this class.
|
||||
*/
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
/**
|
||||
* The <code>MBeanServer</code> instance being used to register beans.
|
||||
* The {@code MBeanServer} instance being used to register beans.
|
||||
*/
|
||||
protected MBeanServer server;
|
||||
|
||||
/**
|
||||
* The beans that have been registered by this exporter.
|
||||
*/
|
||||
protected final Set<ObjectName> registeredBeans = new LinkedHashSet<ObjectName>();
|
||||
protected final Set<ObjectName> registeredBeans = Collections.synchronizedSet(new LinkedHashSet<ObjectName>());
|
||||
|
||||
/**
|
||||
* The action take when registering an MBean and finding that it already exists.
|
||||
@@ -114,16 +115,16 @@ public class MBeanRegistrationSupport {
|
||||
|
||||
|
||||
/**
|
||||
* Specify the <code>MBeanServer</code> instance with which all beans should
|
||||
* be registered. The <code>MBeanExporter</code> will attempt to locate an
|
||||
* existing <code>MBeanServer</code> if none is supplied.
|
||||
* Specify the {@code MBeanServer} instance with which all beans should
|
||||
* be registered. The {@code MBeanExporter} will attempt to locate an
|
||||
* existing {@code MBeanServer} if none is supplied.
|
||||
*/
|
||||
public void setServer(MBeanServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the <code>MBeanServer</code> that the beans will be registered with.
|
||||
* Return the {@code MBeanServer} that the beans will be registered with.
|
||||
*/
|
||||
public final MBeanServer getServer() {
|
||||
return this.server;
|
||||
@@ -205,10 +206,9 @@ public class MBeanRegistrationSupport {
|
||||
* Unregisters all beans that have been registered by an instance of this class.
|
||||
*/
|
||||
protected void unregisterBeans() {
|
||||
for (ObjectName objectName : this.registeredBeans) {
|
||||
for (ObjectName objectName : new LinkedHashSet<ObjectName>(this.registeredBeans)) {
|
||||
doUnregister(objectName);
|
||||
}
|
||||
this.registeredBeans.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,6 +234,7 @@ public class MBeanRegistrationSupport {
|
||||
logger.error("Could not unregister MBean [" + objectName + "]", ex);
|
||||
}
|
||||
}
|
||||
this.registeredBeans.remove(objectName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+27
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +19,7 @@ package org.springframework.remoting.rmi;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.NamingException;
|
||||
import javax.rmi.PortableRemoteObject;
|
||||
|
||||
@@ -54,7 +54,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* Spring's unchecked RemoteAccessException.
|
||||
*
|
||||
* <p>The JNDI environment can be specified as "jndiEnvironment" property,
|
||||
* or be configured in a <code>jndi.properties</code> file or as system properties.
|
||||
* or be configured in a {@code jndi.properties} file or as system properties.
|
||||
* For example:
|
||||
*
|
||||
* <pre class="code"><property name="jndiEnvironment">
|
||||
@@ -88,6 +88,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
|
||||
private boolean refreshStubOnConnectFailure = false;
|
||||
|
||||
private boolean exposeAccessContext = false;
|
||||
|
||||
private Object cachedStub;
|
||||
|
||||
private final Object stubMonitor = new Object();
|
||||
@@ -166,6 +168,18 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
this.refreshStubOnConnectFailure = refreshStubOnConnectFailure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to expose the JNDI environment context for all access to the target
|
||||
* RMI stub, i.e. for all method invocations on the exposed object reference.
|
||||
* <p>Default is "false", i.e. to only expose the JNDI context for object lookup.
|
||||
* Switch this flag to "true" in order to expose the JNDI environment (including
|
||||
* the authorization context) for each RMI invocation, as needed by WebLogic
|
||||
* for RMI stubs with authorization requirements.
|
||||
*/
|
||||
public void setExposeAccessContext(boolean exposeAccessContext) {
|
||||
this.exposeAccessContext = exposeAccessContext;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws NamingException {
|
||||
@@ -190,8 +204,8 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
else if (getServiceInterface() != null) {
|
||||
boolean isImpl = getServiceInterface().isInstance(remoteObj);
|
||||
logger.debug("Using service interface [" + getServiceInterface().getName() +
|
||||
"] for JNDI RMI object [" + getJndiName() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
"] for JNDI RMI object [" + getJndiName() + "] - " +
|
||||
(!isImpl ? "not " : "") + "directly implemented");
|
||||
}
|
||||
}
|
||||
if (this.cacheStub) {
|
||||
@@ -268,13 +282,15 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see java.rmi.NoSuchObjectException
|
||||
*/
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object stub = null;
|
||||
Object stub;
|
||||
try {
|
||||
stub = getStub();
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
throw new RemoteLookupFailureException("JNDI lookup for RMI service [" + getJndiName() + "] failed", ex);
|
||||
}
|
||||
|
||||
Context ctx = (this.exposeAccessContext ? getJndiTemplate().getContext() : null);
|
||||
try {
|
||||
return doInvoke(invocation, stub);
|
||||
}
|
||||
@@ -297,6 +313,9 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
getJndiTemplate().releaseContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +373,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see #invoke
|
||||
*/
|
||||
protected Object refreshAndRetry(MethodInvocation invocation) throws Throwable {
|
||||
Object freshStub = null;
|
||||
Object freshStub;
|
||||
synchronized (this.stubMonitor) {
|
||||
this.cachedStub = null;
|
||||
freshStub = lookupStub();
|
||||
@@ -426,7 +445,7 @@ public class JndiRmiClientInterceptor extends JndiObjectLocator implements Metho
|
||||
* @see org.springframework.remoting.support.RemoteInvocation
|
||||
*/
|
||||
protected Object doInvoke(MethodInvocation methodInvocation, RmiInvocationHandler invocationHandler)
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
throws RemoteException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
|
||||
if (AopUtils.isToStringMethod(methodInvocation.getMethod())) {
|
||||
return "RMI invoker proxy for service URL [" + getJndiName() + "]";
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ import java.lang.annotation.Target;
|
||||
* @see AnnotationAsyncExecutionInterceptor
|
||||
* @see AsyncAnnotationAdvisor
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Async {
|
||||
|
||||
+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) {
|
||||
|
||||
+33
@@ -52,6 +52,39 @@
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mode" default="proxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Should annotated beans be proxied using Spring's AOP framework,
|
||||
or should they rather be weaved with an AspectJ async execution aspect?
|
||||
|
||||
AspectJ weaving requires spring-aspects.jar on the classpath,
|
||||
as well as load-time weaving (or compile-time weaving) enabled.
|
||||
|
||||
Note: The weaving-based aspect requires the @Async annotation to be
|
||||
defined on the concrete class. Annotations in interfaces will not work
|
||||
in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="proxy"/>
|
||||
<xsd:enumeration value="aspectj"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
|
||||
Note: Class-based proxies require the @Async annotation to be defined
|
||||
on the concrete class. Annotations in interfaces will not work in
|
||||
that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
|
||||
+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;
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class AnnotationCacheOperationSourceTest {
|
||||
public class AnnotationCacheOperationSourceTests {
|
||||
|
||||
private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||
|
||||
+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) {
|
||||
|
||||
+53
@@ -25,10 +25,14 @@ import org.junit.Test;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.scheduling.annotation.AsyncAnnotationBeanPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
@@ -77,6 +81,24 @@ public class ImportAwareTests {
|
||||
assertThat(foo, is("xyz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importRegistrar() throws Exception {
|
||||
ImportedRegistrar.called = false;
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ImportingRegistrarConfig.class);
|
||||
ctx.refresh();
|
||||
assertNotNull(ctx.getBean("registrarImportedBean"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importRegistrarWithImport() throws Exception {
|
||||
ImportedRegistrar.called = false;
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
ctx.register(ImportingRegistrarConfigWithImport.class);
|
||||
ctx.refresh();
|
||||
assertNotNull(ctx.getBean("registrarImportedBean"));
|
||||
assertNotNull(ctx.getBean(ImportedConfig.class));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(ImportedConfig.class)
|
||||
@@ -131,4 +153,35 @@ public class ImportAwareTests {
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableImportRegistrar
|
||||
static class ImportingRegistrarConfig {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableImportRegistrar
|
||||
@Import(ImportedConfig.class)
|
||||
static class ImportingRegistrarConfigWithImport {
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(ImportedRegistrar.class)
|
||||
public @interface EnableImportRegistrar {
|
||||
}
|
||||
|
||||
static class ImportedRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
static boolean called;
|
||||
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
BeanDefinition beanDefinition = new GenericBeanDefinition();
|
||||
beanDefinition.setBeanClassName(String.class.getName());
|
||||
registry.registerBeanDefinition("registrarImportedBean", beanDefinition );
|
||||
Assert.state(called == false, "ImportedRegistrar called twice");
|
||||
called = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+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()));
|
||||
}
|
||||
|
||||
}
|
||||
+65
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -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) {
|
||||
@@ -100,8 +115,8 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
if (alwaysUseMessageFormat) {
|
||||
pvs.add("alwaysUseMessageFormat", Boolean.TRUE);
|
||||
}
|
||||
Class clazz = reloadable ?
|
||||
(Class) ReloadableResourceBundleMessageSource.class : ResourceBundleMessageSource.class;
|
||||
Class<?> clazz = reloadable ?
|
||||
(Class<?>) ReloadableResourceBundleMessageSource.class : ResourceBundleMessageSource.class;
|
||||
ac.registerSingleton("messageSource", clazz, pvs);
|
||||
ac.refresh();
|
||||
|
||||
@@ -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,22 @@ public class ResourceBundleMessageSourceTests extends TestCase {
|
||||
assertEquals(0, filenames.size());
|
||||
}
|
||||
|
||||
public void testMessageSourceResourceBundle() {
|
||||
ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
|
||||
ms.setBasename("org/springframework/context/support/messages");
|
||||
MessageSourceResourceBundle rbe = new MessageSourceResourceBundle(ms, Locale.ENGLISH);
|
||||
assertEquals("message1", rbe.getString("code1"));
|
||||
assertTrue(rbe.containsKey("code1"));
|
||||
MessageSourceResourceBundle rbg = new MessageSourceResourceBundle(ms, Locale.GERMAN);
|
||||
assertEquals("nachricht2", rbg.getString("code2"));
|
||||
assertTrue(rbg.containsKey("code2"));
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
+9
-1
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.scheduling.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -200,7 +202,7 @@ public class AsyncExecutionTests {
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e1-"));
|
||||
}
|
||||
|
||||
@Async("e2")
|
||||
@MyAsync
|
||||
public Future<String> returnSomething(int i) {
|
||||
assertTrue(!Thread.currentThread().getName().equals(originalThreadName));
|
||||
assertTrue(Thread.currentThread().getName().startsWith("e2-"));
|
||||
@@ -215,6 +217,12 @@ public class AsyncExecutionTests {
|
||||
}
|
||||
|
||||
|
||||
@Async("e2")
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MyAsync {
|
||||
}
|
||||
|
||||
|
||||
@Async
|
||||
public static class AsyncClassBean {
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
<parent>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-parent</artifactId>
|
||||
<relativePath>../org.springframework.spring-parent</relativePath>
|
||||
<version>3.1.2.RELEASE</version>
|
||||
<version>3.1.5.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<licenses>
|
||||
<license>
|
||||
|
||||
+7
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
@@ -91,7 +90,7 @@ public abstract class BridgeMethodResolver {
|
||||
* Searches for the bridged method in the given candidates.
|
||||
* @param candidateMethods the List of candidate Methods
|
||||
* @param bridgeMethod the bridge method
|
||||
* @return the bridged method, or <code>null</code> if none found
|
||||
* @return the bridged method, or {@code null} if none found
|
||||
*/
|
||||
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
|
||||
if (candidateMethods.isEmpty()) {
|
||||
@@ -114,7 +113,7 @@ public abstract class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the supplied '<code>candidateMethod</code>' can be
|
||||
* Returns {@code true} if the supplied '{@code candidateMethod}' can be
|
||||
* consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
|
||||
* by the supplied {@link Method bridge Method}. This method performs inexpensive
|
||||
* checks and can be used quickly filter for a set of possible matches.
|
||||
@@ -145,7 +144,7 @@ public abstract class BridgeMethodResolver {
|
||||
private static Method findGenericDeclaration(Method bridgeMethod) {
|
||||
// Search parent types for method that has same signature as bridge.
|
||||
Class superclass = bridgeMethod.getDeclaringClass().getSuperclass();
|
||||
while (!Object.class.equals(superclass)) {
|
||||
while (superclass != null && !Object.class.equals(superclass)) {
|
||||
Method method = searchForMatch(superclass, bridgeMethod);
|
||||
if (method != null && !method.isBridge()) {
|
||||
return method;
|
||||
@@ -166,10 +165,10 @@ public abstract class BridgeMethodResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the {@link Type} signature of both the supplied
|
||||
* Returns {@code true} if the {@link Type} signature of both the supplied
|
||||
* {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
|
||||
* are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
|
||||
* TypeVariable Map, otherwise returns <code>false</code>.
|
||||
* TypeVariable Map, otherwise returns {@code false}.
|
||||
*/
|
||||
private static boolean isResolvedTypeMatch(
|
||||
Method genericMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {
|
||||
@@ -205,7 +204,7 @@ public abstract class BridgeMethodResolver {
|
||||
/**
|
||||
* If the supplied {@link Class} has a declared {@link Method} whose signature matches
|
||||
* that of the supplied {@link Method}, then this matching {@link Method} is returned,
|
||||
* otherwise <code>null</code> is returned.
|
||||
* otherwise {@code null} is returned.
|
||||
*/
|
||||
private static Method searchForMatch(Class type, Method bridgeMethod) {
|
||||
return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
|
||||
@@ -219,8 +218,6 @@ public abstract class BridgeMethodResolver {
|
||||
* @return whether signatures match as described
|
||||
*/
|
||||
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
|
||||
Assert.isTrue(bridgeMethod != null);
|
||||
Assert.isTrue(bridgedMethod != null);
|
||||
if (bridgeMethod == bridgedMethod) {
|
||||
return true;
|
||||
}
|
||||
|
||||
+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();
|
||||
}
|
||||
|
||||
|
||||
+8
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,12 +39,11 @@ import org.springframework.util.ClassUtils;
|
||||
/**
|
||||
* Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
|
||||
* information in the method attributes to discover parameter names. Returns
|
||||
* <code>null</code> if the class file was compiled without debug information.
|
||||
* {@code null} if the class file was compiled without debug information.
|
||||
*
|
||||
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer
|
||||
* instance caches the ASM discovered information for each introspected Class, in a
|
||||
* thread-safe manner. It is recommended to reuse discoverer instances
|
||||
* as far as possible.
|
||||
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer instance
|
||||
* caches the ASM discovered information for each introspected Class, in a thread-safe
|
||||
* manner. It is recommended to reuse ParameterNameDiscoverer instances as far as possible.
|
||||
*
|
||||
* @author Adrian Colyer
|
||||
* @author Costin Leau
|
||||
@@ -64,15 +63,15 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
|
||||
|
||||
|
||||
public String[] getParameterNames(Method method) {
|
||||
Class<?> declaringClass = method.getDeclaringClass();
|
||||
Method originalMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||
Class<?> declaringClass = originalMethod.getDeclaringClass();
|
||||
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
|
||||
if (map == null) {
|
||||
// initialize cache
|
||||
map = inspectClass(declaringClass);
|
||||
this.parameterNamesCache.put(declaringClass, map);
|
||||
}
|
||||
if (map != NO_DEBUG_INFO_MAP) {
|
||||
return map.get(method);
|
||||
return map.get(originalMethod);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -82,14 +81,12 @@ public class LocalVariableTableParameterNameDiscoverer implements ParameterNameD
|
||||
Class<?> declaringClass = ctor.getDeclaringClass();
|
||||
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
|
||||
if (map == null) {
|
||||
// initialize cache
|
||||
map = inspectClass(declaringClass);
|
||||
this.parameterNamesCache.put(declaringClass, map);
|
||||
}
|
||||
if (map != NO_DEBUG_INFO_MAP) {
|
||||
return map.get(ctor);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+35
-37
@@ -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.
|
||||
@@ -269,7 +269,6 @@ public class MethodParameter {
|
||||
* @param annotationType the annotation type to look for
|
||||
* @return the annotation object, or <code>null</code> if not found
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
|
||||
return getAnnotatedElement().getAnnotation(annotationType);
|
||||
}
|
||||
@@ -311,14 +310,14 @@ public class MethodParameter {
|
||||
* Return true if the parameter has at least one annotation, false if it has none.
|
||||
*/
|
||||
public boolean hasParameterAnnotations() {
|
||||
return getParameterAnnotations().length != 0;
|
||||
return (getParameterAnnotations().length != 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the parameter has the given annotation type, and false if it doesn't.
|
||||
*/
|
||||
public <T extends Annotation> boolean hasParameterAnnotation(Class<T> annotationType) {
|
||||
return getParameterAnnotation(annotationType) != null;
|
||||
return (getParameterAnnotation(annotationType) != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -417,6 +416,38 @@ public class MethodParameter {
|
||||
return this.typeIndexesPerLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof MethodParameter) {
|
||||
MethodParameter other = (MethodParameter) obj;
|
||||
|
||||
if (this.parameterIndex != other.parameterIndex) {
|
||||
return false;
|
||||
}
|
||||
else if (this.getMember().equals(other.getMember())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.hash;
|
||||
if (result == 0) {
|
||||
result = getMember().hashCode();
|
||||
result = 31 * result + this.parameterIndex;
|
||||
this.hash = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new MethodParameter for the given method or constructor.
|
||||
@@ -439,37 +470,4 @@ public class MethodParameter {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj != null && obj instanceof MethodParameter) {
|
||||
MethodParameter other = (MethodParameter) obj;
|
||||
|
||||
if (this.parameterIndex != other.parameterIndex) {
|
||||
return false;
|
||||
}
|
||||
else if (this.getMember().equals(other.getMember())) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.hash;
|
||||
if (result == 0) {
|
||||
result = getMember().hashCode();
|
||||
result = 31 * result + this.parameterIndex;
|
||||
this.hash = result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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
+36
-28
@@ -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;
|
||||
@@ -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() {
|
||||
@@ -396,8 +403,11 @@ public abstract class AbstractEnvironment implements ConfigurableEnvironment {
|
||||
for (String profile : parent.getActiveProfiles()) {
|
||||
this.activeProfiles.add(profile);
|
||||
}
|
||||
for (String profile : parent.getDefaultProfiles()) {
|
||||
this.defaultProfiles.add(profile);
|
||||
if (parent.getDefaultProfiles().length > 0) {
|
||||
this.defaultProfiles.remove(RESERVED_DEFAULT_PROFILE_NAME);
|
||||
for (String profile : parent.getDefaultProfiles()) {
|
||||
this.defaultProfiles.add(profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,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);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user