@Feature methods accept @Value-annotated params
Previously errors were being raised when trying to inject @Value
annotated paramaters such as:
@Feature
public FeatureSpec feature(@Value("#{environment['foo']}") String foo) {
return new FeatureSpec(foo);
}
This is not so much because dependency resolution of @Value-annotated
types was failing, but rather because the 'early bean reference'
proxying mechanism was throwing an exception if any final type was
detected as a parameter. This is of course because final types are
non-subclassable by CGLIB. On review, however, it's obvious that
certain final types must be allowed for injection. @Value injection
is an obvious one, but the rarer case of a Spring bean of type String
or int is another.
The explicit guard against final types as parameters to @Feature methods
has been removed. Final types are still checked for, however, and if
found, no proxing is attempted. The dependency is immediately resolved
against the current BeanFactory and injected into the @Feature method.
This means that @Value injection, @Qualifier injection, etc all work
as expected, but does mean that premature bean instantiation may occur
if a user unwittingly injects non-String, non-primitive final bean types
as @Feature method parameters.
Issue: SPR-7974
This commit is contained in:
@@ -47,9 +47,9 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.context.config.FeatureSpecification;
|
||||
import org.springframework.context.config.SourceAwareSpecification;
|
||||
import org.springframework.context.config.SpecificationContext;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@@ -331,7 +331,7 @@ public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPo
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
MethodParameter mp = new MethodParameter(featureMethod, i);
|
||||
DependencyDescriptor dd = new DependencyDescriptor(mp, true, false);
|
||||
Object proxiedBean = proxyCreator.createProxy(dd);
|
||||
Object proxiedBean = proxyCreator.createProxyIfPossible(dd);
|
||||
beanArgs.add(proxiedBean);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,10 +47,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
*/
|
||||
class EarlyBeanReferenceProxyCreator {
|
||||
|
||||
static final String FINAL_CLASS_ERROR_MESSAGE =
|
||||
"Cannot create subclass proxy for bean type %s because it is a final class. " +
|
||||
"Make the class non-final or inject the bean by interface rather than by concrete class.";
|
||||
|
||||
static final String MISSING_NO_ARG_CONSTRUCTOR_ERROR_MESSAGE =
|
||||
"Cannot create subclass proxy for bean type %s because it does not have a no-arg constructor. " +
|
||||
"Add a no-arg constructor or attempt to inject the bean by interface rather than by concrete class.";
|
||||
@@ -73,9 +69,14 @@ class EarlyBeanReferenceProxyCreator {
|
||||
|
||||
/**
|
||||
* Create a proxy that will ultimately dereference its target object using
|
||||
* the given dependency descriptor.
|
||||
* the given dependency descriptor. No proxy is created if the dependency type
|
||||
* is final, rather the dependency is resolved immediately. This is important
|
||||
* especially with regard to supporting @Value injection.
|
||||
*/
|
||||
public Object createProxy(DependencyDescriptor descriptor) {
|
||||
public Object createProxyIfPossible(DependencyDescriptor descriptor) {
|
||||
if (Modifier.isFinal(descriptor.getDependencyType().getModifiers())) {
|
||||
return beanFactory.resolveDependency(descriptor, "");
|
||||
}
|
||||
return doCreateProxy(new ResolveDependencyTargetBeanDereferencingInterceptor(descriptor));
|
||||
}
|
||||
|
||||
@@ -123,9 +124,6 @@ class EarlyBeanReferenceProxyCreator {
|
||||
*/
|
||||
private static void assertClassIsProxyCapable(Class<?> clazz) {
|
||||
Assert.isTrue(!clazz.isInterface(), "class parameter must be a concrete type");
|
||||
if ((clazz.getModifiers() & Modifier.FINAL) != 0) {
|
||||
throw new ProxyCreationException(String.format(FINAL_CLASS_ERROR_MESSAGE, clazz.getName()));
|
||||
}
|
||||
try {
|
||||
// attempt to retrieve the no-arg constructor for the class
|
||||
Constructor<?> noArgCtor = clazz.getDeclaredConstructor();
|
||||
|
||||
Reference in New Issue
Block a user