It is now advised that destroyMethod="shutdown" should be used
on @Bean methods returning an ExecutorService.
Backport-Issue: SPR-9280
Backport-Commit: 6da03a61b22696283c2c5c79f8f88b5c36480560
Changes in commit 41ade68b50 introduced
a regression causing all but the first location in the
@PropertySource#value array to be ignored during ${...} placeholder
resolution. This change ensures that all locations are processed and
replaced as expected.
Issue: SPR-9133
Backport-Issue: SPR-9127
Backport-Commit: 4df2a14b13
Previously, a user could specify an empty array of resource locations
to the @PropertySource annotation, which amounts to a meaningless no-op.
ConfigurationClassParser now throws IllegalArgumentException upon
encountering any such misconfiguration.
Prior to this commit, specifying a named @PropertySource with multiple
values would not work as expected. e.g.:
@PropertySource(
name = "ps",
value = { "classpath:a.properties", "classpath:b.properties" })
In this scenario, the implementation would register a.properties with
the name "ps", and subsequently register b.properties with the name
"ps", overwriting the entry for a.properties.
To fix this behavior, a CompositePropertySource type has been introduced
which accepts a single name and a set of PropertySource objects to
iterate over. ConfigurationClassParser's @PropertySource parsing routine
has been updated to use this composite approach when necessary, i.e.
when both an explicit name and more than one location have been
specified.
Note that if no explicit name is specified, the generated property
source names are enough to distinguish the instances and avoid
overwriting each other; this is why the composite wrapper is not used
in these cases.
Issue: SPR-9127
Since the introduction of the AnnotationConfig(Web)ApplicationContext
types in Spring 3.0, it has been possible to specify a custom
bean name generation strategy via the #setBeanNameGenerator methods
available on each of these classes.
If specified, that BeanNameGenerator was delegated to the underlying
AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner. This
meant that any @Configuration classes registered or scanned directly
from the application context, e.g. via #register or #scan methods would
respect the custom name generation strategy as intended.
However, for @Configuration classes picked up via @Import or implicitly
registered due to being nested classes would not be aware of this
strategy, and would rather fall back to a hard-coded default
AnnotationBeanNameGenerator.
This change ensures consistent application of custom BeanNameGenerator
strategies in the following ways:
- Introduction of AnnotationConfigUtils#CONFIGURATION_BEAN_NAME_GENERATOR
singleton
If a custom BeanNameGenerator is specified via #setBeanNameGenerator
the AnnotationConfig* application contexts will, in addition to
delegating this object to the underlying reader and scanner, register
it as a singleton bean within the enclosing bean factory having the
constant name mentioned above.
ConfigurationClassPostProcessor now checks for the presence of this
singleton, falling back to a default AnnotationBeanNameGenerator if
not present. This singleton-based approach is necessary because it is
otherwise impossible to parameterize CCPP, given that it is
registered as a BeanDefinitionRegistryPostProcessor bean definition
in AnnotationConfigUtils#registerAnnotationConfigProcessors
- Introduction of ConfigurationClassPostProcessor#setBeanNameGenerator
As detailed in the Javadoc for this new method, this allows for
customizing the BeanNameGenerator via XML by dropping down to direct
registration of CCPP as a <bean> instead of using
<context:annotation-config> to enable @Configuration class
processing.
- Smarter defaulting for @ComponentScan#beanNameGenerator
Previously, @ComponentScan's #beanNameGenerator attribute had a
default value of AnnotationBeanNameGenerator. The value is now the
BeanNameGenerator interface itself, indicating that the scanner
dedicated to processing each @ComponentScan should fall back to an
inherited generator, i.e. the one originally specified against the
application context, or the original default provided by
ConfigurationClassPostProcessor. This means that name generation
strategies will be consistent with a single point of configuration,
but that individual @ComponentScan declarations may still customize
the strategy for the beans that are picked up by that particular
scanning.
Issue: SPR-9124
Prior to this commit, and based on earlier changes supporting SPR-9023,
ConfigurationClassBeanDefinitionReader employed a simplistic strategy
for extracting the 'value' attribute (if any) from @Configuration in
order to determine the bean name for imported and nested configuration
classes. An example case follows:
@Configuration("myConfig")
public class AppConfig { ... }
This approach is too simplistic however, given that it is possible in
'configuration lite' mode to specify a @Component-annotated class with
@Bean methods, e.g.
@Component("myConfig")
public class AppConfig {
@Bean
public Foo foo() { ... }
}
In this case, it's the 'value' attribute of @Component, not
@Configuration, that should be consulted for the bean name. Or indeed if
it were any other stereotype annotation meta-annotated with @Component,
the value attribute should respected.
This kind of sophisticated discovery is exactly what
AnnotationBeanNameGenerator was designed to do, and
ConfigurationClassBeanDefinitionReader now uses it in favor of the
custom approach described above.
To enable this refactoring, nested and imported configuration classes
are no longer registered as GenericBeanDefinition, but rather as
AnnotatedGenericBeanDefinition given that AnnotationBeanNameGenerator
falls back to a generic strategy unless the bean definition in question
is assignable to AnnotatedBeanDefinition.
A new constructor accepting AnnotationMetadata
has been added to AnnotatedGenericBeanDefinition in order to support
the ASM-based approach in use by configuration class processing. Javadoc
has been updated for both AnnotatedGenericBeanDefinition and its now
very similar cousin ScannedGenericBeanDefinition to make clear the
semantics and intention of these two variants.
Issue: SPR-9023
Prior to this commit, an infinite recursion would occur if a
@Configuration class were nested within its superclass, e.g.
abstract class Parent {
@Configuration
static class Child extends Parent { ... }
}
This is because the processing of the nested class automatically
checks the superclass hierarchy for certain reasons, and each
superclass is in turn checked for nested @Configuration classes.
The ConfigurationClassParser implementation now prevents this by
keeping track of known superclasses, i.e. once a superclass has been
processed, it is never again checked for nested classes, etc.
Issue: SPR-8955
Eclipse allows autoboxing on type inference; Sun javac does not. This
means that variables assigned from calls to
AnnotationAttributes#getNumber should consistently use object wrappers
as opposed to number primitives. There was only one such instance
anyway, and has now been updated accordingly.
- Drop 'expectedType' parameter from #getClass and #getEnum methods and
rely on compiler inference based on type of assigned variable, e.g.
public @interface Example {
Color color();
Class<? extends UserType> userType();
int order() default 0;
}
AnnotationAttributes example =
AnnotationUtils.getAnnotationAttributes(Example.class, true, true);
Color color = example.getEnum("color");
Class<? extends UserType> userType = example.getClass("userType");
or in cases where there is no variable assignment (and thus no
inference possible), use explicit generic type, e.g.
bean.setColor(example.<Color>getEnum("color"));
- Rename #get{Int=>Number} and update return type from int to
<N extends Number>, allowing invocations such as:
int order = example.getNumber("order");
These changes reduce the overall number of methods exposed by
AnnotationAttributes, while at the same time providing comprehensive
access to all possible annotation attribute types -- that is, instead of
requiring explicit #getInt, #getFloat, #getDouble methods, the
single #getNumber method is capabable of handling them all, and without
any casting required. And the obvious additional benefit is more concise
invocation as no redundant 'expectedType' parameters are required.
Uses of AnnotationMetadata#getAnnotationAttributes throughout the
framework have been updated to use the new AnnotationAttributes API in
order to take advantage of the more concise, expressive and type-safe
methods there.
All changes are binary compatible to the 3.1.0 public API, save
the exception below.
A minor binary compatibility issue has been introduced in
AbstractCachingConfiguration, AbstractAsyncConfiguration and
AbstractTransactionManagementConfiguration when updating their
protected Map<String, Object> fields representing annotation attributes
to use the new AnnotationAttributes API. This is a negligible breakage,
however, as the likelilhood of users subclassing these types is very
low, the classes have only been in existence for a short time (further
reducing the likelihood), and it is a source-compatible change given
that AnnotationAttributes is assignable to Map<String, Object>.
Background
Spring 3.1 introduced the @ComponentScan annotation, which can accept
an optional array of include and/or exclude @Filter annotations, e.g.
@ComponentScan(
basePackages = "com.acme.app",
includeFilters = { @Filter(MyStereotype.class), ... }
)
@Configuration
public class AppConfig { ... }
@ComponentScan and other annotations related to @Configuration class
processing such as @Import, @ImportResource and the @Enable*
annotations are parsed using reflection in certain code paths, e.g.
when registered directly against AnnotationConfigApplicationContext,
and via ASM in other code paths, e.g. when a @Configuration class is
discovered via an XML bean definition or when included via the
@Import annotation.
The ASM-based approach is designed to avoid premature classloading of
user types and is instrumental in providing tooling support (STS, etc).
Prior to this commit, the ASM-based routines for reading annotation
attributes were unable to recurse into nested annotations, such as in
the @Filter example above. Prior to Spring 3.1 this was not a problem,
because prior to @ComponentScan, there were no cases of nested
annotations in the framework.
This limitation manifested itself in cases where users encounter
the ASM-based annotation parsing code paths AND declare
@ComponentScan annotations with explicit nested @Filter annotations.
In these cases, the 'includeFilters' and 'excludeFilters' attributes
are simply empty where they should be populated, causing the framework
to ignore the filter directives and provide incorrect results from
component scanning.
The purpose of this change then, is to introduce the capability on the
ASM side to recurse into nested annotations and annotation arrays. The
challenge in doing so is that the nested annotations themselves cannot
be realized as annotation instances, so must be represented as a
nested Map (or, as described below, the new AnnotationAttributes type).
Furthermore, the reflection-based annotation parsing must also be
updated to treat nested annotations in a similar fashion; even though
the reflection-based approach has no problem accessing nested
annotations (it just works out of the box), for substitutability
against the AnnotationMetadata SPI, both ASM- and reflection-based
implementations should return the same results in any case. Therefore,
the reflection-based StandardAnnotationMetadata has also been updated
with an optional 'nestedAnnotationsAsMap' constructor argument that is
false by default to preserve compatibility in the rare case that
StandardAnnotationMetadata is being used outside the core framework.
Within the framework, all uses of StandardAnnotationMetadata have been
updated to set this new flag to true, meaning that nested annotation
results will be consistent regardless the parsing approach used.
Spr9031Tests corners this bug and demonstrates that nested @Filter
annotations can be parsed and read in both the ASM- and
reflection-based paths.
Major changes
- AnnotationAttributes has been introduced as a concrete
LinkedHashMap<String, Object> to be used anywhere annotation
attributes are accessed, providing error reporting on attribute
lookup and convenient type-safe access to common annotation types
such as String, String[], boolean, int, and nested annotation and
annotation arrays, with the latter two also returned as
AnnotationAttributes instances.
- AnnotationUtils#getAnnotationAttributes methods now return
AnnotationAttributes instances, even though for binary compatibility
the signatures of these methods have been preserved as returning
Map<String, Object>.
- AnnotationAttributes#forMap provides a convenient mechanism for
adapting any Map<String, Object> into an AnnotationAttributes
instance. In the case that the Map is already actually of
type AnnotationAttributes, it is simply casted and returned.
Otherwise, the map is supplied to the AnnotationAttributes(Map)
constructor and wrapped in common collections style.
- The protected MetadataUtils#attributesFor(Metadata, Class) provides
further convenience in the many locations throughout the
.context.annotation packagage that depend on annotation attribute
introspection.
- ASM-based core.type.classreading package reworked
Specifically, AnnotationAttributesReadingVisitor has been enhanced to
support recursive reading of annotations and annotation arrays, for
example in @ComponentScan's nested array of @Filter annotations,
ensuring that nested AnnotationAttributes objects are populated as
described above.
AnnotationAttributesReadingVisitor has also been refactored for
clarity, being broken up into several additional ASM
AnnotationVisitor implementations. Given that all types are
package-private here, these changes represent no risk to binary
compatibility.
- Reflection-based StandardAnnotationMetadata updated
As described above, the 'nestedAnnotationsAsMap' constructor argument
has been added, and all framework-internal uses of this class have
been updated to set this flag to true.
Issue: SPR-7979, SPR-8719, SPR-9031
Prior to this commit, @Configuration classes included via @Import (or
via automatic registration of nested configuration classes) would
always be registered with a generated bean name, regardless of whether
the user had specified a 'value' indicating a customized bean name, e.g.
@Configuration("myConfig")
public class AppConfig { ... }
Now this bean name is propagated as intended in all cases, meaning that
in the example above, the resulting bean definition of type AppConfig
will be named "myConfig" regardless how it was registered with the
container -- directly against the application context, via component
scanning, via @Import, or via automatic registration of nested
configuration classes.
Issue: SPR-9023
Prior to this change, the spring-cache XSD allowed a 'key-generator'
attribute, but it was not actually parsed by AnnotationDrivenCacheBDP.
This commit adds the parsing logic as originally intended and the test
to prove it.
Issue: SPR-8939
Prior to this change, the caching reference docs referred to
'root.params', whereas the actual naming should be 'root.args'. This
naming was also reflected in the "#p" syntax for specifying method args.
This change updates the documentation to refer to 'root.args' properly
and also adds "#a" syntax for specifying method arguments more
intuitively. Note that "#p" syntax remains in place as an alias for
backward compatibility.
Issue: SPR-8938
Prior to this change, roughly 5% (~300 out of 6000+) of files under the
source tree had CRLF line endings as opposed to the majority which have
LF endings.
This change normalizes these files to LF for consistency going forward.
Command used:
$ git ls-files | xargs file | grep CRLF | cut -d":" -f1 | xargs dos2unix
Issue: SPR-5608
Prior to this change, ScheduledAnnotationBeanPostProcessor found any
@Scheduled methods against the ultimate targetClass for a given bean
and then attempted to invoke that method against the bean instance. In
cases where the bean instance was in fact a JDK proxy, this attempt
would fail because the proxy is not an instance of the target class.
Now SABPP still attempts to find @Scheduled methods against the target
class, but subsequently checks to see if the bean is a JDK proxy, and if
so attempts to find the corresponding method on the proxy itself. If it
cannot be found (e.g. the @Scheduled method was declared only at the
concrete class level), an appropriate exception is thrown, explaining to
the users their options: (a) use proxyTargetClass=true and go with
subclass proxies which won't have this problem, or (b) pull the
@Scheduled method up into an interface.
Issue: SPR-8651
Prior to this change, an assumption was made in
AbstractAutowireCapableBeanFactory that any factory-method would have
zero parameters. This may not be the case in @Bean methods.
We now look for the factory-method by name in a more flexible fashion
that accomodates the possibility of method parameters.
There remains at least one edge cases here where things could still fail,
for example a @Configuration class could have two FactoryBean-returning
methods of the same name, but each with different generic FactoryBean
types and different parameter lists. In this case, the implementation
may infer and return the wrong object type, as it currently returns
the first match for the given factory-method name. The complexity cost
of ensuring that this never happens is not likely worth the trouble
given the very low likelihood of such an arrangement.
Issue: SPR-8762
In order to determine why Ehcache classloading errors occur after
upgrading to 4.2.0.Final.
To demonstrate this error, uncomment the 4.2.0.Final dependency in
ivy.xml and run `ant test` within the .context module.