Lookup methods can support arguments, find a target bean based on the return type, and be identified by an @Lookup annotation
Issue: SPR-7431 Issue: SPR-5192
This commit is contained in:
parent
e753f23110
commit
eb0ab8431b
|
|
@ -170,8 +170,7 @@ public interface BeanFactory {
|
||||||
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
|
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
|
||||||
* overriding the specified default arguments (if any) in the bean definition.
|
* overriding the specified default arguments (if any) in the bean definition.
|
||||||
* @param name the name of the bean to retrieve
|
* @param name the name of the bean to retrieve
|
||||||
* @param args arguments to use if creating a prototype using explicit arguments to a
|
* @param args arguments to use if creating a prototype using explicit arguments
|
||||||
* static factory method. It is invalid to use a non-null args value in any other case.
|
|
||||||
* @return an instance of the bean
|
* @return an instance of the bean
|
||||||
* @throws NoSuchBeanDefinitionException if there is no such bean definition
|
* @throws NoSuchBeanDefinitionException if there is no such bean definition
|
||||||
* @throws BeanDefinitionStoreException if arguments have been given but
|
* @throws BeanDefinitionStoreException if arguments have been given but
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
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.BeanFactory;
|
||||||
import org.springframework.beans.factory.BeanFactoryAware;
|
import org.springframework.beans.factory.BeanFactoryAware;
|
||||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
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.ConfigurableListableBeanFactory;
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
|
||||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
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.MergedBeanDefinitionPostProcessor;
|
||||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
import org.springframework.core.BridgeMethodResolver;
|
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
|
* thus the latter configuration will override the former for properties wired through
|
||||||
* both approaches.
|
* both approaches.
|
||||||
*
|
*
|
||||||
|
* <p>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 Juergen Hoeller
|
||||||
* @author Mark Fisher
|
* @author Mark Fisher
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
|
|
@ -119,6 +128,9 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
|
|
||||||
private ConfigurableListableBeanFactory beanFactory;
|
private ConfigurableListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
private final Set<String> lookupMethodsChecked =
|
||||||
|
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(64));
|
||||||
|
|
||||||
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache =
|
private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache =
|
||||||
new ConcurrentHashMap<Class<?>, Constructor<?>[]>(64);
|
new ConcurrentHashMap<Class<?>, Constructor<?>[]>(64);
|
||||||
|
|
||||||
|
|
@ -224,7 +236,28 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
|
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeansException {
|
||||||
|
if (!this.lookupMethodsChecked.contains(beanName)) {
|
||||||
|
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
|
||||||
|
@Override
|
||||||
|
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||||
|
Lookup lookup = method.getAnnotation(Lookup.class);
|
||||||
|
if (lookup != null) {
|
||||||
|
LookupOverride override = new LookupOverride(method, lookup.value());
|
||||||
|
try {
|
||||||
|
RootBeanDefinition mbd = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
|
||||||
|
mbd.getMethodOverrides().addOverride(override);
|
||||||
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException ex) {
|
||||||
|
throw new BeanCreationException(beanName,
|
||||||
|
"Cannot apply @Lookup to beans without corresponding bean definition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.lookupMethodsChecked.add(beanName);
|
||||||
|
}
|
||||||
|
|
||||||
// Quick check on the concurrent map first, with minimal locking.
|
// Quick check on the concurrent map first, with minimal locking.
|
||||||
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
|
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
|
||||||
if (candidateConstructors == null) {
|
if (candidateConstructors == null) {
|
||||||
|
|
@ -239,7 +272,8 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
AnnotationAttributes annotation = findAutowiredAnnotation(candidate);
|
AnnotationAttributes annotation = findAutowiredAnnotation(candidate);
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
if (requiredConstructor != null) {
|
if (requiredConstructor != null) {
|
||||||
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
|
throw new BeanCreationException(beanName,
|
||||||
|
"Invalid autowire-marked constructor: " + candidate +
|
||||||
". Found another constructor with 'required' Autowired annotation: " +
|
". Found another constructor with 'required' Autowired annotation: " +
|
||||||
requiredConstructor);
|
requiredConstructor);
|
||||||
}
|
}
|
||||||
|
|
@ -250,10 +284,10 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
boolean required = determineRequiredStatus(annotation);
|
boolean required = determineRequiredStatus(annotation);
|
||||||
if (required) {
|
if (required) {
|
||||||
if (!candidates.isEmpty()) {
|
if (!candidates.isEmpty()) {
|
||||||
throw new BeanCreationException(
|
throw new BeanCreationException(beanName,
|
||||||
"Invalid autowire-marked constructors: " + candidates +
|
"Invalid autowire-marked constructors: " + candidates +
|
||||||
". Found another constructor with 'required' Autowired annotation: " +
|
". Found another constructor with 'required' Autowired annotation: " +
|
||||||
requiredConstructor);
|
candidate);
|
||||||
}
|
}
|
||||||
requiredConstructor = candidate;
|
requiredConstructor = candidate;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.factory.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An annotation that indicates 'lookup' methods, to be overridden by the container
|
||||||
|
* to redirect them back to the {@link org.springframework.beans.factory.BeanFactory}
|
||||||
|
* for a {@code getBean} call. This is essentially an annotation-based version of the
|
||||||
|
* XML {@code lookup-method} attribute, resulting in the same runtime arrangement.
|
||||||
|
*
|
||||||
|
* <p>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.
|
||||||
|
*
|
||||||
|
* <p>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).
|
||||||
|
*
|
||||||
|
* <p>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 "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -285,8 +285,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||||
if (dependsOn != null) {
|
if (dependsOn != null) {
|
||||||
for (String dependsOnBean : dependsOn) {
|
for (String dependsOnBean : dependsOn) {
|
||||||
if (isDependent(beanName, dependsOnBean)) {
|
if (isDependent(beanName, dependsOnBean)) {
|
||||||
throw new BeanCreationException("Circular depends-on relationship between '" +
|
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||||
beanName + "' and '" + dependsOnBean + "'");
|
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
|
||||||
}
|
}
|
||||||
registerDependentBean(dependsOnBean, beanName);
|
registerDependentBean(dependsOnBean, beanName);
|
||||||
getBean(dependsOnBean);
|
getBean(dependsOnBean);
|
||||||
|
|
@ -1274,7 +1274,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
||||||
// Check validity of the usage of the args parameter. This can
|
// Check validity of the usage of the args parameter. This can
|
||||||
// only be used for prototypes constructed via a factory method.
|
// only be used for prototypes constructed via a factory method.
|
||||||
if (args != null && !mbd.isPrototype()) {
|
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");
|
"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.
|
* instantiation within this class is performed by this method.
|
||||||
* @param beanName the name of the bean
|
* @param beanName the name of the bean
|
||||||
* @param mbd the merged bean definition for 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
|
* @param args arguments to use if creating a prototype using explicit arguments
|
||||||
* static factory method. This parameter must be {@code null} except in this case.
|
|
||||||
* @return a new instance of the bean
|
* @return a new instance of the bean
|
||||||
* @throws BeanCreationException if the bean could not be created
|
* @throws BeanCreationException if the bean could not be created
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
import org.springframework.beans.BeanInstantiationException;
|
import org.springframework.beans.BeanInstantiationException;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.factory.BeanFactory;
|
import org.springframework.beans.factory.BeanFactory;
|
||||||
|
|
||||||
import org.springframework.cglib.core.SpringNamingPolicy;
|
import org.springframework.cglib.core.SpringNamingPolicy;
|
||||||
import org.springframework.cglib.proxy.Callback;
|
import org.springframework.cglib.proxy.Callback;
|
||||||
import org.springframework.cglib.proxy.CallbackFilter;
|
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.MethodInterceptor;
|
||||||
import org.springframework.cglib.proxy.MethodProxy;
|
import org.springframework.cglib.proxy.MethodProxy;
|
||||||
import org.springframework.cglib.proxy.NoOp;
|
import org.springframework.cglib.proxy.NoOp;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default object instantiation strategy for use in BeanFactories.
|
* Default object instantiation strategy for use in BeanFactories.
|
||||||
|
|
@ -89,14 +89,13 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
*/
|
*/
|
||||||
private static class CglibSubclassCreator {
|
private static class CglibSubclassCreator {
|
||||||
|
|
||||||
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[] { NoOp.class,
|
private static final Class<?>[] CALLBACK_TYPES = new Class<?>[]
|
||||||
LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class };
|
{NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
|
||||||
|
|
||||||
private final RootBeanDefinition beanDefinition;
|
private final RootBeanDefinition beanDefinition;
|
||||||
|
|
||||||
private final BeanFactory owner;
|
private final BeanFactory owner;
|
||||||
|
|
||||||
|
|
||||||
CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
||||||
this.beanDefinition = beanDefinition;
|
this.beanDefinition = beanDefinition;
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
|
|
@ -113,7 +112,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
*/
|
*/
|
||||||
Object instantiate(Constructor<?> ctor, Object[] args) {
|
Object instantiate(Constructor<?> ctor, Object[] args) {
|
||||||
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
|
Class<?> subclass = createEnhancedSubclass(this.beanDefinition);
|
||||||
|
|
||||||
Object instance;
|
Object instance;
|
||||||
if (ctor == null) {
|
if (ctor == null) {
|
||||||
instance = BeanUtils.instantiate(subclass);
|
instance = BeanUtils.instantiate(subclass);
|
||||||
|
|
@ -123,19 +121,17 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
|
Constructor<?> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
|
||||||
instance = enhancedSubclassConstructor.newInstance(args);
|
instance = enhancedSubclassConstructor.newInstance(args);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception ex) {
|
||||||
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(), String.format(
|
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
|
// SPR-10785: set callbacks directly on the instance instead of in the
|
||||||
// enhanced class (via the Enhancer) in order to avoid memory leaks.
|
// enhanced class (via the Enhancer) in order to avoid memory leaks.
|
||||||
Factory factory = (Factory) instance;
|
Factory factory = (Factory) instance;
|
||||||
factory.setCallbacks(new Callback[] { NoOp.INSTANCE,//
|
factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
|
||||||
new LookupOverrideMethodInterceptor(beanDefinition, owner),//
|
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
|
||||||
new ReplaceOverrideMethodInterceptor(beanDefinition, owner) });
|
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,6 +149,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class providing hashCode and equals methods required by CGLIB to
|
* Class providing hashCode and equals methods required by CGLIB to
|
||||||
* ensure that CGLIB doesn't generate a distinct class per bean.
|
* ensure that CGLIB doesn't generate a distinct class per bean.
|
||||||
|
|
@ -162,7 +159,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
|
|
||||||
private final RootBeanDefinition beanDefinition;
|
private final RootBeanDefinition beanDefinition;
|
||||||
|
|
||||||
|
|
||||||
CglibIdentitySupport(RootBeanDefinition beanDefinition) {
|
CglibIdentitySupport(RootBeanDefinition beanDefinition) {
|
||||||
this.beanDefinition = beanDefinition;
|
this.beanDefinition = beanDefinition;
|
||||||
}
|
}
|
||||||
|
|
@ -173,8 +169,8 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
return other.getClass().equals(this.getClass())
|
return (getClass().equals(other.getClass()) &&
|
||||||
&& ((CglibIdentitySupport) other).getBeanDefinition().equals(this.getBeanDefinition());
|
this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -183,6 +179,7 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CGLIB callback for filtering method interception behavior.
|
* 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);
|
private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
|
||||||
|
|
||||||
|
|
||||||
MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
|
MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
|
||||||
super(beanDefinition);
|
super(beanDefinition);
|
||||||
}
|
}
|
||||||
|
|
@ -210,11 +206,12 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
else if (methodOverride instanceof ReplaceOverride) {
|
else if (methodOverride instanceof ReplaceOverride) {
|
||||||
return METHOD_REPLACER;
|
return METHOD_REPLACER;
|
||||||
}
|
}
|
||||||
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: "
|
throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
|
||||||
+ methodOverride.getClass().getName());
|
methodOverride.getClass().getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CGLIB MethodInterceptor to override methods, replacing them with an
|
* CGLIB MethodInterceptor to override methods, replacing them with an
|
||||||
* implementation that returns a bean looked up in the container.
|
* implementation that returns a bean looked up in the container.
|
||||||
|
|
@ -223,7 +220,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
|
|
||||||
private final BeanFactory owner;
|
private final BeanFactory owner;
|
||||||
|
|
||||||
|
|
||||||
LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
||||||
super(beanDefinition);
|
super(beanDefinition);
|
||||||
this.owner = owner;
|
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 {
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
|
||||||
// Cast is safe, as CallbackFilter filters are used selectively.
|
// Cast is safe, as CallbackFilter filters are used selectively.
|
||||||
LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
|
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
|
* CGLIB MethodInterceptor to override methods, replacing them with a call
|
||||||
* to a generic MethodReplacer.
|
* to a generic MethodReplacer.
|
||||||
|
|
@ -245,7 +248,6 @@ public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationSt
|
||||||
|
|
||||||
private final BeanFactory owner;
|
private final BeanFactory owner;
|
||||||
|
|
||||||
|
|
||||||
ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
|
||||||
super(beanDefinition);
|
super(beanDefinition);
|
||||||
this.owner = owner;
|
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 {
|
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
|
||||||
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
|
ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
|
||||||
// TODO could cache if a singleton for minor performance optimization
|
// 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);
|
return mr.reimplement(obj, method, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -34,20 +34,33 @@ public class LookupOverride extends MethodOverride {
|
||||||
|
|
||||||
private final String beanName;
|
private final String beanName;
|
||||||
|
|
||||||
|
private Method method;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new LookupOverride.
|
* Construct a new LookupOverride.
|
||||||
* @param methodName the name of the method to override.
|
* @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 {@code BeanFactory}
|
||||||
* @param beanName the name of the bean in the current BeanFactory
|
* that the overridden method should return (may be {@code null})
|
||||||
* that the overridden method should return
|
|
||||||
*/
|
*/
|
||||||
public LookupOverride(String methodName, String beanName) {
|
public LookupOverride(String methodName, String beanName) {
|
||||||
super(methodName);
|
super(methodName);
|
||||||
Assert.notNull(beanName, "Bean name must not be null");
|
|
||||||
this.beanName = beanName;
|
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.
|
* 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.
|
||||||
|
* <p>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.
|
||||||
|
* <p>In case of a provided {@link Method}, only straight matches will
|
||||||
|
* be considered, usually demarcated by the {@code @Lookup} annotation.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Method method) {
|
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
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
return (other instanceof LookupOverride && super.equals(other) &&
|
if (!(other instanceof LookupOverride) || !super.equals(other)) {
|
||||||
ObjectUtils.nullSafeEquals(this.beanName, ((LookupOverride) other).beanName));
|
return false;
|
||||||
|
}
|
||||||
|
LookupOverride that = (LookupOverride) other;
|
||||||
|
return (ObjectUtils.nullSafeEquals(this.method, that.method) &&
|
||||||
|
ObjectUtils.nullSafeEquals(this.beanName, that.beanName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -79,4 +103,9 @@ public class LookupOverride extends MethodOverride {
|
||||||
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
|
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LookupOverride for method '" + getMethodName() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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;
|
this.methodReplacerBeanName = methodReplacerBeanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the name of the bean implementing MethodReplacer.
|
* 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
|
@Override
|
||||||
public boolean equals(Object other) {
|
public boolean equals(Object other) {
|
||||||
if (!(other instanceof ReplaceOverride) || !super.equals(other)) {
|
if (!(other instanceof ReplaceOverride) || !super.equals(other)) {
|
||||||
|
|
@ -121,4 +116,9 @@ public class ReplaceOverride extends MethodOverride {
|
||||||
return hashCode;
|
return hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Replace override for method '" + getMethodName() + "'";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -680,7 +680,12 @@
|
||||||
<xsd:attribute name="name" type="xsd:string">
|
<xsd:attribute name="name" type="xsd:string">
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
The name of the lookup method. This method must take no arguments.
|
The name of the lookup method. This method may have arguments which
|
||||||
|
will be passed on to the target constructor or factory method. Note
|
||||||
|
that 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.
|
||||||
|
Consider using the @Lookup annotation for more specific demarcation.
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
|
@ -688,9 +693,10 @@
|
||||||
<xsd:annotation>
|
<xsd:annotation>
|
||||||
<xsd:documentation><![CDATA[
|
<xsd:documentation><![CDATA[
|
||||||
The name of the bean in the current or ancestor factories that
|
The name of the bean in the current or ancestor factories that
|
||||||
the lookup method should resolve to. Often this bean will be a
|
the lookup method should resolve to. Usually this bean will be a
|
||||||
prototype, in which case the lookup method will return a distinct
|
prototype, in which case the lookup method will return a distinct
|
||||||
instance on every invocation. This is useful for single-threaded objects.
|
instance on every invocation. If not specified, the lookup method's
|
||||||
|
return type will be used for a type-based lookup.
|
||||||
]]></xsd:documentation>
|
]]></xsd:documentation>
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.beans.factory.annotation;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Pietrzak
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
*/
|
||||||
|
public class LookupAnnotationTests {
|
||||||
|
|
||||||
|
private DefaultListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
beanFactory = new DefaultListableBeanFactory();
|
||||||
|
AutowiredAnnotationBeanPostProcessor aabpp = new AutowiredAnnotationBeanPostProcessor();
|
||||||
|
aabpp.setBeanFactory(beanFactory);
|
||||||
|
beanFactory.addBeanPostProcessor(aabpp);
|
||||||
|
beanFactory.registerBeanDefinition("abstractBean", new RootBeanDefinition(AbstractBean.class));
|
||||||
|
RootBeanDefinition tbd = new RootBeanDefinition(TestBean.class);
|
||||||
|
tbd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||||
|
beanFactory.registerBeanDefinition("testBean", tbd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithoutConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
Object expected = bean.get();
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithOverloadedArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.get("haha");
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithOneConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.getOneArgument("haha");
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithTwoConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.getTwoArguments("haha", 72);
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
assertEquals(72, expected.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithThreeArgsShouldFail() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
try {
|
||||||
|
bean.getThreeArguments("name", 1, 2);
|
||||||
|
fail("TestBean does not have a three arg constructor so this should not have worked");
|
||||||
|
}
|
||||||
|
catch (AbstractMethodError ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static abstract class AbstractBean {
|
||||||
|
|
||||||
|
@Lookup
|
||||||
|
public abstract TestBean get();
|
||||||
|
|
||||||
|
@Lookup
|
||||||
|
public abstract TestBean get(String name); // overloaded
|
||||||
|
|
||||||
|
@Lookup
|
||||||
|
public abstract TestBean getOneArgument(String name);
|
||||||
|
|
||||||
|
@Lookup
|
||||||
|
public abstract TestBean getTwoArguments(String name, int age);
|
||||||
|
|
||||||
|
public abstract TestBean getThreeArguments(String name, int age, int anotherArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
* 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.support;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.tests.sample.beans.TestBean;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Karl Pietrzak
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
*/
|
||||||
|
public class LookupMethodTests {
|
||||||
|
|
||||||
|
private DefaultListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
beanFactory = new DefaultListableBeanFactory();
|
||||||
|
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
|
||||||
|
reader.loadBeanDefinitions(new ClassPathResource("lookupMethodTests.xml", getClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithoutConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
Object expected = bean.get();
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithOverloadedArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.get("haha");
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithOneConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.getOneArgument("haha");
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithTwoConstructorArg() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
TestBean expected = bean.getTwoArguments("haha", 72);
|
||||||
|
assertEquals(TestBean.class, expected.getClass());
|
||||||
|
assertEquals("haha", expected.getName());
|
||||||
|
assertEquals(72, expected.getAge());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWithThreeArgsShouldFail() {
|
||||||
|
AbstractBean bean = (AbstractBean) beanFactory.getBean("abstractBean");
|
||||||
|
assertNotNull(bean);
|
||||||
|
try {
|
||||||
|
bean.getThreeArguments("name", 1, 2);
|
||||||
|
fail("TestBean does not have a three arg constructor so this should not have worked");
|
||||||
|
}
|
||||||
|
catch (AbstractMethodError ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static abstract class AbstractBean {
|
||||||
|
|
||||||
|
public abstract TestBean get();
|
||||||
|
|
||||||
|
public abstract TestBean get(String name); // overloaded
|
||||||
|
|
||||||
|
public abstract TestBean getOneArgument(String name);
|
||||||
|
|
||||||
|
public abstract TestBean getTwoArguments(String name, int age);
|
||||||
|
|
||||||
|
public abstract TestBean getThreeArguments(String name, int age, int anotherArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<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-4.1.xsd">
|
||||||
|
|
||||||
|
<bean id="abstractBean" class="org.springframework.beans.factory.support.LookupMethodTests$AbstractBean">
|
||||||
|
<lookup-method name="get"/> <!-- applying to overloaded methods, and based on return type since no bean name is given -->
|
||||||
|
<lookup-method name="getOneArgument" bean="testBean"/>
|
||||||
|
<lookup-method name="getTwoArguments" bean="testBean"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="testBean" class="org.springframework.tests.sample.beans.TestBean" scope="prototype"/>
|
||||||
|
|
||||||
|
</beans>
|
||||||
Loading…
Reference in New Issue