diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanMetadataElement.java b/spring-beans/src/main/java/org/springframework/beans/BeanMetadataElement.java index e4436a1d57..7126c64ef2 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanMetadataElement.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanMetadataElement.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2019 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. @@ -32,6 +32,8 @@ public interface BeanMetadataElement { * (may be {@code null}). */ @Nullable - Object getSource(); + default Object getSource() { + return null; + } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowiredPropertyMarker.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowiredPropertyMarker.java new file mode 100644 index 0000000000..6da668ebc8 --- /dev/null +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AutowiredPropertyMarker.java @@ -0,0 +1,68 @@ +/* + * Copyright 2002-2019 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 + * + * https://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.config; + +import java.io.Serializable; + +import org.springframework.lang.Nullable; + +/** + * Simple marker class for an individually autowired property value, to be added + * to {@link BeanDefinition#getPropertyValues()} for a specific bean property. + * + *
At runtime, this will be replaced with a {@link DependencyDescriptor}
+ * for the corresponding bean property's write method, eventually to be resolved
+ * through a {@link AutowireCapableBeanFactory#resolveDependency} step.
+ *
+ * @author Juergen Hoeller
+ * @since 5.2
+ * @see AutowireCapableBeanFactory#resolveDependency
+ * @see BeanDefinition#getPropertyValues()
+ * @see org.springframework.beans.factory.support.BeanDefinitionBuilder#addAutowiredProperty
+ */
+public final class AutowiredPropertyMarker implements Serializable {
+
+ /**
+ * The canonical instance for the autowired marker value.
+ */
+ public static final Object INSTANCE = new AutowiredPropertyMarker();
+
+
+ private AutowiredPropertyMarker() {
+ }
+
+ private Object readResolve() {
+ return INSTANCE;
+ }
+
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ return (this == obj);
+ }
+
+ @Override
+ public int hashCode() {
+ return AutowiredPropertyMarker.class.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "(autowired)";
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanReference.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanReference.java
index caf10c538d..11b4f1ac79 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanReference.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2019 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.
@@ -26,12 +26,16 @@ import org.springframework.util.Assert;
* @author Rod Johnson
* @author Juergen Hoeller
* @see BeanDefinition#getPropertyValues()
- * @see org.springframework.beans.factory.BeanFactory#getBean
+ * @see org.springframework.beans.factory.BeanFactory#getBean(String)
+ * @see org.springframework.beans.factory.BeanFactory#getBean(Class)
*/
public class RuntimeBeanReference implements BeanReference {
private final String beanName;
+ @Nullable
+ private final Class> beanType;
+
private final boolean toParent;
@Nullable
@@ -39,9 +43,7 @@ public class RuntimeBeanReference implements BeanReference {
/**
- * Create a new RuntimeBeanReference to the given bean name,
- * without explicitly marking it as reference to a bean in
- * the parent factory.
+ * Create a new RuntimeBeanReference to the given bean name.
* @param beanName name of the target bean
*/
public RuntimeBeanReference(String beanName) {
@@ -50,27 +52,64 @@ public class RuntimeBeanReference implements BeanReference {
/**
* Create a new RuntimeBeanReference to the given bean name,
- * with the option to mark it as reference to a bean in
- * the parent factory.
+ * with the option to mark it as reference to a bean in the parent factory.
* @param beanName name of the target bean
- * @param toParent whether this is an explicit reference to
- * a bean in the parent factory
+ * @param toParent whether this is an explicit reference to a bean in the
+ * parent factory
*/
public RuntimeBeanReference(String beanName, boolean toParent) {
Assert.hasText(beanName, "'beanName' must not be empty");
this.beanName = beanName;
+ this.beanType = null;
+ this.toParent = toParent;
+ }
+
+ /**
+ * Create a new RuntimeBeanReference to a bean of the given type.
+ * @param beanType type of the target bean
+ * @since 5.2
+ */
+ public RuntimeBeanReference(Class> beanType) {
+ this(beanType, false);
+ }
+
+ /**
+ * Create a new RuntimeBeanReference to a bean of the given type,
+ * with the option to mark it as reference to a bean in the parent factory.
+ * @param beanType type of the target bean
+ * @param toParent whether this is an explicit reference to a bean in the
+ * parent factory
+ * @since 5.2
+ */
+ public RuntimeBeanReference(Class> beanType, boolean toParent) {
+ Assert.notNull(beanType, "'beanType' must not be empty");
+ this.beanName = beanType.getName();
+ this.beanType = beanType;
this.toParent = toParent;
}
+ /**
+ * Return the requested bean name, or the fully-qualified type name
+ * in case of by-type resolution.
+ * @see #getBeanType()
+ */
@Override
public String getBeanName() {
return this.beanName;
}
/**
- * Return whether this is an explicit reference to a bean
- * in the parent factory.
+ * Return the requested bean type if resolution by type is demanded.
+ * @since 5.2
+ */
+ @Nullable
+ public Class> getBeanType() {
+ return this.beanType;
+ }
+
+ /**
+ * Return whether this is an explicit reference to a bean in the parent factory.
*/
public boolean isToParent() {
return this.toParent;
@@ -100,7 +139,8 @@ public class RuntimeBeanReference implements BeanReference {
return false;
}
RuntimeBeanReference that = (RuntimeBeanReference) other;
- return (this.beanName.equals(that.beanName) && this.toParent == that.toParent);
+ return (this.beanName.equals(that.beanName) && this.beanType == that.beanType &&
+ this.toParent == that.toParent);
}
@Override
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
index beb00f5078..f59ca87481 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -61,6 +61,7 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.AutowiredPropertyMarker;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
@@ -1683,6 +1684,13 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
+ if (originalValue == AutowiredPropertyMarker.INSTANCE) {
+ Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod();
+ if (writeMethod == null) {
+ throw new IllegalArgumentException("Autowire marker for property without write method: " + pv);
+ }
+ originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true);
+ }
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
index 2b04fb155e..dcd428cb71 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
@@ -18,6 +18,7 @@ package org.springframework.beans.factory.support;
import java.util.function.Supplier;
+import org.springframework.beans.factory.config.AutowiredPropertyMarker;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.lang.Nullable;
@@ -209,7 +210,7 @@ public final class BeanDefinitionBuilder {
}
/**
- * Add the supplied property value under the given name.
+ * Add the supplied property value under the given property name.
*/
public BeanDefinitionBuilder addPropertyValue(String name, @Nullable Object value) {
this.beanDefinition.getPropertyValues().add(name, value);
@@ -226,6 +227,17 @@ public final class BeanDefinitionBuilder {
return this;
}
+ /**
+ * Add an autowired marker for the specified property on the specified bean.
+ * @param name the name of the property to mark as autowired
+ * @since 5.2
+ * @see AutowiredPropertyMarker
+ */
+ public BeanDefinitionBuilder addAutowiredProperty(String name) {
+ this.beanDefinition.getPropertyValues().add(name, AutowiredPropertyMarker.INSTANCE);
+ return this;
+ }
+
/**
* Set the init method for this definition.
*/
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
index 776a610ea0..abb5040f89 100644
--- a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValueResolver.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2019 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,10 +30,13 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.NamedBeanHolder;
import org.springframework.beans.factory.config.RuntimeBeanNameReference;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
@@ -57,7 +60,7 @@ import org.springframework.util.StringUtils;
*/
class BeanDefinitionValueResolver {
- private final AbstractBeanFactory beanFactory;
+ private final AbstractAutowireCapableBeanFactory beanFactory;
private final String beanName;
@@ -73,8 +76,8 @@ class BeanDefinitionValueResolver {
* @param beanDefinition the BeanDefinition of the bean that we work on
* @param typeConverter the TypeConverter to use for resolving TypedStringValues
*/
- public BeanDefinitionValueResolver(
- AbstractBeanFactory beanFactory, String beanName, BeanDefinition beanDefinition, TypeConverter typeConverter) {
+ public BeanDefinitionValueResolver(AbstractAutowireCapableBeanFactory beanFactory, String beanName,
+ BeanDefinition beanDefinition, TypeConverter typeConverter) {
this.beanFactory = beanFactory;
this.beanName = beanName;
@@ -130,6 +133,17 @@ class BeanDefinitionValueResolver {
ObjectUtils.getIdentityHexString(bd);
return resolveInnerBean(argName, innerBeanName, bd);
}
+ else if (value instanceof DependencyDescriptor) {
+ Set