Enforce non-null value from getBean and at injection points
Bean-derived null values may still get passed into bean properties and injection points but only if those are declared as non-required. Note that getBean will never return null; a manual bean.equals(null) / "null".equals(bean.toString()) check identifies expected null values now. This will only ever happen with custom FactoryBeans or factory methods returning null - and since all common cases are handled by autowiring or bean property values in bean definitions, there should be no need to ever manually check for such a null value received from getBean. Issue: SPR-15829
This commit is contained in:
parent
10dcaa9bf6
commit
b94302b5bd
|
@ -45,7 +45,6 @@ public interface ObjectFactory<T> {
|
|||
* @return the resulting instance
|
||||
* @throws BeansException in case of creation errors
|
||||
*/
|
||||
@Nullable
|
||||
T getObject() throws BeansException;
|
||||
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ public interface ObjectProvider<T> extends ObjectFactory<T> {
|
|||
* @throws BeansException in case of creation errors
|
||||
* @see #getObject()
|
||||
*/
|
||||
@Nullable
|
||||
T getObject(Object... args) throws BeansException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -725,7 +725,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
|||
|
||||
@Override
|
||||
public Object resolveShortcut(BeanFactory beanFactory) {
|
||||
return resolveCandidate(this.shortcut, this.requiredType, beanFactory);
|
||||
return beanFactory.getBean(this.shortcut, this.requiredType);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -268,7 +268,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
|
|||
* @return the bean instance to use, either the original or a wrapped one
|
||||
* @throws BeansException if the initialization failed
|
||||
*/
|
||||
@Nullable
|
||||
Object initializeBean(Object existingBean, String beanName) throws BeansException;
|
||||
|
||||
/**
|
||||
|
@ -281,7 +280,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
|
|||
* @throws BeansException if any post-processing failed
|
||||
* @see BeanPostProcessor#postProcessBeforeInitialization
|
||||
*/
|
||||
@Nullable
|
||||
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
|
||||
throws BeansException;
|
||||
|
||||
|
@ -295,7 +293,6 @@ public interface AutowireCapableBeanFactory extends BeanFactory {
|
|||
* @throws BeansException if any post-processing failed
|
||||
* @see BeanPostProcessor#postProcessAfterInitialization
|
||||
*/
|
||||
@Nullable
|
||||
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
|
||||
throws BeansException;
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
|
||||
return beanFactory.getBean(beanName, requiredType);
|
||||
return beanFactory.getBean(beanName);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -87,7 +87,6 @@ public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationA
|
|||
* (typically with the passed-in bean instance as default)
|
||||
* @throws org.springframework.beans.BeansException in case of errors
|
||||
*/
|
||||
@Nullable
|
||||
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
|
|
@ -410,37 +410,36 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object initializeBean(Object existingBean, String beanName) {
|
||||
return initializeBean(beanName, existingBean, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
Object result = existingBean;
|
||||
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
|
||||
result = beanProcessor.postProcessBeforeInitialization(result, beanName);
|
||||
if (result == null) {
|
||||
return null;
|
||||
Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
|
||||
if (current == null) {
|
||||
return result;
|
||||
}
|
||||
result = current;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
|
||||
throws BeansException {
|
||||
|
||||
Object result = existingBean;
|
||||
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
|
||||
result = beanProcessor.postProcessAfterInitialization(result, beanName);
|
||||
if (result == null) {
|
||||
return null;
|
||||
Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
|
||||
if (current == null) {
|
||||
return result;
|
||||
}
|
||||
result = current;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -461,7 +460,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @see #doCreateBean
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
|
||||
throws BeanCreationException {
|
||||
|
||||
|
@ -535,7 +533,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @see #instantiateUsingFactoryMethod
|
||||
* @see #autowireConstructor
|
||||
*/
|
||||
@Nullable
|
||||
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
|
||||
throws BeanCreationException {
|
||||
|
||||
|
@ -547,23 +544,23 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
if (instanceWrapper == null) {
|
||||
instanceWrapper = createBeanInstance(beanName, mbd, args);
|
||||
}
|
||||
final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
|
||||
Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
|
||||
mbd.resolvedTargetType = beanType;
|
||||
final Object bean = instanceWrapper.getWrappedInstance();
|
||||
Class<?> beanType = instanceWrapper.getWrappedClass();
|
||||
if (beanType != NullBean.class) {
|
||||
mbd.resolvedTargetType = beanType;
|
||||
}
|
||||
|
||||
if (beanType != null) {
|
||||
// Allow post-processors to modify the merged bean definition.
|
||||
synchronized (mbd.postProcessingLock) {
|
||||
if (!mbd.postProcessed) {
|
||||
try {
|
||||
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Post-processing of merged bean definition failed", ex);
|
||||
}
|
||||
mbd.postProcessed = true;
|
||||
// Allow post-processors to modify the merged bean definition.
|
||||
synchronized (mbd.postProcessingLock) {
|
||||
if (!mbd.postProcessed) {
|
||||
try {
|
||||
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||
"Post-processing of merged bean definition failed", ex);
|
||||
}
|
||||
mbd.postProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -583,9 +580,7 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
Object exposedObject = bean;
|
||||
try {
|
||||
populateBean(beanName, mbd, instanceWrapper);
|
||||
if (exposedObject != null) {
|
||||
exposedObject = initializeBean(beanName, exposedObject, mbd);
|
||||
}
|
||||
exposedObject = initializeBean(beanName, exposedObject, mbd);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
|
||||
|
@ -624,15 +619,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
}
|
||||
}
|
||||
|
||||
if (bean != null) {
|
||||
// Register bean as disposable.
|
||||
try {
|
||||
registerDisposableBeanIfNecessary(beanName, bean, mbd);
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanCreationException(
|
||||
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
|
||||
}
|
||||
// Register bean as disposable.
|
||||
try {
|
||||
registerDisposableBeanIfNecessary(beanName, bean, mbd);
|
||||
}
|
||||
catch (BeanDefinitionValidationException ex) {
|
||||
throw new BeanCreationException(
|
||||
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
|
||||
}
|
||||
|
||||
return exposedObject;
|
||||
|
@ -904,17 +897,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @param bean the raw bean instance
|
||||
* @return the object to expose as bean reference
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, @Nullable Object bean) {
|
||||
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
|
||||
Object exposedObject = bean;
|
||||
if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
|
||||
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
|
||||
for (BeanPostProcessor bp : getBeanPostProcessors()) {
|
||||
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
|
||||
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
|
||||
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
|
||||
if (exposedObject == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -954,9 +943,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
instance = resolveBeforeInstantiation(beanName, mbd);
|
||||
if (instance == null) {
|
||||
bw = createBeanInstance(beanName, mbd, null);
|
||||
if (bw == null) {
|
||||
return null;
|
||||
}
|
||||
instance = bw.getWrappedInstance();
|
||||
}
|
||||
}
|
||||
|
@ -995,9 +981,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
instance = resolveBeforeInstantiation(beanName, mbd);
|
||||
if (instance == null) {
|
||||
BeanWrapper bw = createBeanInstance(beanName, mbd, null);
|
||||
if (bw == null) {
|
||||
return null;
|
||||
}
|
||||
instance = bw.getWrappedInstance();
|
||||
}
|
||||
}
|
||||
|
@ -1097,7 +1080,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @see #autowireConstructor
|
||||
* @see #instantiateBean
|
||||
*/
|
||||
@Nullable
|
||||
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
|
||||
// Make sure bean class is actually resolved at this point.
|
||||
Class<?> beanClass = resolveBeanClass(mbd, beanName);
|
||||
|
@ -1184,9 +1166,8 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @see #obtainFromSupplier
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object getObjectForBeanInstance(
|
||||
@Nullable Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
|
||||
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
|
||||
|
||||
String currentlyCreatedBean = this.currentlyCreatedBean.get();
|
||||
if (currentlyCreatedBean != null) {
|
||||
|
@ -1262,7 +1243,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @return a BeanWrapper for the new instance
|
||||
* @see #getBean(String, Object[])
|
||||
*/
|
||||
@Nullable
|
||||
protected BeanWrapper instantiateUsingFactoryMethod(
|
||||
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
|
||||
|
||||
|
@ -1698,7 +1678,6 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* @see #invokeInitMethods
|
||||
* @see #applyBeanPostProcessorsAfterInitialization
|
||||
*/
|
||||
@Nullable
|
||||
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
|
||||
if (System.getSecurityManager() != null) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
|
||||
|
@ -1715,18 +1694,16 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
|
||||
}
|
||||
|
||||
if (wrappedBean != null) {
|
||||
try {
|
||||
invokeInitMethods(beanName, wrappedBean, mbd);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(
|
||||
(mbd != null ? mbd.getResourceDescription() : null),
|
||||
beanName, "Invocation of init method failed", ex);
|
||||
}
|
||||
if (mbd == null || !mbd.isSynthetic()) {
|
||||
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
|
||||
}
|
||||
try {
|
||||
invokeInitMethods(beanName, wrappedBean, mbd);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new BeanCreationException(
|
||||
(mbd != null ? mbd.getResourceDescription() : null),
|
||||
beanName, "Invocation of init method failed", ex);
|
||||
}
|
||||
if (mbd == null || !mbd.isSynthetic()) {
|
||||
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
|
||||
}
|
||||
|
||||
return wrappedBean;
|
||||
|
|
|
@ -380,9 +380,13 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
// corner cases, in particular in functional configuration and Kotlin scenarios.
|
||||
// A future Spring generation might eventually forbid null values completely
|
||||
// and throw IllegalStateExceptions instead of leniently passing them through.
|
||||
if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
|
||||
if (requiredType != null && !requiredType.isInstance(bean)) {
|
||||
try {
|
||||
return getTypeConverter().convertIfNecessary(bean, requiredType);
|
||||
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
|
||||
if (convertedBean == null) {
|
||||
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
|
||||
}
|
||||
return convertedBean;
|
||||
}
|
||||
catch (TypeMismatchException ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
@ -421,9 +425,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
return !BeanFactoryUtils.isFactoryDereference(name);
|
||||
}
|
||||
}
|
||||
else if (containsSingleton(beanName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// No singleton instance found -> check bean definition.
|
||||
BeanFactory parentBeanFactory = getParentBeanFactory();
|
||||
|
@ -496,7 +497,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
|
||||
// Check manually registered singletons.
|
||||
Object beanInstance = getSingleton(beanName, false);
|
||||
if (beanInstance != null) {
|
||||
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
|
||||
if (beanInstance instanceof FactoryBean) {
|
||||
if (!BeanFactoryUtils.isFactoryDereference(name)) {
|
||||
Class<?> type = getTypeForFactoryBean((FactoryBean<?>) beanInstance);
|
||||
|
@ -605,7 +606,7 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
|
||||
// Check manually registered singletons.
|
||||
Object beanInstance = getSingleton(beanName, false);
|
||||
if (beanInstance != null) {
|
||||
if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
|
||||
if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
|
||||
return getTypeForFactoryBean((FactoryBean<?>) beanInstance);
|
||||
}
|
||||
|
@ -613,10 +614,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
return beanInstance.getClass();
|
||||
}
|
||||
}
|
||||
else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
|
||||
// null instance registered
|
||||
return null;
|
||||
}
|
||||
|
||||
// No singleton instance found -> check bean definition.
|
||||
BeanFactory parentBeanFactory = getParentBeanFactory();
|
||||
|
@ -1014,10 +1011,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
if (beanInstance != null) {
|
||||
return (beanInstance instanceof FactoryBean);
|
||||
}
|
||||
else if (containsSingleton(beanName)) {
|
||||
// null instance registered
|
||||
return false;
|
||||
}
|
||||
|
||||
// No singleton instance found -> check bean definition.
|
||||
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
|
||||
|
@ -1631,13 +1624,8 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
* @param mbd the merged bean definition
|
||||
* @return the object to expose for the bean
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getObjectForBeanInstance(
|
||||
@Nullable Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
|
||||
|
||||
if (beanInstance == null) {
|
||||
return null;
|
||||
}
|
||||
Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
|
||||
|
||||
// Don't let calling code try to dereference the factory if the bean isn't a factory.
|
||||
if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
|
||||
|
@ -1782,7 +1770,6 @@ public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport imp
|
|||
* @return a new instance of the bean
|
||||
* @throws BeanCreationException if the bean could not be created
|
||||
*/
|
||||
@Nullable
|
||||
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
|
||||
throws BeanCreationException;
|
||||
|
||||
|
|
|
@ -205,6 +205,9 @@ class BeanDefinitionValueResolver {
|
|||
"Error converting typed String value for " + argName, ex);
|
||||
}
|
||||
}
|
||||
else if (value instanceof NullBean) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return evaluate(value);
|
||||
}
|
||||
|
@ -309,12 +312,13 @@ class BeanDefinitionValueResolver {
|
|||
Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null);
|
||||
if (innerBean instanceof FactoryBean) {
|
||||
boolean synthetic = mbd.isSynthetic();
|
||||
return this.beanFactory.getObjectFromFactoryBean(
|
||||
innerBean = this.beanFactory.getObjectFromFactoryBean(
|
||||
(FactoryBean<?>) innerBean, actualInnerBeanName, !synthetic);
|
||||
}
|
||||
else {
|
||||
return innerBean;
|
||||
if (innerBean instanceof NullBean) {
|
||||
innerBean = null;
|
||||
}
|
||||
return innerBean;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new BeanCreationException(
|
||||
|
@ -344,8 +348,10 @@ class BeanDefinitionValueResolver {
|
|||
/**
|
||||
* Resolve a reference to another bean in the factory.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
|
||||
try {
|
||||
Object bean;
|
||||
String refName = ref.getBeanName();
|
||||
refName = String.valueOf(doEvaluate(refName));
|
||||
if (ref.isToParent()) {
|
||||
|
@ -355,13 +361,16 @@ class BeanDefinitionValueResolver {
|
|||
"Can't resolve reference to bean '" + refName +
|
||||
"' in parent factory: no parent factory available");
|
||||
}
|
||||
return this.beanFactory.getParentBeanFactory().getBean(refName);
|
||||
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
|
||||
}
|
||||
else {
|
||||
Object bean = this.beanFactory.getBean(refName);
|
||||
bean = this.beanFactory.getBean(refName);
|
||||
this.beanFactory.registerDependentBean(refName, this.beanName);
|
||||
return bean;
|
||||
}
|
||||
if (bean instanceof NullBean) {
|
||||
bean = null;
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
catch (BeansException ex) {
|
||||
throw new BeanCreationException(
|
||||
|
|
|
@ -348,7 +348,6 @@ class ConstructorResolver {
|
|||
* method, or {@code null} if none (-> use constructor argument values from bean definition)
|
||||
* @return a BeanWrapper for the new instance
|
||||
*/
|
||||
@Nullable
|
||||
public BeanWrapper instantiateUsingFactoryMethod(
|
||||
final String beanName, final RootBeanDefinition mbd, @Nullable final Object[] explicitArgs) {
|
||||
|
||||
|
@ -576,9 +575,6 @@ class ConstructorResolver {
|
|||
mbd, beanName, this.beanFactory, factoryBean, factoryMethodToUse, argsToUse);
|
||||
}
|
||||
|
||||
if (beanInstance == null) {
|
||||
return null;
|
||||
}
|
||||
bw.setBeanInstance(beanInstance);
|
||||
return bw;
|
||||
}
|
||||
|
|
|
@ -1129,14 +1129,27 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
if (autowiredBeanNames != null) {
|
||||
autowiredBeanNames.add(autowiredBeanName);
|
||||
}
|
||||
return (instanceCandidate instanceof Class ?
|
||||
descriptor.resolveCandidate(autowiredBeanName, type, this) : instanceCandidate);
|
||||
if (instanceCandidate instanceof Class) {
|
||||
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
|
||||
}
|
||||
Object result = instanceCandidate;
|
||||
if (result instanceof NullBean) {
|
||||
if (isRequired(descriptor)) {
|
||||
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
if (!ClassUtils.isAssignableValue(type, result)) {
|
||||
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally {
|
||||
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
|
||||
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
|
||||
|
||||
|
@ -1500,7 +1513,8 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
isAutowireCandidate(beanName, mbd, descriptor, getAutowireCandidateResolver())) {
|
||||
// Probably a proxy interfering with target type match -> throw meaningful exception.
|
||||
Object beanInstance = getSingleton(beanName, false);
|
||||
Class<?> beanType = (beanInstance != null ? beanInstance.getClass() : predictBeanType(beanName, mbd));
|
||||
Class<?> beanType = (beanInstance != null && beanInstance.getClass() != NullBean.class) ?
|
||||
beanInstance.getClass() : predictBeanType(beanName, mbd);
|
||||
if (beanType != null && !type.isAssignableFrom(beanType)) {
|
||||
throw new BeanNotOfRequiredTypeException(beanName, type, beanType);
|
||||
}
|
||||
|
@ -1526,7 +1540,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
}
|
||||
@Override
|
||||
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
|
||||
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, requiredType, args) :
|
||||
return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
|
||||
super.resolveCandidate(beanName, requiredType, beanFactory));
|
||||
}
|
||||
};
|
||||
|
@ -1615,18 +1629,20 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject() throws BeansException {
|
||||
if (this.optional) {
|
||||
return createOptionalDependency(this.descriptor, this.beanName);
|
||||
}
|
||||
else {
|
||||
return doResolveDependency(this.descriptor, this.beanName, null, null);
|
||||
Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
|
||||
if (result == null) {
|
||||
throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object getObject(final Object... args) throws BeansException {
|
||||
if (this.optional) {
|
||||
return createOptionalDependency(this.descriptor, this.beanName, args);
|
||||
|
@ -1635,10 +1651,14 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
DependencyDescriptor descriptorToUse = new DependencyDescriptor(descriptor) {
|
||||
@Override
|
||||
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) {
|
||||
return ((AbstractBeanFactory) beanFactory).getBean(beanName, requiredType, args);
|
||||
return beanFactory.getBean(beanName, args);
|
||||
}
|
||||
};
|
||||
return doResolveDependency(descriptorToUse, this.beanName, null, null);
|
||||
Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
|
||||
if (result == null) {
|
||||
throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1680,6 +1700,16 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
return doResolveDependency(descriptorToUse, this.beanName, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Object getValue() throws BeansException {
|
||||
if (this.optional) {
|
||||
return createOptionalDependency(this.descriptor, this.beanName);
|
||||
}
|
||||
else {
|
||||
return doResolveDependency(this.descriptor, this.beanName, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1695,7 +1725,7 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
|||
@Override
|
||||
@Nullable
|
||||
public Object get() throws BeansException {
|
||||
return getObject();
|
||||
return getValue();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -73,13 +73,6 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
|
||||
|
||||
/**
|
||||
* Internal marker for a null singleton object:
|
||||
* used as marker value for concurrent Maps (which don't support null values).
|
||||
*/
|
||||
protected static final Object NULL_OBJECT = new Object();
|
||||
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
@ -125,7 +118,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||
|
||||
@Override
|
||||
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
|
||||
Assert.notNull(beanName, "'beanName' must not be null");
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
Assert.notNull(singletonObject, "Singleton object must not be null");
|
||||
synchronized (this.singletonObjects) {
|
||||
Object oldObject = this.singletonObjects.get(beanName);
|
||||
if (oldObject != null) {
|
||||
|
@ -142,9 +136,9 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||
* @param beanName the name of the bean
|
||||
* @param singletonObject the singleton object
|
||||
*/
|
||||
protected void addSingleton(String beanName, @Nullable Object singletonObject) {
|
||||
protected void addSingleton(String beanName, Object singletonObject) {
|
||||
synchronized (this.singletonObjects) {
|
||||
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
|
||||
this.singletonObjects.put(beanName, singletonObject);
|
||||
this.singletonFactories.remove(beanName);
|
||||
this.earlySingletonObjects.remove(beanName);
|
||||
this.registeredSingletons.add(beanName);
|
||||
|
@ -200,7 +194,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||
}
|
||||
}
|
||||
}
|
||||
return (singletonObject != NULL_OBJECT ? singletonObject : null);
|
||||
return singletonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,9 +205,8 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||
* with, if necessary
|
||||
* @return the registered singleton object
|
||||
*/
|
||||
@Nullable
|
||||
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
|
||||
Assert.notNull(beanName, "'beanName' must not be null");
|
||||
Assert.notNull(beanName, "Bean name must not be null");
|
||||
synchronized (this.singletonObjects) {
|
||||
Object singletonObject = this.singletonObjects.get(beanName);
|
||||
if (singletonObject == null) {
|
||||
|
@ -261,7 +254,7 @@ public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements
|
|||
addSingleton(beanName, singletonObject);
|
||||
}
|
||||
}
|
||||
return (singletonObject != NULL_OBJECT ? singletonObject : null);
|
||||
return singletonObject;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,8 +81,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
*/
|
||||
@Nullable
|
||||
protected Object getCachedObjectForFactoryBean(String beanName) {
|
||||
Object object = this.factoryBeanObjectCache.get(beanName);
|
||||
return (object != NULL_OBJECT ? object : null);
|
||||
return this.factoryBeanObjectCache.get(beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +93,6 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
* @throws BeanCreationException if FactoryBean object creation failed
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Nullable
|
||||
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
|
||||
if (factory.isSingleton() && containsSingleton(beanName)) {
|
||||
synchronized (getSingletonMutex()) {
|
||||
|
@ -108,7 +106,7 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
object = alreadyThere;
|
||||
}
|
||||
else {
|
||||
if (object != null && shouldPostProcess) {
|
||||
if (shouldPostProcess) {
|
||||
try {
|
||||
object = postProcessObjectFromFactoryBean(object, beanName);
|
||||
}
|
||||
|
@ -117,15 +115,15 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
"Post-processing of FactoryBean's singleton object failed", ex);
|
||||
}
|
||||
}
|
||||
this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
|
||||
this.factoryBeanObjectCache.put(beanName, object);
|
||||
}
|
||||
}
|
||||
return (object != NULL_OBJECT ? object : null);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Object object = doGetObjectFromFactoryBean(factory, beanName);
|
||||
if (object != null && shouldPostProcess) {
|
||||
if (shouldPostProcess) {
|
||||
try {
|
||||
object = postProcessObjectFromFactoryBean(object, beanName);
|
||||
}
|
||||
|
@ -145,7 +143,6 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
* @throws BeanCreationException if FactoryBean object creation failed
|
||||
* @see org.springframework.beans.factory.FactoryBean#getObject()
|
||||
*/
|
||||
@Nullable
|
||||
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
|
||||
throws BeanCreationException {
|
||||
|
||||
|
@ -174,9 +171,12 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
|
||||
// Do not accept a null value for a FactoryBean that's not fully
|
||||
// initialized yet: Many FactoryBeans just return null then.
|
||||
if (object == null && isSingletonCurrentlyInCreation(beanName)) {
|
||||
throw new BeanCurrentlyInCreationException(
|
||||
beanName, "FactoryBean which is currently in creation returned null from getObject");
|
||||
if (object == null) {
|
||||
if (isSingletonCurrentlyInCreation(beanName)) {
|
||||
throw new BeanCurrentlyInCreationException(
|
||||
beanName, "FactoryBean which is currently in creation returned null from getObject");
|
||||
}
|
||||
object = new NullBean();
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
@ -191,7 +191,6 @@ public abstract class FactoryBeanRegistrySupport extends DefaultSingletonBeanReg
|
|||
* @return the object to expose
|
||||
* @throws org.springframework.beans.BeansException if any post-processing failed
|
||||
*/
|
||||
@Nullable
|
||||
protected Object postProcessObjectFromFactoryBean(Object object, String beanName) throws BeansException {
|
||||
return object;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 2002-2017 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.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Internal representation of a null bean instance, e.g. for a {@code null} value
|
||||
* returned from {@link FactoryBean#getObject()} or from a factory method.
|
||||
*
|
||||
* <p>Each such null bean is represented by a dedicated {@code NullBean} instance
|
||||
* which are not equal to each other, uniquely differentiating each bean as returned
|
||||
* from all variants of {@link org.springframework.beans.factory.BeanFactory#getBean}.
|
||||
* However, each such instance will return {@code true} for {@code #equals(null)}
|
||||
* and returns "null" from {@code #toString()}, which is how they can be tested
|
||||
* externally (since this class itself is not public).
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 5.0
|
||||
*/
|
||||
final class NullBean {
|
||||
|
||||
NullBean() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return (this == obj || obj == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return NullBean.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
}
|
|
@ -152,7 +152,11 @@ public class SimpleInstantiationStrategy implements InstantiationStrategy {
|
|||
Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
|
||||
try {
|
||||
currentlyInvokedFactoryMethod.set(factoryMethod);
|
||||
return factoryMethod.invoke(factoryBean, args);
|
||||
Object result = factoryMethod.invoke(factoryBean, args);
|
||||
if (result == null) {
|
||||
result = new NullBean();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
finally {
|
||||
if (priorInvokedFactoryMethod != null) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
@ -51,8 +51,8 @@ public class FactoryBeanTests {
|
|||
public void testFactoryBeanReturnsNull() throws Exception {
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(factory).loadBeanDefinitions(RETURNS_NULL_CONTEXT);
|
||||
Object result = factory.getBean("factoryBean");
|
||||
assertNull(result);
|
||||
|
||||
assertEquals("null", factory.getBean("factoryBean").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -572,8 +572,9 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithNull() {
|
||||
public void testConstructorResourceInjectionWithNullFromFactoryBean() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
bf.registerResolvableDependency(BeanFactory.class, bf);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
|
@ -603,6 +604,42 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
assertSame(bf, bean.getBeanFactory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithNullFromFactoryMethod() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
bf.registerResolvableDependency(BeanFactory.class, bf);
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
RootBeanDefinition bd = new RootBeanDefinition(ConstructorResourceInjectionBean.class);
|
||||
bd.setScope(RootBeanDefinition.SCOPE_PROTOTYPE);
|
||||
bf.registerBeanDefinition("annotatedBean", bd);
|
||||
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
RootBeanDefinition ntb = new RootBeanDefinition(NullFactoryMethods.class);
|
||||
ntb.setFactoryMethodName("createNestedTestBean");
|
||||
bf.registerBeanDefinition("nestedTestBean", ntb);
|
||||
bf.registerSingleton("nestedTestBean2", new NestedTestBean());
|
||||
|
||||
ConstructorResourceInjectionBean bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNull(bean.getTestBean());
|
||||
assertNull(bean.getTestBean2());
|
||||
assertNull(bean.getTestBean3());
|
||||
assertNull(bean.getTestBean4());
|
||||
assertNull(bean.getNestedTestBean());
|
||||
assertSame(bf, bean.getBeanFactory());
|
||||
|
||||
bean = (ConstructorResourceInjectionBean) bf.getBean("annotatedBean");
|
||||
assertNull(bean.getTestBean());
|
||||
assertNull(bean.getTestBean2());
|
||||
assertNull(bean.getTestBean3());
|
||||
assertNull(bean.getTestBean4());
|
||||
assertNull(bean.getNestedTestBean());
|
||||
assertSame(bf, bean.getBeanFactory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleCandidates() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
@ -756,6 +793,33 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
@Test(expected = UnsatisfiedDependencyException.class)
|
||||
public void testSingleConstructorInjectionWithMissingDependency() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
|
||||
bf.getBean("annotatedBean");
|
||||
}
|
||||
|
||||
@Test(expected = UnsatisfiedDependencyException.class)
|
||||
public void testSingleConstructorInjectionWithNullDependency() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
|
||||
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
|
||||
bpp.setBeanFactory(bf);
|
||||
bf.addBeanPostProcessor(bpp);
|
||||
bf.registerBeanDefinition("annotatedBean", new RootBeanDefinition(SingleConstructorCollectionInjectionBean.class));
|
||||
RootBeanDefinition tb = new RootBeanDefinition(NullFactoryMethods.class);
|
||||
tb.setFactoryMethodName("createTestBean");
|
||||
bf.registerBeanDefinition("testBean", tb);
|
||||
|
||||
bf.getBean("annotatedBean");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorResourceInjectionWithMultipleCandidatesAndFallback() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
@ -2634,7 +2698,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
|
||||
public static class ConstructorResourceInjectionBean extends ResourceInjectionBean {
|
||||
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
protected ITestBean testBean3;
|
||||
|
||||
private ITestBean testBean4;
|
||||
|
@ -2643,7 +2707,6 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
|
||||
public ConstructorResourceInjectionBean() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
@ -2653,7 +2716,8 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
@Autowired
|
||||
public ConstructorResourceInjectionBean(ITestBean testBean4, NestedTestBean nestedTestBean,
|
||||
public ConstructorResourceInjectionBean(@Autowired(required = false) ITestBean testBean4,
|
||||
@Autowired(required = false) NestedTestBean nestedTestBean,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
this.testBean4 = testBean4;
|
||||
this.nestedTestBean = nestedTestBean;
|
||||
|
@ -2669,7 +2733,7 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Autowired
|
||||
@Autowired(required = false)
|
||||
public void setTestBean2(TestBean testBean2) {
|
||||
super.setTestBean2(testBean2);
|
||||
}
|
||||
|
@ -3802,6 +3866,18 @@ public class AutowiredAnnotationBeanPostProcessorTests {
|
|||
}
|
||||
|
||||
|
||||
public static class NullFactoryMethods {
|
||||
|
||||
public static TestBean createTestBean() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static NestedTestBean createNestedTestBean() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class ProvidedArgumentBean {
|
||||
|
||||
public ProvidedArgumentBean(String[] args) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
@ -38,6 +38,7 @@ public class PropertyPathFactoryBeanTests {
|
|||
|
||||
private static final Resource CONTEXT = qualifiedResource(PropertyPathFactoryBeanTests.class, "context.xml");
|
||||
|
||||
|
||||
@Test
|
||||
public void testPropertyPathFactoryBeanWithSingletonResult() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
|
@ -78,7 +79,7 @@ public class PropertyPathFactoryBeanTests {
|
|||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
|
||||
assertNull(xbf.getType("tb.spouse.spouse"));
|
||||
assertNull(xbf.getBean("tb.spouse.spouse"));
|
||||
assertEquals("null", xbf.getBean("tb.spouse.spouse").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -92,4 +93,18 @@ public class PropertyPathFactoryBeanTests {
|
|||
assertSame(spouse, tbWithInner.getFriends().iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyPathFactoryBeanAsNullReference() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
|
||||
assertNull(xbf.getBean("tbWithNullReference", TestBean.class).getSpouse());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertyPathFactoryBeanAsInnerNull() {
|
||||
DefaultListableBeanFactory xbf = new DefaultListableBeanFactory();
|
||||
new XmlBeanDefinitionReader(xbf).loadBeanDefinitions(CONTEXT);
|
||||
assertNull(xbf.getBean("tbWithInnerNull", TestBean.class).getSpouse());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
@ -91,8 +91,7 @@ public class FactoryMethodTests {
|
|||
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(xbf);
|
||||
reader.loadBeanDefinitions(new ClassPathResource("factory-methods.xml", getClass()));
|
||||
|
||||
FactoryMethods fm = (FactoryMethods) xbf.getBean("null");
|
||||
assertNull(fm);
|
||||
assertEquals("null", xbf.getBean("null").toString());
|
||||
|
||||
try {
|
||||
xbf.getBean("nullWithProperty");
|
||||
|
|
|
@ -57,6 +57,16 @@
|
|||
<property name="friends">
|
||||
<bean name="otb.spouse" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
|
||||
</property>
|
||||
</bean>
|
||||
</bean>
|
||||
|
||||
<bean id="tbWithNullReference" class="org.springframework.tests.sample.beans.TestBean">
|
||||
<property name="spouse" ref="tb.spouse.spouse"/>
|
||||
</bean>
|
||||
|
||||
<bean id="tbWithInnerNull" class="org.springframework.tests.sample.beans.TestBean">
|
||||
<property name="spouse">
|
||||
<bean name="tb.spouse.spouse" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
|
|
|
@ -18,6 +18,8 @@ package org.springframework.cache.support;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Simple serializable class that serves as a {@code null} replacement
|
||||
* for cache stores which otherwise do not support {@code null} values.
|
||||
|
@ -46,4 +48,20 @@ public final class NullValue implements Serializable {
|
|||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
return (this == obj || obj == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return NullValue.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "null";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,7 +72,6 @@ public class SimpleThreadScope implements Scope {
|
|||
Object scopedObject = scope.get(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
Assert.state(scopedObject != null, "Scoped object resolved to null");
|
||||
scope.put(name, scopedObject);
|
||||
}
|
||||
return scopedObject;
|
||||
|
|
|
@ -508,7 +508,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
//---------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Registers the defined beans with the {@link MBeanServer}.
|
||||
* Register the defined beans with the {@link MBeanServer}.
|
||||
* <p>Each bean is exposed to the {@code MBeanServer} via a
|
||||
* {@code ModelMBean}. The actual implemetation of the
|
||||
* {@code ModelMBean} interface used depends on the implementation of
|
||||
|
@ -566,7 +566,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers an individual bean with the {@link #setServer MBeanServer}.
|
||||
* Register an individual bean with the {@link #setServer MBeanServer}.
|
||||
* <p>This method is responsible for deciding <strong>how</strong> a bean
|
||||
* should be exposed to the {@code MBeanServer}. Specifically, if the
|
||||
* supplied {@code mapValue} is the name of a bean that is configured
|
||||
|
@ -579,15 +579,13 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
* @param mapValue the value configured for this bean in the beans map;
|
||||
* may be either the {@code String} name of a bean, or the bean itself
|
||||
* @param beanKey the key associated with this bean in the beans map
|
||||
* @return the {@code ObjectName} under which the resource was registered,
|
||||
* or {@code null} if the actual resource was {@code null} as well
|
||||
* @return the {@code ObjectName} under which the resource was registered
|
||||
* @throws MBeanExportException if the export failed
|
||||
* @see #setBeans
|
||||
* @see #registerBeanInstance
|
||||
* @see #registerLazyInit
|
||||
*/
|
||||
@Nullable
|
||||
protected ObjectName registerBeanNameOrInstance(@Nullable Object mapValue, String beanKey) throws MBeanExportException {
|
||||
protected ObjectName registerBeanNameOrInstance(Object mapValue, String beanKey) throws MBeanExportException {
|
||||
try {
|
||||
if (mapValue instanceof String) {
|
||||
// Bean name pointing to a potentially lazy-init bean in the factory.
|
||||
|
@ -607,7 +605,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
return objectName;
|
||||
}
|
||||
}
|
||||
else if (mapValue != null) {
|
||||
else {
|
||||
// Plain bean instance -> register it directly.
|
||||
if (this.beanFactory != null) {
|
||||
Map<String, ?> beansOfSameType =
|
||||
|
@ -623,9 +621,6 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
}
|
||||
return registerBeanInstance(mapValue, beanKey);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new UnableToRegisterMBeanException(
|
||||
|
@ -634,14 +629,14 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
}
|
||||
|
||||
/**
|
||||
* Replaces any bean names used as keys in the {@code NotificationListener}
|
||||
* Replace any bean names used as keys in the {@code NotificationListener}
|
||||
* mappings with their corresponding {@code ObjectName} values.
|
||||
* @param beanName the name of the bean to be registered
|
||||
* @param objectName the {@code ObjectName} under which the bean will be registered
|
||||
* with the {@code MBeanServer}
|
||||
*/
|
||||
private void replaceNotificationListenerBeanNameKeysIfNecessary(String beanName, @Nullable ObjectName objectName) {
|
||||
if (objectName != null && this.notificationListeners != null) {
|
||||
private void replaceNotificationListenerBeanNameKeysIfNecessary(String beanName, ObjectName objectName) {
|
||||
if (this.notificationListeners != null) {
|
||||
for (NotificationListenerBean notificationListener : this.notificationListeners) {
|
||||
notificationListener.replaceObjectName(beanName, objectName);
|
||||
}
|
||||
|
@ -656,12 +651,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
* @return the {@code ObjectName} under which the bean was registered
|
||||
* with the {@code MBeanServer}
|
||||
*/
|
||||
@Nullable
|
||||
private ObjectName registerBeanInstance(@Nullable Object bean, String beanKey) throws JMException {
|
||||
if (bean == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ObjectName registerBeanInstance(Object bean, String beanKey) throws JMException {
|
||||
ObjectName objectName = getObjectName(bean, beanKey);
|
||||
Object mbeanToExpose = null;
|
||||
if (isMBean(bean.getClass())) {
|
||||
|
@ -695,7 +685,7 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers beans that are configured for lazy initialization with the
|
||||
* Register beans that are configured for lazy initialization with the
|
||||
* {@code MBeanServer} indirectly through a proxy.
|
||||
* @param beanName the name of the bean in the {@code BeanFactory}
|
||||
* @param beanKey the key associated with this bean in the beans map
|
||||
|
@ -890,7 +880,13 @@ public class MBeanExporter extends MBeanRegistrationSupport implements MBeanExpo
|
|||
Class<?> beanClass = this.beanFactory.getType(beanName);
|
||||
if (beanClass != null && callback.include(beanClass, beanName)) {
|
||||
boolean lazyInit = isBeanDefinitionLazyInit(this.beanFactory, beanName);
|
||||
Object beanInstance = (!lazyInit ? this.beanFactory.getBean(beanName) : null);
|
||||
Object beanInstance = null;
|
||||
if (!lazyInit) {
|
||||
beanInstance = this.beanFactory.getBean(beanName);
|
||||
if (!beanClass.isInstance(beanInstance)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!ScopedProxyUtils.isScopedTarget(beanName) && !beans.containsValue(beanName) &&
|
||||
(beanInstance == null ||
|
||||
!CollectionUtils.containsInstance(beans.values(), beanInstance))) {
|
||||
|
|
|
@ -58,14 +58,14 @@ public class CacheResolverCustomizationTests {
|
|||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setup() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
this.cacheManager = context.getBean("cacheManager", CacheManager.class);
|
||||
this.anotherCacheManager = context.getBean("anotherCacheManager", CacheManager.class);
|
||||
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noCustomization() {
|
||||
Cache cache = this.cacheManager.getCache("default");
|
||||
|
@ -162,12 +162,6 @@ public class CacheResolverCustomizationTests {
|
|||
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator keyGenerator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManager anotherCacheManager() {
|
||||
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
@ -114,7 +114,7 @@ public class CommonAnnotationBeanPostProcessorTests {
|
|||
rbd.setFactoryMethodName("create");
|
||||
bf.registerBeanDefinition("bean", rbd);
|
||||
|
||||
assertNull(bf.getBean("bean"));
|
||||
assertEquals("null", bf.getBean("bean").toString());
|
||||
bf.destroySingletons();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
@ -30,13 +30,6 @@ import static org.junit.Assert.*;
|
|||
*/
|
||||
public class GenericApplicationContextTests {
|
||||
|
||||
@Test
|
||||
public void nullBeanRegistration() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerSingleton("nullBean", null);
|
||||
new GenericApplicationContext(bf).refresh();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBeanForClass() {
|
||||
GenericApplicationContext ac = new GenericApplicationContext();
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.messaging.simp;
|
|||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link Scope} implementation exposing the attributes of a SiMP session
|
||||
|
@ -44,7 +43,6 @@ public class SimpSessionScope implements Scope {
|
|||
scopedObject = simpAttributes.getAttribute(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
Assert.state(scopedObject != null, "Scoped object resolved to null");
|
||||
simpAttributes.setAttribute(name, scopedObject);
|
||||
}
|
||||
return scopedObject;
|
||||
|
|
|
@ -54,7 +54,6 @@ public class SimpleTransactionScope implements Scope {
|
|||
Object scopedObject = scopedObjects.scopedInstances.get(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
Assert.state(scopedObject != null, "Scoped object resolved to null");
|
||||
scopedObjects.scopedInstances.put(name, scopedObject);
|
||||
}
|
||||
return scopedObject;
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.springframework.web.context.request;
|
|||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.config.Scope;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract {@link Scope} implementation that reads from a particular scope
|
||||
|
@ -44,7 +43,6 @@ public abstract class AbstractRequestAttributesScope implements Scope {
|
|||
Object scopedObject = attributes.getAttribute(name, getScope());
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
Assert.state(scopedObject != null, "Scoped object resolved to null");
|
||||
attributes.setAttribute(name, scopedObject, getScope());
|
||||
// Retrieve object again, registering it for implicit session attribute updates.
|
||||
// As a bonus, we also allow for potential decoration at the getAttribute level.
|
||||
|
|
|
@ -68,7 +68,6 @@ public class ServletContextScope implements Scope, DisposableBean {
|
|||
Object scopedObject = this.servletContext.getAttribute(name);
|
||||
if (scopedObject == null) {
|
||||
scopedObject = objectFactory.getObject();
|
||||
Assert.state(scopedObject != null, "Scoped object resolved to null");
|
||||
this.servletContext.setAttribute(name, scopedObject);
|
||||
}
|
||||
return scopedObject;
|
||||
|
|
Loading…
Reference in New Issue