diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java index a2a1648eb74..428999cd7bc 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/BeanFactory.java @@ -170,8 +170,7 @@ public interface BeanFactory { *
Allows for specifying explicit constructor arguments / factory method arguments, * overriding the specified default arguments (if any) in the bean definition. * @param name the name of the bean to retrieve - * @param args arguments to use if creating a prototype using explicit arguments to a - * static factory method. It is invalid to use a non-null args value in any other case. + * @param args arguments to use if creating a prototype using explicit arguments * @return an instance of the bean * @throws NoSuchBeanDefinitionException if there is no such bean definition * @throws BeanDefinitionStoreException if arguments have been given but diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index 189a76c81e2..0681c0ffe78 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -44,10 +45,12 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.LookupOverride; import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.core.BridgeMethodResolver; @@ -96,6 +99,12 @@ import org.springframework.util.StringUtils; * thus the latter configuration will override the former for properties wired through * both approaches. * + *
In addition to regular injection points as discussed above, this post-processor
+ * also handles Spring's {@link Lookup @Lookup} annotation which identifies lookup
+ * methods to be replaced by the container at runtime. This is essentially a type-safe
+ * version of {@code getBean(Class, args)} and {@code getBean(String, args)},
+ * See {@link Lookup @Lookup's javadoc} for details.
+ *
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.5
@@ -119,6 +128,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
private ConfigurableListableBeanFactory beanFactory;
+ private final Set The resolution of the target bean can either be based on the return type
+ * ({@code getBean(Class)}) or on a suggested bean name ({@code getBean(String)}),
+ * in both cases passing the method's arguments to the {@code getBean} call
+ * for applying them as target factory method arguments or constructor arguments.
+ *
+ * Such lookup methods can have default (stub) implementations that will simply
+ * get replaced by the container, or they can be declared as abstract - for the
+ * container to fill them in at runtime. In both cases, the container will generate
+ * runtime subclasses of the method's containing class via CGLIB, which is why such
+ * lookup methods can only work on beans that the container instantiates through
+ * regular constructors (i.e. lookup methods cannot get replaced on beans returned
+ * from factory methods where we can't dynamically provide a subclass for them).
+ *
+ * Note: When used with component scanning or any other mechanism that filters
+ * out abstract beans, provide stub implementations of your lookup methods to be
+ * able to declare them as concrete classes.
+ *
+ * @author Juergen Hoeller
+ * @since 4.1
+ * @see org.springframework.beans.factory.BeanFactory#getBean(Class, Object...)
+ * @see org.springframework.beans.factory.BeanFactory#getBean(String, Object...)
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface Lookup {
+
+ /**
+ * This annotation attribute may suggest a target bean name to look up.
+ * If not specified, the target bean will be resolved based on the
+ * annotated method's return type declaration.
+ */
+ String value() default "";
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
index 8fa8bc1b0a0..526ebd73c55 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -285,8 +285,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
- throw new BeanCreationException("Circular depends-on relationship between '" +
- beanName + "' and '" + dependsOnBean + "'");
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
@@ -1274,7 +1274,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
// Check validity of the usage of the args parameter. This can
// only be used for prototypes constructed via a factory method.
if (args != null && !mbd.isPrototype()) {
- throw new BeanDefinitionStoreException(
+ throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
"Can only specify arguments for the getBean method when referring to a prototype bean definition");
}
}
@@ -1625,8 +1625,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
* instantiation within this class is performed by this method.
* @param beanName the name of the bean
* @param mbd the merged bean definition for the bean
- * @param args arguments to use if creating a prototype using explicit arguments to a
- * static factory method. This parameter must be {@code null} except in this case.
+ * @param args arguments to use if creating a prototype using explicit arguments
* @return a new instance of the bean
* @throws BeanCreationException if the bean could not be created
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
index 740be138d72..8821bcab39c 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
@@ -25,7 +25,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
-
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
@@ -34,6 +33,7 @@ import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
+import org.springframework.util.StringUtils;
/**
* Default object instantiation strategy for use in BeanFactories.
@@ -89,14 +89,13 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
*/
private static class CglibSubclassCreator {
- private static final Class>[] CALLBACK_TYPES = new Class>[] { NoOp.class,
- LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class };
+ private static final Class>[] CALLBACK_TYPES = new Class>[]
+ {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
private final RootBeanDefinition beanDefinition;
private final BeanFactory owner;
-
CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
this.beanDefinition = beanDefinition;
this.owner = owner;
@@ -113,7 +112,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
*/
Object instantiate(Constructor> ctor, Object[] args) {
Class> subclass = createEnhancedSubclass(this.beanDefinition);
-
Object instance;
if (ctor == null) {
instance = BeanUtils.instantiate(subclass);
@@ -123,19 +121,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
Constructor> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
instance = enhancedSubclassConstructor.newInstance(args);
}
- catch (Exception e) {
+ catch (Exception ex) {
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(), String.format(
- "Failed to invoke construcor for CGLIB enhanced subclass [%s]", subclass.getName()), e);
+ "Failed to invoke constructor for CGLIB enhanced subclass [%s]", subclass.getName()), ex);
}
}
-
// SPR-10785: set callbacks directly on the instance instead of in the
// enhanced class (via the Enhancer) in order to avoid memory leaks.
Factory factory = (Factory) instance;
- factory.setCallbacks(new Callback[] { NoOp.INSTANCE,//
- new LookupOverrideMethodInterceptor(beanDefinition, owner),//
- new ReplaceOverrideMethodInterceptor(beanDefinition, owner) });
-
+ factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
+ new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
+ new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
return instance;
}
@@ -153,6 +149,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
}
}
+
/**
* Class providing hashCode and equals methods required by CGLIB to
* ensure that CGLIB doesn't generate a distinct class per bean.
@@ -162,7 +159,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final RootBeanDefinition beanDefinition;
-
CglibIdentitySupport(RootBeanDefinition beanDefinition) {
this.beanDefinition = beanDefinition;
}
@@ -173,8 +169,8 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
@Override
public boolean equals(Object other) {
- return other.getClass().equals(this.getClass())
- && ((CglibIdentitySupport) other).getBeanDefinition().equals(this.getBeanDefinition());
+ return (getClass().equals(other.getClass()) &&
+ this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
}
@Override
@@ -183,6 +179,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
}
}
+
/**
* CGLIB callback for filtering method interception behavior.
*/
@@ -190,7 +187,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
-
MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
super(beanDefinition);
}
@@ -210,11 +206,12 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
else if (methodOverride instanceof ReplaceOverride) {
return METHOD_REPLACER;
}
- throw new UnsupportedOperationException("Unexpected MethodOverride subclass: "
- + methodOverride.getClass().getName());
+ throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
+ methodOverride.getClass().getName());
}
}
+
/**
* CGLIB MethodInterceptor to override methods, replacing them with an
* implementation that returns a bean looked up in the container.
@@ -223,7 +220,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final BeanFactory owner;
-
LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
super(beanDefinition);
this.owner = owner;
@@ -233,10 +229,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
- return this.owner.getBean(lo.getBeanName());
+ Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
+ if (StringUtils.hasText(lo.getBeanName())) {
+ return this.owner.getBean(lo.getBeanName(), argsToUse);
+ }
+ else {
+ return this.owner.getBean(method.getReturnType(), argsToUse);
+ }
}
}
+
/**
* CGLIB MethodInterceptor to override methods, replacing them with a call
* to a generic MethodReplacer.
@@ -245,7 +248,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
private final BeanFactory owner;
-
ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
super(beanDefinition);
this.owner = owner;
@@ -255,7 +257,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
// TODO could cache if a singleton for minor performance optimization
- MethodReplacer mr = owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
+ MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
return mr.reimplement(obj, method, args);
}
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java
index 75077729d55..5800d38a3f6 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/LookupOverride.java
@@ -17,8 +17,8 @@
package org.springframework.beans.factory.support;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
-import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
@@ -34,20 +34,33 @@ public class LookupOverride extends MethodOverride {
private final String beanName;
+ private Method method;
+
/**
* Construct a new LookupOverride.
- * @param methodName the name of the method to override.
- * This method must have no arguments.
- * @param beanName the name of the bean in the current BeanFactory
- * that the overridden method should return
+ * @param methodName the name of the method to override
+ * @param beanName the name of the bean in the current {@code BeanFactory}
+ * that the overridden method should return (may be {@code null})
*/
public LookupOverride(String methodName, String beanName) {
super(methodName);
- Assert.notNull(beanName, "Bean name must not be null");
this.beanName = beanName;
}
+ /**
+ * Construct a new LookupOverride.
+ * @param method the method to override
+ * @param beanName the name of the bean in the current {@code BeanFactory}
+ * that the overridden method should return (may be {@code null})
+ */
+ public LookupOverride(Method method, String beanName) {
+ super(method.getName());
+ this.method = method;
+ this.beanName = beanName;
+ }
+
+
/**
* Return the name of the bean that should be returned by this method.
*/
@@ -56,22 +69,33 @@ public class LookupOverride extends MethodOverride {
}
/**
- * Match the method of the given name, with no parameters.
+ * Match the specified method by {@link Method} reference or method name.
+ * For backwards compatibility reasons, in a scenario with overloaded
+ * non-abstract methods of the given name, only the no-arg variant of a
+ * method will be turned into a container-driven lookup method.
+ * In case of a provided {@link Method}, only straight matches will
+ * be considered, usually demarcated by the {@code @Lookup} annotation.
*/
@Override
public boolean matches(Method method) {
- return (method.getName().equals(getMethodName()) && method.getParameterTypes().length == 0);
+ if (this.method != null) {
+ return method.equals(this.method);
+ }
+ else {
+ return (method.getName().equals(getMethodName()) && (!isOverloaded() ||
+ Modifier.isAbstract(method.getModifiers()) || method.getParameterTypes().length == 0));
+ }
}
- @Override
- public String toString() {
- return "LookupOverride for method '" + getMethodName() + "'; will return bean '" + this.beanName + "'";
- }
@Override
public boolean equals(Object other) {
- return (other instanceof LookupOverride && super.equals(other) &&
- ObjectUtils.nullSafeEquals(this.beanName, ((LookupOverride) other).beanName));
+ if (!(other instanceof LookupOverride) || !super.equals(other)) {
+ return false;
+ }
+ LookupOverride that = (LookupOverride) other;
+ return (ObjectUtils.nullSafeEquals(this.method, that.method) &&
+ ObjectUtils.nullSafeEquals(this.beanName, that.beanName));
}
@Override
@@ -79,4 +103,9 @@ public class LookupOverride extends MethodOverride {
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
}
+ @Override
+ public String toString() {
+ return "LookupOverride for method '" + getMethodName() + "'";
+ }
+
}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java
index e570a24c573..957d3d17795 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ReplaceOverride.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2012 the original author or authors.
+ * Copyright 2002-2014 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,6 +52,7 @@ public class ReplaceOverride extends MethodOverride {
this.methodReplacerBeanName = methodReplacerBeanName;
}
+
/**
* Return the name of the bean implementing MethodReplacer.
*/
@@ -97,12 +98,6 @@ public class ReplaceOverride extends MethodOverride {
}
- @Override
- public String toString() {
- return "Replace override for method '" + getMethodName() + "; will call bean '" +
- this.methodReplacerBeanName + "'";
- }
-
@Override
public boolean equals(Object other) {
if (!(other instanceof ReplaceOverride) || !super.equals(other)) {
@@ -121,4 +116,9 @@ public class ReplaceOverride extends MethodOverride {
return hashCode;
}
+ @Override
+ public String toString() {
+ return "Replace override for method '" + getMethodName() + "'";
+ }
+
}
diff --git a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd
index 78ef2dcc101..9a9cb52bb11 100644
--- a/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd
+++ b/spring-beans/src/main/resources/org/springframework/beans/factory/xml/spring-beans-4.1.xsd
@@ -680,7 +680,12 @@