Browse Source

Support for functional instance supplier callback at BeanDefinition level

Issue: SPR-14832
pull/1274/head
Juergen Hoeller 8 years ago
parent
commit
3ee6286eb5
  1. 8
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
  2. 27
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
  3. 34
      spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java
  4. 28
      spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java

8
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java

@ -38,6 +38,7 @@ import java.util.Set; @@ -38,6 +38,7 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
@ -1036,6 +1037,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac @@ -1036,6 +1037,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
BeanWrapper bw = new BeanWrapperImpl(instanceSupplier.get());
initBeanWrapper(bw);
return bw;
}
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}

27
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java

@ -22,6 +22,7 @@ import java.util.LinkedHashMap; @@ -22,6 +22,7 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.beans.BeanMetadataAttributeAccessor;
import org.springframework.beans.MutablePropertyValues;
@ -165,6 +166,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess @@ -165,6 +166,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private MethodOverrides methodOverrides = new MethodOverrides();
private Supplier<?> instanceSupplier;
private String factoryBeanName;
private String factoryMethodName;
@ -234,6 +237,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess @@ -234,6 +237,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
setPrimary(originalAbd.isPrimary());
setNonPublicAccessAllowed(originalAbd.isNonPublicAccessAllowed());
setLenientConstructorResolution(originalAbd.isLenientConstructorResolution());
setInstanceSupplier(originalAbd.getInstanceSupplier());
setInitMethodName(originalAbd.getInitMethodName());
setEnforceInitMethod(originalAbd.isEnforceInitMethod());
setDestroyMethodName(originalAbd.getDestroyMethodName());
@ -298,6 +302,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess @@ -298,6 +302,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
setDependsOn(otherAbd.getDependsOn());
setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed());
setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
setInstanceSupplier(otherAbd.getInstanceSupplier());
if (StringUtils.hasLength(otherAbd.getInitMethodName())) {
setInitMethodName(otherAbd.getInitMethodName());
setEnforceInitMethod(otherAbd.isEnforceInitMethod());
@ -738,6 +743,28 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess @@ -738,6 +743,28 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
}
/**
* Specify a callback for creating an instance of the bean,
* as an alternative to a declaratively specified factory method.
* <p>If such a callback is set, it will override any other constructor
* or factory method metadata. However, bean property population and
* potential annotation-driven injection will still apply as usual.
* @since 5.0
* @see #setConstructorArgumentValues(ConstructorArgumentValues)
* @see #setPropertyValues(MutablePropertyValues)
*/
public void setInstanceSupplier(Supplier<?> instanceSupplier) {
this.instanceSupplier = instanceSupplier;
}
/**
* Return a callback for creating an instance of the bean, if any.
* @since 5.0
*/
public Supplier<?> getInstanceSupplier() {
return this.instanceSupplier;
}
@Override
public void setFactoryBeanName(String factoryBeanName) {
this.factoryBeanName = factoryBeanName;

34
spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

@ -22,6 +22,7 @@ import java.lang.reflect.Member; @@ -22,6 +22,7 @@ import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
@ -116,12 +117,45 @@ public class RootBeanDefinition extends AbstractBeanDefinition { @@ -116,12 +117,45 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
/**
* Create a new RootBeanDefinition for a singleton.
* @param beanClass the class of the bean to instantiate
* @see #setBeanClass
*/
public RootBeanDefinition(Class<?> beanClass) {
super();
setBeanClass(beanClass);
}
/**
* Create a new RootBeanDefinition for a singleton bean, constructing each instance
* through calling the given supplier (possibly a lambda or method reference).
* @param beanClass the class of the bean to instantiate
* @param instanceSupplier the supplier to construct a bean instance,
* as an alternative to a declaratively specified factory method
* @since 5.0
* @see #setInstanceSupplier(Supplier)
*/
public <T> RootBeanDefinition(Class<T> beanClass, Supplier<T> instanceSupplier) {
super();
setBeanClass(beanClass);
setInstanceSupplier(instanceSupplier);
}
/**
* Create a new RootBeanDefinition for a scoped bean, constructing each instance
* through calling the given supplier (possibly a lambda or method reference).
* @param beanClass the class of the bean to instantiate
* @param scope the name of the corresponding scope
* @param instanceSupplier the supplier to construct a bean instance,
* as an alternative to a declaratively specified factory method
* @since 5.0
* @see #setInstanceSupplier(Supplier)
*/
public <T> RootBeanDefinition(Class<T> beanClass, String scope, Supplier<T> instanceSupplier) {
super();
setBeanClass(beanClass);
setScope(scope);
setInstanceSupplier(instanceSupplier);
}
/**
* Create a new RootBeanDefinition for a singleton,
* using the given autowire mode.

28
spring-context/src/test/java/org/springframework/context/support/GenericApplicationContextTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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,6 +43,7 @@ public class GenericApplicationContextTests { @@ -43,6 +43,7 @@ public class GenericApplicationContextTests {
ac.registerBeanDefinition("testBean", new RootBeanDefinition(String.class));
ac.refresh();
assertEquals("", ac.getBean("testBean"));
assertSame(ac.getBean("testBean"), ac.getBean(String.class));
assertSame(ac.getBean("testBean"), ac.getBean(CharSequence.class));
@ -55,6 +56,31 @@ public class GenericApplicationContextTests { @@ -55,6 +56,31 @@ public class GenericApplicationContextTests {
}
}
@Test
public void withSingletonSupplier() {
GenericApplicationContext ac = new GenericApplicationContext();
ac.registerBeanDefinition("testBean", new RootBeanDefinition(String.class, ac::toString));
ac.refresh();
assertSame(ac.getBean("testBean"), ac.getBean("testBean"));
assertSame(ac.getBean("testBean"), ac.getBean(String.class));
assertSame(ac.getBean("testBean"), ac.getBean(CharSequence.class));
assertEquals(ac.toString(), ac.getBean("testBean"));
}
@Test
public void withScopedSupplier() {
GenericApplicationContext ac = new GenericApplicationContext();
ac.registerBeanDefinition("testBean",
new RootBeanDefinition(String.class, RootBeanDefinition.SCOPE_PROTOTYPE, ac::toString));
ac.refresh();
assertNotSame(ac.getBean("testBean"), ac.getBean("testBean"));
assertEquals(ac.getBean("testBean"), ac.getBean(String.class));
assertEquals(ac.getBean("testBean"), ac.getBean(CharSequence.class));
assertEquals(ac.toString(), ac.getBean("testBean"));
}
@Test
public void accessAfterClosing() {
GenericApplicationContext ac = new GenericApplicationContext();

Loading…
Cancel
Save