diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
new file mode 100644
index 0000000..b72baf5
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/AbstractFactoryBean.java
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2002-2018 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.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.FactoryBeanNotInitializedException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * Simple template superclass for {@link FactoryBean} implementations that
+ * creates a singleton or a prototype object, depending on a flag.
+ *
+ *
If the "singleton" flag is {@code true} (the default),
+ * this class will create the object that it creates exactly once
+ * on initialization and subsequently return said singleton instance
+ * on all calls to the {@link #getObject()} method.
+ *
+ *
Else, this class will create a new instance every time the
+ * {@link #getObject()} method is invoked. Subclasses are responsible
+ * for implementing the abstract {@link #createInstance()} template
+ * method to actually create the object(s) to expose.
+ *
+ * @author Juergen Hoeller
+ * @author Keith Donald
+ * @since 1.0.2
+ * @param the bean type
+ * @see #setSingleton
+ * @see #createInstance()
+ */
+public abstract class AbstractFactoryBean
+ implements FactoryBean, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
+
+ /** Logger available to subclasses. */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private boolean singleton = true;
+
+ @Nullable
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ @Nullable
+ private BeanFactory beanFactory;
+
+ private boolean initialized = false;
+
+ @Nullable
+ private T singletonInstance;
+
+ @Nullable
+ private T earlySingletonInstance;
+
+
+ /**
+ * Set if a singleton should be created, or a new object on each request
+ * otherwise. Default is {@code true} (a singleton).
+ */
+ public void setSingleton(boolean singleton) {
+ this.singleton = singleton;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return this.singleton;
+ }
+
+ @Override
+ public void setBeanClassLoader(ClassLoader classLoader) {
+ this.beanClassLoader = classLoader;
+ }
+
+ @Override
+ public void setBeanFactory(@Nullable BeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ }
+
+ /**
+ * Return the BeanFactory that this bean runs in.
+ */
+ @Nullable
+ protected BeanFactory getBeanFactory() {
+ return this.beanFactory;
+ }
+
+ /**
+ * Obtain a bean type converter from the BeanFactory that this bean
+ * runs in. This is typically a fresh instance for each call,
+ * since TypeConverters are usually not thread-safe.
+ * Falls back to a SimpleTypeConverter when not running in a BeanFactory.
+ * @see ConfigurableBeanFactory#getTypeConverter()
+ * @see org.springframework.beans.SimpleTypeConverter
+ */
+ protected TypeConverter getBeanTypeConverter() {
+ BeanFactory beanFactory = getBeanFactory();
+ if (beanFactory instanceof ConfigurableBeanFactory) {
+ return ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
+ }
+ else {
+ return new SimpleTypeConverter();
+ }
+ }
+
+ /**
+ * Eagerly create the singleton instance, if necessary.
+ */
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ if (isSingleton()) {
+ this.initialized = true;
+ this.singletonInstance = createInstance();
+ this.earlySingletonInstance = null;
+ }
+ }
+
+
+ /**
+ * Expose the singleton instance or create a new prototype instance.
+ * @see #createInstance()
+ * @see #getEarlySingletonInterfaces()
+ */
+ @Override
+ public final T getObject() throws Exception {
+ if (isSingleton()) {
+ return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
+ }
+ else {
+ return createInstance();
+ }
+ }
+
+ /**
+ * Determine an 'early singleton' instance, exposed in case of a
+ * circular reference. Not called in a non-circular scenario.
+ */
+ @SuppressWarnings("unchecked")
+ private T getEarlySingletonInstance() throws Exception {
+ Class>[] ifcs = getEarlySingletonInterfaces();
+ if (ifcs == null) {
+ throw new FactoryBeanNotInitializedException(
+ getClass().getName() + " does not support circular references");
+ }
+ if (this.earlySingletonInstance == null) {
+ this.earlySingletonInstance = (T) Proxy.newProxyInstance(
+ this.beanClassLoader, ifcs, new EarlySingletonInvocationHandler());
+ }
+ return this.earlySingletonInstance;
+ }
+
+ /**
+ * Expose the singleton instance (for access through the 'early singleton' proxy).
+ * @return the singleton instance that this FactoryBean holds
+ * @throws IllegalStateException if the singleton instance is not initialized
+ */
+ @Nullable
+ private T getSingletonInstance() throws IllegalStateException {
+ Assert.state(this.initialized, "Singleton instance not initialized yet");
+ return this.singletonInstance;
+ }
+
+ /**
+ * Destroy the singleton instance, if any.
+ * @see #destroyInstance(Object)
+ */
+ @Override
+ public void destroy() throws Exception {
+ if (isSingleton()) {
+ destroyInstance(this.singletonInstance);
+ }
+ }
+
+
+ /**
+ * This abstract method declaration mirrors the method in the FactoryBean
+ * interface, for a consistent offering of abstract template methods.
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ */
+ @Override
+ @Nullable
+ public abstract Class> getObjectType();
+
+ /**
+ * Template method that subclasses must override to construct
+ * the object returned by this factory.
+ *
Invoked on initialization of this FactoryBean in case of
+ * a singleton; else, on each {@link #getObject()} call.
+ * @return the object returned by this factory
+ * @throws Exception if an exception occurred during object creation
+ * @see #getObject()
+ */
+ protected abstract T createInstance() throws Exception;
+
+ /**
+ * Return an array of interfaces that a singleton object exposed by this
+ * FactoryBean is supposed to implement, for use with an 'early singleton
+ * proxy' that will be exposed in case of a circular reference.
+ *
The default implementation returns this FactoryBean's object type,
+ * provided that it is an interface, or {@code null} otherwise. The latter
+ * indicates that early singleton access is not supported by this FactoryBean.
+ * This will lead to a FactoryBeanNotInitializedException getting thrown.
+ * @return the interfaces to use for 'early singletons',
+ * or {@code null} to indicate a FactoryBeanNotInitializedException
+ * @see org.springframework.beans.factory.FactoryBeanNotInitializedException
+ */
+ @Nullable
+ protected Class>[] getEarlySingletonInterfaces() {
+ Class> type = getObjectType();
+ return (type != null && type.isInterface() ? new Class>[] {type} : null);
+ }
+
+ /**
+ * Callback for destroying a singleton instance. Subclasses may
+ * override this to destroy the previously created instance.
+ *
The default implementation is empty.
+ * @param instance the singleton instance, as returned by
+ * {@link #createInstance()}
+ * @throws Exception in case of shutdown errors
+ * @see #createInstance()
+ */
+ protected void destroyInstance(@Nullable T instance) throws Exception {
+ }
+
+
+ /**
+ * Reflective InvocationHandler for lazy access to the actual singleton object.
+ */
+ private class EarlySingletonInvocationHandler implements InvocationHandler {
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (ReflectionUtils.isEqualsMethod(method)) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (ReflectionUtils.isHashCodeMethod(method)) {
+ // Use hashCode of reference proxy.
+ return System.identityHashCode(proxy);
+ }
+ else if (!initialized && ReflectionUtils.isToStringMethod(method)) {
+ return "Early singleton proxy for interfaces " +
+ ObjectUtils.nullSafeToString(getEarlySingletonInterfaces());
+ }
+ try {
+ return method.invoke(getSingletonInstance(), args);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanReference.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanReference.java
new file mode 100644
index 0000000..69f4909
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/BeanReference.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2012 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 org.springframework.beans.BeanMetadataElement;
+
+/**
+ * Interface that exposes a reference to a bean name in an abstract fashion.
+ * This interface does not necessarily imply a reference to an actual bean
+ * instance; it just expresses a logical reference to the name of a bean.
+ *
+ *
Serves as common interface implemented by any kind of bean reference
+ * holder, such as {@link RuntimeBeanReference RuntimeBeanReference} and
+ * {@link RuntimeBeanNameReference RuntimeBeanNameReference}.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public interface BeanReference extends BeanMetadataElement {
+
+ /**
+ * Return the target bean name that this reference points to (never {@code null}).
+ */
+ String getBeanName();
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DeprecatedBeanWarner.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DeprecatedBeanWarner.java
new file mode 100644
index 0000000..6a1369a
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DeprecatedBeanWarner.java
@@ -0,0 +1,114 @@
+/*
+ * 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
+ *
+ * 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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Bean factory post processor that logs a warning for {@link Deprecated @Deprecated} beans.
+ *
+ * @author Arjen Poutsma
+ * @since 3.0.3
+ */
+public class DeprecatedBeanWarner implements BeanFactoryPostProcessor {
+
+ /**
+ * Logger available to subclasses.
+ */
+ protected transient Log logger = LogFactory.getLog(getClass());
+
+ /**
+ * Set the name of the logger to use.
+ * The name will be passed to the underlying logger implementation through Commons Logging,
+ * getting interpreted as log category according to the logger's configuration.
+ *
This can be specified to not log into the category of this warner class but rather
+ * into a specific named category.
+ * @see org.apache.commons.logging.LogFactory#getLog(String)
+ * @see java.util.logging.Logger#getLogger(String)
+ */
+ public void setLoggerName(String loggerName) {
+ this.logger = LogFactory.getLog(loggerName);
+ }
+
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ if (isLogEnabled()) {
+ String[] beanNames = beanFactory.getBeanDefinitionNames();
+ for (String beanName : beanNames) {
+ String nameToLookup = beanName;
+ if (beanFactory.isFactoryBean(beanName)) {
+ nameToLookup = BeanFactory.FACTORY_BEAN_PREFIX + beanName;
+ }
+ Class> beanType = beanFactory.getType(nameToLookup);
+ if (beanType != null) {
+ Class> userClass = ClassUtils.getUserClass(beanType);
+ if (userClass.isAnnotationPresent(Deprecated.class)) {
+ BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
+ logDeprecatedBean(beanName, beanType, beanDefinition);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Logs a warning for a bean annotated with {@link Deprecated @Deprecated}.
+ * @param beanName the name of the deprecated bean
+ * @param beanType the user-specified type of the deprecated bean
+ * @param beanDefinition the definition of the deprecated bean
+ */
+ protected void logDeprecatedBean(String beanName, Class> beanType, BeanDefinition beanDefinition) {
+ StringBuilder builder = new StringBuilder();
+ builder.append(beanType);
+ builder.append(" ['");
+ builder.append(beanName);
+ builder.append('\'');
+ String resourceDescription = beanDefinition.getResourceDescription();
+ if (StringUtils.hasLength(resourceDescription)) {
+ builder.append(" in ");
+ builder.append(resourceDescription);
+ }
+ builder.append("] has been deprecated");
+ writeToLog(builder.toString());
+ }
+
+ /**
+ * Actually write to the underlying log.
+ *
The default implementations logs the message at "warn" level.
+ * @param message the message to write
+ */
+ protected void writeToLog(String message) {
+ logger.warn(message);
+ }
+
+ /**
+ * Determine whether the {@link #logger} field is enabled.
+ *
Default is {@code true} when the "warn" level is enabled.
+ * Subclasses can override this to change the level under which logging occurs.
+ */
+ protected boolean isLogEnabled() {
+ return logger.isWarnEnabled();
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java
new file mode 100644
index 0000000..86a6be9
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/MethodInvokingFactoryBean.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2002-2018 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 org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.FactoryBeanNotInitializedException;
+import org.springframework.lang.Nullable;
+
+/**
+ * {@link FactoryBean} which returns a value which is the result of a static or instance
+ * method invocation. For most use cases it is better to just use the container's
+ * built-in factory method support for the same purpose, since that is smarter at
+ * converting arguments. This factory bean is still useful though when you need to
+ * call a method which doesn't return any value (for example, a static class method
+ * to force some sort of initialization to happen). This use case is not supported
+ * by factory methods, since a return value is needed to obtain the bean instance.
+ *
+ *
Note that as it is expected to be used mostly for accessing factory methods,
+ * this factory by default operates in a singleton fashion. The first request
+ * to {@link #getObject} by the owning bean factory will cause a method invocation,
+ * whose return value will be cached for subsequent requests. An internal
+ * {@link #setSingleton singleton} property may be set to "false", to cause this
+ * factory to invoke the target method each time it is asked for an object.
+ *
+ *
NOTE: If your target method does not produce a result to expose, consider
+ * {@link MethodInvokingBean} instead, which avoids the type determination and
+ * lifecycle limitations that this {@link MethodInvokingFactoryBean} comes with.
+ *
+ *
This invoker supports any kind of target method. A static method may be specified
+ * by setting the {@link #setTargetMethod targetMethod} property to a String representing
+ * the static method name, with {@link #setTargetClass targetClass} specifying the Class
+ * that the static method is defined on. Alternatively, a target instance method may be
+ * specified, by setting the {@link #setTargetObject targetObject} property as the target
+ * object, and the {@link #setTargetMethod targetMethod} property as the name of the
+ * method to call on that target object. Arguments for the method invocation may be
+ * specified by setting the {@link #setArguments arguments} property.
+ *
+ *
This class depends on {@link #afterPropertiesSet()} being called once
+ * all properties have been set, as per the InitializingBean contract.
+ *
+ *
An example (in an XML based bean factory definition) of a bean definition
+ * which uses this class to call a static factory method:
+ *
+ *
+ * <bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ * <property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
+ * </bean>
+ *
+ * An example of calling a static method then an instance method to get at a
+ * Java system property. Somewhat verbose, but it works.
+ *
+ *
+ * <bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ * <property name="targetClass" value="java.lang.System"/>
+ * <property name="targetMethod" value="getProperties"/>
+ * </bean>
+ *
+ * <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
+ * <property name="targetObject" ref="sysProps"/>
+ * <property name="targetMethod" value="getProperty"/>
+ * <property name="arguments" value="java.version"/>
+ * </bean>
+ *
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @since 21.11.2003
+ * @see MethodInvokingBean
+ * @see org.springframework.util.MethodInvoker
+ */
+public class MethodInvokingFactoryBean extends MethodInvokingBean implements FactoryBean {
+
+ private boolean singleton = true;
+
+ private boolean initialized = false;
+
+ /** Method call result in the singleton case. */
+ @Nullable
+ private Object singletonObject;
+
+
+ /**
+ * Set if a singleton should be created, or a new object on each
+ * {@link #getObject()} request otherwise. Default is "true".
+ */
+ public void setSingleton(boolean singleton) {
+ this.singleton = singleton;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ prepare();
+ if (this.singleton) {
+ this.initialized = true;
+ this.singletonObject = invokeWithTargetException();
+ }
+ }
+
+
+ /**
+ * Returns the same value each time if the singleton property is set
+ * to "true", otherwise returns the value returned from invoking the
+ * specified method on the fly.
+ */
+ @Override
+ @Nullable
+ public Object getObject() throws Exception {
+ if (this.singleton) {
+ if (!this.initialized) {
+ throw new FactoryBeanNotInitializedException();
+ }
+ // Singleton: return shared object.
+ return this.singletonObject;
+ }
+ else {
+ // Prototype: new object on each call.
+ return invokeWithTargetException();
+ }
+ }
+
+ /**
+ * Return the type of object that this FactoryBean creates,
+ * or {@code null} if not known in advance.
+ */
+ @Override
+ public Class> getObjectType() {
+ if (!isPrepared()) {
+ // Not fully initialized yet -> return null to indicate "not known yet".
+ return null;
+ }
+ return getPreparedMethod().getReturnType();
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return this.singleton;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBean.java
new file mode 100644
index 0000000..e1f9208
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ObjectFactoryCreatingFactoryBean.java
@@ -0,0 +1,157 @@
+/*
+ * 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
+ *
+ * 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.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * A {@link org.springframework.beans.factory.FactoryBean} implementation that
+ * returns a value which is an {@link org.springframework.beans.factory.ObjectFactory}
+ * that in turn returns a bean sourced from a {@link org.springframework.beans.factory.BeanFactory}.
+ *
+ * As such, this may be used to avoid having a client object directly calling
+ * {@link org.springframework.beans.factory.BeanFactory#getBean(String)} to get
+ * a (typically prototype) bean from a
+ * {@link org.springframework.beans.factory.BeanFactory}, which would be a
+ * violation of the inversion of control principle. Instead, with the use
+ * of this class, the client object can be fed an
+ * {@link org.springframework.beans.factory.ObjectFactory} instance as a
+ * property which directly returns only the one target bean (again, which is
+ * typically a prototype bean).
+ *
+ *
A sample config in an XML-based
+ * {@link org.springframework.beans.factory.BeanFactory} might look as follows:
+ *
+ *
<beans>
+ *
+ * <!-- Prototype bean since we have state -->
+ * <bean id="myService" class="a.b.c.MyService" scope="prototype"/>
+ *
+ * <bean id="myServiceFactory"
+ * class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
+ * <property name="targetBeanName"><idref local="myService"/></property>
+ * </bean>
+ *
+ * <bean id="clientBean" class="a.b.c.MyClientBean">
+ * <property name="myServiceFactory" ref="myServiceFactory"/>
+ * </bean>
+ *
+ *</beans>
+ *
+ * The attendant {@code MyClientBean} class implementation might look
+ * something like this:
+ *
+ *
package a.b.c;
+ *
+ * import org.springframework.beans.factory.ObjectFactory;
+ *
+ * public class MyClientBean {
+ *
+ * private ObjectFactory<MyService> myServiceFactory;
+ *
+ * public void setMyServiceFactory(ObjectFactory<MyService> myServiceFactory) {
+ * this.myServiceFactory = myServiceFactory;
+ * }
+ *
+ * public void someBusinessMethod() {
+ * // get a 'fresh', brand new MyService instance
+ * MyService service = this.myServiceFactory.getObject();
+ * // use the service object to effect the business logic...
+ * }
+ * }
+ *
+ * An alternate approach to this application of an object creational pattern
+ * would be to use the {@link ServiceLocatorFactoryBean}
+ * to source (prototype) beans. The {@link ServiceLocatorFactoryBean} approach
+ * has the advantage of the fact that one doesn't have to depend on any
+ * Spring-specific interface such as {@link org.springframework.beans.factory.ObjectFactory},
+ * but has the disadvantage of requiring runtime class generation. Please do
+ * consult the {@link ServiceLocatorFactoryBean ServiceLocatorFactoryBean JavaDoc}
+ * for a fuller discussion of this issue.
+ *
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @since 1.0.2
+ * @see org.springframework.beans.factory.ObjectFactory
+ * @see ServiceLocatorFactoryBean
+ */
+public class ObjectFactoryCreatingFactoryBean extends AbstractFactoryBean> {
+
+ @Nullable
+ private String targetBeanName;
+
+
+ /**
+ * Set the name of the target bean.
+ * The target does not have to be a non-singleton bean, but realistically
+ * always will be (because if the target bean were a singleton, then said singleton
+ * bean could simply be injected straight into the dependent object, thus obviating
+ * the need for the extra level of indirection afforded by this factory approach).
+ */
+ public void setTargetBeanName(String targetBeanName) {
+ this.targetBeanName = targetBeanName;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ Assert.hasText(this.targetBeanName, "Property 'targetBeanName' is required");
+ super.afterPropertiesSet();
+ }
+
+
+ @Override
+ public Class> getObjectType() {
+ return ObjectFactory.class;
+ }
+
+ @Override
+ protected ObjectFactory createInstance() {
+ BeanFactory beanFactory = getBeanFactory();
+ Assert.state(beanFactory != null, "No BeanFactory available");
+ Assert.state(this.targetBeanName != null, "No target bean name specified");
+ return new TargetBeanObjectFactory(beanFactory, this.targetBeanName);
+ }
+
+
+ /**
+ * Independent inner class - for serialization purposes.
+ */
+ @SuppressWarnings("serial")
+ private static class TargetBeanObjectFactory implements ObjectFactory, Serializable {
+
+ private final BeanFactory beanFactory;
+
+ private final String targetBeanName;
+
+ public TargetBeanObjectFactory(BeanFactory beanFactory, String targetBeanName) {
+ this.beanFactory = beanFactory;
+ this.targetBeanName = targetBeanName;
+ }
+
+ @Override
+ public Object getObject() throws BeansException {
+ return this.beanFactory.getBean(this.targetBeanName);
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java
new file mode 100644
index 0000000..fc616b8
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/PreferencesPlaceholderConfigurer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.util.Properties;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.lang.Nullable;
+
+/**
+ * Subclass of PropertyPlaceholderConfigurer that supports JDK 1.4's
+ * Preferences API ({@code java.util.prefs}).
+ *
+ * Tries to resolve placeholders as keys first in the user preferences,
+ * then in the system preferences, then in this configurer's properties.
+ * Thus, behaves like PropertyPlaceholderConfigurer if no corresponding
+ * preferences defined.
+ *
+ *
Supports custom paths for the system and user preferences trees. Also
+ * supports custom paths specified in placeholders ("myPath/myPlaceholderKey").
+ * Uses the respective root node if not specified.
+ *
+ * @author Juergen Hoeller
+ * @since 16.02.2004
+ * @see #setSystemTreePath
+ * @see #setUserTreePath
+ * @see java.util.prefs.Preferences
+ * @deprecated as of 5.2, along with {@link PropertyPlaceholderConfigurer}
+ */
+@Deprecated
+public class PreferencesPlaceholderConfigurer extends PropertyPlaceholderConfigurer implements InitializingBean {
+
+ @Nullable
+ private String systemTreePath;
+
+ @Nullable
+ private String userTreePath;
+
+ private Preferences systemPrefs = Preferences.systemRoot();
+
+ private Preferences userPrefs = Preferences.userRoot();
+
+
+ /**
+ * Set the path in the system preferences tree to use for resolving
+ * placeholders. Default is the root node.
+ */
+ public void setSystemTreePath(String systemTreePath) {
+ this.systemTreePath = systemTreePath;
+ }
+
+ /**
+ * Set the path in the system preferences tree to use for resolving
+ * placeholders. Default is the root node.
+ */
+ public void setUserTreePath(String userTreePath) {
+ this.userTreePath = userTreePath;
+ }
+
+
+ /**
+ * This implementation eagerly fetches the Preferences instances
+ * for the required system and user tree nodes.
+ */
+ @Override
+ public void afterPropertiesSet() {
+ if (this.systemTreePath != null) {
+ this.systemPrefs = this.systemPrefs.node(this.systemTreePath);
+ }
+ if (this.userTreePath != null) {
+ this.userPrefs = this.userPrefs.node(this.userTreePath);
+ }
+ }
+
+ /**
+ * This implementation tries to resolve placeholders as keys first
+ * in the user preferences, then in the system preferences, then in
+ * the passed-in properties.
+ */
+ @Override
+ protected String resolvePlaceholder(String placeholder, Properties props) {
+ String path = null;
+ String key = placeholder;
+ int endOfPath = placeholder.lastIndexOf('/');
+ if (endOfPath != -1) {
+ path = placeholder.substring(0, endOfPath);
+ key = placeholder.substring(endOfPath + 1);
+ }
+ String value = resolvePlaceholder(path, key, this.userPrefs);
+ if (value == null) {
+ value = resolvePlaceholder(path, key, this.systemPrefs);
+ if (value == null) {
+ value = props.getProperty(placeholder);
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Resolve the given path and key against the given Preferences.
+ * @param path the preferences path (placeholder part before '/')
+ * @param key the preferences key (placeholder part after '/')
+ * @param preferences the Preferences to resolve against
+ * @return the value for the placeholder, or {@code null} if none found
+ */
+ @Nullable
+ protected String resolvePlaceholder(@Nullable String path, String key, Preferences preferences) {
+ if (path != null) {
+ // Do not create the node if it does not exist...
+ try {
+ if (preferences.nodeExists(path)) {
+ return preferences.node(path).get(key, null);
+ }
+ else {
+ return null;
+ }
+ }
+ catch (BackingStoreException ex) {
+ throw new BeanDefinitionStoreException("Cannot access specified node path [" + path + "]", ex);
+ }
+ }
+ else {
+ return preferences.get(key, null);
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanNameReference.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanNameReference.java
new file mode 100644
index 0000000..805f6bb
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/RuntimeBeanNameReference.java
@@ -0,0 +1,91 @@
+/*
+ * 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
+ *
+ * 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 org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Immutable placeholder class used for a property value object when it's a
+ * reference to another bean name in the factory, to be resolved at runtime.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see RuntimeBeanReference
+ * @see BeanDefinition#getPropertyValues()
+ * @see org.springframework.beans.factory.BeanFactory#getBean
+ */
+public class RuntimeBeanNameReference implements BeanReference {
+
+ private final String beanName;
+
+ @Nullable
+ private Object source;
+
+
+ /**
+ * Create a new RuntimeBeanNameReference to the given bean name.
+ * @param beanName name of the target bean
+ */
+ public RuntimeBeanNameReference(String beanName) {
+ Assert.hasText(beanName, "'beanName' must not be empty");
+ this.beanName = beanName;
+ }
+
+ @Override
+ public String getBeanName() {
+ return this.beanName;
+ }
+
+ /**
+ * Set the configuration source {@code Object} for this metadata element.
+ *
The exact type of the object will depend on the configuration mechanism used.
+ */
+ public void setSource(@Nullable Object source) {
+ this.source = source;
+ }
+
+ @Override
+ @Nullable
+ public Object getSource() {
+ return this.source;
+ }
+
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof RuntimeBeanNameReference)) {
+ return false;
+ }
+ RuntimeBeanNameReference that = (RuntimeBeanNameReference) other;
+ return this.beanName.equals(that.beanName);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.beanName.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return '<' + getBeanName() + '>';
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java
new file mode 100644
index 0000000..e0ee01e
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java
@@ -0,0 +1,427 @@
+/*
+ * 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
+ *
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Properties;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.FatalBeanException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * A {@link FactoryBean} implementation that takes an interface which must have one or more
+ * methods with the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
+ * (typically, {@code MyService getService()} or {@code MyService getService(String id)})
+ * and creates a dynamic proxy which implements that interface, delegating to an
+ * underlying {@link org.springframework.beans.factory.BeanFactory}.
+ *
+ *
Such service locators permit the decoupling of calling code from
+ * the {@link org.springframework.beans.factory.BeanFactory} API, by using an
+ * appropriate custom locator interface. They will typically be used for
+ * prototype beans , i.e. for factory methods that are supposed to
+ * return a new instance for each call. The client receives a reference to the
+ * service locator via setter or constructor injection, to be able to invoke
+ * the locator's factory methods on demand. For singleton beans, direct
+ * setter or constructor injection of the target bean is preferable.
+ *
+ *
On invocation of the no-arg factory method, or the single-arg factory
+ * method with a String id of {@code null} or empty String, if exactly
+ * one bean in the factory matches the return type of the factory
+ * method, that bean is returned, otherwise a
+ * {@link org.springframework.beans.factory.NoSuchBeanDefinitionException}
+ * is thrown.
+ *
+ *
On invocation of the single-arg factory method with a non-null (and
+ * non-empty) argument, the proxy returns the result of a
+ * {@link org.springframework.beans.factory.BeanFactory#getBean(String)} call,
+ * using a stringified version of the passed-in id as bean name.
+ *
+ *
A factory method argument will usually be a String, but can also be an
+ * int or a custom enumeration type, for example, stringified via
+ * {@code toString}. The resulting String can be used as bean name as-is,
+ * provided that corresponding beans are defined in the bean factory.
+ * Alternatively, {@linkplain #setServiceMappings(java.util.Properties) a custom
+ * mapping} between service IDs and bean names can be defined.
+ *
+ *
By way of an example, consider the following service locator interface.
+ * Note that this interface is not dependent on any Spring APIs.
+ *
+ *
package a.b.c;
+ *
+ *public interface ServiceFactory {
+ *
+ * public MyService getService();
+ *}
+ *
+ * A sample config in an XML-based
+ * {@link org.springframework.beans.factory.BeanFactory} might look as follows:
+ *
+ *
<beans>
+ *
+ * <!-- Prototype bean since we have state -->
+ * <bean id="myService" class="a.b.c.MyService" singleton="false"/>
+ *
+ * <!-- will lookup the above 'myService' bean by *TYPE* -->
+ * <bean id="myServiceFactory"
+ * class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
+ * <property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
+ * </bean>
+ *
+ * <bean id="clientBean" class="a.b.c.MyClientBean">
+ * <property name="myServiceFactory" ref="myServiceFactory"/>
+ * </bean>
+ *
+ *</beans>
+ *
+ * The attendant {@code MyClientBean} class implementation might then
+ * look something like this:
+ *
+ *
package a.b.c;
+ *
+ *public class MyClientBean {
+ *
+ * private ServiceFactory myServiceFactory;
+ *
+ * // actual implementation provided by the Spring container
+ * public void setServiceFactory(ServiceFactory myServiceFactory) {
+ * this.myServiceFactory = myServiceFactory;
+ * }
+ *
+ * public void someBusinessMethod() {
+ * // get a 'fresh', brand new MyService instance
+ * MyService service = this.myServiceFactory.getService();
+ * // use the service object to effect the business logic...
+ * }
+ *}
+ *
+ * By way of an example that looks up a bean by name , consider
+ * the following service locator interface. Again, note that this
+ * interface is not dependent on any Spring APIs.
+ *
+ *
package a.b.c;
+ *
+ *public interface ServiceFactory {
+ *
+ * public MyService getService (String serviceName);
+ *}
+ *
+ * A sample config in an XML-based
+ * {@link org.springframework.beans.factory.BeanFactory} might look as follows:
+ *
+ *
<beans>
+ *
+ * <!-- Prototype beans since we have state (both extend MyService) -->
+ * <bean id="specialService" class="a.b.c.SpecialService" singleton="false"/>
+ * <bean id="anotherService" class="a.b.c.AnotherService" singleton="false"/>
+ *
+ * <bean id="myServiceFactory"
+ * class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
+ * <property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
+ * </bean>
+ *
+ * <bean id="clientBean" class="a.b.c.MyClientBean">
+ * <property name="myServiceFactory" ref="myServiceFactory"/>
+ * </bean>
+ *
+ *</beans>
+ *
+ * The attendant {@code MyClientBean} class implementation might then
+ * look something like this:
+ *
+ *
package a.b.c;
+ *
+ *public class MyClientBean {
+ *
+ * private ServiceFactory myServiceFactory;
+ *
+ * // actual implementation provided by the Spring container
+ * public void setServiceFactory(ServiceFactory myServiceFactory) {
+ * this.myServiceFactory = myServiceFactory;
+ * }
+ *
+ * public void someBusinessMethod() {
+ * // get a 'fresh', brand new MyService instance
+ * MyService service = this.myServiceFactory.getService("specialService");
+ * // use the service object to effect the business logic...
+ * }
+ *
+ * public void anotherBusinessMethod() {
+ * // get a 'fresh', brand new MyService instance
+ * MyService service = this.myServiceFactory.getService("anotherService");
+ * // use the service object to effect the business logic...
+ * }
+ *}
+ *
+ * See {@link ObjectFactoryCreatingFactoryBean} for an alternate approach.
+ *
+ * @author Colin Sampaleanu
+ * @author Juergen Hoeller
+ * @since 1.1.4
+ * @see #setServiceLocatorInterface
+ * @see #setServiceMappings
+ * @see ObjectFactoryCreatingFactoryBean
+ */
+public class ServiceLocatorFactoryBean implements FactoryBean, BeanFactoryAware, InitializingBean {
+
+ @Nullable
+ private Class> serviceLocatorInterface;
+
+ @Nullable
+ private Constructor serviceLocatorExceptionConstructor;
+
+ @Nullable
+ private Properties serviceMappings;
+
+ @Nullable
+ private ListableBeanFactory beanFactory;
+
+ @Nullable
+ private Object proxy;
+
+
+ /**
+ * Set the service locator interface to use, which must have one or more methods with
+ * the signatures {@code MyType xxx()} or {@code MyType xxx(MyIdType id)}
+ * (typically, {@code MyService getService()} or {@code MyService getService(String id)}).
+ * See the {@link ServiceLocatorFactoryBean class-level Javadoc} for
+ * information on the semantics of such methods.
+ */
+ public void setServiceLocatorInterface(Class> interfaceType) {
+ this.serviceLocatorInterface = interfaceType;
+ }
+
+ /**
+ * Set the exception class that the service locator should throw if service
+ * lookup failed. The specified exception class must have a constructor
+ * with one of the following parameter types: {@code (String, Throwable)}
+ * or {@code (Throwable)} or {@code (String)}.
+ * If not specified, subclasses of Spring's BeansException will be thrown,
+ * for example NoSuchBeanDefinitionException. As those are unchecked, the
+ * caller does not need to handle them, so it might be acceptable that
+ * Spring exceptions get thrown as long as they are just handled generically.
+ * @see #determineServiceLocatorExceptionConstructor
+ * @see #createServiceLocatorException
+ */
+ public void setServiceLocatorExceptionClass(Class extends Exception> serviceLocatorExceptionClass) {
+ this.serviceLocatorExceptionConstructor =
+ determineServiceLocatorExceptionConstructor(serviceLocatorExceptionClass);
+ }
+
+ /**
+ * Set mappings between service ids (passed into the service locator)
+ * and bean names (in the bean factory). Service ids that are not defined
+ * here will be treated as bean names as-is.
+ *
The empty string as service id key defines the mapping for {@code null} and
+ * empty string, and for factory methods without parameter. If not defined,
+ * a single matching bean will be retrieved from the bean factory.
+ * @param serviceMappings mappings between service ids and bean names,
+ * with service ids as keys as bean names as values
+ */
+ public void setServiceMappings(Properties serviceMappings) {
+ this.serviceMappings = serviceMappings;
+ }
+
+ @Override
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ if (!(beanFactory instanceof ListableBeanFactory)) {
+ throw new FatalBeanException(
+ "ServiceLocatorFactoryBean needs to run in a BeanFactory that is a ListableBeanFactory");
+ }
+ this.beanFactory = (ListableBeanFactory) beanFactory;
+ }
+
+ @Override
+ public void afterPropertiesSet() {
+ if (this.serviceLocatorInterface == null) {
+ throw new IllegalArgumentException("Property 'serviceLocatorInterface' is required");
+ }
+
+ // Create service locator proxy.
+ this.proxy = Proxy.newProxyInstance(
+ this.serviceLocatorInterface.getClassLoader(),
+ new Class>[] {this.serviceLocatorInterface},
+ new ServiceLocatorInvocationHandler());
+ }
+
+
+ /**
+ * Determine the constructor to use for the given service locator exception
+ * class. Only called in case of a custom service locator exception.
+ *
The default implementation looks for a constructor with one of the
+ * following parameter types: {@code (String, Throwable)}
+ * or {@code (Throwable)} or {@code (String)}.
+ * @param exceptionClass the exception class
+ * @return the constructor to use
+ * @see #setServiceLocatorExceptionClass
+ */
+ @SuppressWarnings("unchecked")
+ protected Constructor determineServiceLocatorExceptionConstructor(Class extends Exception> exceptionClass) {
+ try {
+ return (Constructor) exceptionClass.getConstructor(String.class, Throwable.class);
+ }
+ catch (NoSuchMethodException ex) {
+ try {
+ return (Constructor) exceptionClass.getConstructor(Throwable.class);
+ }
+ catch (NoSuchMethodException ex2) {
+ try {
+ return (Constructor) exceptionClass.getConstructor(String.class);
+ }
+ catch (NoSuchMethodException ex3) {
+ throw new IllegalArgumentException(
+ "Service locator exception [" + exceptionClass.getName() +
+ "] neither has a (String, Throwable) constructor nor a (String) constructor");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a service locator exception for the given cause.
+ * Only called in case of a custom service locator exception.
+ * The default implementation can handle all variations of
+ * message and exception arguments.
+ * @param exceptionConstructor the constructor to use
+ * @param cause the cause of the service lookup failure
+ * @return the service locator exception to throw
+ * @see #setServiceLocatorExceptionClass
+ */
+ protected Exception createServiceLocatorException(Constructor exceptionConstructor, BeansException cause) {
+ Class>[] paramTypes = exceptionConstructor.getParameterTypes();
+ Object[] args = new Object[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ if (String.class == paramTypes[i]) {
+ args[i] = cause.getMessage();
+ }
+ else if (paramTypes[i].isInstance(cause)) {
+ args[i] = cause;
+ }
+ }
+ return BeanUtils.instantiateClass(exceptionConstructor, args);
+ }
+
+
+ @Override
+ @Nullable
+ public Object getObject() {
+ return this.proxy;
+ }
+
+ @Override
+ public Class> getObjectType() {
+ return this.serviceLocatorInterface;
+ }
+
+ @Override
+ public boolean isSingleton() {
+ return true;
+ }
+
+
+ /**
+ * Invocation handler that delegates service locator calls to the bean factory.
+ */
+ private class ServiceLocatorInvocationHandler implements InvocationHandler {
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (ReflectionUtils.isEqualsMethod(method)) {
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ }
+ else if (ReflectionUtils.isHashCodeMethod(method)) {
+ // Use hashCode of service locator proxy.
+ return System.identityHashCode(proxy);
+ }
+ else if (ReflectionUtils.isToStringMethod(method)) {
+ return "Service locator: " + serviceLocatorInterface;
+ }
+ else {
+ return invokeServiceLocatorMethod(method, args);
+ }
+ }
+
+ private Object invokeServiceLocatorMethod(Method method, Object[] args) throws Exception {
+ Class> serviceLocatorMethodReturnType = getServiceLocatorMethodReturnType(method);
+ try {
+ String beanName = tryGetBeanName(args);
+ Assert.state(beanFactory != null, "No BeanFactory available");
+ if (StringUtils.hasLength(beanName)) {
+ // Service locator for a specific bean name
+ return beanFactory.getBean(beanName, serviceLocatorMethodReturnType);
+ }
+ else {
+ // Service locator for a bean type
+ return beanFactory.getBean(serviceLocatorMethodReturnType);
+ }
+ }
+ catch (BeansException ex) {
+ if (serviceLocatorExceptionConstructor != null) {
+ throw createServiceLocatorException(serviceLocatorExceptionConstructor, ex);
+ }
+ throw ex;
+ }
+ }
+
+ /**
+ * Check whether a service id was passed in.
+ */
+ private String tryGetBeanName(@Nullable Object[] args) {
+ String beanName = "";
+ if (args != null && args.length == 1 && args[0] != null) {
+ beanName = args[0].toString();
+ }
+ // Look for explicit serviceId-to-beanName mappings.
+ if (serviceMappings != null) {
+ String mappedName = serviceMappings.getProperty(beanName);
+ if (mappedName != null) {
+ beanName = mappedName;
+ }
+ }
+ return beanName;
+ }
+
+ private Class> getServiceLocatorMethodReturnType(Method method) throws NoSuchMethodException {
+ Assert.state(serviceLocatorInterface != null, "No service locator interface specified");
+ Class>[] paramTypes = method.getParameterTypes();
+ Method interfaceMethod = serviceLocatorInterface.getMethod(method.getName(), paramTypes);
+ Class> serviceLocatorReturnType = interfaceMethod.getReturnType();
+
+ // Check whether the method is a valid service locator.
+ if (paramTypes.length > 1 || void.class == serviceLocatorReturnType) {
+ throw new UnsupportedOperationException(
+ "May only call methods with signature ' xxx()' or ' xxx( id)' " +
+ "on factory interface, but tried to call: " + interfaceMethod);
+ }
+ return serviceLocatorReturnType;
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
new file mode 100644
index 0000000..756cc1c
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/SetFactoryBean.java
@@ -0,0 +1,106 @@
+/*
+ * 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
+ *
+ * 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.util.LinkedHashSet;
+import java.util.Set;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.TypeConverter;
+import org.springframework.core.ResolvableType;
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple factory for shared Set instances. Allows for central setup
+ * of Sets via the "set" element in XML bean definitions.
+ *
+ * @author Juergen Hoeller
+ * @since 09.12.2003
+ * @see ListFactoryBean
+ * @see MapFactoryBean
+ */
+public class SetFactoryBean extends AbstractFactoryBean> {
+
+ @Nullable
+ private Set> sourceSet;
+
+ @SuppressWarnings("rawtypes")
+ @Nullable
+ private Class extends Set> targetSetClass;
+
+
+ /**
+ * Set the source Set, typically populated via XML "set" elements.
+ */
+ public void setSourceSet(Set> sourceSet) {
+ this.sourceSet = sourceSet;
+ }
+
+ /**
+ * Set the class to use for the target Set. Can be populated with a fully
+ * qualified class name when defined in a Spring application context.
+ * Default is a linked HashSet, keeping the registration order.
+ * @see java.util.LinkedHashSet
+ */
+ @SuppressWarnings("rawtypes")
+ public void setTargetSetClass(@Nullable Class extends Set> targetSetClass) {
+ if (targetSetClass == null) {
+ throw new IllegalArgumentException("'targetSetClass' must not be null");
+ }
+ if (!Set.class.isAssignableFrom(targetSetClass)) {
+ throw new IllegalArgumentException("'targetSetClass' must implement [java.util.Set]");
+ }
+ this.targetSetClass = targetSetClass;
+ }
+
+
+ @Override
+ @SuppressWarnings("rawtypes")
+ public Class getObjectType() {
+ return Set.class;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Set createInstance() {
+ if (this.sourceSet == null) {
+ throw new IllegalArgumentException("'sourceSet' is required");
+ }
+ Set result = null;
+ if (this.targetSetClass != null) {
+ result = BeanUtils.instantiateClass(this.targetSetClass);
+ }
+ else {
+ result = new LinkedHashSet<>(this.sourceSet.size());
+ }
+ Class> valueType = null;
+ if (this.targetSetClass != null) {
+ valueType = ResolvableType.forClass(this.targetSetClass).asCollection().resolveGeneric();
+ }
+ if (valueType != null) {
+ TypeConverter converter = getBeanTypeConverter();
+ for (Object elem : this.sourceSet) {
+ result.add(converter.convertIfNecessary(elem, valueType));
+ }
+ }
+ else {
+ result.addAll(this.sourceSet);
+ }
+ return result;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/SingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/SingletonBeanRegistry.java
new file mode 100644
index 0000000..ee6d4a7
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/SingletonBeanRegistry.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2015 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 org.springframework.lang.Nullable;
+
+/**
+ * Interface that defines a registry for shared bean instances.
+ * Can be implemented by {@link org.springframework.beans.factory.BeanFactory}
+ * implementations in order to expose their singleton management facility
+ * in a uniform manner.
+ *
+ * The {@link ConfigurableBeanFactory} interface extends this interface.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ConfigurableBeanFactory
+ * @see org.springframework.beans.factory.support.DefaultSingletonBeanRegistry
+ * @see org.springframework.beans.factory.support.AbstractBeanFactory
+ */
+public interface SingletonBeanRegistry {
+
+ /**
+ * Register the given existing object as singleton in the bean registry,
+ * under the given bean name.
+ *
The given instance is supposed to be fully initialized; the registry
+ * will not perform any initialization callbacks (in particular, it won't
+ * call InitializingBean's {@code afterPropertiesSet} method).
+ * The given instance will not receive any destruction callbacks
+ * (like DisposableBean's {@code destroy} method) either.
+ *
When running within a full BeanFactory: Register a bean definition
+ * instead of an existing instance if your bean is supposed to receive
+ * initialization and/or destruction callbacks.
+ *
Typically invoked during registry configuration, but can also be used
+ * for runtime registration of singletons. As a consequence, a registry
+ * implementation should synchronize singleton access; it will have to do
+ * this anyway if it supports a BeanFactory's lazy initialization of singletons.
+ * @param beanName the name of the bean
+ * @param singletonObject the existing singleton object
+ * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
+ * @see org.springframework.beans.factory.DisposableBean#destroy
+ * @see org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition
+ */
+ void registerSingleton(String beanName, Object singletonObject);
+
+ /**
+ * Return the (raw) singleton object registered under the given name.
+ *
Only checks already instantiated singletons; does not return an Object
+ * for singleton bean definitions which have not been instantiated yet.
+ *
The main purpose of this method is to access manually registered singletons
+ * (see {@link #registerSingleton}). Can also be used to access a singleton
+ * defined by a bean definition that already been created, in a raw fashion.
+ *
NOTE: This lookup method is not aware of FactoryBean prefixes or aliases.
+ * You need to resolve the canonical bean name first before obtaining the singleton instance.
+ * @param beanName the name of the bean to look for
+ * @return the registered singleton object, or {@code null} if none found
+ * @see ConfigurableListableBeanFactory#getBeanDefinition
+ */
+ @Nullable
+ Object getSingleton(String beanName);
+
+ /**
+ * Check if this registry contains a singleton instance with the given name.
+ *
Only checks already instantiated singletons; does not return {@code true}
+ * for singleton bean definitions which have not been instantiated yet.
+ *
The main purpose of this method is to check manually registered singletons
+ * (see {@link #registerSingleton}). Can also be used to check whether a
+ * singleton defined by a bean definition has already been created.
+ *
To check whether a bean factory contains a bean definition with a given name,
+ * use ListableBeanFactory's {@code containsBeanDefinition}. Calling both
+ * {@code containsBeanDefinition} and {@code containsSingleton} answers
+ * whether a specific bean factory contains a local bean instance with the given name.
+ *
Use BeanFactory's {@code containsBean} for general checks whether the
+ * factory knows about a bean with a given name (whether manually registered singleton
+ * instance or created by bean definition), also checking ancestor factories.
+ *
NOTE: This lookup method is not aware of FactoryBean prefixes or aliases.
+ * You need to resolve the canonical bean name first before checking the singleton status.
+ * @param beanName the name of the bean to look for
+ * @return if this bean factory contains a singleton instance with the given name
+ * @see #registerSingleton
+ * @see org.springframework.beans.factory.ListableBeanFactory#containsBeanDefinition
+ * @see org.springframework.beans.factory.BeanFactory#containsBean
+ */
+ boolean containsSingleton(String beanName);
+
+ /**
+ * Return the names of singleton beans registered in this registry.
+ *
Only checks already instantiated singletons; does not return names
+ * for singleton bean definitions which have not been instantiated yet.
+ *
The main purpose of this method is to check manually registered singletons
+ * (see {@link #registerSingleton}). Can also be used to check which singletons
+ * defined by a bean definition have already been created.
+ * @return the list of names as a String array (never {@code null})
+ * @see #registerSingleton
+ * @see org.springframework.beans.factory.support.BeanDefinitionRegistry#getBeanDefinitionNames
+ * @see org.springframework.beans.factory.ListableBeanFactory#getBeanDefinitionNames
+ */
+ String[] getSingletonNames();
+
+ /**
+ * Return the number of singleton beans registered in this registry.
+ *
Only checks already instantiated singletons; does not count
+ * singleton bean definitions which have not been instantiated yet.
+ *
The main purpose of this method is to check manually registered singletons
+ * (see {@link #registerSingleton}). Can also be used to count the number of
+ * singletons defined by a bean definition that have already been created.
+ * @return the number of singleton beans
+ * @see #registerSingleton
+ * @see org.springframework.beans.factory.support.BeanDefinitionRegistry#getBeanDefinitionCount
+ * @see org.springframework.beans.factory.ListableBeanFactory#getBeanDefinitionCount
+ */
+ int getSingletonCount();
+
+ /**
+ * Return the singleton mutex used by this registry (for external collaborators).
+ * @return the mutex object (never {@code null})
+ * @since 4.2
+ */
+ Object getSingletonMutex();
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java
new file mode 100644
index 0000000..70d4ab2
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/TypedStringValue.java
@@ -0,0 +1,240 @@
+/*
+ * 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 org.springframework.beans.BeanMetadataElement;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Holder for a typed String value. Can be added to bean definitions
+ * in order to explicitly specify a target type for a String value,
+ * for example for collection elements.
+ *
+ *
This holder will just store the String value and the target type.
+ * The actual conversion will be performed by the bean factory.
+ *
+ * @author Juergen Hoeller
+ * @since 1.2
+ * @see BeanDefinition#getPropertyValues
+ * @see org.springframework.beans.MutablePropertyValues#addPropertyValue
+ */
+public class TypedStringValue implements BeanMetadataElement {
+
+ @Nullable
+ private String value;
+
+ @Nullable
+ private volatile Object targetType;
+
+ @Nullable
+ private Object source;
+
+ @Nullable
+ private String specifiedTypeName;
+
+ private volatile boolean dynamic;
+
+
+ /**
+ * Create a new {@link TypedStringValue} for the given String value.
+ * @param value the String value
+ */
+ public TypedStringValue(@Nullable String value) {
+ setValue(value);
+ }
+
+ /**
+ * Create a new {@link TypedStringValue} for the given String value
+ * and target type.
+ * @param value the String value
+ * @param targetType the type to convert to
+ */
+ public TypedStringValue(@Nullable String value, Class> targetType) {
+ setValue(value);
+ setTargetType(targetType);
+ }
+
+ /**
+ * Create a new {@link TypedStringValue} for the given String value
+ * and target type.
+ * @param value the String value
+ * @param targetTypeName the type to convert to
+ */
+ public TypedStringValue(@Nullable String value, String targetTypeName) {
+ setValue(value);
+ setTargetTypeName(targetTypeName);
+ }
+
+
+ /**
+ * Set the String value.
+ *
Only necessary for manipulating a registered value,
+ * for example in BeanFactoryPostProcessors.
+ */
+ public void setValue(@Nullable String value) {
+ this.value = value;
+ }
+
+ /**
+ * Return the String value.
+ */
+ @Nullable
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * Set the type to convert to.
+ *
Only necessary for manipulating a registered value,
+ * for example in BeanFactoryPostProcessors.
+ */
+ public void setTargetType(Class> targetType) {
+ Assert.notNull(targetType, "'targetType' must not be null");
+ this.targetType = targetType;
+ }
+
+ /**
+ * Return the type to convert to.
+ */
+ public Class> getTargetType() {
+ Object targetTypeValue = this.targetType;
+ if (!(targetTypeValue instanceof Class)) {
+ throw new IllegalStateException("Typed String value does not carry a resolved target type");
+ }
+ return (Class>) targetTypeValue;
+ }
+
+ /**
+ * Specify the type to convert to.
+ */
+ public void setTargetTypeName(@Nullable String targetTypeName) {
+ this.targetType = targetTypeName;
+ }
+
+ /**
+ * Return the type to convert to.
+ */
+ @Nullable
+ public String getTargetTypeName() {
+ Object targetTypeValue = this.targetType;
+ if (targetTypeValue instanceof Class) {
+ return ((Class>) targetTypeValue).getName();
+ }
+ else {
+ return (String) targetTypeValue;
+ }
+ }
+
+ /**
+ * Return whether this typed String value carries a target type .
+ */
+ public boolean hasTargetType() {
+ return (this.targetType instanceof Class);
+ }
+
+ /**
+ * Determine the type to convert to, resolving it from a specified class name
+ * if necessary. Will also reload a specified Class from its name when called
+ * with the target type already resolved.
+ * @param classLoader the ClassLoader to use for resolving a (potential) class name
+ * @return the resolved type to convert to
+ * @throws ClassNotFoundException if the type cannot be resolved
+ */
+ @Nullable
+ public Class> resolveTargetType(@Nullable ClassLoader classLoader) throws ClassNotFoundException {
+ String typeName = getTargetTypeName();
+ if (typeName == null) {
+ return null;
+ }
+ Class> resolvedClass = ClassUtils.forName(typeName, classLoader);
+ this.targetType = resolvedClass;
+ return resolvedClass;
+ }
+
+
+ /**
+ * Set the configuration source {@code Object} for this metadata element.
+ *
The exact type of the object will depend on the configuration mechanism used.
+ */
+ public void setSource(@Nullable Object source) {
+ this.source = source;
+ }
+
+ @Override
+ @Nullable
+ public Object getSource() {
+ return this.source;
+ }
+
+ /**
+ * Set the type name as actually specified for this particular value, if any.
+ */
+ public void setSpecifiedTypeName(@Nullable String specifiedTypeName) {
+ this.specifiedTypeName = specifiedTypeName;
+ }
+
+ /**
+ * Return the type name as actually specified for this particular value, if any.
+ */
+ @Nullable
+ public String getSpecifiedTypeName() {
+ return this.specifiedTypeName;
+ }
+
+ /**
+ * Mark this value as dynamic, i.e. as containing an expression
+ * and hence not being subject to caching.
+ */
+ public void setDynamic() {
+ this.dynamic = true;
+ }
+
+ /**
+ * Return whether this value has been marked as dynamic.
+ */
+ public boolean isDynamic() {
+ return this.dynamic;
+ }
+
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof TypedStringValue)) {
+ return false;
+ }
+ TypedStringValue otherValue = (TypedStringValue) other;
+ return (ObjectUtils.nullSafeEquals(this.value, otherValue.value) &&
+ ObjectUtils.nullSafeEquals(this.targetType, otherValue.targetType));
+ }
+
+ @Override
+ public int hashCode() {
+ return ObjectUtils.nullSafeHashCode(this.value) * 29 + ObjectUtils.nullSafeHashCode(this.targetType);
+ }
+
+ @Override
+ public String toString() {
+ return "TypedStringValue: value [" + this.value + "], target type [" + this.targetType + "]";
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
new file mode 100644
index 0000000..cf13a40
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionReader.java
@@ -0,0 +1,859 @@
+/*
+ * 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.groovy;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import groovy.lang.Binding;
+import groovy.lang.Closure;
+import groovy.lang.GString;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyShell;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
+import org.springframework.beans.factory.parsing.Location;
+import org.springframework.beans.factory.parsing.Problem;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.support.ManagedMap;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.NamespaceHandler;
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
+import org.springframework.beans.factory.xml.XmlReaderContext;
+import org.springframework.core.io.DescriptiveResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.EncodedResource;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * A Groovy-based reader for Spring bean definitions: like a Groovy builder,
+ * but more of a DSL for Spring configuration.
+ *
+ *
This bean definition reader also understands XML bean definition files,
+ * allowing for seamless mixing and matching with Groovy bean definition files.
+ *
+ *
Typically applied to a
+ * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
+ * or a {@link org.springframework.context.support.GenericApplicationContext},
+ * but can be used against any {@link BeanDefinitionRegistry} implementation.
+ *
+ *
Example Syntax
+ *
+ * import org.hibernate.SessionFactory
+ * import org.apache.commons.dbcp.BasicDataSource
+ *
+ * def reader = new GroovyBeanDefinitionReader(myApplicationContext)
+ * reader.beans {
+ * dataSource(BasicDataSource) { // <--- invokeMethod
+ * driverClassName = "org.hsqldb.jdbcDriver"
+ * url = "jdbc:hsqldb:mem:grailsDB"
+ * username = "sa" // <-- setProperty
+ * password = ""
+ * settings = [mynew:"setting"]
+ * }
+ * sessionFactory(SessionFactory) {
+ * dataSource = dataSource // <-- getProperty for retrieving references
+ * }
+ * myService(MyService) {
+ * nestedBean = { AnotherBean bean -> // <-- setProperty with closure for nested bean
+ * dataSource = dataSource
+ * }
+ * }
+ * }
+ *
+ * You can also load resources containing beans defined in a Groovy script using
+ * either the {@link #loadBeanDefinitions(Resource...)} or
+ * {@link #loadBeanDefinitions(String...)} method, with a script looking similar to
+ * the following.
+ *
+ *
+ * import org.hibernate.SessionFactory
+ * import org.apache.commons.dbcp.BasicDataSource
+ *
+ * beans {
+ * dataSource(BasicDataSource) {
+ * driverClassName = "org.hsqldb.jdbcDriver"
+ * url = "jdbc:hsqldb:mem:grailsDB"
+ * username = "sa"
+ * password = ""
+ * settings = [mynew:"setting"]
+ * }
+ * sessionFactory(SessionFactory) {
+ * dataSource = dataSource
+ * }
+ * myService(MyService) {
+ * nestedBean = { AnotherBean bean ->
+ * dataSource = dataSource
+ * }
+ * }
+ * }
+ *
+ * @author Jeff Brown
+ * @author Graeme Rocher
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @since 4.0
+ * @see BeanDefinitionRegistry
+ * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
+ * @see org.springframework.context.support.GenericApplicationContext
+ * @see org.springframework.context.support.GenericGroovyApplicationContext
+ */
+public class GroovyBeanDefinitionReader extends AbstractBeanDefinitionReader implements GroovyObject {
+
+ /**
+ * Standard {@code XmlBeanDefinitionReader} created with default
+ * settings for loading bean definitions from XML files.
+ */
+ private final XmlBeanDefinitionReader standardXmlBeanDefinitionReader;
+
+ /**
+ * Groovy DSL {@code XmlBeanDefinitionReader} for loading bean definitions
+ * via the Groovy DSL, typically configured with XML validation disabled.
+ */
+ private final XmlBeanDefinitionReader groovyDslXmlBeanDefinitionReader;
+
+ private final Map namespaces = new HashMap<>();
+
+ private final Map deferredProperties = new HashMap<>();
+
+ private MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(getClass());
+
+ private Binding binding;
+
+ private GroovyBeanDefinitionWrapper currentBeanDefinition;
+
+
+ /**
+ * Create a new {@code GroovyBeanDefinitionReader} for the given
+ * {@link BeanDefinitionRegistry}.
+ * @param registry the {@code BeanDefinitionRegistry} to load bean definitions into
+ */
+ public GroovyBeanDefinitionReader(BeanDefinitionRegistry registry) {
+ super(registry);
+ this.standardXmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
+ this.groovyDslXmlBeanDefinitionReader = new XmlBeanDefinitionReader(registry);
+ this.groovyDslXmlBeanDefinitionReader.setValidating(false);
+ }
+
+ /**
+ * Create a new {@code GroovyBeanDefinitionReader} based on the given
+ * {@link XmlBeanDefinitionReader}, loading bean definitions into its
+ * {@code BeanDefinitionRegistry} and delegating Groovy DSL loading to it.
+ * The supplied {@code XmlBeanDefinitionReader} should typically
+ * be pre-configured with XML validation disabled.
+ * @param xmlBeanDefinitionReader the {@code XmlBeanDefinitionReader} to
+ * derive the registry from and to delegate Groovy DSL loading to
+ */
+ public GroovyBeanDefinitionReader(XmlBeanDefinitionReader xmlBeanDefinitionReader) {
+ super(xmlBeanDefinitionReader.getRegistry());
+ this.standardXmlBeanDefinitionReader = new XmlBeanDefinitionReader(xmlBeanDefinitionReader.getRegistry());
+ this.groovyDslXmlBeanDefinitionReader = xmlBeanDefinitionReader;
+ }
+
+
+ @Override
+ public void setMetaClass(MetaClass metaClass) {
+ this.metaClass = metaClass;
+ }
+
+ @Override
+ public MetaClass getMetaClass() {
+ return this.metaClass;
+ }
+
+ /**
+ * Set the binding, i.e. the Groovy variables available in the scope
+ * of a {@code GroovyBeanDefinitionReader} closure.
+ */
+ public void setBinding(Binding binding) {
+ this.binding = binding;
+ }
+
+ /**
+ * Return a specified binding for Groovy variables, if any.
+ */
+ public Binding getBinding() {
+ return this.binding;
+ }
+
+
+ // TRADITIONAL BEAN DEFINITION READER METHODS
+
+ /**
+ * Load bean definitions from the specified Groovy script or XML file.
+ *
Note that {@code ".xml"} files will be parsed as XML content; all other kinds
+ * of resources will be parsed as Groovy scripts.
+ * @param resource the resource descriptor for the Groovy script or XML file
+ * @return the number of bean definitions found
+ * @throws BeanDefinitionStoreException in case of loading or parsing errors
+ */
+ @Override
+ public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
+ return loadBeanDefinitions(new EncodedResource(resource));
+ }
+
+ /**
+ * Load bean definitions from the specified Groovy script or XML file.
+ *
Note that {@code ".xml"} files will be parsed as XML content; all other kinds
+ * of resources will be parsed as Groovy scripts.
+ * @param encodedResource the resource descriptor for the Groovy script or XML file,
+ * allowing specification of an encoding to use for parsing the file
+ * @return the number of bean definitions found
+ * @throws BeanDefinitionStoreException in case of loading or parsing errors
+ */
+ public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
+ // Check for XML files and redirect them to the "standard" XmlBeanDefinitionReader
+ String filename = encodedResource.getResource().getFilename();
+ if (StringUtils.endsWithIgnoreCase(filename, ".xml")) {
+ return this.standardXmlBeanDefinitionReader.loadBeanDefinitions(encodedResource);
+ }
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Loading Groovy bean definitions from " + encodedResource);
+ }
+
+ @SuppressWarnings("serial")
+ Closure beans = new Closure(this) {
+ @Override
+ public Object call(Object... args) {
+ invokeBeanDefiningClosure((Closure>) args[0]);
+ return null;
+ }
+ };
+ Binding binding = new Binding() {
+ @Override
+ public void setVariable(String name, Object value) {
+ if (currentBeanDefinition != null) {
+ applyPropertyToBeanDefinition(name, value);
+ }
+ else {
+ super.setVariable(name, value);
+ }
+ }
+ };
+ binding.setVariable("beans", beans);
+
+ int countBefore = getRegistry().getBeanDefinitionCount();
+ try {
+ GroovyShell shell = new GroovyShell(getBeanClassLoader(), binding);
+ shell.evaluate(encodedResource.getReader(), "beans");
+ }
+ catch (Throwable ex) {
+ throw new BeanDefinitionParsingException(new Problem("Error evaluating Groovy script: " + ex.getMessage(),
+ new Location(encodedResource.getResource()), null, ex));
+ }
+
+ int count = getRegistry().getBeanDefinitionCount() - countBefore;
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loaded " + count + " bean definitions from " + encodedResource);
+ }
+ return count;
+ }
+
+
+ // METHODS FOR CONSUMPTION IN A GROOVY CLOSURE
+
+ /**
+ * Defines a set of beans for the given block or closure.
+ * @param closure the block or closure
+ * @return this {@code GroovyBeanDefinitionReader} instance
+ */
+ public GroovyBeanDefinitionReader beans(Closure> closure) {
+ return invokeBeanDefiningClosure(closure);
+ }
+
+ /**
+ * Define an inner bean definition.
+ * @param type the bean type
+ * @return the bean definition
+ */
+ public GenericBeanDefinition bean(Class> type) {
+ GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
+ beanDefinition.setBeanClass(type);
+ return beanDefinition;
+ }
+
+ /**
+ * Define an inner bean definition.
+ * @param type the bean type
+ * @param args the constructors arguments and closure configurer
+ * @return the bean definition
+ */
+ public AbstractBeanDefinition bean(Class> type, Object...args) {
+ GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
+ try {
+ Closure> callable = null;
+ Collection constructorArgs = null;
+ if (!ObjectUtils.isEmpty(args)) {
+ int index = args.length;
+ Object lastArg = args[index - 1];
+ if (lastArg instanceof Closure>) {
+ callable = (Closure>) lastArg;
+ index--;
+ }
+ constructorArgs = resolveConstructorArguments(args, 0, index);
+ }
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, type, constructorArgs);
+ if (callable != null) {
+ callable.call(this.currentBeanDefinition);
+ }
+ return this.currentBeanDefinition.getBeanDefinition();
+ }
+ finally {
+ this.currentBeanDefinition = current;
+ }
+ }
+
+ /**
+ * Define a Spring XML namespace definition to use.
+ * @param definition the namespace definition
+ */
+ public void xmlns(Map definition) {
+ if (!definition.isEmpty()) {
+ for (Map.Entry entry : definition.entrySet()) {
+ String namespace = entry.getKey();
+ String uri = entry.getValue();
+ if (uri == null) {
+ throw new IllegalArgumentException("Namespace definition must supply a non-null URI");
+ }
+ NamespaceHandler namespaceHandler =
+ this.groovyDslXmlBeanDefinitionReader.getNamespaceHandlerResolver().resolve(uri);
+ if (namespaceHandler == null) {
+ throw new BeanDefinitionParsingException(new Problem("No namespace handler found for URI: " + uri,
+ new Location(new DescriptiveResource(("Groovy")))));
+ }
+ this.namespaces.put(namespace, uri);
+ }
+ }
+ }
+
+ /**
+ * Import Spring bean definitions from either XML or Groovy sources into the
+ * current bean builder instance.
+ * @param resourcePattern the resource pattern
+ */
+ public void importBeans(String resourcePattern) throws IOException {
+ loadBeanDefinitions(resourcePattern);
+ }
+
+
+ // INTERNAL HANDLING OF GROOVY CLOSURES AND PROPERTIES
+
+ /**
+ * This method overrides method invocation to create beans for each method name that
+ * takes a class argument.
+ */
+ @Override
+ public Object invokeMethod(String name, Object arg) {
+ Object[] args = (Object[])arg;
+ if ("beans".equals(name) && args.length == 1 && args[0] instanceof Closure) {
+ return beans((Closure>) args[0]);
+ }
+ else if ("ref".equals(name)) {
+ String refName;
+ if (args[0] == null) {
+ throw new IllegalArgumentException("Argument to ref() is not a valid bean or was not found");
+ }
+ if (args[0] instanceof RuntimeBeanReference) {
+ refName = ((RuntimeBeanReference) args[0]).getBeanName();
+ }
+ else {
+ refName = args[0].toString();
+ }
+ boolean parentRef = false;
+ if (args.length > 1 && args[1] instanceof Boolean) {
+ parentRef = (Boolean) args[1];
+ }
+ return new RuntimeBeanReference(refName, parentRef);
+ }
+ else if (this.namespaces.containsKey(name) && args.length > 0 && args[0] instanceof Closure) {
+ GroovyDynamicElementReader reader = createDynamicElementReader(name);
+ reader.invokeMethod("doCall", args);
+ }
+ else if (args.length > 0 && args[0] instanceof Closure) {
+ // abstract bean definition
+ return invokeBeanDefiningMethod(name, args);
+ }
+ else if (args.length > 0 &&
+ (args[0] instanceof Class || args[0] instanceof RuntimeBeanReference || args[0] instanceof Map)) {
+ return invokeBeanDefiningMethod(name, args);
+ }
+ else if (args.length > 1 && args[args.length -1] instanceof Closure) {
+ return invokeBeanDefiningMethod(name, args);
+ }
+ MetaClass mc = DefaultGroovyMethods.getMetaClass(getRegistry());
+ if (!mc.respondsTo(getRegistry(), name, args).isEmpty()){
+ return mc.invokeMethod(getRegistry(), name, args);
+ }
+ return this;
+ }
+
+ private boolean addDeferredProperty(String property, Object newValue) {
+ if (newValue instanceof List || newValue instanceof Map) {
+ this.deferredProperties.put(this.currentBeanDefinition.getBeanName() + '.' + property,
+ new DeferredProperty(this.currentBeanDefinition, property, newValue));
+ return true;
+ }
+ return false;
+ }
+
+ private void finalizeDeferredProperties() {
+ for (DeferredProperty dp : this.deferredProperties.values()) {
+ if (dp.value instanceof List) {
+ dp.value = manageListIfNecessary((List>) dp.value);
+ }
+ else if (dp.value instanceof Map) {
+ dp.value = manageMapIfNecessary((Map, ?>) dp.value);
+ }
+ dp.apply();
+ }
+ this.deferredProperties.clear();
+ }
+
+ /**
+ * When a method argument is only a closure it is a set of bean definitions.
+ * @param callable the closure argument
+ * @return this {@code GroovyBeanDefinitionReader} instance
+ */
+ protected GroovyBeanDefinitionReader invokeBeanDefiningClosure(Closure> callable) {
+ callable.setDelegate(this);
+ callable.call();
+ finalizeDeferredProperties();
+ return this;
+ }
+
+ /**
+ * This method is called when a bean definition node is called.
+ * @param beanName the name of the bean to define
+ * @param args the arguments to the bean. The first argument is the class name, the last
+ * argument is sometimes a closure. All the arguments in between are constructor arguments.
+ * @return the bean definition wrapper
+ */
+ private GroovyBeanDefinitionWrapper invokeBeanDefiningMethod(String beanName, Object[] args) {
+ boolean hasClosureArgument = (args[args.length - 1] instanceof Closure);
+ if (args[0] instanceof Class) {
+ Class> beanClass = (Class>) args[0];
+ if (hasClosureArgument) {
+ if (args.length - 1 != 1) {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
+ beanName, beanClass, resolveConstructorArguments(args, 1, args.length - 1));
+ }
+ else {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, beanClass);
+ }
+ }
+ else {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(
+ beanName, beanClass, resolveConstructorArguments(args, 1, args.length));
+ }
+ }
+ else if (args[0] instanceof RuntimeBeanReference) {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
+ this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(((RuntimeBeanReference) args[0]).getBeanName());
+ }
+ else if (args[0] instanceof Map) {
+ // named constructor arguments
+ if (args.length > 1 && args[1] instanceof Class) {
+ List constructorArgs =
+ resolveConstructorArguments(args, 2, hasClosureArgument ? args.length - 1 : args.length);
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, (Class>) args[1], constructorArgs);
+ Map, ?> namedArgs = (Map, ?>) args[0];
+ for (Object o : namedArgs.keySet()) {
+ String propName = (String) o;
+ setProperty(propName, namedArgs.get(propName));
+ }
+ }
+ // factory method syntax
+ else {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
+ // First arg is the map containing factoryBean : factoryMethod
+ Map.Entry, ?> factoryBeanEntry = ((Map, ?>) args[0]).entrySet().iterator().next();
+ // If we have a closure body, that will be the last argument.
+ // In between are the constructor args
+ int constructorArgsTest = (hasClosureArgument ? 2 : 1);
+ // If we have more than this number of args, we have constructor args
+ if (args.length > constructorArgsTest){
+ // factory-method requires args
+ int endOfConstructArgs = (hasClosureArgument ? args.length - 1 : args.length);
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null,
+ resolveConstructorArguments(args, 1, endOfConstructArgs));
+ }
+ else {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
+ }
+ this.currentBeanDefinition.getBeanDefinition().setFactoryBeanName(factoryBeanEntry.getKey().toString());
+ this.currentBeanDefinition.getBeanDefinition().setFactoryMethodName(factoryBeanEntry.getValue().toString());
+ }
+
+ }
+ else if (args[0] instanceof Closure) {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName);
+ this.currentBeanDefinition.getBeanDefinition().setAbstract(true);
+ }
+ else {
+ List constructorArgs =
+ resolveConstructorArguments(args, 0, hasClosureArgument ? args.length - 1 : args.length);
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(beanName, null, constructorArgs);
+ }
+
+ if (hasClosureArgument) {
+ Closure> callable = (Closure>) args[args.length - 1];
+ callable.setDelegate(this);
+ callable.setResolveStrategy(Closure.DELEGATE_FIRST);
+ callable.call(this.currentBeanDefinition);
+ }
+
+ GroovyBeanDefinitionWrapper beanDefinition = this.currentBeanDefinition;
+ this.currentBeanDefinition = null;
+ beanDefinition.getBeanDefinition().setAttribute(GroovyBeanDefinitionWrapper.class.getName(), beanDefinition);
+ getRegistry().registerBeanDefinition(beanName, beanDefinition.getBeanDefinition());
+ return beanDefinition;
+ }
+
+ protected List resolveConstructorArguments(Object[] args, int start, int end) {
+ Object[] constructorArgs = Arrays.copyOfRange(args, start, end);
+ for (int i = 0; i < constructorArgs.length; i++) {
+ if (constructorArgs[i] instanceof GString) {
+ constructorArgs[i] = constructorArgs[i].toString();
+ }
+ else if (constructorArgs[i] instanceof List) {
+ constructorArgs[i] = manageListIfNecessary((List>) constructorArgs[i]);
+ }
+ else if (constructorArgs[i] instanceof Map){
+ constructorArgs[i] = manageMapIfNecessary((Map, ?>) constructorArgs[i]);
+ }
+ }
+ return Arrays.asList(constructorArgs);
+ }
+
+ /**
+ * Checks whether there are any {@link RuntimeBeanReference RuntimeBeanReferences}
+ * inside the {@link Map} and converts it to a {@link ManagedMap} if necessary.
+ * @param map the original Map
+ * @return either the original map or a managed copy of it
+ */
+ private Object manageMapIfNecessary(Map, ?> map) {
+ boolean containsRuntimeRefs = false;
+ for (Object element : map.values()) {
+ if (element instanceof RuntimeBeanReference) {
+ containsRuntimeRefs = true;
+ break;
+ }
+ }
+ if (containsRuntimeRefs) {
+ Map managedMap = new ManagedMap<>();
+ managedMap.putAll(map);
+ return managedMap;
+ }
+ return map;
+ }
+
+ /**
+ * Checks whether there are any {@link RuntimeBeanReference RuntimeBeanReferences}
+ * inside the {@link List} and converts it to a {@link ManagedList} if necessary.
+ * @param list the original List
+ * @return either the original list or a managed copy of it
+ */
+ private Object manageListIfNecessary(List> list) {
+ boolean containsRuntimeRefs = false;
+ for (Object element : list) {
+ if (element instanceof RuntimeBeanReference) {
+ containsRuntimeRefs = true;
+ break;
+ }
+ }
+ if (containsRuntimeRefs) {
+ List managedList = new ManagedList<>();
+ managedList.addAll(list);
+ return managedList;
+ }
+ return list;
+ }
+
+ /**
+ * This method overrides property setting in the scope of the {@code GroovyBeanDefinitionReader}
+ * to set properties on the current bean definition.
+ */
+ @Override
+ public void setProperty(String name, Object value) {
+ if (this.currentBeanDefinition != null) {
+ applyPropertyToBeanDefinition(name, value);
+ }
+ }
+
+ protected void applyPropertyToBeanDefinition(String name, Object value) {
+ if (value instanceof GString) {
+ value = value.toString();
+ }
+ if (addDeferredProperty(name, value)) {
+ return;
+ }
+ else if (value instanceof Closure) {
+ GroovyBeanDefinitionWrapper current = this.currentBeanDefinition;
+ try {
+ Closure> callable = (Closure>) value;
+ Class> parameterType = callable.getParameterTypes()[0];
+ if (Object.class == parameterType) {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper("");
+ callable.call(this.currentBeanDefinition);
+ }
+ else {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(null, parameterType);
+ callable.call((Object) null);
+ }
+
+ value = this.currentBeanDefinition.getBeanDefinition();
+ }
+ finally {
+ this.currentBeanDefinition = current;
+ }
+ }
+ this.currentBeanDefinition.addProperty(name, value);
+ }
+
+ /**
+ * This method overrides property retrieval in the scope of the
+ * {@code GroovyBeanDefinitionReader}. A property retrieval will either:
+ *
+ * Retrieve a variable from the bean builder's binding if it exists
+ * Retrieve a RuntimeBeanReference for a specific bean if it exists
+ * Otherwise just delegate to MetaClass.getProperty which will resolve
+ * properties from the {@code GroovyBeanDefinitionReader} itself
+ *
+ */
+ @Override
+ public Object getProperty(String name) {
+ Binding binding = getBinding();
+ if (binding != null && binding.hasVariable(name)) {
+ return binding.getVariable(name);
+ }
+ else {
+ if (this.namespaces.containsKey(name)) {
+ return createDynamicElementReader(name);
+ }
+ if (getRegistry().containsBeanDefinition(name)) {
+ GroovyBeanDefinitionWrapper beanDefinition = (GroovyBeanDefinitionWrapper)
+ getRegistry().getBeanDefinition(name).getAttribute(GroovyBeanDefinitionWrapper.class.getName());
+ if (beanDefinition != null) {
+ return new GroovyRuntimeBeanReference(name, beanDefinition, false);
+ }
+ else {
+ return new RuntimeBeanReference(name, false);
+ }
+ }
+ // This is to deal with the case where the property setter is the last
+ // statement in a closure (hence the return value)
+ else if (this.currentBeanDefinition != null) {
+ MutablePropertyValues pvs = this.currentBeanDefinition.getBeanDefinition().getPropertyValues();
+ if (pvs.contains(name)) {
+ return pvs.get(name);
+ }
+ else {
+ DeferredProperty dp = this.deferredProperties.get(this.currentBeanDefinition.getBeanName() + name);
+ if (dp != null) {
+ return dp.value;
+ }
+ else {
+ return getMetaClass().getProperty(this, name);
+ }
+ }
+ }
+ else {
+ return getMetaClass().getProperty(this, name);
+ }
+ }
+ }
+
+ private GroovyDynamicElementReader createDynamicElementReader(String namespace) {
+ XmlReaderContext readerContext = this.groovyDslXmlBeanDefinitionReader.createReaderContext(
+ new DescriptiveResource("Groovy"));
+ BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
+ boolean decorating = (this.currentBeanDefinition != null);
+ if (!decorating) {
+ this.currentBeanDefinition = new GroovyBeanDefinitionWrapper(namespace);
+ }
+ return new GroovyDynamicElementReader(namespace, this.namespaces, delegate, this.currentBeanDefinition, decorating) {
+ @Override
+ protected void afterInvocation() {
+ if (!this.decorating) {
+ currentBeanDefinition = null;
+ }
+ }
+ };
+ }
+
+
+ /**
+ * This class is used to defer the adding of a property to a bean definition
+ * until later. This is for a case where you assign a property to a list that
+ * may not contain bean references at that point of assignment, but may later;
+ * hence, it would need to be managed.
+ */
+ private static class DeferredProperty {
+
+ private final GroovyBeanDefinitionWrapper beanDefinition;
+
+ private final String name;
+
+ public Object value;
+
+ public DeferredProperty(GroovyBeanDefinitionWrapper beanDefinition, String name, Object value) {
+ this.beanDefinition = beanDefinition;
+ this.name = name;
+ this.value = value;
+ }
+
+ public void apply() {
+ this.beanDefinition.addProperty(this.name, this.value);
+ }
+ }
+
+
+ /**
+ * A RuntimeBeanReference that takes care of adding new properties to runtime references.
+ */
+ private class GroovyRuntimeBeanReference extends RuntimeBeanReference implements GroovyObject {
+
+ private final GroovyBeanDefinitionWrapper beanDefinition;
+
+ private MetaClass metaClass;
+
+ public GroovyRuntimeBeanReference(String beanName, GroovyBeanDefinitionWrapper beanDefinition, boolean toParent) {
+ super(beanName, toParent);
+ this.beanDefinition = beanDefinition;
+ this.metaClass = InvokerHelper.getMetaClass(this);
+ }
+
+ @Override
+ public MetaClass getMetaClass() {
+ return this.metaClass;
+ }
+
+ @Override
+ public Object getProperty(String property) {
+ if (property.equals("beanName")) {
+ return getBeanName();
+ }
+ else if (property.equals("source")) {
+ return getSource();
+ }
+ else if (this.beanDefinition != null) {
+ return new GroovyPropertyValue(
+ property, this.beanDefinition.getBeanDefinition().getPropertyValues().get(property));
+ }
+ else {
+ return this.metaClass.getProperty(this, property);
+ }
+ }
+
+ @Override
+ public Object invokeMethod(String name, Object args) {
+ return this.metaClass.invokeMethod(this, name, args);
+ }
+
+ @Override
+ public void setMetaClass(MetaClass metaClass) {
+ this.metaClass = metaClass;
+ }
+
+ @Override
+ public void setProperty(String property, Object newValue) {
+ if (!addDeferredProperty(property, newValue)) {
+ this.beanDefinition.getBeanDefinition().getPropertyValues().add(property, newValue);
+ }
+ }
+
+
+ /**
+ * Wraps a bean definition property and ensures that any RuntimeBeanReference
+ * additions to it are deferred for resolution later.
+ */
+ private class GroovyPropertyValue extends GroovyObjectSupport {
+
+ private final String propertyName;
+
+ private final Object propertyValue;
+
+ public GroovyPropertyValue(String propertyName, Object propertyValue) {
+ this.propertyName = propertyName;
+ this.propertyValue = propertyValue;
+ }
+
+ @SuppressWarnings("unused")
+ public void leftShift(Object value) {
+ InvokerHelper.invokeMethod(this.propertyValue, "leftShift", value);
+ updateDeferredProperties(value);
+ }
+
+ @SuppressWarnings("unused")
+ public boolean add(Object value) {
+ boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "add", value);
+ updateDeferredProperties(value);
+ return retVal;
+ }
+
+ @SuppressWarnings("unused")
+ public boolean addAll(Collection> values) {
+ boolean retVal = (Boolean) InvokerHelper.invokeMethod(this.propertyValue, "addAll", values);
+ for (Object value : values) {
+ updateDeferredProperties(value);
+ }
+ return retVal;
+ }
+
+ @Override
+ public Object invokeMethod(String name, Object args) {
+ return InvokerHelper.invokeMethod(this.propertyValue, name, args);
+ }
+
+ @Override
+ public Object getProperty(String name) {
+ return InvokerHelper.getProperty(this.propertyValue, name);
+ }
+
+ @Override
+ public void setProperty(String name, Object value) {
+ InvokerHelper.setProperty(this.propertyValue, name, value);
+ }
+
+ private void updateDeferredProperties(Object value) {
+ if (value instanceof RuntimeBeanReference) {
+ deferredProperties.put(beanDefinition.getBeanName(),
+ new DeferredProperty(beanDefinition, this.propertyName, this.propertyValue));
+ }
+ }
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java
new file mode 100644
index 0000000..d1aeff7
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/GroovyBeanDefinitionWrapper.java
@@ -0,0 +1,243 @@
+/*
+ * 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.groovy;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import groovy.lang.GroovyObjectSupport;
+
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Internal wrapper for a Spring BeanDefinition, allowing for Groovy-style
+ * property access within a {@link GroovyBeanDefinitionReader} closure.
+ *
+ * @author Jeff Brown
+ * @author Juergen Hoeller
+ * @since 4.0
+ */
+class GroovyBeanDefinitionWrapper extends GroovyObjectSupport {
+
+ private static final String PARENT = "parent";
+ private static final String AUTOWIRE = "autowire";
+ private static final String CONSTRUCTOR_ARGS = "constructorArgs";
+ private static final String FACTORY_BEAN = "factoryBean";
+ private static final String FACTORY_METHOD = "factoryMethod";
+ private static final String INIT_METHOD = "initMethod";
+ private static final String DESTROY_METHOD = "destroyMethod";
+ private static final String SINGLETON = "singleton";
+
+ private static final List dynamicProperties = new ArrayList<>(8);
+
+ static {
+ dynamicProperties.add(PARENT);
+ dynamicProperties.add(AUTOWIRE);
+ dynamicProperties.add(CONSTRUCTOR_ARGS);
+ dynamicProperties.add(FACTORY_BEAN);
+ dynamicProperties.add(FACTORY_METHOD);
+ dynamicProperties.add(INIT_METHOD);
+ dynamicProperties.add(DESTROY_METHOD);
+ dynamicProperties.add(SINGLETON);
+ }
+
+
+ private String beanName;
+
+ private Class> clazz;
+
+ private Collection> constructorArgs;
+
+ private AbstractBeanDefinition definition;
+
+ private BeanWrapper definitionWrapper;
+
+ private String parentName;
+
+
+ public GroovyBeanDefinitionWrapper(String beanName) {
+ this.beanName = beanName;
+ }
+
+ public GroovyBeanDefinitionWrapper(String beanName, Class> clazz) {
+ this.beanName = beanName;
+ this.clazz = clazz;
+ }
+
+ public GroovyBeanDefinitionWrapper(String beanName, Class> clazz, Collection> constructorArgs) {
+ this.beanName = beanName;
+ this.clazz = clazz;
+ this.constructorArgs = constructorArgs;
+ }
+
+
+ public String getBeanName() {
+ return this.beanName;
+ }
+
+ public void setBeanDefinition(AbstractBeanDefinition definition) {
+ this.definition = definition;
+ }
+
+ public AbstractBeanDefinition getBeanDefinition() {
+ if (this.definition == null) {
+ this.definition = createBeanDefinition();
+ }
+ return this.definition;
+ }
+
+ protected AbstractBeanDefinition createBeanDefinition() {
+ AbstractBeanDefinition bd = new GenericBeanDefinition();
+ bd.setBeanClass(this.clazz);
+ if (!CollectionUtils.isEmpty(this.constructorArgs)) {
+ ConstructorArgumentValues cav = new ConstructorArgumentValues();
+ for (Object constructorArg : this.constructorArgs) {
+ cav.addGenericArgumentValue(constructorArg);
+ }
+ bd.setConstructorArgumentValues(cav);
+ }
+ if (this.parentName != null) {
+ bd.setParentName(this.parentName);
+ }
+ this.definitionWrapper = new BeanWrapperImpl(bd);
+ return bd;
+ }
+
+ public void setBeanDefinitionHolder(BeanDefinitionHolder holder) {
+ this.definition = (AbstractBeanDefinition) holder.getBeanDefinition();
+ this.beanName = holder.getBeanName();
+ }
+
+ public BeanDefinitionHolder getBeanDefinitionHolder() {
+ return new BeanDefinitionHolder(getBeanDefinition(), getBeanName());
+ }
+
+ public void setParent(Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("Parent bean cannot be set to a null runtime bean reference!");
+ }
+ if (obj instanceof String) {
+ this.parentName = (String) obj;
+ }
+ else if (obj instanceof RuntimeBeanReference) {
+ this.parentName = ((RuntimeBeanReference) obj).getBeanName();
+ }
+ else if (obj instanceof GroovyBeanDefinitionWrapper) {
+ this.parentName = ((GroovyBeanDefinitionWrapper) obj).getBeanName();
+ }
+ getBeanDefinition().setParentName(this.parentName);
+ getBeanDefinition().setAbstract(false);
+ }
+
+ public GroovyBeanDefinitionWrapper addProperty(String propertyName, Object propertyValue) {
+ if (propertyValue instanceof GroovyBeanDefinitionWrapper) {
+ propertyValue = ((GroovyBeanDefinitionWrapper) propertyValue).getBeanDefinition();
+ }
+ getBeanDefinition().getPropertyValues().add(propertyName, propertyValue);
+ return this;
+ }
+
+
+ @Override
+ public Object getProperty(String property) {
+ if (this.definitionWrapper.isReadableProperty(property)) {
+ return this.definitionWrapper.getPropertyValue(property);
+ }
+ else if (dynamicProperties.contains(property)) {
+ return null;
+ }
+ return super.getProperty(property);
+ }
+
+ @Override
+ public void setProperty(String property, Object newValue) {
+ if (PARENT.equals(property)) {
+ setParent(newValue);
+ }
+ else {
+ AbstractBeanDefinition bd = getBeanDefinition();
+ if (AUTOWIRE.equals(property)) {
+ if ("byName".equals(newValue)) {
+ bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
+ }
+ else if ("byType".equals(newValue)) {
+ bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
+ }
+ else if ("constructor".equals(newValue)) {
+ bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
+ }
+ else if (Boolean.TRUE.equals(newValue)) {
+ bd.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
+ }
+ }
+ // constructorArgs
+ else if (CONSTRUCTOR_ARGS.equals(property) && newValue instanceof List) {
+ ConstructorArgumentValues cav = new ConstructorArgumentValues();
+ List> args = (List>) newValue;
+ for (Object arg : args) {
+ cav.addGenericArgumentValue(arg);
+ }
+ bd.setConstructorArgumentValues(cav);
+ }
+ // factoryBean
+ else if (FACTORY_BEAN.equals(property)) {
+ if (newValue != null) {
+ bd.setFactoryBeanName(newValue.toString());
+ }
+ }
+ // factoryMethod
+ else if (FACTORY_METHOD.equals(property)) {
+ if (newValue != null) {
+ bd.setFactoryMethodName(newValue.toString());
+ }
+ }
+ // initMethod
+ else if (INIT_METHOD.equals(property)) {
+ if (newValue != null) {
+ bd.setInitMethodName(newValue.toString());
+ }
+ }
+ // destroyMethod
+ else if (DESTROY_METHOD.equals(property)) {
+ if (newValue != null) {
+ bd.setDestroyMethodName(newValue.toString());
+ }
+ }
+ // singleton property
+ else if (SINGLETON.equals(property)) {
+ bd.setScope(Boolean.TRUE.equals(newValue) ?
+ BeanDefinition.SCOPE_SINGLETON : BeanDefinition.SCOPE_PROTOTYPE);
+ }
+ else if (this.definitionWrapper.isWritableProperty(property)) {
+ this.definitionWrapper.setPropertyValue(property, newValue);
+ }
+ else {
+ super.setProperty(property, newValue);
+ }
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/groovy/package-info.java b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/package-info.java
new file mode 100644
index 0000000..d956cd5
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/groovy/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Support package for Groovy-based bean definitions.
+ */
+package org.springframework.beans.factory.groovy;
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AbstractComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AbstractComponentDefinition.java
new file mode 100644
index 0000000..d4fdc29
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AbstractComponentDefinition.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanReference;
+
+/**
+ * Base implementation of {@link ComponentDefinition} that provides a basic implementation of
+ * {@link #getDescription} which delegates to {@link #getName}. Also provides a base implementation
+ * of {@link #toString} which delegates to {@link #getDescription} in keeping with the recommended
+ * implementation strategy. Also provides default implementations of {@link #getInnerBeanDefinitions}
+ * and {@link #getBeanReferences} that return an empty array.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public abstract class AbstractComponentDefinition implements ComponentDefinition {
+
+ /**
+ * Delegates to {@link #getName}.
+ */
+ @Override
+ public String getDescription() {
+ return getName();
+ }
+
+ /**
+ * Returns an empty array.
+ */
+ @Override
+ public BeanDefinition[] getBeanDefinitions() {
+ return new BeanDefinition[0];
+ }
+
+ /**
+ * Returns an empty array.
+ */
+ @Override
+ public BeanDefinition[] getInnerBeanDefinitions() {
+ return new BeanDefinition[0];
+ }
+
+ /**
+ * Returns an empty array.
+ */
+ @Override
+ public BeanReference[] getBeanReferences() {
+ return new BeanReference[0];
+ }
+
+ /**
+ * Delegates to {@link #getDescription}.
+ */
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AliasDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AliasDefinition.java
new file mode 100644
index 0000000..27bed9e
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/AliasDefinition.java
@@ -0,0 +1,84 @@
+/*
+ * 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
+ *
+ * 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.parsing;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Representation of an alias that has been registered during the parsing process.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ReaderEventListener#aliasRegistered(AliasDefinition)
+ */
+public class AliasDefinition implements BeanMetadataElement {
+
+ private final String beanName;
+
+ private final String alias;
+
+ @Nullable
+ private final Object source;
+
+
+ /**
+ * Create a new AliasDefinition.
+ * @param beanName the canonical name of the bean
+ * @param alias the alias registered for the bean
+ */
+ public AliasDefinition(String beanName, String alias) {
+ this(beanName, alias, null);
+ }
+
+ /**
+ * Create a new AliasDefinition.
+ * @param beanName the canonical name of the bean
+ * @param alias the alias registered for the bean
+ * @param source the source object (may be {@code null})
+ */
+ public AliasDefinition(String beanName, String alias, @Nullable Object source) {
+ Assert.notNull(beanName, "Bean name must not be null");
+ Assert.notNull(alias, "Alias must not be null");
+ this.beanName = beanName;
+ this.alias = alias;
+ this.source = source;
+ }
+
+
+ /**
+ * Return the canonical name of the bean.
+ */
+ public final String getBeanName() {
+ return this.beanName;
+ }
+
+ /**
+ * Return the alias registered for the bean.
+ */
+ public final String getAlias() {
+ return this.alias;
+ }
+
+ @Override
+ @Nullable
+ public final Object getSource() {
+ return this.source;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java
new file mode 100644
index 0000000..32305ab
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanComponentDefinition.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2002-2018 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.parsing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.BeanReference;
+import org.springframework.lang.Nullable;
+
+/**
+ * ComponentDefinition based on a standard BeanDefinition, exposing the given bean
+ * definition as well as inner bean definitions and bean references for the given bean.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class BeanComponentDefinition extends BeanDefinitionHolder implements ComponentDefinition {
+
+ private BeanDefinition[] innerBeanDefinitions;
+
+ private BeanReference[] beanReferences;
+
+
+ /**
+ * Create a new BeanComponentDefinition for the given bean.
+ * @param beanDefinition the BeanDefinition
+ * @param beanName the name of the bean
+ */
+ public BeanComponentDefinition(BeanDefinition beanDefinition, String beanName) {
+ this(new BeanDefinitionHolder(beanDefinition, beanName));
+ }
+
+ /**
+ * Create a new BeanComponentDefinition for the given bean.
+ * @param beanDefinition the BeanDefinition
+ * @param beanName the name of the bean
+ * @param aliases alias names for the bean, or {@code null} if none
+ */
+ public BeanComponentDefinition(BeanDefinition beanDefinition, String beanName, @Nullable String[] aliases) {
+ this(new BeanDefinitionHolder(beanDefinition, beanName, aliases));
+ }
+
+ /**
+ * Create a new BeanComponentDefinition for the given bean.
+ * @param beanDefinitionHolder the BeanDefinitionHolder encapsulating
+ * the bean definition as well as the name of the bean
+ */
+ public BeanComponentDefinition(BeanDefinitionHolder beanDefinitionHolder) {
+ super(beanDefinitionHolder);
+
+ List innerBeans = new ArrayList<>();
+ List references = new ArrayList<>();
+ PropertyValues propertyValues = beanDefinitionHolder.getBeanDefinition().getPropertyValues();
+ for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
+ Object value = propertyValue.getValue();
+ if (value instanceof BeanDefinitionHolder) {
+ innerBeans.add(((BeanDefinitionHolder) value).getBeanDefinition());
+ }
+ else if (value instanceof BeanDefinition) {
+ innerBeans.add((BeanDefinition) value);
+ }
+ else if (value instanceof BeanReference) {
+ references.add((BeanReference) value);
+ }
+ }
+ this.innerBeanDefinitions = innerBeans.toArray(new BeanDefinition[0]);
+ this.beanReferences = references.toArray(new BeanReference[0]);
+ }
+
+
+ @Override
+ public String getName() {
+ return getBeanName();
+ }
+
+ @Override
+ public String getDescription() {
+ return getShortDescription();
+ }
+
+ @Override
+ public BeanDefinition[] getBeanDefinitions() {
+ return new BeanDefinition[] {getBeanDefinition()};
+ }
+
+ @Override
+ public BeanDefinition[] getInnerBeanDefinitions() {
+ return this.innerBeanDefinitions;
+ }
+
+ @Override
+ public BeanReference[] getBeanReferences() {
+ return this.beanReferences;
+ }
+
+
+ /**
+ * This implementation returns this ComponentDefinition's description.
+ * @see #getDescription()
+ */
+ @Override
+ public String toString() {
+ return getDescription();
+ }
+
+ /**
+ * This implementations expects the other object to be of type BeanComponentDefinition
+ * as well, in addition to the superclass's equality requirements.
+ */
+ @Override
+ public boolean equals(@Nullable Object other) {
+ return (this == other || (other instanceof BeanComponentDefinition && super.equals(other)));
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanDefinitionParsingException.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanDefinitionParsingException.java
new file mode 100644
index 0000000..6cdf858
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanDefinitionParsingException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+
+/**
+ * Exception thrown when a bean definition reader encounters an error
+ * during the parsing process.
+ *
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @since 2.0
+ */
+@SuppressWarnings("serial")
+public class BeanDefinitionParsingException extends BeanDefinitionStoreException {
+
+ /**
+ * Create a new BeanDefinitionParsingException.
+ * @param problem the configuration problem that was detected during the parsing process
+ */
+ public BeanDefinitionParsingException(Problem problem) {
+ super(problem.getResourceDescription(), problem.toString(), problem.getRootCause());
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java
new file mode 100644
index 0000000..ccba6d7
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/BeanEntry.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2002-2020 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.parsing;
+
+/**
+ * {@link ParseState} entry representing a bean definition.
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class BeanEntry implements ParseState.Entry {
+
+ private final String beanDefinitionName;
+
+
+ /**
+ * Create a new {@code BeanEntry} instance.
+ * @param beanDefinitionName the name of the associated bean definition
+ */
+ public BeanEntry(String beanDefinitionName) {
+ this.beanDefinitionName = beanDefinitionName;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Bean '" + this.beanDefinitionName + "'";
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java
new file mode 100644
index 0000000..a899025
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ComponentDefinition.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanReference;
+
+/**
+ * Interface that describes the logical view of a set of {@link BeanDefinition BeanDefinitions}
+ * and {@link BeanReference BeanReferences} as presented in some configuration context.
+ *
+ * With the introduction of {@link org.springframework.beans.factory.xml.NamespaceHandler pluggable custom XML tags},
+ * it is now possible for a single logical configuration entity, in this case an XML tag, to
+ * create multiple {@link BeanDefinition BeanDefinitions} and {@link BeanReference RuntimeBeanReferences}
+ * in order to provide more succinct configuration and greater convenience to end users. As such, it can
+ * no longer be assumed that each configuration entity (e.g. XML tag) maps to one {@link BeanDefinition}.
+ * For tool vendors and other users who wish to present visualization or support for configuring Spring
+ * applications it is important that there is some mechanism in place to tie the {@link BeanDefinition BeanDefinitions}
+ * in the {@link org.springframework.beans.factory.BeanFactory} back to the configuration data in a way
+ * that has concrete meaning to the end user. As such, {@link org.springframework.beans.factory.xml.NamespaceHandler}
+ * implementations are able to publish events in the form of a {@code ComponentDefinition} for each
+ * logical entity being configured. Third parties can then {@link ReaderEventListener subscribe to these events},
+ * allowing for a user-centric view of the bean metadata.
+ *
+ *
Each {@code ComponentDefinition} has a {@link #getSource source object} which is configuration-specific.
+ * In the case of XML-based configuration this is typically the {@link org.w3c.dom.Node} which contains the user
+ * supplied configuration information. In addition to this, each {@link BeanDefinition} enclosed in a
+ * {@code ComponentDefinition} has its own {@link BeanDefinition#getSource() source object} which may point
+ * to a different, more specific, set of configuration data. Beyond this, individual pieces of bean metadata such
+ * as the {@link org.springframework.beans.PropertyValue PropertyValues} may also have a source object giving an
+ * even greater level of detail. Source object extraction is handled through the
+ * {@link SourceExtractor} which can be customized as required.
+ *
+ *
Whilst direct access to important {@link BeanReference BeanReferences} is provided through
+ * {@link #getBeanReferences}, tools may wish to inspect all {@link BeanDefinition BeanDefinitions} to gather
+ * the full set of {@link BeanReference BeanReferences}. Implementations are required to provide
+ * all {@link BeanReference BeanReferences} that are required to validate the configuration of the
+ * overall logical entity as well as those required to provide full user visualisation of the configuration.
+ * It is expected that certain {@link BeanReference BeanReferences} will not be important to
+ * validation or to the user view of the configuration and as such these may be omitted. A tool may wish to
+ * display any additional {@link BeanReference BeanReferences} sourced through the supplied
+ * {@link BeanDefinition BeanDefinitions} but this is not considered to be a typical case.
+ *
+ *
Tools can determine the important of contained {@link BeanDefinition BeanDefinitions} by checking the
+ * {@link BeanDefinition#getRole role identifier}. The role is essentially a hint to the tool as to how
+ * important the configuration provider believes a {@link BeanDefinition} is to the end user. It is expected
+ * that tools will not display all {@link BeanDefinition BeanDefinitions} for a given
+ * {@code ComponentDefinition} choosing instead to filter based on the role. Tools may choose to make
+ * this filtering user configurable. Particular notice should be given to the
+ * {@link BeanDefinition#ROLE_INFRASTRUCTURE INFRASTRUCTURE role identifier}. {@link BeanDefinition BeanDefinitions}
+ * classified with this role are completely unimportant to the end user and are required only for
+ * internal implementation reasons.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see AbstractComponentDefinition
+ * @see CompositeComponentDefinition
+ * @see BeanComponentDefinition
+ * @see ReaderEventListener#componentRegistered(ComponentDefinition)
+ */
+public interface ComponentDefinition extends BeanMetadataElement {
+
+ /**
+ * Get the user-visible name of this {@code ComponentDefinition}.
+ *
This should link back directly to the corresponding configuration data
+ * for this component in a given context.
+ */
+ String getName();
+
+ /**
+ * Return a friendly description of the described component.
+ *
Implementations are encouraged to return the same value from
+ * {@code toString()}.
+ */
+ String getDescription();
+
+ /**
+ * Return the {@link BeanDefinition BeanDefinitions} that were registered
+ * to form this {@code ComponentDefinition}.
+ *
It should be noted that a {@code ComponentDefinition} may well be related with
+ * other {@link BeanDefinition BeanDefinitions} via {@link BeanReference references},
+ * however these are not included as they may be not available immediately.
+ * Important {@link BeanReference BeanReferences} are available from {@link #getBeanReferences()}.
+ * @return the array of BeanDefinitions, or an empty array if none
+ */
+ BeanDefinition[] getBeanDefinitions();
+
+ /**
+ * Return the {@link BeanDefinition BeanDefinitions} that represent all relevant
+ * inner beans within this component.
+ *
Other inner beans may exist within the associated {@link BeanDefinition BeanDefinitions},
+ * however these are not considered to be needed for validation or for user visualization.
+ * @return the array of BeanDefinitions, or an empty array if none
+ */
+ BeanDefinition[] getInnerBeanDefinitions();
+
+ /**
+ * Return the set of {@link BeanReference BeanReferences} that are considered
+ * to be important to this {@code ComponentDefinition}.
+ *
Other {@link BeanReference BeanReferences} may exist within the associated
+ * {@link BeanDefinition BeanDefinitions}, however these are not considered
+ * to be needed for validation or for user visualization.
+ * @return the array of BeanReferences, or an empty array if none
+ */
+ BeanReference[] getBeanReferences();
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/CompositeComponentDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/CompositeComponentDefinition.java
new file mode 100644
index 0000000..efd846c
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/CompositeComponentDefinition.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2002-2018 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.parsing;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * {@link ComponentDefinition} implementation that holds one or more nested
+ * {@link ComponentDefinition} instances, aggregating them into a named group
+ * of components.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0.1
+ * @see #getNestedComponents()
+ */
+public class CompositeComponentDefinition extends AbstractComponentDefinition {
+
+ private final String name;
+
+ @Nullable
+ private final Object source;
+
+ private final List nestedComponents = new ArrayList<>();
+
+
+ /**
+ * Create a new CompositeComponentDefinition.
+ * @param name the name of the composite component
+ * @param source the source element that defines the root of the composite component
+ */
+ public CompositeComponentDefinition(String name, @Nullable Object source) {
+ Assert.notNull(name, "Name must not be null");
+ this.name = name;
+ this.source = source;
+ }
+
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ @Nullable
+ public Object getSource() {
+ return this.source;
+ }
+
+
+ /**
+ * Add the given component as nested element of this composite component.
+ * @param component the nested component to add
+ */
+ public void addNestedComponent(ComponentDefinition component) {
+ Assert.notNull(component, "ComponentDefinition must not be null");
+ this.nestedComponents.add(component);
+ }
+
+ /**
+ * Return the nested components that this composite component holds.
+ * @return the array of nested components, or an empty array if none
+ */
+ public ComponentDefinition[] getNestedComponents() {
+ return this.nestedComponents.toArray(new ComponentDefinition[0]);
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ConstructorArgumentEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ConstructorArgumentEntry.java
new file mode 100644
index 0000000..b6c5956
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ConstructorArgumentEntry.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.util.Assert;
+
+/**
+ * {@link ParseState} entry representing a (possibly indexed)
+ * constructor argument.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class ConstructorArgumentEntry implements ParseState.Entry {
+
+ private final int index;
+
+
+ /**
+ * Creates a new instance of the {@link ConstructorArgumentEntry} class
+ * representing a constructor argument with a (currently) unknown index.
+ */
+ public ConstructorArgumentEntry() {
+ this.index = -1;
+ }
+
+ /**
+ * Creates a new instance of the {@link ConstructorArgumentEntry} class
+ * representing a constructor argument at the supplied {@code index}.
+ * @param index the index of the constructor argument
+ * @throws IllegalArgumentException if the supplied {@code index}
+ * is less than zero
+ */
+ public ConstructorArgumentEntry(int index) {
+ Assert.isTrue(index >= 0, "Constructor argument index must be greater than or equal to zero");
+ this.index = index;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Constructor-arg" + (this.index >= 0 ? " #" + this.index : "");
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/DefaultsDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/DefaultsDefinition.java
new file mode 100644
index 0000000..90b2442
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/DefaultsDefinition.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2002-2007 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.parsing;
+
+import org.springframework.beans.BeanMetadataElement;
+
+/**
+ * Marker interface for a defaults definition,
+ * extending BeanMetadataElement to inherit source exposure.
+ *
+ * Concrete implementations are typically based on 'document defaults',
+ * for example specified at the root tag level within an XML document.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0.2
+ * @see org.springframework.beans.factory.xml.DocumentDefaultsDefinition
+ * @see ReaderEventListener#defaultsRegistered(DefaultsDefinition)
+ */
+public interface DefaultsDefinition extends BeanMetadataElement {
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/EmptyReaderEventListener.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/EmptyReaderEventListener.java
new file mode 100644
index 0000000..397db05
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/EmptyReaderEventListener.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+/**
+ * Empty implementation of the {@link ReaderEventListener} interface,
+ * providing no-op implementations of all callback methods.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class EmptyReaderEventListener implements ReaderEventListener {
+
+ @Override
+ public void defaultsRegistered(DefaultsDefinition defaultsDefinition) {
+ // no-op
+ }
+
+ @Override
+ public void componentRegistered(ComponentDefinition componentDefinition) {
+ // no-op
+ }
+
+ @Override
+ public void aliasRegistered(AliasDefinition aliasDefinition) {
+ // no-op
+ }
+
+ @Override
+ public void importProcessed(ImportDefinition importDefinition) {
+ // no-op
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/FailFastProblemReporter.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/FailFastProblemReporter.java
new file mode 100644
index 0000000..3dc71bb
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/FailFastProblemReporter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2018 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.parsing;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple {@link ProblemReporter} implementation that exhibits fail-fast
+ * behavior when errors are encountered.
+ *
+ *
The first error encountered results in a {@link BeanDefinitionParsingException}
+ * being thrown.
+ *
+ *
Warnings are written to
+ * {@link #setLogger(org.apache.commons.logging.Log) the log} for this class.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @author Rick Evans
+ * @since 2.0
+ */
+public class FailFastProblemReporter implements ProblemReporter {
+
+ private Log logger = LogFactory.getLog(getClass());
+
+
+ /**
+ * Set the {@link Log logger} that is to be used to report warnings.
+ *
If set to {@code null} then a default {@link Log logger} set to
+ * the name of the instance class will be used.
+ * @param logger the {@link Log logger} that is to be used to report warnings
+ */
+ public void setLogger(@Nullable Log logger) {
+ this.logger = (logger != null ? logger : LogFactory.getLog(getClass()));
+ }
+
+
+ /**
+ * Throws a {@link BeanDefinitionParsingException} detailing the error
+ * that has occurred.
+ * @param problem the source of the error
+ */
+ @Override
+ public void fatal(Problem problem) {
+ throw new BeanDefinitionParsingException(problem);
+ }
+
+ /**
+ * Throws a {@link BeanDefinitionParsingException} detailing the error
+ * that has occurred.
+ * @param problem the source of the error
+ */
+ @Override
+ public void error(Problem problem) {
+ throw new BeanDefinitionParsingException(problem);
+ }
+
+ /**
+ * Writes the supplied {@link Problem} to the {@link Log} at {@code WARN} level.
+ * @param problem the source of the warning
+ */
+ @Override
+ public void warning(Problem problem) {
+ logger.warn(problem, problem.getRootCause());
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ImportDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ImportDefinition.java
new file mode 100644
index 0000000..3740250
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ImportDefinition.java
@@ -0,0 +1,90 @@
+/*
+ * 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
+ *
+ * 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.parsing;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Representation of an import that has been processed during the parsing process.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ReaderEventListener#importProcessed(ImportDefinition)
+ */
+public class ImportDefinition implements BeanMetadataElement {
+
+ private final String importedResource;
+
+ @Nullable
+ private final Resource[] actualResources;
+
+ @Nullable
+ private final Object source;
+
+
+ /**
+ * Create a new ImportDefinition.
+ * @param importedResource the location of the imported resource
+ */
+ public ImportDefinition(String importedResource) {
+ this(importedResource, null, null);
+ }
+
+ /**
+ * Create a new ImportDefinition.
+ * @param importedResource the location of the imported resource
+ * @param source the source object (may be {@code null})
+ */
+ public ImportDefinition(String importedResource, @Nullable Object source) {
+ this(importedResource, null, source);
+ }
+
+ /**
+ * Create a new ImportDefinition.
+ * @param importedResource the location of the imported resource
+ * @param source the source object (may be {@code null})
+ */
+ public ImportDefinition(String importedResource, @Nullable Resource[] actualResources, @Nullable Object source) {
+ Assert.notNull(importedResource, "Imported resource must not be null");
+ this.importedResource = importedResource;
+ this.actualResources = actualResources;
+ this.source = source;
+ }
+
+
+ /**
+ * Return the location of the imported resource.
+ */
+ public final String getImportedResource() {
+ return this.importedResource;
+ }
+
+ @Nullable
+ public final Resource[] getActualResources() {
+ return this.actualResources;
+ }
+
+ @Override
+ @Nullable
+ public final Object getSource() {
+ return this.source;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Location.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Location.java
new file mode 100644
index 0000000..b06c524
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Location.java
@@ -0,0 +1,83 @@
+/*
+ * 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
+ *
+ * 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.parsing;
+
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Class that models an arbitrary location in a {@link Resource resource}.
+ *
+ *
Typically used to track the location of problematic or erroneous
+ * metadata in XML configuration files. For example, a
+ * {@link #getSource() source} location might be 'The bean defined on
+ * line 76 of beans.properties has an invalid Class'; another source might
+ * be the actual DOM Element from a parsed XML {@link org.w3c.dom.Document};
+ * or the source object might simply be {@code null}.
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class Location {
+
+ private final Resource resource;
+
+ @Nullable
+ private final Object source;
+
+
+ /**
+ * Create a new instance of the {@link Location} class.
+ * @param resource the resource with which this location is associated
+ */
+ public Location(Resource resource) {
+ this(resource, null);
+ }
+
+ /**
+ * Create a new instance of the {@link Location} class.
+ * @param resource the resource with which this location is associated
+ * @param source the actual location within the associated resource
+ * (may be {@code null})
+ */
+ public Location(Resource resource, @Nullable Object source) {
+ Assert.notNull(resource, "Resource must not be null");
+ this.resource = resource;
+ this.source = source;
+ }
+
+
+ /**
+ * Get the resource with which this location is associated.
+ */
+ public Resource getResource() {
+ return this.resource;
+ }
+
+ /**
+ * Get the actual location within the associated {@link #getResource() resource}
+ * (may be {@code null}).
+ *
See the {@link Location class level javadoc for this class} for examples
+ * of what the actual type of the returned object may be.
+ */
+ @Nullable
+ public Object getSource() {
+ return this.source;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/NullSourceExtractor.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/NullSourceExtractor.java
new file mode 100644
index 0000000..1205b3f
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/NullSourceExtractor.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple implementation of {@link SourceExtractor} that returns {@code null}
+ * as the source metadata.
+ *
+ *
This is the default implementation and prevents too much metadata from being
+ * held in memory during normal (non-tooled) runtime usage.
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class NullSourceExtractor implements SourceExtractor {
+
+ /**
+ * This implementation simply returns {@code null} for any input.
+ */
+ @Override
+ @Nullable
+ public Object extractSource(Object sourceCandidate, @Nullable Resource definitionResource) {
+ return null;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java
new file mode 100644
index 0000000..0277bc0
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ParseState.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2002-2020 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.parsing;
+
+import java.util.ArrayDeque;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple {@link ArrayDeque}-based structure for tracking the logical position during
+ * a parsing process. {@link Entry entries} are added to the ArrayDeque at each point
+ * during the parse phase in a reader-specific manner.
+ *
+ *
Calling {@link #toString()} will render a tree-style view of the current logical
+ * position in the parse phase. This representation is intended for use in error messages.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public final class ParseState {
+
+ /**
+ * Internal {@link ArrayDeque} storage.
+ */
+ private final ArrayDeque state;
+
+
+ /**
+ * Create a new {@code ParseState} with an empty {@link ArrayDeque}.
+ */
+ public ParseState() {
+ this.state = new ArrayDeque<>();
+ }
+
+ /**
+ * Create a new {@code ParseState} whose {@link ArrayDeque} is a clone
+ * of the state in the passed-in {@code ParseState}.
+ */
+ private ParseState(ParseState other) {
+ this.state = other.state.clone();
+ }
+
+
+ /**
+ * Add a new {@link Entry} to the {@link ArrayDeque}.
+ */
+ public void push(Entry entry) {
+ this.state.push(entry);
+ }
+
+ /**
+ * Remove an {@link Entry} from the {@link ArrayDeque}.
+ */
+ public void pop() {
+ this.state.pop();
+ }
+
+ /**
+ * Return the {@link Entry} currently at the top of the {@link ArrayDeque} or
+ * {@code null} if the {@link ArrayDeque} is empty.
+ */
+ @Nullable
+ public Entry peek() {
+ return this.state.peek();
+ }
+
+ /**
+ * Create a new instance of {@link ParseState} which is an independent snapshot
+ * of this instance.
+ */
+ public ParseState snapshot() {
+ return new ParseState(this);
+ }
+
+
+ /**
+ * Returns a tree-style representation of the current {@code ParseState}.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(64);
+ int i = 0;
+ for (ParseState.Entry entry : this.state) {
+ if (i > 0) {
+ sb.append('\n');
+ for (int j = 0; j < i; j++) {
+ sb.append('\t');
+ }
+ sb.append("-> ");
+ }
+ sb.append(entry);
+ i++;
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Marker interface for entries into the {@link ParseState}.
+ */
+ public interface Entry {
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PassThroughSourceExtractor.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PassThroughSourceExtractor.java
new file mode 100644
index 0000000..1365c99
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PassThroughSourceExtractor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple {@link SourceExtractor} implementation that just passes
+ * the candidate source metadata object through for attachment.
+ *
+ * Using this implementation means that tools will get raw access to the
+ * underlying configuration source metadata provided by the tool.
+ *
+ *
This implementation should not be used in a production
+ * application since it is likely to keep too much metadata in memory
+ * (unnecessarily).
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class PassThroughSourceExtractor implements SourceExtractor {
+
+ /**
+ * Simply returns the supplied {@code sourceCandidate} as-is.
+ * @param sourceCandidate the source metadata
+ * @return the supplied {@code sourceCandidate}
+ */
+ @Override
+ public Object extractSource(Object sourceCandidate, @Nullable Resource definingResource) {
+ return sourceCandidate;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java
new file mode 100644
index 0000000..86d5800
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/Problem.java
@@ -0,0 +1,134 @@
+/*
+ * 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
+ *
+ * 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.parsing;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Represents a problem with a bean definition configuration.
+ * Mainly serves as common argument passed into a {@link ProblemReporter}.
+ *
+ *
May indicate a potentially fatal problem (an error) or just a warning.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ProblemReporter
+ */
+public class Problem {
+
+ private final String message;
+
+ private final Location location;
+
+ @Nullable
+ private final ParseState parseState;
+
+ @Nullable
+ private final Throwable rootCause;
+
+
+ /**
+ * Create a new instance of the {@link Problem} class.
+ * @param message a message detailing the problem
+ * @param location the location within a bean configuration source that triggered the error
+ */
+ public Problem(String message, Location location) {
+ this(message, location, null, null);
+ }
+
+ /**
+ * Create a new instance of the {@link Problem} class.
+ * @param message a message detailing the problem
+ * @param parseState the {@link ParseState} at the time of the error
+ * @param location the location within a bean configuration source that triggered the error
+ */
+ public Problem(String message, Location location, ParseState parseState) {
+ this(message, location, parseState, null);
+ }
+
+ /**
+ * Create a new instance of the {@link Problem} class.
+ * @param message a message detailing the problem
+ * @param rootCause the underlying exception that caused the error (may be {@code null})
+ * @param parseState the {@link ParseState} at the time of the error
+ * @param location the location within a bean configuration source that triggered the error
+ */
+ public Problem(String message, Location location, @Nullable ParseState parseState, @Nullable Throwable rootCause) {
+ Assert.notNull(message, "Message must not be null");
+ Assert.notNull(location, "Location must not be null");
+ this.message = message;
+ this.location = location;
+ this.parseState = parseState;
+ this.rootCause = rootCause;
+ }
+
+
+ /**
+ * Get the message detailing the problem.
+ */
+ public String getMessage() {
+ return this.message;
+ }
+
+ /**
+ * Get the location within a bean configuration source that triggered the error.
+ */
+ public Location getLocation() {
+ return this.location;
+ }
+
+ /**
+ * Get the description of the bean configuration source that triggered the error,
+ * as contained within this Problem's Location object.
+ * @see #getLocation()
+ */
+ public String getResourceDescription() {
+ return getLocation().getResource().getDescription();
+ }
+
+ /**
+ * Get the {@link ParseState} at the time of the error (may be {@code null}).
+ */
+ @Nullable
+ public ParseState getParseState() {
+ return this.parseState;
+ }
+
+ /**
+ * Get the underlying exception that caused the error (may be {@code null}).
+ */
+ @Nullable
+ public Throwable getRootCause() {
+ return this.rootCause;
+ }
+
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Configuration problem: ");
+ sb.append(getMessage());
+ sb.append("\nOffending resource: ").append(getResourceDescription());
+ if (getParseState() != null) {
+ sb.append('\n').append(getParseState());
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ProblemReporter.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ProblemReporter.java
new file mode 100644
index 0000000..b9ded86
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ProblemReporter.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2002-2012 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.parsing;
+
+/**
+ * SPI interface allowing tools and other external processes to handle errors
+ * and warnings reported during bean definition parsing.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see Problem
+ */
+public interface ProblemReporter {
+
+ /**
+ * Called when a fatal error is encountered during the parsing process.
+ *
Implementations must treat the given problem as fatal,
+ * i.e. they have to eventually raise an exception.
+ * @param problem the source of the error (never {@code null})
+ */
+ void fatal(Problem problem);
+
+ /**
+ * Called when an error is encountered during the parsing process.
+ *
Implementations may choose to treat errors as fatal.
+ * @param problem the source of the error (never {@code null})
+ */
+ void error(Problem problem);
+
+ /**
+ * Called when a warning is raised during the parsing process.
+ *
Warnings are never considered to be fatal.
+ * @param problem the source of the warning (never {@code null})
+ */
+ void warning(Problem problem);
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java
new file mode 100644
index 0000000..c20235a
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/PropertyEntry.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2020 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.parsing;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ParseState} entry representing a JavaBean property.
+ *
+ * @author Rob Harrop
+ * @since 2.0
+ */
+public class PropertyEntry implements ParseState.Entry {
+
+ private final String name;
+
+
+ /**
+ * Create a new {@code PropertyEntry} instance.
+ * @param name the name of the JavaBean property represented by this instance
+ */
+ public PropertyEntry(String name) {
+ if (!StringUtils.hasText(name)) {
+ throw new IllegalArgumentException("Invalid property name '" + name + "'");
+ }
+ this.name = name;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Property '" + this.name + "'";
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java
new file mode 100644
index 0000000..45283e5
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/QualifierEntry.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2020 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.parsing;
+
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ParseState} entry representing an autowire candidate qualifier.
+ *
+ * @author Mark Fisher
+ * @since 2.5
+ */
+public class QualifierEntry implements ParseState.Entry {
+
+ private final String typeName;
+
+
+ /**
+ * Create a new {@code QualifierEntry} instance.
+ * @param typeName the name of the qualifier type
+ */
+ public QualifierEntry(String typeName) {
+ if (!StringUtils.hasText(typeName)) {
+ throw new IllegalArgumentException("Invalid qualifier type '" + typeName + "'");
+ }
+ this.typeName = typeName;
+ }
+
+
+ @Override
+ public String toString() {
+ return "Qualifier '" + this.typeName + "'";
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java
new file mode 100644
index 0000000..2b95aa8
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderContext.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2002-2020 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.parsing;
+
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+
+/**
+ * Context that gets passed along a bean definition reading process,
+ * encapsulating all relevant configuration as well as state.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public class ReaderContext {
+
+ private final Resource resource;
+
+ private final ProblemReporter problemReporter;
+
+ private final ReaderEventListener eventListener;
+
+ private final SourceExtractor sourceExtractor;
+
+
+ /**
+ * Construct a new {@code ReaderContext}.
+ * @param resource the XML bean definition resource
+ * @param problemReporter the problem reporter in use
+ * @param eventListener the event listener in use
+ * @param sourceExtractor the source extractor in use
+ */
+ public ReaderContext(Resource resource, ProblemReporter problemReporter,
+ ReaderEventListener eventListener, SourceExtractor sourceExtractor) {
+
+ this.resource = resource;
+ this.problemReporter = problemReporter;
+ this.eventListener = eventListener;
+ this.sourceExtractor = sourceExtractor;
+ }
+
+ public final Resource getResource() {
+ return this.resource;
+ }
+
+
+ // Errors and warnings
+
+ /**
+ * Raise a fatal error.
+ */
+ public void fatal(String message, @Nullable Object source) {
+ fatal(message, source, null, null);
+ }
+
+ /**
+ * Raise a fatal error.
+ */
+ public void fatal(String message, @Nullable Object source, @Nullable Throwable cause) {
+ fatal(message, source, null, cause);
+ }
+
+ /**
+ * Raise a fatal error.
+ */
+ public void fatal(String message, @Nullable Object source, @Nullable ParseState parseState) {
+ fatal(message, source, parseState, null);
+ }
+
+ /**
+ * Raise a fatal error.
+ */
+ public void fatal(String message, @Nullable Object source, @Nullable ParseState parseState, @Nullable Throwable cause) {
+ Location location = new Location(getResource(), source);
+ this.problemReporter.fatal(new Problem(message, location, parseState, cause));
+ }
+
+ /**
+ * Raise a regular error.
+ */
+ public void error(String message, @Nullable Object source) {
+ error(message, source, null, null);
+ }
+
+ /**
+ * Raise a regular error.
+ */
+ public void error(String message, @Nullable Object source, @Nullable Throwable cause) {
+ error(message, source, null, cause);
+ }
+
+ /**
+ * Raise a regular error.
+ */
+ public void error(String message, @Nullable Object source, @Nullable ParseState parseState) {
+ error(message, source, parseState, null);
+ }
+
+ /**
+ * Raise a regular error.
+ */
+ public void error(String message, @Nullable Object source, @Nullable ParseState parseState, @Nullable Throwable cause) {
+ Location location = new Location(getResource(), source);
+ this.problemReporter.error(new Problem(message, location, parseState, cause));
+ }
+
+ /**
+ * Raise a non-critical warning.
+ */
+ public void warning(String message, @Nullable Object source) {
+ warning(message, source, null, null);
+ }
+
+ /**
+ * Raise a non-critical warning.
+ */
+ public void warning(String message, @Nullable Object source, @Nullable Throwable cause) {
+ warning(message, source, null, cause);
+ }
+
+ /**
+ * Raise a non-critical warning.
+ */
+ public void warning(String message, @Nullable Object source, @Nullable ParseState parseState) {
+ warning(message, source, parseState, null);
+ }
+
+ /**
+ * Raise a non-critical warning.
+ */
+ public void warning(String message, @Nullable Object source, @Nullable ParseState parseState, @Nullable Throwable cause) {
+ Location location = new Location(getResource(), source);
+ this.problemReporter.warning(new Problem(message, location, parseState, cause));
+ }
+
+
+ // Explicit parse events
+
+ /**
+ * Fire a defaults-registered event.
+ */
+ public void fireDefaultsRegistered(DefaultsDefinition defaultsDefinition) {
+ this.eventListener.defaultsRegistered(defaultsDefinition);
+ }
+
+ /**
+ * Fire a component-registered event.
+ */
+ public void fireComponentRegistered(ComponentDefinition componentDefinition) {
+ this.eventListener.componentRegistered(componentDefinition);
+ }
+
+ /**
+ * Fire an alias-registered event.
+ */
+ public void fireAliasRegistered(String beanName, String alias, @Nullable Object source) {
+ this.eventListener.aliasRegistered(new AliasDefinition(beanName, alias, source));
+ }
+
+ /**
+ * Fire an import-processed event.
+ */
+ public void fireImportProcessed(String importedResource, @Nullable Object source) {
+ this.eventListener.importProcessed(new ImportDefinition(importedResource, source));
+ }
+
+ /**
+ * Fire an import-processed event.
+ */
+ public void fireImportProcessed(String importedResource, Resource[] actualResources, @Nullable Object source) {
+ this.eventListener.importProcessed(new ImportDefinition(importedResource, actualResources, source));
+ }
+
+
+ // Source extraction
+
+ /**
+ * Return the source extractor in use.
+ */
+ public SourceExtractor getSourceExtractor() {
+ return this.sourceExtractor;
+ }
+
+ /**
+ * Call the source extractor for the given source object.
+ * @param sourceCandidate the original source object
+ * @return the source object to store, or {@code null} for none.
+ * @see #getSourceExtractor()
+ * @see SourceExtractor#extractSource
+ */
+ @Nullable
+ public Object extractSource(Object sourceCandidate) {
+ return this.sourceExtractor.extractSource(sourceCandidate, this.resource);
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderEventListener.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderEventListener.java
new file mode 100644
index 0000000..24a4050
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/ReaderEventListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2007 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.parsing;
+
+import java.util.EventListener;
+
+/**
+ * Interface that receives callbacks for component, alias and import
+ * registrations during a bean definition reading process.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see ReaderContext
+ */
+public interface ReaderEventListener extends EventListener {
+
+ /**
+ * Notification that the given defaults has been registered.
+ * @param defaultsDefinition a descriptor for the defaults
+ * @see org.springframework.beans.factory.xml.DocumentDefaultsDefinition
+ */
+ void defaultsRegistered(DefaultsDefinition defaultsDefinition);
+
+ /**
+ * Notification that the given component has been registered.
+ * @param componentDefinition a descriptor for the new component
+ * @see BeanComponentDefinition
+ */
+ void componentRegistered(ComponentDefinition componentDefinition);
+
+ /**
+ * Notification that the given alias has been registered.
+ * @param aliasDefinition a descriptor for the new alias
+ */
+ void aliasRegistered(AliasDefinition aliasDefinition);
+
+ /**
+ * Notification that the given import has been processed.
+ * @param importDefinition a descriptor for the import
+ */
+ void importProcessed(ImportDefinition importDefinition);
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/SourceExtractor.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/SourceExtractor.java
new file mode 100644
index 0000000..8809cd2
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/SourceExtractor.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ * 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.parsing;
+
+import org.springframework.core.io.Resource;
+import org.springframework.lang.Nullable;
+
+/**
+ * Simple strategy allowing tools to control how source metadata is attached
+ * to the bean definition metadata.
+ *
+ *
Configuration parsers may provide the ability to attach
+ * source metadata during the parse phase. They will offer this metadata in a
+ * generic format which can be further modified by a {@link SourceExtractor}
+ * before being attached to the bean definition metadata.
+ *
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ * @see org.springframework.beans.BeanMetadataElement#getSource()
+ * @see org.springframework.beans.factory.config.BeanDefinition
+ */
+@FunctionalInterface
+public interface SourceExtractor {
+
+ /**
+ * Extract the source metadata from the candidate object supplied
+ * by the configuration parser.
+ * @param sourceCandidate the original source metadata (never {@code null})
+ * @param definingResource the resource that defines the given source object
+ * (may be {@code null})
+ * @return the source metadata object to store (may be {@code null})
+ */
+ @Nullable
+ Object extractSource(Object sourceCandidate, @Nullable Resource definingResource);
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/parsing/package-info.java b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/package-info.java
new file mode 100644
index 0000000..0f57ef1
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/parsing/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Support infrastructure for bean definition parsing.
+ */
+@NonNullApi
+@NonNullFields
+package org.springframework.beans.factory.parsing;
+
+import org.springframework.lang.NonNullApi;
+import org.springframework.lang.NonNullFields;
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
new file mode 100644
index 0000000..4defe32
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
@@ -0,0 +1,2026 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+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.apache.commons.logging.Log;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.MutablePropertyValues;
+import org.springframework.beans.PropertyAccessorUtils;
+import org.springframework.beans.PropertyValue;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.factory.Aware;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanCurrentlyInCreationException;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanNameAware;
+import org.springframework.beans.factory.FactoryBean;
+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;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.core.PriorityOrdered;
+import org.springframework.core.ResolvableType;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.ReflectionUtils.MethodCallback;
+import org.springframework.util.StringUtils;
+
+/**
+ * Abstract bean factory superclass that implements default bean creation,
+ * with the full capabilities specified by the {@link RootBeanDefinition} class.
+ * Implements the {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory}
+ * interface in addition to AbstractBeanFactory's {@link #createBean} method.
+ *
+ *
Provides bean creation (with constructor resolution), property population,
+ * wiring (including autowiring), and initialization. Handles runtime bean
+ * references, resolves managed collections, calls initialization methods, etc.
+ * Supports autowiring constructors, properties by name, and properties by type.
+ *
+ *
The main template method to be implemented by subclasses is
+ * {@link #resolveDependency(DependencyDescriptor, String, Set, TypeConverter)},
+ * used for autowiring by type. In case of a factory which is capable of searching
+ * its bean definitions, matching beans will typically be implemented through such
+ * a search. For other factory styles, simplified matching algorithms can be implemented.
+ *
+ *
Note that this class does not assume or implement bean definition
+ * registry capabilities. See {@link DefaultListableBeanFactory} for an implementation
+ * of the {@link org.springframework.beans.factory.ListableBeanFactory} and
+ * {@link BeanDefinitionRegistry} interfaces, which represent the API and SPI
+ * view of such a factory, respectively.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @author Mark Fisher
+ * @author Costin Leau
+ * @author Chris Beams
+ * @author Sam Brannen
+ * @author Phillip Webb
+ * @since 13.02.2004
+ * @see RootBeanDefinition
+ * @see DefaultListableBeanFactory
+ * @see BeanDefinitionRegistry
+ */
+public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
+ implements AutowireCapableBeanFactory {
+
+ /**
+ * Whether this environment lives within a native image.
+ * Exposed as a private static field rather than in a {@code NativeImageDetector.inNativeImage()} static method due to https://github.com/oracle/graal/issues/2594.
+ * @see ImageInfo.java
+ */
+ private static final boolean IN_NATIVE_IMAGE = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
+
+
+ /** Strategy for creating bean instances. */
+ private InstantiationStrategy instantiationStrategy;
+
+ /** Resolver strategy for method parameter names. */
+ @Nullable
+ private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
+
+ /** Whether to automatically try to resolve circular references between beans. */
+ private boolean allowCircularReferences = true;
+
+ /**
+ * Whether to resort to injecting a raw bean instance in case of circular reference,
+ * even if the injected bean eventually got wrapped.
+ */
+ private boolean allowRawInjectionDespiteWrapping = false;
+
+ /**
+ * Dependency types to ignore on dependency check and autowire, as Set of
+ * Class objects: for example, String. Default is none.
+ */
+ private final Set> ignoredDependencyTypes = new HashSet<>();
+
+ /**
+ * Dependency interfaces to ignore on dependency check and autowire, as Set of
+ * Class objects. By default, only the BeanFactory interface is ignored.
+ */
+ private final Set> ignoredDependencyInterfaces = new HashSet<>();
+
+ /**
+ * The name of the currently created bean, for implicit dependency registration
+ * on getBean etc invocations triggered from a user-specified Supplier callback.
+ */
+ private final NamedThreadLocal currentlyCreatedBean = new NamedThreadLocal<>("Currently created bean");
+
+ /** Cache of unfinished FactoryBean instances: FactoryBean name to BeanWrapper. */
+ private final ConcurrentMap factoryBeanInstanceCache = new ConcurrentHashMap<>();
+
+ /** Cache of candidate factory methods per factory class. */
+ private final ConcurrentMap, Method[]> factoryMethodCandidateCache = new ConcurrentHashMap<>();
+
+ /** Cache of filtered PropertyDescriptors: bean Class to PropertyDescriptor array. */
+ private final ConcurrentMap, PropertyDescriptor[]> filteredPropertyDescriptorsCache =
+ new ConcurrentHashMap<>();
+
+
+ /**
+ * Create a new AbstractAutowireCapableBeanFactory.
+ */
+ public AbstractAutowireCapableBeanFactory() {
+ super();
+ ignoreDependencyInterface(BeanNameAware.class);
+ ignoreDependencyInterface(BeanFactoryAware.class);
+ ignoreDependencyInterface(BeanClassLoaderAware.class);
+ if (IN_NATIVE_IMAGE) {
+ this.instantiationStrategy = new SimpleInstantiationStrategy();
+ }
+ else {
+ this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
+ }
+ }
+
+ /**
+ * Create a new AbstractAutowireCapableBeanFactory with the given parent.
+ * @param parentBeanFactory parent bean factory, or {@code null} if none
+ */
+ public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
+ this();
+ setParentBeanFactory(parentBeanFactory);
+ }
+
+
+ /**
+ * Set the instantiation strategy to use for creating bean instances.
+ * Default is CglibSubclassingInstantiationStrategy.
+ * @see CglibSubclassingInstantiationStrategy
+ */
+ public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
+ this.instantiationStrategy = instantiationStrategy;
+ }
+
+ /**
+ * Return the instantiation strategy to use for creating bean instances.
+ */
+ protected InstantiationStrategy getInstantiationStrategy() {
+ return this.instantiationStrategy;
+ }
+
+ /**
+ * Set the ParameterNameDiscoverer to use for resolving method parameter
+ * names if needed (e.g. for constructor names).
+ * Default is a {@link DefaultParameterNameDiscoverer}.
+ */
+ public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) {
+ this.parameterNameDiscoverer = parameterNameDiscoverer;
+ }
+
+ /**
+ * Return the ParameterNameDiscoverer to use for resolving method parameter
+ * names if needed.
+ */
+ @Nullable
+ protected ParameterNameDiscoverer getParameterNameDiscoverer() {
+ return this.parameterNameDiscoverer;
+ }
+
+ /**
+ * Set whether to allow circular references between beans - and automatically
+ * try to resolve them.
+ *
Note that circular reference resolution means that one of the involved beans
+ * will receive a reference to another bean that is not fully initialized yet.
+ * This can lead to subtle and not-so-subtle side effects on initialization;
+ * it does work fine for many scenarios, though.
+ *
Default is "true". Turn this off to throw an exception when encountering
+ * a circular reference, disallowing them completely.
+ *
NOTE: It is generally recommended to not rely on circular references
+ * between your beans. Refactor your application logic to have the two beans
+ * involved delegate to a third bean that encapsulates their common logic.
+ */
+ public void setAllowCircularReferences(boolean allowCircularReferences) {
+ this.allowCircularReferences = allowCircularReferences;
+ }
+
+ /**
+ * Set whether to allow the raw injection of a bean instance into some other
+ * bean's property, despite the injected bean eventually getting wrapped
+ * (for example, through AOP auto-proxying).
+ *
This will only be used as a last resort in case of a circular reference
+ * that cannot be resolved otherwise: essentially, preferring a raw instance
+ * getting injected over a failure of the entire bean wiring process.
+ *
Default is "false", as of Spring 2.0. Turn this on to allow for non-wrapped
+ * raw beans injected into some of your references, which was Spring 1.2's
+ * (arguably unclean) default behavior.
+ *
NOTE: It is generally recommended to not rely on circular references
+ * between your beans, in particular with auto-proxying involved.
+ * @see #setAllowCircularReferences
+ */
+ public void setAllowRawInjectionDespiteWrapping(boolean allowRawInjectionDespiteWrapping) {
+ this.allowRawInjectionDespiteWrapping = allowRawInjectionDespiteWrapping;
+ }
+
+ /**
+ * Ignore the given dependency type for autowiring:
+ * for example, String. Default is none.
+ */
+ public void ignoreDependencyType(Class> type) {
+ this.ignoredDependencyTypes.add(type);
+ }
+
+ /**
+ * Ignore the given dependency interface for autowiring.
+ *
This will typically be used by application contexts to register
+ * dependencies that are resolved in other ways, like BeanFactory through
+ * BeanFactoryAware or ApplicationContext through ApplicationContextAware.
+ *
By default, only the BeanFactoryAware interface is ignored.
+ * For further types to ignore, invoke this method for each type.
+ * @see org.springframework.beans.factory.BeanFactoryAware
+ * @see org.springframework.context.ApplicationContextAware
+ */
+ public void ignoreDependencyInterface(Class> ifc) {
+ this.ignoredDependencyInterfaces.add(ifc);
+ }
+
+ @Override
+ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
+ super.copyConfigurationFrom(otherFactory);
+ if (otherFactory instanceof AbstractAutowireCapableBeanFactory) {
+ AbstractAutowireCapableBeanFactory otherAutowireFactory =
+ (AbstractAutowireCapableBeanFactory) otherFactory;
+ this.instantiationStrategy = otherAutowireFactory.instantiationStrategy;
+ this.allowCircularReferences = otherAutowireFactory.allowCircularReferences;
+ this.ignoredDependencyTypes.addAll(otherAutowireFactory.ignoredDependencyTypes);
+ this.ignoredDependencyInterfaces.addAll(otherAutowireFactory.ignoredDependencyInterfaces);
+ }
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Typical methods for creating and populating external bean instances
+ //-------------------------------------------------------------------------
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public T createBean(Class beanClass) throws BeansException {
+ // Use prototype bean definition, to avoid registering bean as dependent bean.
+ RootBeanDefinition bd = new RootBeanDefinition(beanClass);
+ bd.setScope(SCOPE_PROTOTYPE);
+ bd.allowCaching = ClassUtils.isCacheSafe(beanClass, getBeanClassLoader());
+ return (T) createBean(beanClass.getName(), bd, null);
+ }
+
+ @Override
+ public void autowireBean(Object existingBean) {
+ // Use non-singleton bean definition, to avoid registering bean as dependent bean.
+ RootBeanDefinition bd = new RootBeanDefinition(ClassUtils.getUserClass(existingBean));
+ bd.setScope(SCOPE_PROTOTYPE);
+ bd.allowCaching = ClassUtils.isCacheSafe(bd.getBeanClass(), getBeanClassLoader());
+ BeanWrapper bw = new BeanWrapperImpl(existingBean);
+ initBeanWrapper(bw);
+ populateBean(bd.getBeanClass().getName(), bd, bw);
+ }
+
+ @Override
+ public Object configureBean(Object existingBean, String beanName) throws BeansException {
+ markBeanAsCreated(beanName);
+ BeanDefinition mbd = getMergedBeanDefinition(beanName);
+ RootBeanDefinition bd = null;
+ if (mbd instanceof RootBeanDefinition) {
+ RootBeanDefinition rbd = (RootBeanDefinition) mbd;
+ bd = (rbd.isPrototype() ? rbd : rbd.cloneBeanDefinition());
+ }
+ if (bd == null) {
+ bd = new RootBeanDefinition(mbd);
+ }
+ if (!bd.isPrototype()) {
+ bd.setScope(SCOPE_PROTOTYPE);
+ bd.allowCaching = ClassUtils.isCacheSafe(ClassUtils.getUserClass(existingBean), getBeanClassLoader());
+ }
+ BeanWrapper bw = new BeanWrapperImpl(existingBean);
+ initBeanWrapper(bw);
+ populateBean(beanName, bd, bw);
+ return initializeBean(beanName, existingBean, bd);
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Specialized methods for fine-grained control over the bean lifecycle
+ //-------------------------------------------------------------------------
+
+ @Override
+ public Object createBean(Class> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
+ // Use non-singleton bean definition, to avoid registering bean as dependent bean.
+ RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
+ bd.setScope(SCOPE_PROTOTYPE);
+ return createBean(beanClass.getName(), bd, null);
+ }
+
+ @Override
+ public Object autowire(Class> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException {
+ // Use non-singleton bean definition, to avoid registering bean as dependent bean.
+ RootBeanDefinition bd = new RootBeanDefinition(beanClass, autowireMode, dependencyCheck);
+ bd.setScope(SCOPE_PROTOTYPE);
+ if (bd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR) {
+ return autowireConstructor(beanClass.getName(), bd, null, null).getWrappedInstance();
+ }
+ else {
+ Object bean;
+ if (System.getSecurityManager() != null) {
+ bean = AccessController.doPrivileged(
+ (PrivilegedAction) () -> getInstantiationStrategy().instantiate(bd, null, this),
+ getAccessControlContext());
+ }
+ else {
+ bean = getInstantiationStrategy().instantiate(bd, null, this);
+ }
+ populateBean(beanClass.getName(), bd, new BeanWrapperImpl(bean));
+ return bean;
+ }
+ }
+
+ @Override
+ public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
+ throws BeansException {
+
+ if (autowireMode == AUTOWIRE_CONSTRUCTOR) {
+ throw new IllegalArgumentException("AUTOWIRE_CONSTRUCTOR not supported for existing bean instance");
+ }
+ // Use non-singleton bean definition, to avoid registering bean as dependent bean.
+ RootBeanDefinition bd =
+ new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck);
+ bd.setScope(SCOPE_PROTOTYPE);
+ BeanWrapper bw = new BeanWrapperImpl(existingBean);
+ initBeanWrapper(bw);
+ populateBean(bd.getBeanClass().getName(), bd, bw);
+ }
+
+ @Override
+ public void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException {
+ markBeanAsCreated(beanName);
+ BeanDefinition bd = getMergedBeanDefinition(beanName);
+ BeanWrapper bw = new BeanWrapperImpl(existingBean);
+ initBeanWrapper(bw);
+ applyPropertyValues(beanName, bd, bw, bd.getPropertyValues());
+ }
+
+ @Override
+ public Object initializeBean(Object existingBean, String beanName) {
+ return initializeBean(beanName, existingBean, null);
+ }
+
+ @Override
+ public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
+ throws BeansException {
+
+ Object result = existingBean;
+ for (BeanPostProcessor processor : getBeanPostProcessors()) {
+ Object current = processor.postProcessBeforeInitialization(result, beanName);
+ if (current == null) {
+ return result;
+ }
+ result = current;
+ }
+ return result;
+ }
+
+ @Override
+ public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
+ throws BeansException {
+
+ Object result = existingBean;
+ for (BeanPostProcessor processor : getBeanPostProcessors()) {
+ Object current = processor.postProcessAfterInitialization(result, beanName);
+ if (current == null) {
+ return result;
+ }
+ result = current;
+ }
+ return result;
+ }
+
+ @Override
+ public void destroyBean(Object existingBean) {
+ new DisposableBeanAdapter(
+ existingBean, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy();
+ }
+
+
+ //-------------------------------------------------------------------------
+ // Delegate methods for resolving injection points
+ //-------------------------------------------------------------------------
+
+ @Override
+ public Object resolveBeanByName(String name, DependencyDescriptor descriptor) {
+ InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
+ try {
+ return getBean(name, descriptor.getDependencyType());
+ }
+ finally {
+ ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
+ }
+ }
+
+ @Override
+ @Nullable
+ public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException {
+ return resolveDependency(descriptor, requestingBeanName, null, null);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of relevant AbstractBeanFactory template methods
+ //---------------------------------------------------------------------
+
+ /**
+ * Central method of this class: creates a bean instance,
+ * populates the bean instance, applies post-processors, etc.
+ * @see #doCreateBean
+ */
+ @Override
+ protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
+ throws BeanCreationException {
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Creating instance of bean '" + beanName + "'");
+ }
+ RootBeanDefinition mbdToUse = mbd;
+
+ // Make sure bean class is actually resolved at this point, and
+ // clone the bean definition in case of a dynamically resolved Class
+ // which cannot be stored in the shared merged bean definition.
+ Class> resolvedClass = resolveBeanClass(mbd, beanName);
+ if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
+ mbdToUse = new RootBeanDefinition(mbd);
+ mbdToUse.setBeanClass(resolvedClass);
+ }
+
+ // Prepare method overrides.
+ try {
+ mbdToUse.prepareMethodOverrides();
+ }
+ catch (BeanDefinitionValidationException ex) {
+ throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
+ beanName, "Validation of method overrides failed", ex);
+ }
+
+ try {
+ // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
+ Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
+ if (bean != null) {
+ return bean;
+ }
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
+ "BeanPostProcessor before instantiation of bean failed", ex);
+ }
+
+ try {
+ Object beanInstance = doCreateBean(beanName, mbdToUse, args);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Finished creating instance of bean '" + beanName + "'");
+ }
+ return beanInstance;
+ }
+ catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
+ // A previously detected exception with proper bean creation context already,
+ // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
+ throw ex;
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(
+ mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
+ }
+ }
+
+ /**
+ * Actually create the specified bean. Pre-creation processing has already happened
+ * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
+ * Differentiates between default bean instantiation, use of a
+ * factory method, and autowiring a constructor.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @param args explicit arguments to use for constructor or factory method invocation
+ * @return a new instance of the bean
+ * @throws BeanCreationException if the bean could not be created
+ * @see #instantiateBean
+ * @see #instantiateUsingFactoryMethod
+ * @see #autowireConstructor
+ */
+ protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
+ throws BeanCreationException {
+
+ // Instantiate the bean.
+ BeanWrapper instanceWrapper = null;
+ if (mbd.isSingleton()) {
+ instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
+ }
+ if (instanceWrapper == null) {
+ instanceWrapper = createBeanInstance(beanName, mbd, args);
+ }
+ Object bean = instanceWrapper.getWrappedInstance();
+ Class> beanType = instanceWrapper.getWrappedClass();
+ if (beanType != NullBean.class) {
+ mbd.resolvedTargetType = beanType;
+ }
+
+ // 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;
+ }
+ }
+
+ // Eagerly cache singletons to be able to resolve circular references
+ // even when triggered by lifecycle interfaces like BeanFactoryAware.
+ boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
+ isSingletonCurrentlyInCreation(beanName));
+ if (earlySingletonExposure) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Eagerly caching bean '" + beanName +
+ "' to allow for resolving potential circular references");
+ }
+ addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
+ }
+
+ // Initialize the bean instance.
+ Object exposedObject = bean;
+ try {
+ populateBean(beanName, mbd, instanceWrapper);
+ exposedObject = initializeBean(beanName, exposedObject, mbd);
+ }
+ catch (Throwable ex) {
+ if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
+ throw (BeanCreationException) ex;
+ }
+ else {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
+ }
+ }
+
+ if (earlySingletonExposure) {
+ Object earlySingletonReference = getSingleton(beanName, false);
+ if (earlySingletonReference != null) {
+ if (exposedObject == bean) {
+ exposedObject = earlySingletonReference;
+ }
+ else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
+ String[] dependentBeans = getDependentBeans(beanName);
+ Set actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
+ for (String dependentBean : dependentBeans) {
+ if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
+ actualDependentBeans.add(dependentBean);
+ }
+ }
+ if (!actualDependentBeans.isEmpty()) {
+ throw new BeanCurrentlyInCreationException(beanName,
+ "Bean with name '" + beanName + "' has been injected into other beans [" +
+ StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
+ "] in its raw version as part of a circular reference, but has eventually been " +
+ "wrapped. This means that said other beans do not use the final version of the " +
+ "bean. This is often the result of over-eager type matching - consider using " +
+ "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
+ }
+ }
+ }
+ }
+
+ // Register bean as disposable.
+ try {
+ registerDisposableBeanIfNecessary(beanName, bean, mbd);
+ }
+ catch (BeanDefinitionValidationException ex) {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
+ }
+
+ return exposedObject;
+ }
+
+ @Override
+ @Nullable
+ protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
+ Class> targetType = determineTargetType(beanName, mbd, typesToMatch);
+ // Apply SmartInstantiationAwareBeanPostProcessors to predict the
+ // eventual type after a before-instantiation shortcut.
+ if (targetType != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
+ boolean matchingOnlyFactoryBean = typesToMatch.length == 1 && typesToMatch[0] == FactoryBean.class;
+ for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
+ Class> predicted = bp.predictBeanType(targetType, beanName);
+ if (predicted != null &&
+ (!matchingOnlyFactoryBean || FactoryBean.class.isAssignableFrom(predicted))) {
+ return predicted;
+ }
+ }
+ }
+ return targetType;
+ }
+
+ /**
+ * Determine the target type for the given bean definition.
+ * @param beanName the name of the bean (for error handling purposes)
+ * @param mbd the merged bean definition for the bean
+ * @param typesToMatch the types to match in case of internal type matching purposes
+ * (also signals that the returned {@code Class} will never be exposed to application code)
+ * @return the type for the bean if determinable, or {@code null} otherwise
+ */
+ @Nullable
+ protected Class> determineTargetType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
+ Class> targetType = mbd.getTargetType();
+ if (targetType == null) {
+ targetType = (mbd.getFactoryMethodName() != null ?
+ getTypeForFactoryMethod(beanName, mbd, typesToMatch) :
+ resolveBeanClass(mbd, beanName, typesToMatch));
+ if (ObjectUtils.isEmpty(typesToMatch) || getTempClassLoader() == null) {
+ mbd.resolvedTargetType = targetType;
+ }
+ }
+ return targetType;
+ }
+
+ /**
+ * Determine the target type for the given bean definition which is based on
+ * a factory method. Only called if there is no singleton instance registered
+ * for the target bean already.
+ * This implementation determines the type matching {@link #createBean}'s
+ * different creation strategies. As far as possible, we'll perform static
+ * type checking to avoid creation of the target bean.
+ * @param beanName the name of the bean (for error handling purposes)
+ * @param mbd the merged bean definition for the bean
+ * @param typesToMatch the types to match in case of internal type matching purposes
+ * (also signals that the returned {@code Class} will never be exposed to application code)
+ * @return the type for the bean if determinable, or {@code null} otherwise
+ * @see #createBean
+ */
+ @Nullable
+ protected Class> getTypeForFactoryMethod(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
+ ResolvableType cachedReturnType = mbd.factoryMethodReturnType;
+ if (cachedReturnType != null) {
+ return cachedReturnType.resolve();
+ }
+
+ Class> commonType = null;
+ Method uniqueCandidate = mbd.factoryMethodToIntrospect;
+
+ if (uniqueCandidate == null) {
+ Class> factoryClass;
+ boolean isStatic = true;
+
+ String factoryBeanName = mbd.getFactoryBeanName();
+ if (factoryBeanName != null) {
+ if (factoryBeanName.equals(beanName)) {
+ throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
+ "factory-bean reference points back to the same bean definition");
+ }
+ // Check declared factory method return type on factory class.
+ factoryClass = getType(factoryBeanName);
+ isStatic = false;
+ }
+ else {
+ // Check declared factory method return type on bean class.
+ factoryClass = resolveBeanClass(mbd, beanName, typesToMatch);
+ }
+
+ if (factoryClass == null) {
+ return null;
+ }
+ factoryClass = ClassUtils.getUserClass(factoryClass);
+
+ // If all factory methods have the same return type, return that type.
+ // Can't clearly figure out exact method due to type converting / autowiring!
+ int minNrOfArgs =
+ (mbd.hasConstructorArgumentValues() ? mbd.getConstructorArgumentValues().getArgumentCount() : 0);
+ Method[] candidates = this.factoryMethodCandidateCache.computeIfAbsent(factoryClass,
+ clazz -> ReflectionUtils.getUniqueDeclaredMethods(clazz, ReflectionUtils.USER_DECLARED_METHODS));
+
+ for (Method candidate : candidates) {
+ if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate) &&
+ candidate.getParameterCount() >= minNrOfArgs) {
+ // Declared type variables to inspect?
+ if (candidate.getTypeParameters().length > 0) {
+ try {
+ // Fully resolve parameter names and argument values.
+ Class>[] paramTypes = candidate.getParameterTypes();
+ String[] paramNames = null;
+ ParameterNameDiscoverer pnd = getParameterNameDiscoverer();
+ if (pnd != null) {
+ paramNames = pnd.getParameterNames(candidate);
+ }
+ ConstructorArgumentValues cav = mbd.getConstructorArgumentValues();
+ Set usedValueHolders = new HashSet<>(paramTypes.length);
+ Object[] args = new Object[paramTypes.length];
+ for (int i = 0; i < args.length; i++) {
+ ConstructorArgumentValues.ValueHolder valueHolder = cav.getArgumentValue(
+ i, paramTypes[i], (paramNames != null ? paramNames[i] : null), usedValueHolders);
+ if (valueHolder == null) {
+ valueHolder = cav.getGenericArgumentValue(null, null, usedValueHolders);
+ }
+ if (valueHolder != null) {
+ args[i] = valueHolder.getValue();
+ usedValueHolders.add(valueHolder);
+ }
+ }
+ Class> returnType = AutowireUtils.resolveReturnTypeForFactoryMethod(
+ candidate, args, getBeanClassLoader());
+ uniqueCandidate = (commonType == null && returnType == candidate.getReturnType() ?
+ candidate : null);
+ commonType = ClassUtils.determineCommonAncestor(returnType, commonType);
+ if (commonType == null) {
+ // Ambiguous return types found: return null to indicate "not determinable".
+ return null;
+ }
+ }
+ catch (Throwable ex) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to resolve generic return type for factory method: " + ex);
+ }
+ }
+ }
+ else {
+ uniqueCandidate = (commonType == null ? candidate : null);
+ commonType = ClassUtils.determineCommonAncestor(candidate.getReturnType(), commonType);
+ if (commonType == null) {
+ // Ambiguous return types found: return null to indicate "not determinable".
+ return null;
+ }
+ }
+ }
+ }
+
+ mbd.factoryMethodToIntrospect = uniqueCandidate;
+ if (commonType == null) {
+ return null;
+ }
+ }
+
+ // Common return type found: all factory methods return same type. For a non-parameterized
+ // unique candidate, cache the full type declaration context of the target factory method.
+ cachedReturnType = (uniqueCandidate != null ?
+ ResolvableType.forMethodReturnType(uniqueCandidate) : ResolvableType.forClass(commonType));
+ mbd.factoryMethodReturnType = cachedReturnType;
+ return cachedReturnType.resolve();
+ }
+
+ /**
+ * This implementation attempts to query the FactoryBean's generic parameter metadata
+ * if present to determine the object type. If not present, i.e. the FactoryBean is
+ * declared as a raw type, checks the FactoryBean's {@code getObjectType} method
+ * on a plain instance of the FactoryBean, without bean properties applied yet.
+ * If this doesn't return a type yet, and {@code allowInit} is {@code true} a
+ * full creation of the FactoryBean is used as fallback (through delegation to the
+ * superclass's implementation).
+ * The shortcut check for a FactoryBean is only applied in case of a singleton
+ * FactoryBean. If the FactoryBean instance itself is not kept as singleton,
+ * it will be fully created to check the type of its exposed object.
+ */
+ @Override
+ protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {
+ // Check if the bean definition itself has defined the type with an attribute
+ ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
+ if (result != ResolvableType.NONE) {
+ return result;
+ }
+
+ ResolvableType beanType =
+ (mbd.hasBeanClass() ? ResolvableType.forClass(mbd.getBeanClass()) : ResolvableType.NONE);
+
+ // For instance supplied beans try the target type and bean class
+ if (mbd.getInstanceSupplier() != null) {
+ result = getFactoryBeanGeneric(mbd.targetType);
+ if (result.resolve() != null) {
+ return result;
+ }
+ result = getFactoryBeanGeneric(beanType);
+ if (result.resolve() != null) {
+ return result;
+ }
+ }
+
+ // Consider factory methods
+ String factoryBeanName = mbd.getFactoryBeanName();
+ String factoryMethodName = mbd.getFactoryMethodName();
+
+ // Scan the factory bean methods
+ if (factoryBeanName != null) {
+ if (factoryMethodName != null) {
+ // Try to obtain the FactoryBean's object type from its factory method
+ // declaration without instantiating the containing bean at all.
+ BeanDefinition factoryBeanDefinition = getBeanDefinition(factoryBeanName);
+ Class> factoryBeanClass;
+ if (factoryBeanDefinition instanceof AbstractBeanDefinition &&
+ ((AbstractBeanDefinition) factoryBeanDefinition).hasBeanClass()) {
+ factoryBeanClass = ((AbstractBeanDefinition) factoryBeanDefinition).getBeanClass();
+ }
+ else {
+ RootBeanDefinition fbmbd = getMergedBeanDefinition(factoryBeanName, factoryBeanDefinition);
+ factoryBeanClass = determineTargetType(factoryBeanName, fbmbd);
+ }
+ if (factoryBeanClass != null) {
+ result = getTypeForFactoryBeanFromMethod(factoryBeanClass, factoryMethodName);
+ if (result.resolve() != null) {
+ return result;
+ }
+ }
+ }
+ // If not resolvable above and the referenced factory bean doesn't exist yet,
+ // exit here - we don't want to force the creation of another bean just to
+ // obtain a FactoryBean's object type...
+ if (!isBeanEligibleForMetadataCaching(factoryBeanName)) {
+ return ResolvableType.NONE;
+ }
+ }
+
+ // If we're allowed, we can create the factory bean and call getObjectType() early
+ if (allowInit) {
+ FactoryBean> factoryBean = (mbd.isSingleton() ?
+ getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
+ getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));
+ if (factoryBean != null) {
+ // Try to obtain the FactoryBean's object type from this early stage of the instance.
+ Class> type = getTypeForFactoryBean(factoryBean);
+ if (type != null) {
+ return ResolvableType.forClass(type);
+ }
+ // No type found for shortcut FactoryBean instance:
+ // fall back to full creation of the FactoryBean instance.
+ return super.getTypeForFactoryBean(beanName, mbd, true);
+ }
+ }
+
+ if (factoryBeanName == null && mbd.hasBeanClass() && factoryMethodName != null) {
+ // No early bean instantiation possible: determine FactoryBean's type from
+ // static factory method signature or from class inheritance hierarchy...
+ return getTypeForFactoryBeanFromMethod(mbd.getBeanClass(), factoryMethodName);
+ }
+ result = getFactoryBeanGeneric(beanType);
+ if (result.resolve() != null) {
+ return result;
+ }
+ return ResolvableType.NONE;
+ }
+
+ private ResolvableType getFactoryBeanGeneric(@Nullable ResolvableType type) {
+ if (type == null) {
+ return ResolvableType.NONE;
+ }
+ return type.as(FactoryBean.class).getGeneric();
+ }
+
+ /**
+ * Introspect the factory method signatures on the given bean class,
+ * trying to find a common {@code FactoryBean} object type declared there.
+ * @param beanClass the bean class to find the factory method on
+ * @param factoryMethodName the name of the factory method
+ * @return the common {@code FactoryBean} object type, or {@code null} if none
+ */
+ private ResolvableType getTypeForFactoryBeanFromMethod(Class> beanClass, String factoryMethodName) {
+ // CGLIB subclass methods hide generic parameters; look at the original user class.
+ Class> factoryBeanClass = ClassUtils.getUserClass(beanClass);
+ FactoryBeanMethodTypeFinder finder = new FactoryBeanMethodTypeFinder(factoryMethodName);
+ ReflectionUtils.doWithMethods(factoryBeanClass, finder, ReflectionUtils.USER_DECLARED_METHODS);
+ return finder.getResult();
+ }
+
+ /**
+ * This implementation attempts to query the FactoryBean's generic parameter metadata
+ * if present to determine the object type. If not present, i.e. the FactoryBean is
+ * declared as a raw type, checks the FactoryBean's {@code getObjectType} method
+ * on a plain instance of the FactoryBean, without bean properties applied yet.
+ * If this doesn't return a type yet, a full creation of the FactoryBean is
+ * used as fallback (through delegation to the superclass's implementation).
+ *
The shortcut check for a FactoryBean is only applied in case of a singleton
+ * FactoryBean. If the FactoryBean instance itself is not kept as singleton,
+ * it will be fully created to check the type of its exposed object.
+ */
+ @Override
+ @Deprecated
+ @Nullable
+ protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
+ return getTypeForFactoryBean(beanName, mbd, true).resolve();
+ }
+
+ /**
+ * Obtain a reference for early access to the specified bean,
+ * typically for the purpose of resolving a circular reference.
+ * @param beanName the name of the bean (for error handling purposes)
+ * @param mbd the merged bean definition for the bean
+ * @param bean the raw bean instance
+ * @return the object to expose as bean reference
+ */
+ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
+ Object exposedObject = bean;
+ if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
+ for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
+ exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
+ }
+ }
+ return exposedObject;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation methods
+ //---------------------------------------------------------------------
+
+ /**
+ * Obtain a "shortcut" singleton FactoryBean instance to use for a
+ * {@code getObjectType()} call, without full initialization of the FactoryBean.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @return the FactoryBean instance, or {@code null} to indicate
+ * that we couldn't obtain a shortcut FactoryBean instance
+ */
+ @Nullable
+ private FactoryBean> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
+ synchronized (getSingletonMutex()) {
+ BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
+ if (bw != null) {
+ return (FactoryBean>) bw.getWrappedInstance();
+ }
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance instanceof FactoryBean) {
+ return (FactoryBean>) beanInstance;
+ }
+ if (isSingletonCurrentlyInCreation(beanName) ||
+ (mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
+ return null;
+ }
+
+ Object instance;
+ try {
+ // Mark this bean as currently in creation, even if just partially.
+ beforeSingletonCreation(beanName);
+ // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
+ instance = resolveBeforeInstantiation(beanName, mbd);
+ if (instance == null) {
+ bw = createBeanInstance(beanName, mbd, null);
+ instance = bw.getWrappedInstance();
+ }
+ }
+ catch (UnsatisfiedDependencyException ex) {
+ // Don't swallow, probably misconfiguration...
+ throw ex;
+ }
+ catch (BeanCreationException ex) {
+ // Instantiation failure, maybe too early...
+ if (logger.isDebugEnabled()) {
+ logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex);
+ }
+ onSuppressedException(ex);
+ return null;
+ }
+ finally {
+ // Finished partial creation of this bean.
+ afterSingletonCreation(beanName);
+ }
+
+ FactoryBean> fb = getFactoryBean(beanName, instance);
+ if (bw != null) {
+ this.factoryBeanInstanceCache.put(beanName, bw);
+ }
+ return fb;
+ }
+ }
+
+ /**
+ * Obtain a "shortcut" non-singleton FactoryBean instance to use for a
+ * {@code getObjectType()} call, without full initialization of the FactoryBean.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @return the FactoryBean instance, or {@code null} to indicate
+ * that we couldn't obtain a shortcut FactoryBean instance
+ */
+ @Nullable
+ private FactoryBean> getNonSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
+ if (isPrototypeCurrentlyInCreation(beanName)) {
+ return null;
+ }
+
+ Object instance;
+ try {
+ // Mark this bean as currently in creation, even if just partially.
+ beforePrototypeCreation(beanName);
+ // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
+ instance = resolveBeforeInstantiation(beanName, mbd);
+ if (instance == null) {
+ BeanWrapper bw = createBeanInstance(beanName, mbd, null);
+ instance = bw.getWrappedInstance();
+ }
+ }
+ catch (UnsatisfiedDependencyException ex) {
+ // Don't swallow, probably misconfiguration...
+ throw ex;
+ }
+ catch (BeanCreationException ex) {
+ // Instantiation failure, maybe too early...
+ if (logger.isDebugEnabled()) {
+ logger.debug("Bean creation exception on non-singleton FactoryBean type check: " + ex);
+ }
+ onSuppressedException(ex);
+ return null;
+ }
+ finally {
+ // Finished partial creation of this bean.
+ afterPrototypeCreation(beanName);
+ }
+
+ return getFactoryBean(beanName, instance);
+ }
+
+ /**
+ * Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
+ * invoking their {@code postProcessMergedBeanDefinition} methods.
+ * @param mbd the merged bean definition for the bean
+ * @param beanType the actual type of the managed bean instance
+ * @param beanName the name of the bean
+ * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
+ */
+ protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class> beanType, String beanName) {
+ for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
+ processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
+ }
+ }
+
+ /**
+ * Apply before-instantiation post-processors, resolving whether there is a
+ * before-instantiation shortcut for the specified bean.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @return the shortcut-determined bean instance, or {@code null} if none
+ */
+ @Nullable
+ protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
+ Object bean = null;
+ if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
+ // Make sure bean class is actually resolved at this point.
+ if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
+ Class> targetType = determineTargetType(beanName, mbd);
+ if (targetType != null) {
+ bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
+ if (bean != null) {
+ bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
+ }
+ }
+ }
+ mbd.beforeInstantiationResolved = (bean != null);
+ }
+ return bean;
+ }
+
+ /**
+ * Apply InstantiationAwareBeanPostProcessors to the specified bean definition
+ * (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
+ *
Any returned object will be used as the bean instead of actually instantiating
+ * the target bean. A {@code null} return value from the post-processor will
+ * result in the target bean being instantiated.
+ * @param beanClass the class of the bean to be instantiated
+ * @param beanName the name of the bean
+ * @return the bean object to use instead of a default instance of the target bean, or {@code null}
+ * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
+ */
+ @Nullable
+ protected Object applyBeanPostProcessorsBeforeInstantiation(Class> beanClass, String beanName) {
+ for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
+ Object result = bp.postProcessBeforeInstantiation(beanClass, beanName);
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create a new instance for the specified bean, using an appropriate instantiation strategy:
+ * factory method, constructor autowiring, or simple instantiation.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @param args explicit arguments to use for constructor or factory method invocation
+ * @return a BeanWrapper for the new instance
+ * @see #obtainFromSupplier
+ * @see #instantiateUsingFactoryMethod
+ * @see #autowireConstructor
+ * @see #instantiateBean
+ */
+ 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);
+
+ if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
+ }
+
+ Supplier> instanceSupplier = mbd.getInstanceSupplier();
+ if (instanceSupplier != null) {
+ return obtainFromSupplier(instanceSupplier, beanName);
+ }
+
+ if (mbd.getFactoryMethodName() != null) {
+ return instantiateUsingFactoryMethod(beanName, mbd, args);
+ }
+
+ // Shortcut when re-creating the same bean...
+ boolean resolved = false;
+ boolean autowireNecessary = false;
+ if (args == null) {
+ synchronized (mbd.constructorArgumentLock) {
+ if (mbd.resolvedConstructorOrFactoryMethod != null) {
+ resolved = true;
+ autowireNecessary = mbd.constructorArgumentsResolved;
+ }
+ }
+ }
+ if (resolved) {
+ if (autowireNecessary) {
+ return autowireConstructor(beanName, mbd, null, null);
+ }
+ else {
+ return instantiateBean(beanName, mbd);
+ }
+ }
+
+ // Candidate constructors for autowiring?
+ Constructor>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
+ if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
+ mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
+ return autowireConstructor(beanName, mbd, ctors, args);
+ }
+
+ // Preferred constructors for default construction?
+ ctors = mbd.getPreferredConstructors();
+ if (ctors != null) {
+ return autowireConstructor(beanName, mbd, ctors, null);
+ }
+
+ // No special handling: simply use no-arg constructor.
+ return instantiateBean(beanName, mbd);
+ }
+
+ /**
+ * Obtain a bean instance from the given supplier.
+ * @param instanceSupplier the configured supplier
+ * @param beanName the corresponding bean name
+ * @return a BeanWrapper for the new instance
+ * @since 5.0
+ * @see #getObjectForBeanInstance
+ */
+ protected BeanWrapper obtainFromSupplier(Supplier> instanceSupplier, String beanName) {
+ Object instance;
+
+ String outerBean = this.currentlyCreatedBean.get();
+ this.currentlyCreatedBean.set(beanName);
+ try {
+ instance = instanceSupplier.get();
+ }
+ finally {
+ if (outerBean != null) {
+ this.currentlyCreatedBean.set(outerBean);
+ }
+ else {
+ this.currentlyCreatedBean.remove();
+ }
+ }
+
+ if (instance == null) {
+ instance = new NullBean();
+ }
+ BeanWrapper bw = new BeanWrapperImpl(instance);
+ initBeanWrapper(bw);
+ return bw;
+ }
+
+ /**
+ * Overridden in order to implicitly register the currently created bean as
+ * dependent on further beans getting programmatically retrieved during a
+ * {@link Supplier} callback.
+ * @since 5.0
+ * @see #obtainFromSupplier
+ */
+ @Override
+ protected Object getObjectForBeanInstance(
+ Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
+
+ String currentlyCreatedBean = this.currentlyCreatedBean.get();
+ if (currentlyCreatedBean != null) {
+ registerDependentBean(beanName, currentlyCreatedBean);
+ }
+
+ return super.getObjectForBeanInstance(beanInstance, name, beanName, mbd);
+ }
+
+ /**
+ * Determine candidate constructors to use for the given bean, checking all registered
+ * {@link SmartInstantiationAwareBeanPostProcessor SmartInstantiationAwareBeanPostProcessors}.
+ * @param beanClass the raw class of the bean
+ * @param beanName the name of the bean
+ * @return the candidate constructors, or {@code null} if none specified
+ * @throws org.springframework.beans.BeansException in case of errors
+ * @see org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
+ */
+ @Nullable
+ protected Constructor>[] determineConstructorsFromBeanPostProcessors(@Nullable Class> beanClass, String beanName)
+ throws BeansException {
+
+ if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) {
+ for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
+ Constructor>[] ctors = bp.determineCandidateConstructors(beanClass, beanName);
+ if (ctors != null) {
+ return ctors;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Instantiate the given bean using its default constructor.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @return a BeanWrapper for the new instance
+ */
+ protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) {
+ try {
+ Object beanInstance;
+ if (System.getSecurityManager() != null) {
+ beanInstance = AccessController.doPrivileged(
+ (PrivilegedAction) () -> getInstantiationStrategy().instantiate(mbd, beanName, this),
+ getAccessControlContext());
+ }
+ else {
+ beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, this);
+ }
+ BeanWrapper bw = new BeanWrapperImpl(beanInstance);
+ initBeanWrapper(bw);
+ return bw;
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
+ }
+ }
+
+ /**
+ * Instantiate the bean using a named factory method. The method may be static, if the
+ * mbd parameter specifies a class, rather than a factoryBean, or an instance variable
+ * on a factory object itself configured using Dependency Injection.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @param explicitArgs argument values passed in programmatically via the getBean method,
+ * or {@code null} if none (-> use constructor argument values from bean definition)
+ * @return a BeanWrapper for the new instance
+ * @see #getBean(String, Object[])
+ */
+ protected BeanWrapper instantiateUsingFactoryMethod(
+ String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
+
+ return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
+ }
+
+ /**
+ * "autowire constructor" (with constructor arguments by type) behavior.
+ * Also applied if explicit constructor argument values are specified,
+ * matching all remaining arguments with beans from the bean factory.
+ * This corresponds to constructor injection: In this mode, a Spring
+ * bean factory is able to host components that expect constructor-based
+ * dependency resolution.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @param ctors the chosen candidate constructors
+ * @param explicitArgs argument values passed in programmatically via the getBean method,
+ * or {@code null} if none (-> use constructor argument values from bean definition)
+ * @return a BeanWrapper for the new instance
+ */
+ protected BeanWrapper autowireConstructor(
+ String beanName, RootBeanDefinition mbd, @Nullable Constructor>[] ctors, @Nullable Object[] explicitArgs) {
+
+ return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
+ }
+
+ /**
+ * Populate the bean instance in the given BeanWrapper with the property values
+ * from the bean definition.
+ * @param beanName the name of the bean
+ * @param mbd the bean definition for the bean
+ * @param bw the BeanWrapper with bean instance
+ */
+ @SuppressWarnings("deprecation") // for postProcessPropertyValues
+ protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
+ if (bw == null) {
+ if (mbd.hasPropertyValues()) {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
+ }
+ else {
+ // Skip property population phase for null instance.
+ return;
+ }
+ }
+
+ // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
+ // state of the bean before properties are set. This can be used, for example,
+ // to support styles of field injection.
+ if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
+ for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
+ if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
+ return;
+ }
+ }
+ }
+
+ PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
+
+ int resolvedAutowireMode = mbd.getResolvedAutowireMode();
+ if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
+ MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
+ // Add property values based on autowire by name if applicable.
+ if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
+ autowireByName(beanName, mbd, bw, newPvs);
+ }
+ // Add property values based on autowire by type if applicable.
+ if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
+ autowireByType(beanName, mbd, bw, newPvs);
+ }
+ pvs = newPvs;
+ }
+
+ boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
+ boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
+
+ PropertyDescriptor[] filteredPds = null;
+ if (hasInstAwareBpps) {
+ if (pvs == null) {
+ pvs = mbd.getPropertyValues();
+ }
+ for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
+ PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
+ if (pvsToUse == null) {
+ if (filteredPds == null) {
+ filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
+ }
+ pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
+ if (pvsToUse == null) {
+ return;
+ }
+ }
+ pvs = pvsToUse;
+ }
+ }
+ if (needsDepCheck) {
+ if (filteredPds == null) {
+ filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
+ }
+ checkDependencies(beanName, mbd, filteredPds, pvs);
+ }
+
+ if (pvs != null) {
+ applyPropertyValues(beanName, mbd, bw, pvs);
+ }
+ }
+
+ /**
+ * Fill in any missing property values with references to
+ * other beans in this factory if autowire is set to "byName".
+ * @param beanName the name of the bean we're wiring up.
+ * Useful for debugging messages; not used functionally.
+ * @param mbd bean definition to update through autowiring
+ * @param bw the BeanWrapper from which we can obtain information about the bean
+ * @param pvs the PropertyValues to register wired objects with
+ */
+ protected void autowireByName(
+ String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
+
+ String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
+ for (String propertyName : propertyNames) {
+ if (containsBean(propertyName)) {
+ Object bean = getBean(propertyName);
+ pvs.add(propertyName, bean);
+ registerDependentBean(propertyName, beanName);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Added autowiring by name from bean name '" + beanName +
+ "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
+ }
+ }
+ else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
+ "' by name: no matching bean found");
+ }
+ }
+ }
+ }
+
+ /**
+ * Abstract method defining "autowire by type" (bean properties by type) behavior.
+ *
This is like PicoContainer default, in which there must be exactly one bean
+ * of the property type in the bean factory. This makes bean factories simple to
+ * configure for small namespaces, but doesn't work as well as standard Spring
+ * behavior for bigger applications.
+ * @param beanName the name of the bean to autowire by type
+ * @param mbd the merged bean definition to update through autowiring
+ * @param bw the BeanWrapper from which we can obtain information about the bean
+ * @param pvs the PropertyValues to register wired objects with
+ */
+ protected void autowireByType(
+ String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
+
+ TypeConverter converter = getCustomTypeConverter();
+ if (converter == null) {
+ converter = bw;
+ }
+
+ Set autowiredBeanNames = new LinkedHashSet<>(4);
+ String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
+ for (String propertyName : propertyNames) {
+ try {
+ PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
+ // Don't try autowiring by type for type Object: never makes sense,
+ // even if it technically is a unsatisfied, non-simple property.
+ if (Object.class != pd.getPropertyType()) {
+ MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
+ // Do not allow eager init for type matching in case of a prioritized post-processor.
+ boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
+ DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
+ Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
+ if (autowiredArgument != null) {
+ pvs.add(propertyName, autowiredArgument);
+ }
+ for (String autowiredBeanName : autowiredBeanNames) {
+ registerDependentBean(autowiredBeanName, beanName);
+ if (logger.isTraceEnabled()) {
+ logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" +
+ propertyName + "' to bean named '" + autowiredBeanName + "'");
+ }
+ }
+ autowiredBeanNames.clear();
+ }
+ }
+ catch (BeansException ex) {
+ throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
+ }
+ }
+ }
+
+
+ /**
+ * Return an array of non-simple bean properties that are unsatisfied.
+ * These are probably unsatisfied references to other beans in the
+ * factory. Does not include simple properties like primitives or Strings.
+ * @param mbd the merged bean definition the bean was created with
+ * @param bw the BeanWrapper the bean was created with
+ * @return an array of bean property names
+ * @see org.springframework.beans.BeanUtils#isSimpleProperty
+ */
+ protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
+ Set result = new TreeSet<>();
+ PropertyValues pvs = mbd.getPropertyValues();
+ PropertyDescriptor[] pds = bw.getPropertyDescriptors();
+ for (PropertyDescriptor pd : pds) {
+ if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
+ !BeanUtils.isSimpleProperty(pd.getPropertyType())) {
+ result.add(pd.getName());
+ }
+ }
+ return StringUtils.toStringArray(result);
+ }
+
+ /**
+ * Extract a filtered set of PropertyDescriptors from the given BeanWrapper,
+ * excluding ignored dependency types or properties defined on ignored dependency interfaces.
+ * @param bw the BeanWrapper the bean was created with
+ * @param cache whether to cache filtered PropertyDescriptors for the given bean Class
+ * @return the filtered PropertyDescriptors
+ * @see #isExcludedFromDependencyCheck
+ * @see #filterPropertyDescriptorsForDependencyCheck(org.springframework.beans.BeanWrapper)
+ */
+ protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanWrapper bw, boolean cache) {
+ PropertyDescriptor[] filtered = this.filteredPropertyDescriptorsCache.get(bw.getWrappedClass());
+ if (filtered == null) {
+ filtered = filterPropertyDescriptorsForDependencyCheck(bw);
+ if (cache) {
+ PropertyDescriptor[] existing =
+ this.filteredPropertyDescriptorsCache.putIfAbsent(bw.getWrappedClass(), filtered);
+ if (existing != null) {
+ filtered = existing;
+ }
+ }
+ }
+ return filtered;
+ }
+
+ /**
+ * Extract a filtered set of PropertyDescriptors from the given BeanWrapper,
+ * excluding ignored dependency types or properties defined on ignored dependency interfaces.
+ * @param bw the BeanWrapper the bean was created with
+ * @return the filtered PropertyDescriptors
+ * @see #isExcludedFromDependencyCheck
+ */
+ protected PropertyDescriptor[] filterPropertyDescriptorsForDependencyCheck(BeanWrapper bw) {
+ List pds = new ArrayList<>(Arrays.asList(bw.getPropertyDescriptors()));
+ pds.removeIf(this::isExcludedFromDependencyCheck);
+ return pds.toArray(new PropertyDescriptor[0]);
+ }
+
+ /**
+ * Determine whether the given bean property is excluded from dependency checks.
+ * This implementation excludes properties defined by CGLIB and
+ * properties whose type matches an ignored dependency type or which
+ * are defined by an ignored dependency interface.
+ * @param pd the PropertyDescriptor of the bean property
+ * @return whether the bean property is excluded
+ * @see #ignoreDependencyType(Class)
+ * @see #ignoreDependencyInterface(Class)
+ */
+ protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
+ return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
+ this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||
+ AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));
+ }
+
+ /**
+ * Perform a dependency check that all properties exposed have been set,
+ * if desired. Dependency checks can be objects (collaborating beans),
+ * simple (primitives and String), or all (both).
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition the bean was created with
+ * @param pds the relevant property descriptors for the target bean
+ * @param pvs the property values to be applied to the bean
+ * @see #isExcludedFromDependencyCheck(java.beans.PropertyDescriptor)
+ */
+ protected void checkDependencies(
+ String beanName, AbstractBeanDefinition mbd, PropertyDescriptor[] pds, @Nullable PropertyValues pvs)
+ throws UnsatisfiedDependencyException {
+
+ int dependencyCheck = mbd.getDependencyCheck();
+ for (PropertyDescriptor pd : pds) {
+ if (pd.getWriteMethod() != null && (pvs == null || !pvs.contains(pd.getName()))) {
+ boolean isSimple = BeanUtils.isSimpleProperty(pd.getPropertyType());
+ boolean unsatisfied = (dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_ALL) ||
+ (isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE) ||
+ (!isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
+ if (unsatisfied) {
+ throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, pd.getName(),
+ "Set this property value or disable dependency checking for this bean.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Apply the given property values, resolving any runtime references
+ * to other beans in this bean factory. Must use deep copy, so we
+ * don't permanently modify this property.
+ * @param beanName the bean name passed for better exception information
+ * @param mbd the merged bean definition
+ * @param bw the BeanWrapper wrapping the target object
+ * @param pvs the new property values
+ */
+ protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
+ if (pvs.isEmpty()) {
+ return;
+ }
+
+ if (System.getSecurityManager() != null && bw instanceof BeanWrapperImpl) {
+ ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
+ }
+
+ MutablePropertyValues mpvs = null;
+ List original;
+
+ if (pvs instanceof MutablePropertyValues) {
+ mpvs = (MutablePropertyValues) pvs;
+ if (mpvs.isConverted()) {
+ // Shortcut: use the pre-converted values as-is.
+ try {
+ bw.setPropertyValues(mpvs);
+ return;
+ }
+ catch (BeansException ex) {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Error setting property values", ex);
+ }
+ }
+ original = mpvs.getPropertyValueList();
+ }
+ else {
+ original = Arrays.asList(pvs.getPropertyValues());
+ }
+
+ TypeConverter converter = getCustomTypeConverter();
+ if (converter == null) {
+ converter = bw;
+ }
+ BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
+
+ // Create a deep copy, resolving any references for values.
+ List deepCopy = new ArrayList<>(original.size());
+ boolean resolveNecessary = false;
+ for (PropertyValue pv : original) {
+ if (pv.isConverted()) {
+ deepCopy.add(pv);
+ }
+ 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) &&
+ !PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
+ if (convertible) {
+ convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
+ }
+ // Possibly store converted value in merged bean definition,
+ // in order to avoid re-conversion for every created bean instance.
+ if (resolvedValue == originalValue) {
+ if (convertible) {
+ pv.setConvertedValue(convertedValue);
+ }
+ deepCopy.add(pv);
+ }
+ else if (convertible && originalValue instanceof TypedStringValue &&
+ !((TypedStringValue) originalValue).isDynamic() &&
+ !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
+ pv.setConvertedValue(convertedValue);
+ deepCopy.add(pv);
+ }
+ else {
+ resolveNecessary = true;
+ deepCopy.add(new PropertyValue(pv, convertedValue));
+ }
+ }
+ }
+ if (mpvs != null && !resolveNecessary) {
+ mpvs.setConverted();
+ }
+
+ // Set our (possibly massaged) deep copy.
+ try {
+ bw.setPropertyValues(new MutablePropertyValues(deepCopy));
+ }
+ catch (BeansException ex) {
+ throw new BeanCreationException(
+ mbd.getResourceDescription(), beanName, "Error setting property values", ex);
+ }
+ }
+
+ /**
+ * Convert the given value for the specified target property.
+ */
+ @Nullable
+ private Object convertForProperty(
+ @Nullable Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
+
+ if (converter instanceof BeanWrapperImpl) {
+ return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
+ }
+ else {
+ PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
+ MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
+ return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
+ }
+ }
+
+
+ /**
+ * Initialize the given bean instance, applying factory callbacks
+ * as well as init methods and bean post processors.
+ * Called from {@link #createBean} for traditionally defined beans,
+ * and from {@link #initializeBean} for existing bean instances.
+ * @param beanName the bean name in the factory (for debugging purposes)
+ * @param bean the new bean instance we may need to initialize
+ * @param mbd the bean definition that the bean was created with
+ * (can also be {@code null}, if given an existing bean instance)
+ * @return the initialized bean instance (potentially wrapped)
+ * @see BeanNameAware
+ * @see BeanClassLoaderAware
+ * @see BeanFactoryAware
+ * @see #applyBeanPostProcessorsBeforeInitialization
+ * @see #invokeInitMethods
+ * @see #applyBeanPostProcessorsAfterInitialization
+ */
+ protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ invokeAwareMethods(beanName, bean);
+ return null;
+ }, getAccessControlContext());
+ }
+ else {
+ invokeAwareMethods(beanName, bean);
+ }
+
+ Object wrappedBean = bean;
+ if (mbd == null || !mbd.isSynthetic()) {
+ wrappedBean = applyBeanPostProcessorsBeforeInitialization(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;
+ }
+
+ private void invokeAwareMethods(String beanName, Object bean) {
+ if (bean instanceof Aware) {
+ if (bean instanceof BeanNameAware) {
+ ((BeanNameAware) bean).setBeanName(beanName);
+ }
+ if (bean instanceof BeanClassLoaderAware) {
+ ClassLoader bcl = getBeanClassLoader();
+ if (bcl != null) {
+ ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
+ }
+ }
+ if (bean instanceof BeanFactoryAware) {
+ ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
+ }
+ }
+ }
+
+ /**
+ * Give a bean a chance to react now all its properties are set,
+ * and a chance to know about its owning bean factory (this object).
+ * This means checking whether the bean implements InitializingBean or defines
+ * a custom init method, and invoking the necessary callback(s) if it does.
+ * @param beanName the bean name in the factory (for debugging purposes)
+ * @param bean the new bean instance we may need to initialize
+ * @param mbd the merged bean definition that the bean was created with
+ * (can also be {@code null}, if given an existing bean instance)
+ * @throws Throwable if thrown by init methods or by the invocation process
+ * @see #invokeCustomInitMethod
+ */
+ protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
+ throws Throwable {
+
+ boolean isInitializingBean = (bean instanceof InitializingBean);
+ if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
+ }
+ if (System.getSecurityManager() != null) {
+ try {
+ AccessController.doPrivileged((PrivilegedExceptionAction) () -> {
+ ((InitializingBean) bean).afterPropertiesSet();
+ return null;
+ }, getAccessControlContext());
+ }
+ catch (PrivilegedActionException pae) {
+ throw pae.getException();
+ }
+ }
+ else {
+ ((InitializingBean) bean).afterPropertiesSet();
+ }
+ }
+
+ if (mbd != null && bean.getClass() != NullBean.class) {
+ String initMethodName = mbd.getInitMethodName();
+ if (StringUtils.hasLength(initMethodName) &&
+ !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
+ !mbd.isExternallyManagedInitMethod(initMethodName)) {
+ invokeCustomInitMethod(beanName, bean, mbd);
+ }
+ }
+ }
+
+ /**
+ * Invoke the specified custom init method on the given bean.
+ * Called by invokeInitMethods.
+ * Can be overridden in subclasses for custom resolution of init
+ * methods with arguments.
+ * @see #invokeInitMethods
+ */
+ protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd)
+ throws Throwable {
+
+ String initMethodName = mbd.getInitMethodName();
+ Assert.state(initMethodName != null, "No init method set");
+ Method initMethod = (mbd.isNonPublicAccessAllowed() ?
+ BeanUtils.findMethod(bean.getClass(), initMethodName) :
+ ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
+
+ if (initMethod == null) {
+ if (mbd.isEnforceInitMethod()) {
+ throw new BeanDefinitionValidationException("Could not find an init method named '" +
+ initMethodName + "' on bean with name '" + beanName + "'");
+ }
+ else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("No default init method named '" + initMethodName +
+ "' found on bean with name '" + beanName + "'");
+ }
+ // Ignore non-existent default lifecycle methods.
+ return;
+ }
+ }
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");
+ }
+ Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
+
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ ReflectionUtils.makeAccessible(methodToInvoke);
+ return null;
+ });
+ try {
+ AccessController.doPrivileged((PrivilegedExceptionAction)
+ () -> methodToInvoke.invoke(bean), getAccessControlContext());
+ }
+ catch (PrivilegedActionException pae) {
+ InvocationTargetException ex = (InvocationTargetException) pae.getException();
+ throw ex.getTargetException();
+ }
+ }
+ else {
+ try {
+ ReflectionUtils.makeAccessible(methodToInvoke);
+ methodToInvoke.invoke(bean);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
+
+ /**
+ * Applies the {@code postProcessAfterInitialization} callback of all
+ * registered BeanPostProcessors, giving them a chance to post-process the
+ * object obtained from FactoryBeans (for example, to auto-proxy them).
+ * @see #applyBeanPostProcessorsAfterInitialization
+ */
+ @Override
+ protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
+ return applyBeanPostProcessorsAfterInitialization(object, beanName);
+ }
+
+ /**
+ * Overridden to clear FactoryBean instance cache as well.
+ */
+ @Override
+ protected void removeSingleton(String beanName) {
+ synchronized (getSingletonMutex()) {
+ super.removeSingleton(beanName);
+ this.factoryBeanInstanceCache.remove(beanName);
+ }
+ }
+
+ /**
+ * Overridden to clear FactoryBean instance cache as well.
+ */
+ @Override
+ protected void clearSingletonCache() {
+ synchronized (getSingletonMutex()) {
+ super.clearSingletonCache();
+ this.factoryBeanInstanceCache.clear();
+ }
+ }
+
+ /**
+ * Expose the logger to collaborating delegates.
+ * @since 5.0.7
+ */
+ Log getLogger() {
+ return logger;
+ }
+
+
+ /**
+ * Special DependencyDescriptor variant for Spring's good old autowire="byType" mode.
+ * Always optional; never considering the parameter name for choosing a primary candidate.
+ */
+ @SuppressWarnings("serial")
+ private static class AutowireByTypeDependencyDescriptor extends DependencyDescriptor {
+
+ public AutowireByTypeDependencyDescriptor(MethodParameter methodParameter, boolean eager) {
+ super(methodParameter, false, eager);
+ }
+
+ @Override
+ public String getDependencyName() {
+ return null;
+ }
+ }
+
+
+ /**
+ * {@link MethodCallback} used to find {@link FactoryBean} type information.
+ */
+ private static class FactoryBeanMethodTypeFinder implements MethodCallback {
+
+ private final String factoryMethodName;
+
+ private ResolvableType result = ResolvableType.NONE;
+
+ FactoryBeanMethodTypeFinder(String factoryMethodName) {
+ this.factoryMethodName = factoryMethodName;
+ }
+
+ @Override
+ public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
+ if (isFactoryBeanMethod(method)) {
+ ResolvableType returnType = ResolvableType.forMethodReturnType(method);
+ ResolvableType candidate = returnType.as(FactoryBean.class).getGeneric();
+ if (this.result == ResolvableType.NONE) {
+ this.result = candidate;
+ }
+ else {
+ Class> resolvedResult = this.result.resolve();
+ Class> commonAncestor = ClassUtils.determineCommonAncestor(candidate.resolve(), resolvedResult);
+ if (!ObjectUtils.nullSafeEquals(resolvedResult, commonAncestor)) {
+ this.result = ResolvableType.forClass(commonAncestor);
+ }
+ }
+ }
+ }
+
+ private boolean isFactoryBeanMethod(Method method) {
+ return (method.getName().equals(this.factoryMethodName) &&
+ FactoryBean.class.isAssignableFrom(method.getReturnType()));
+ }
+
+ ResolvableType getResult() {
+ Class> resolved = this.result.resolve();
+ boolean foundResult = resolved != null && resolved != Object.class;
+ return (foundResult ? this.result : ResolvableType.NONE);
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java
new file mode 100644
index 0000000..a9a556c
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinitionReader.java
@@ -0,0 +1,262 @@
+/*
+ * 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.support;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.EnvironmentCapable;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Abstract base class for bean definition readers which implement
+ * the {@link BeanDefinitionReader} interface.
+ *
+ * Provides common properties like the bean factory to work on
+ * and the class loader to use for loading bean classes.
+ *
+ * @author Juergen Hoeller
+ * @author Chris Beams
+ * @since 11.12.2003
+ * @see BeanDefinitionReaderUtils
+ */
+public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader, EnvironmentCapable {
+
+ /** Logger available to subclasses. */
+ protected final Log logger = LogFactory.getLog(getClass());
+
+ private final BeanDefinitionRegistry registry;
+
+ @Nullable
+ private ResourceLoader resourceLoader;
+
+ @Nullable
+ private ClassLoader beanClassLoader;
+
+ private Environment environment;
+
+ private BeanNameGenerator beanNameGenerator = DefaultBeanNameGenerator.INSTANCE;
+
+
+ /**
+ * Create a new AbstractBeanDefinitionReader for the given bean factory.
+ *
If the passed-in bean factory does not only implement the BeanDefinitionRegistry
+ * interface but also the ResourceLoader interface, it will be used as default
+ * ResourceLoader as well. This will usually be the case for
+ * {@link org.springframework.context.ApplicationContext} implementations.
+ *
If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
+ * {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
+ *
If the passed-in bean factory also implements {@link EnvironmentCapable} its
+ * environment will be used by this reader. Otherwise, the reader will initialize and
+ * use a {@link StandardEnvironment}. All ApplicationContext implementations are
+ * EnvironmentCapable, while normal BeanFactory implementations are not.
+ * @param registry the BeanFactory to load bean definitions into,
+ * in the form of a BeanDefinitionRegistry
+ * @see #setResourceLoader
+ * @see #setEnvironment
+ */
+ protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
+ Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
+ this.registry = registry;
+
+ // Determine ResourceLoader to use.
+ if (this.registry instanceof ResourceLoader) {
+ this.resourceLoader = (ResourceLoader) this.registry;
+ }
+ else {
+ this.resourceLoader = new PathMatchingResourcePatternResolver();
+ }
+
+ // Inherit Environment if possible
+ if (this.registry instanceof EnvironmentCapable) {
+ this.environment = ((EnvironmentCapable) this.registry).getEnvironment();
+ }
+ else {
+ this.environment = new StandardEnvironment();
+ }
+ }
+
+
+ public final BeanDefinitionRegistry getBeanFactory() {
+ return this.registry;
+ }
+
+ @Override
+ public final BeanDefinitionRegistry getRegistry() {
+ return this.registry;
+ }
+
+ /**
+ * Set the ResourceLoader to use for resource locations.
+ * If specifying a ResourcePatternResolver, the bean definition reader
+ * will be capable of resolving resource patterns to Resource arrays.
+ *
Default is PathMatchingResourcePatternResolver, also capable of
+ * resource pattern resolving through the ResourcePatternResolver interface.
+ *
Setting this to {@code null} suggests that absolute resource loading
+ * is not available for this bean definition reader.
+ * @see org.springframework.core.io.support.ResourcePatternResolver
+ * @see org.springframework.core.io.support.PathMatchingResourcePatternResolver
+ */
+ public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+ @Override
+ @Nullable
+ public ResourceLoader getResourceLoader() {
+ return this.resourceLoader;
+ }
+
+ /**
+ * Set the ClassLoader to use for bean classes.
+ *
Default is {@code null}, which suggests to not load bean classes
+ * eagerly but rather to just register bean definitions with class names,
+ * with the corresponding Classes to be resolved later (or never).
+ * @see Thread#getContextClassLoader()
+ */
+ public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
+ this.beanClassLoader = beanClassLoader;
+ }
+
+ @Override
+ @Nullable
+ public ClassLoader getBeanClassLoader() {
+ return this.beanClassLoader;
+ }
+
+ /**
+ * Set the Environment to use when reading bean definitions. Most often used
+ * for evaluating profile information to determine which bean definitions
+ * should be read and which should be omitted.
+ */
+ public void setEnvironment(Environment environment) {
+ Assert.notNull(environment, "Environment must not be null");
+ this.environment = environment;
+ }
+
+ @Override
+ public Environment getEnvironment() {
+ return this.environment;
+ }
+
+ /**
+ * Set the BeanNameGenerator to use for anonymous beans
+ * (without explicit bean name specified).
+ *
Default is a {@link DefaultBeanNameGenerator}.
+ */
+ public void setBeanNameGenerator(@Nullable BeanNameGenerator beanNameGenerator) {
+ this.beanNameGenerator = (beanNameGenerator != null ? beanNameGenerator : DefaultBeanNameGenerator.INSTANCE);
+ }
+
+ @Override
+ public BeanNameGenerator getBeanNameGenerator() {
+ return this.beanNameGenerator;
+ }
+
+
+ @Override
+ public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
+ Assert.notNull(resources, "Resource array must not be null");
+ int count = 0;
+ for (Resource resource : resources) {
+ count += loadBeanDefinitions(resource);
+ }
+ return count;
+ }
+
+ @Override
+ public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
+ return loadBeanDefinitions(location, null);
+ }
+
+ /**
+ * Load bean definitions from the specified resource location.
+ *
The location can also be a location pattern, provided that the
+ * ResourceLoader of this bean definition reader is a ResourcePatternResolver.
+ * @param location the resource location, to be loaded with the ResourceLoader
+ * (or ResourcePatternResolver) of this bean definition reader
+ * @param actualResources a Set to be filled with the actual Resource objects
+ * that have been resolved during the loading process. May be {@code null}
+ * to indicate that the caller is not interested in those Resource objects.
+ * @return the number of bean definitions found
+ * @throws BeanDefinitionStoreException in case of loading or parsing errors
+ * @see #getResourceLoader()
+ * @see #loadBeanDefinitions(org.springframework.core.io.Resource)
+ * @see #loadBeanDefinitions(org.springframework.core.io.Resource[])
+ */
+ public int loadBeanDefinitions(String location, @Nullable Set actualResources) throws BeanDefinitionStoreException {
+ ResourceLoader resourceLoader = getResourceLoader();
+ if (resourceLoader == null) {
+ throw new BeanDefinitionStoreException(
+ "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available");
+ }
+
+ if (resourceLoader instanceof ResourcePatternResolver) {
+ // Resource pattern matching available.
+ try {
+ Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
+ int count = loadBeanDefinitions(resources);
+ if (actualResources != null) {
+ Collections.addAll(actualResources, resources);
+ }
+ if (logger.isTraceEnabled()) {
+ logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]");
+ }
+ return count;
+ }
+ catch (IOException ex) {
+ throw new BeanDefinitionStoreException(
+ "Could not resolve bean definition resource pattern [" + location + "]", ex);
+ }
+ }
+ else {
+ // Can only load single resources by absolute URL.
+ Resource resource = resourceLoader.getResource(location);
+ int count = loadBeanDefinitions(resource);
+ if (actualResources != null) {
+ actualResources.add(resource);
+ }
+ if (logger.isTraceEnabled()) {
+ logger.trace("Loaded " + count + " bean definitions from location [" + location + "]");
+ }
+ return count;
+ }
+ }
+
+ @Override
+ public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
+ Assert.notNull(locations, "Location array must not be null");
+ int count = 0;
+ for (String location : locations) {
+ count += loadBeanDefinitions(location);
+ }
+ return count;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
new file mode 100644
index 0000000..443b7ee
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanFactory.java
@@ -0,0 +1,2114 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.beans.PropertyEditor;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyEditorRegistrar;
+import org.springframework.beans.PropertyEditorRegistry;
+import org.springframework.beans.PropertyEditorRegistrySupport;
+import org.springframework.beans.SimpleTypeConverter;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanCurrentlyInCreationException;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.BeanIsAbstractException;
+import org.springframework.beans.factory.BeanIsNotAFactoryException;
+import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
+import org.springframework.beans.factory.CannotLoadBeanClassException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.SmartFactoryBean;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.BeanExpressionContext;
+import org.springframework.beans.factory.config.BeanExpressionResolver;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.beans.factory.config.Scope;
+import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
+import org.springframework.core.AttributeAccessor;
+import org.springframework.core.DecoratingClassLoader;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.core.ResolvableType;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.log.LogMessage;
+import org.springframework.core.metrics.ApplicationStartup;
+import org.springframework.core.metrics.StartupStep;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.util.StringValueResolver;
+
+/**
+ * Abstract base class for {@link org.springframework.beans.factory.BeanFactory}
+ * implementations, providing the full capabilities of the
+ * {@link org.springframework.beans.factory.config.ConfigurableBeanFactory} SPI.
+ * Does not assume a listable bean factory: can therefore also be used
+ * as base class for bean factory implementations which obtain bean definitions
+ * from some backend resource (where bean definition access is an expensive operation).
+ *
+ * This class provides a singleton cache (through its base class
+ * {@link org.springframework.beans.factory.support.DefaultSingletonBeanRegistry},
+ * singleton/prototype determination, {@link org.springframework.beans.factory.FactoryBean}
+ * handling, aliases, bean definition merging for child bean definitions,
+ * and bean destruction ({@link org.springframework.beans.factory.DisposableBean}
+ * interface, custom destroy methods). Furthermore, it can manage a bean factory
+ * hierarchy (delegating to the parent in case of an unknown bean), through implementing
+ * the {@link org.springframework.beans.factory.HierarchicalBeanFactory} interface.
+ *
+ *
The main template methods to be implemented by subclasses are
+ * {@link #getBeanDefinition} and {@link #createBean}, retrieving a bean definition
+ * for a given bean name and creating a bean instance for a given bean definition,
+ * respectively. Default implementations of those operations can be found in
+ * {@link DefaultListableBeanFactory} and {@link AbstractAutowireCapableBeanFactory}.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Costin Leau
+ * @author Chris Beams
+ * @author Phillip Webb
+ * @since 15 April 2001
+ * @see #getBeanDefinition
+ * @see #createBean
+ * @see AbstractAutowireCapableBeanFactory#createBean
+ * @see DefaultListableBeanFactory#getBeanDefinition
+ */
+public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
+
+ /** Parent bean factory, for bean inheritance support. */
+ @Nullable
+ private BeanFactory parentBeanFactory;
+
+ /** ClassLoader to resolve bean class names with, if necessary. */
+ @Nullable
+ private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
+
+ /** ClassLoader to temporarily resolve bean class names with, if necessary. */
+ @Nullable
+ private ClassLoader tempClassLoader;
+
+ /** Whether to cache bean metadata or rather reobtain it for every access. */
+ private boolean cacheBeanMetadata = true;
+
+ /** Resolution strategy for expressions in bean definition values. */
+ @Nullable
+ private BeanExpressionResolver beanExpressionResolver;
+
+ /** Spring ConversionService to use instead of PropertyEditors. */
+ @Nullable
+ private ConversionService conversionService;
+
+ /** Custom PropertyEditorRegistrars to apply to the beans of this factory. */
+ private final Set propertyEditorRegistrars = new LinkedHashSet<>(4);
+
+ /** Custom PropertyEditors to apply to the beans of this factory. */
+ private final Map, Class extends PropertyEditor>> customEditors = new HashMap<>(4);
+
+ /** A custom TypeConverter to use, overriding the default PropertyEditor mechanism. */
+ @Nullable
+ private TypeConverter typeConverter;
+
+ /** String resolvers to apply e.g. to annotation attribute values. */
+ private final List embeddedValueResolvers = new CopyOnWriteArrayList<>();
+
+ /** BeanPostProcessors to apply. */
+ private final List beanPostProcessors = new BeanPostProcessorCacheAwareList();
+
+ /** Cache of pre-filtered post-processors. */
+ @Nullable
+ private volatile BeanPostProcessorCache beanPostProcessorCache;
+
+ /** Map from scope identifier String to corresponding Scope. */
+ private final Map scopes = new LinkedHashMap<>(8);
+
+ /** Security context used when running with a SecurityManager. */
+ @Nullable
+ private SecurityContextProvider securityContextProvider;
+
+ /** Map from bean name to merged RootBeanDefinition. */
+ private final Map mergedBeanDefinitions = new ConcurrentHashMap<>(256);
+
+ /** Names of beans that have already been created at least once. */
+ private final Set alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
+
+ /** Names of beans that are currently in creation. */
+ private final ThreadLocal prototypesCurrentlyInCreation =
+ new NamedThreadLocal<>("Prototype beans currently in creation");
+
+ /** Application startup metrics. **/
+ private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;
+
+ /**
+ * Create a new AbstractBeanFactory.
+ */
+ public AbstractBeanFactory() {
+ }
+
+ /**
+ * Create a new AbstractBeanFactory with the given parent.
+ * @param parentBeanFactory parent bean factory, or {@code null} if none
+ * @see #getBean
+ */
+ public AbstractBeanFactory(@Nullable BeanFactory parentBeanFactory) {
+ this.parentBeanFactory = parentBeanFactory;
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of BeanFactory interface
+ //---------------------------------------------------------------------
+
+ @Override
+ public Object getBean(String name) throws BeansException {
+ return doGetBean(name, null, null, false);
+ }
+
+ @Override
+ public T getBean(String name, Class requiredType) throws BeansException {
+ return doGetBean(name, requiredType, null, false);
+ }
+
+ @Override
+ public Object getBean(String name, Object... args) throws BeansException {
+ return doGetBean(name, null, args, false);
+ }
+
+ /**
+ * Return an instance, which may be shared or independent, of the specified bean.
+ * @param name the name of the bean to retrieve
+ * @param requiredType the required type of the bean to retrieve
+ * @param args arguments to use when creating a bean instance using explicit arguments
+ * (only applied when creating a new instance as opposed to retrieving an existing one)
+ * @return an instance of the bean
+ * @throws BeansException if the bean could not be created
+ */
+ public T getBean(String name, @Nullable Class requiredType, @Nullable Object... args)
+ throws BeansException {
+
+ return doGetBean(name, requiredType, args, false);
+ }
+
+ /**
+ * Return an instance, which may be shared or independent, of the specified bean.
+ * @param name the name of the bean to retrieve
+ * @param requiredType the required type of the bean to retrieve
+ * @param args arguments to use when creating a bean instance using explicit arguments
+ * (only applied when creating a new instance as opposed to retrieving an existing one)
+ * @param typeCheckOnly whether the instance is obtained for a type check,
+ * not for actual use
+ * @return an instance of the bean
+ * @throws BeansException if the bean could not be created
+ */
+ @SuppressWarnings("unchecked")
+ protected T doGetBean(
+ String name, @Nullable Class requiredType, @Nullable Object[] args, boolean typeCheckOnly)
+ throws BeansException {
+
+ String beanName = transformedBeanName(name);
+ Object bean;
+
+ // Eagerly check singleton cache for manually registered singletons.
+ Object sharedInstance = getSingleton(beanName);
+ if (sharedInstance != null && args == null) {
+ if (logger.isTraceEnabled()) {
+ if (isSingletonCurrentlyInCreation(beanName)) {
+ logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
+ "' that is not fully initialized yet - a consequence of a circular reference");
+ }
+ else {
+ logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
+ }
+ }
+ bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
+ }
+
+ else {
+ // Fail if we're already creating this bean instance:
+ // We're assumably within a circular reference.
+ if (isPrototypeCurrentlyInCreation(beanName)) {
+ throw new BeanCurrentlyInCreationException(beanName);
+ }
+
+ // Check if bean definition exists in this factory.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // Not found -> check parent.
+ String nameToLookup = originalBeanName(name);
+ if (parentBeanFactory instanceof AbstractBeanFactory) {
+ return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
+ nameToLookup, requiredType, args, typeCheckOnly);
+ }
+ else if (args != null) {
+ // Delegation to parent with explicit args.
+ return (T) parentBeanFactory.getBean(nameToLookup, args);
+ }
+ else if (requiredType != null) {
+ // No args -> delegate to standard getBean method.
+ return parentBeanFactory.getBean(nameToLookup, requiredType);
+ }
+ else {
+ return (T) parentBeanFactory.getBean(nameToLookup);
+ }
+ }
+
+ if (!typeCheckOnly) {
+ markBeanAsCreated(beanName);
+ }
+
+ StartupStep beanCreation = this.applicationStartup.start("spring.beans.instantiate")
+ .tag("beanName", name);
+ try {
+ if (requiredType != null) {
+ beanCreation.tag("beanType", requiredType::toString);
+ }
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ checkMergedBeanDefinition(mbd, beanName, args);
+
+ // Guarantee initialization of beans that the current bean depends on.
+ String[] dependsOn = mbd.getDependsOn();
+ if (dependsOn != null) {
+ for (String dep : dependsOn) {
+ if (isDependent(beanName, dep)) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
+ }
+ registerDependentBean(dep, beanName);
+ try {
+ getBean(dep);
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
+ }
+ }
+ }
+
+ // Create bean instance.
+ if (mbd.isSingleton()) {
+ sharedInstance = getSingleton(beanName, () -> {
+ try {
+ return createBean(beanName, mbd, args);
+ }
+ catch (BeansException ex) {
+ // Explicitly remove instance from singleton cache: It might have been put there
+ // eagerly by the creation process, to allow for circular reference resolution.
+ // Also remove any beans that received a temporary reference to the bean.
+ destroySingleton(beanName);
+ throw ex;
+ }
+ });
+ bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
+ }
+
+ else if (mbd.isPrototype()) {
+ // It's a prototype -> create a new instance.
+ Object prototypeInstance = null;
+ try {
+ beforePrototypeCreation(beanName);
+ prototypeInstance = createBean(beanName, mbd, args);
+ }
+ finally {
+ afterPrototypeCreation(beanName);
+ }
+ bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
+ }
+
+ else {
+ String scopeName = mbd.getScope();
+ if (!StringUtils.hasLength(scopeName)) {
+ throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
+ }
+ Scope scope = this.scopes.get(scopeName);
+ if (scope == null) {
+ throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
+ }
+ try {
+ Object scopedInstance = scope.get(beanName, () -> {
+ beforePrototypeCreation(beanName);
+ try {
+ return createBean(beanName, mbd, args);
+ }
+ finally {
+ afterPrototypeCreation(beanName);
+ }
+ });
+ bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
+ }
+ catch (IllegalStateException ex) {
+ throw new ScopeNotActiveException(beanName, scopeName, ex);
+ }
+ }
+ }
+ catch (BeansException ex) {
+ beanCreation.tag("exception", ex.getClass().toString());
+ beanCreation.tag("message", String.valueOf(ex.getMessage()));
+ cleanupAfterBeanCreationFailure(beanName);
+ throw ex;
+ }
+ finally {
+ beanCreation.end();
+ }
+ }
+
+ // Check if required type matches the type of the actual bean instance.
+ if (requiredType != null && !requiredType.isInstance(bean)) {
+ try {
+ T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
+ if (convertedBean == null) {
+ throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
+ }
+ return convertedBean;
+ }
+ catch (TypeMismatchException ex) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Failed to convert bean '" + name + "' to required type '" +
+ ClassUtils.getQualifiedName(requiredType) + "'", ex);
+ }
+ throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
+ }
+ }
+ return (T) bean;
+ }
+
+ @Override
+ public boolean containsBean(String name) {
+ String beanName = transformedBeanName(name);
+ if (containsSingleton(beanName) || containsBeanDefinition(beanName)) {
+ return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(name));
+ }
+ // Not found -> check parent.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ return (parentBeanFactory != null && parentBeanFactory.containsBean(originalBeanName(name)));
+ }
+
+ @Override
+ public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
+ String beanName = transformedBeanName(name);
+
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance != null) {
+ if (beanInstance instanceof FactoryBean) {
+ return (BeanFactoryUtils.isFactoryDereference(name) || ((FactoryBean>) beanInstance).isSingleton());
+ }
+ else {
+ return !BeanFactoryUtils.isFactoryDereference(name);
+ }
+ }
+
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.isSingleton(originalBeanName(name));
+ }
+
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+
+ // In case of FactoryBean, return singleton status of created object if not a dereference.
+ if (mbd.isSingleton()) {
+ if (isFactoryBean(beanName, mbd)) {
+ if (BeanFactoryUtils.isFactoryDereference(name)) {
+ return true;
+ }
+ FactoryBean> factoryBean = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
+ return factoryBean.isSingleton();
+ }
+ else {
+ return !BeanFactoryUtils.isFactoryDereference(name);
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
+ String beanName = transformedBeanName(name);
+
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.isPrototype(originalBeanName(name));
+ }
+
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ if (mbd.isPrototype()) {
+ // In case of FactoryBean, return singleton status of created object if not a dereference.
+ return (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName, mbd));
+ }
+
+ // Singleton or scoped - not a prototype.
+ // However, FactoryBean may still produce a prototype object...
+ if (BeanFactoryUtils.isFactoryDereference(name)) {
+ return false;
+ }
+ if (isFactoryBean(beanName, mbd)) {
+ FactoryBean> fb = (FactoryBean>) getBean(FACTORY_BEAN_PREFIX + beanName);
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged(
+ (PrivilegedAction) () ->
+ ((fb instanceof SmartFactoryBean && ((SmartFactoryBean>) fb).isPrototype()) ||
+ !fb.isSingleton()),
+ getAccessControlContext());
+ }
+ else {
+ return ((fb instanceof SmartFactoryBean && ((SmartFactoryBean>) fb).isPrototype()) ||
+ !fb.isSingleton());
+ }
+ }
+ else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
+ return isTypeMatch(name, typeToMatch, true);
+ }
+
+ /**
+ * Internal extended variant of {@link #isTypeMatch(String, ResolvableType)}
+ * to check whether the bean with the given name matches the specified type. Allow
+ * additional constraints to be applied to ensure that beans are not created early.
+ * @param name the name of the bean to query
+ * @param typeToMatch the type to match against (as a
+ * {@code ResolvableType})
+ * @return {@code true} if the bean type matches, {@code false} if it
+ * doesn't match or cannot be determined yet
+ * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+ * @since 5.2
+ * @see #getBean
+ * @see #getType
+ */
+ protected boolean isTypeMatch(String name, ResolvableType typeToMatch, boolean allowFactoryBeanInit)
+ throws NoSuchBeanDefinitionException {
+
+ String beanName = transformedBeanName(name);
+ boolean isFactoryDereference = BeanFactoryUtils.isFactoryDereference(name);
+
+ // Check manually registered singletons.
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
+ if (beanInstance instanceof FactoryBean) {
+ if (!isFactoryDereference) {
+ Class> type = getTypeForFactoryBean((FactoryBean>) beanInstance);
+ return (type != null && typeToMatch.isAssignableFrom(type));
+ }
+ else {
+ return typeToMatch.isInstance(beanInstance);
+ }
+ }
+ else if (!isFactoryDereference) {
+ if (typeToMatch.isInstance(beanInstance)) {
+ // Direct match for exposed instance?
+ return true;
+ }
+ else if (typeToMatch.hasGenerics() && containsBeanDefinition(beanName)) {
+ // Generics potentially only match on the target class, not on the proxy...
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ Class> targetType = mbd.getTargetType();
+ if (targetType != null && targetType != ClassUtils.getUserClass(beanInstance)) {
+ // Check raw class match as well, making sure it's exposed on the proxy.
+ Class> classToMatch = typeToMatch.resolve();
+ if (classToMatch != null && !classToMatch.isInstance(beanInstance)) {
+ return false;
+ }
+ if (typeToMatch.isAssignableFrom(targetType)) {
+ return true;
+ }
+ }
+ ResolvableType resolvableType = mbd.targetType;
+ if (resolvableType == null) {
+ resolvableType = mbd.factoryMethodReturnType;
+ }
+ return (resolvableType != null && typeToMatch.isAssignableFrom(resolvableType));
+ }
+ }
+ return false;
+ }
+ else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
+ // null instance registered
+ return false;
+ }
+
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
+ }
+
+ // Retrieve corresponding bean definition.
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
+
+ // Setup the types that we want to match against
+ Class> classToMatch = typeToMatch.resolve();
+ if (classToMatch == null) {
+ classToMatch = FactoryBean.class;
+ }
+ Class>[] typesToMatch = (FactoryBean.class == classToMatch ?
+ new Class>[] {classToMatch} : new Class>[] {FactoryBean.class, classToMatch});
+
+
+ // Attempt to predict the bean type
+ Class> predictedType = null;
+
+ // We're looking for a regular reference but we're a factory bean that has
+ // a decorated bean definition. The target bean should be the same type
+ // as FactoryBean would ultimately return.
+ if (!isFactoryDereference && dbd != null && isFactoryBean(beanName, mbd)) {
+ // We should only attempt if the user explicitly set lazy-init to true
+ // and we know the merged bean definition is for a factory bean.
+ if (!mbd.isLazyInit() || allowFactoryBeanInit) {
+ RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
+ Class> targetType = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
+ if (targetType != null && !FactoryBean.class.isAssignableFrom(targetType)) {
+ predictedType = targetType;
+ }
+ }
+ }
+
+ // If we couldn't use the target type, try regular prediction.
+ if (predictedType == null) {
+ predictedType = predictBeanType(beanName, mbd, typesToMatch);
+ if (predictedType == null) {
+ return false;
+ }
+ }
+
+ // Attempt to get the actual ResolvableType for the bean.
+ ResolvableType beanType = null;
+
+ // If it's a FactoryBean, we want to look at what it creates, not the factory class.
+ if (FactoryBean.class.isAssignableFrom(predictedType)) {
+ if (beanInstance == null && !isFactoryDereference) {
+ beanType = getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit);
+ predictedType = beanType.resolve();
+ if (predictedType == null) {
+ return false;
+ }
+ }
+ }
+ else if (isFactoryDereference) {
+ // Special case: A SmartInstantiationAwareBeanPostProcessor returned a non-FactoryBean
+ // type but we nevertheless are being asked to dereference a FactoryBean...
+ // Let's check the original bean class and proceed with it if it is a FactoryBean.
+ predictedType = predictBeanType(beanName, mbd, FactoryBean.class);
+ if (predictedType == null || !FactoryBean.class.isAssignableFrom(predictedType)) {
+ return false;
+ }
+ }
+
+ // We don't have an exact type but if bean definition target type or the factory
+ // method return type matches the predicted type then we can use that.
+ if (beanType == null) {
+ ResolvableType definedType = mbd.targetType;
+ if (definedType == null) {
+ definedType = mbd.factoryMethodReturnType;
+ }
+ if (definedType != null && definedType.resolve() == predictedType) {
+ beanType = definedType;
+ }
+ }
+
+ // If we have a bean type use it so that generics are considered
+ if (beanType != null) {
+ return typeToMatch.isAssignableFrom(beanType);
+ }
+
+ // If we don't have a bean type, fallback to the predicted type
+ return typeToMatch.isAssignableFrom(predictedType);
+ }
+
+ @Override
+ public boolean isTypeMatch(String name, Class> typeToMatch) throws NoSuchBeanDefinitionException {
+ return isTypeMatch(name, ResolvableType.forRawClass(typeToMatch));
+ }
+
+ @Override
+ @Nullable
+ public Class> getType(String name) throws NoSuchBeanDefinitionException {
+ return getType(name, true);
+ }
+
+ @Override
+ @Nullable
+ public Class> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
+ String beanName = transformedBeanName(name);
+
+ // Check manually registered singletons.
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
+ if (beanInstance instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
+ return getTypeForFactoryBean((FactoryBean>) beanInstance);
+ }
+ else {
+ return beanInstance.getClass();
+ }
+ }
+
+ // No singleton instance found -> check bean definition.
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
+ // No bean definition found in this factory -> delegate to parent.
+ return parentBeanFactory.getType(originalBeanName(name));
+ }
+
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+
+ // Check decorated bean definition, if any: We assume it'll be easier
+ // to determine the decorated bean's type than the proxy's type.
+ BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
+ if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
+ RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
+ Class> targetClass = predictBeanType(dbd.getBeanName(), tbd);
+ if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
+ return targetClass;
+ }
+ }
+
+ Class> beanClass = predictBeanType(beanName, mbd);
+
+ // Check bean class whether we're dealing with a FactoryBean.
+ if (beanClass != null && FactoryBean.class.isAssignableFrom(beanClass)) {
+ if (!BeanFactoryUtils.isFactoryDereference(name)) {
+ // If it's a FactoryBean, we want to look at what it creates, not at the factory class.
+ return getTypeForFactoryBean(beanName, mbd, allowFactoryBeanInit).resolve();
+ }
+ else {
+ return beanClass;
+ }
+ }
+ else {
+ return (!BeanFactoryUtils.isFactoryDereference(name) ? beanClass : null);
+ }
+ }
+
+ @Override
+ public String[] getAliases(String name) {
+ String beanName = transformedBeanName(name);
+ List aliases = new ArrayList<>();
+ boolean factoryPrefix = name.startsWith(FACTORY_BEAN_PREFIX);
+ String fullBeanName = beanName;
+ if (factoryPrefix) {
+ fullBeanName = FACTORY_BEAN_PREFIX + beanName;
+ }
+ if (!fullBeanName.equals(name)) {
+ aliases.add(fullBeanName);
+ }
+ String[] retrievedAliases = super.getAliases(beanName);
+ String prefix = factoryPrefix ? FACTORY_BEAN_PREFIX : "";
+ for (String retrievedAlias : retrievedAliases) {
+ String alias = prefix + retrievedAlias;
+ if (!alias.equals(name)) {
+ aliases.add(alias);
+ }
+ }
+ if (!containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
+ BeanFactory parentBeanFactory = getParentBeanFactory();
+ if (parentBeanFactory != null) {
+ aliases.addAll(Arrays.asList(parentBeanFactory.getAliases(fullBeanName)));
+ }
+ }
+ return StringUtils.toStringArray(aliases);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of HierarchicalBeanFactory interface
+ //---------------------------------------------------------------------
+
+ @Override
+ @Nullable
+ public BeanFactory getParentBeanFactory() {
+ return this.parentBeanFactory;
+ }
+
+ @Override
+ public boolean containsLocalBean(String name) {
+ String beanName = transformedBeanName(name);
+ return ((containsSingleton(beanName) || containsBeanDefinition(beanName)) &&
+ (!BeanFactoryUtils.isFactoryDereference(name) || isFactoryBean(beanName)));
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ConfigurableBeanFactory interface
+ //---------------------------------------------------------------------
+
+ @Override
+ public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
+ if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
+ throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
+ }
+ if (this == parentBeanFactory) {
+ throw new IllegalStateException("Cannot set parent bean factory to self");
+ }
+ this.parentBeanFactory = parentBeanFactory;
+ }
+
+ @Override
+ public void setBeanClassLoader(@Nullable ClassLoader beanClassLoader) {
+ this.beanClassLoader = (beanClassLoader != null ? beanClassLoader : ClassUtils.getDefaultClassLoader());
+ }
+
+ @Override
+ @Nullable
+ public ClassLoader getBeanClassLoader() {
+ return this.beanClassLoader;
+ }
+
+ @Override
+ public void setTempClassLoader(@Nullable ClassLoader tempClassLoader) {
+ this.tempClassLoader = tempClassLoader;
+ }
+
+ @Override
+ @Nullable
+ public ClassLoader getTempClassLoader() {
+ return this.tempClassLoader;
+ }
+
+ @Override
+ public void setCacheBeanMetadata(boolean cacheBeanMetadata) {
+ this.cacheBeanMetadata = cacheBeanMetadata;
+ }
+
+ @Override
+ public boolean isCacheBeanMetadata() {
+ return this.cacheBeanMetadata;
+ }
+
+ @Override
+ public void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver) {
+ this.beanExpressionResolver = resolver;
+ }
+
+ @Override
+ @Nullable
+ public BeanExpressionResolver getBeanExpressionResolver() {
+ return this.beanExpressionResolver;
+ }
+
+ @Override
+ public void setConversionService(@Nullable ConversionService conversionService) {
+ this.conversionService = conversionService;
+ }
+
+ @Override
+ @Nullable
+ public ConversionService getConversionService() {
+ return this.conversionService;
+ }
+
+ @Override
+ public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
+ Assert.notNull(registrar, "PropertyEditorRegistrar must not be null");
+ this.propertyEditorRegistrars.add(registrar);
+ }
+
+ /**
+ * Return the set of PropertyEditorRegistrars.
+ */
+ public Set getPropertyEditorRegistrars() {
+ return this.propertyEditorRegistrars;
+ }
+
+ @Override
+ public void registerCustomEditor(Class> requiredType, Class extends PropertyEditor> propertyEditorClass) {
+ Assert.notNull(requiredType, "Required type must not be null");
+ Assert.notNull(propertyEditorClass, "PropertyEditor class must not be null");
+ this.customEditors.put(requiredType, propertyEditorClass);
+ }
+
+ @Override
+ public void copyRegisteredEditorsTo(PropertyEditorRegistry registry) {
+ registerCustomEditors(registry);
+ }
+
+ /**
+ * Return the map of custom editors, with Classes as keys and PropertyEditor classes as values.
+ */
+ public Map, Class extends PropertyEditor>> getCustomEditors() {
+ return this.customEditors;
+ }
+
+ @Override
+ public void setTypeConverter(TypeConverter typeConverter) {
+ this.typeConverter = typeConverter;
+ }
+
+ /**
+ * Return the custom TypeConverter to use, if any.
+ * @return the custom TypeConverter, or {@code null} if none specified
+ */
+ @Nullable
+ protected TypeConverter getCustomTypeConverter() {
+ return this.typeConverter;
+ }
+
+ @Override
+ public TypeConverter getTypeConverter() {
+ TypeConverter customConverter = getCustomTypeConverter();
+ if (customConverter != null) {
+ return customConverter;
+ }
+ else {
+ // Build default TypeConverter, registering custom editors.
+ SimpleTypeConverter typeConverter = new SimpleTypeConverter();
+ typeConverter.setConversionService(getConversionService());
+ registerCustomEditors(typeConverter);
+ return typeConverter;
+ }
+ }
+
+ @Override
+ public void addEmbeddedValueResolver(StringValueResolver valueResolver) {
+ Assert.notNull(valueResolver, "StringValueResolver must not be null");
+ this.embeddedValueResolvers.add(valueResolver);
+ }
+
+ @Override
+ public boolean hasEmbeddedValueResolver() {
+ return !this.embeddedValueResolvers.isEmpty();
+ }
+
+ @Override
+ @Nullable
+ public String resolveEmbeddedValue(@Nullable String value) {
+ if (value == null) {
+ return null;
+ }
+ String result = value;
+ for (StringValueResolver resolver : this.embeddedValueResolvers) {
+ result = resolver.resolveStringValue(result);
+ if (result == null) {
+ return null;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
+ Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
+ // Remove from old position, if any
+ this.beanPostProcessors.remove(beanPostProcessor);
+ // Add to end of list
+ this.beanPostProcessors.add(beanPostProcessor);
+ }
+
+ /**
+ * Add new BeanPostProcessors that will get applied to beans created
+ * by this factory. To be invoked during factory configuration.
+ * @since 5.3
+ * @see #addBeanPostProcessor
+ */
+ public void addBeanPostProcessors(Collection extends BeanPostProcessor> beanPostProcessors) {
+ this.beanPostProcessors.removeAll(beanPostProcessors);
+ this.beanPostProcessors.addAll(beanPostProcessors);
+ }
+
+ @Override
+ public int getBeanPostProcessorCount() {
+ return this.beanPostProcessors.size();
+ }
+
+ /**
+ * Return the list of BeanPostProcessors that will get applied
+ * to beans created with this factory.
+ */
+ public List getBeanPostProcessors() {
+ return this.beanPostProcessors;
+ }
+
+ /**
+ * Return the internal cache of pre-filtered post-processors,
+ * freshly (re-)building it if necessary.
+ * @since 5.3
+ */
+ BeanPostProcessorCache getBeanPostProcessorCache() {
+ BeanPostProcessorCache bpCache = this.beanPostProcessorCache;
+ if (bpCache == null) {
+ bpCache = new BeanPostProcessorCache();
+ for (BeanPostProcessor bp : this.beanPostProcessors) {
+ if (bp instanceof InstantiationAwareBeanPostProcessor) {
+ bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);
+ if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
+ bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);
+ }
+ }
+ if (bp instanceof DestructionAwareBeanPostProcessor) {
+ bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);
+ }
+ if (bp instanceof MergedBeanDefinitionPostProcessor) {
+ bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);
+ }
+ }
+ this.beanPostProcessorCache = bpCache;
+ }
+ return bpCache;
+ }
+
+ /**
+ * Return whether this factory holds a InstantiationAwareBeanPostProcessor
+ * that will get applied to singleton beans on creation.
+ * @see #addBeanPostProcessor
+ * @see org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor
+ */
+ protected boolean hasInstantiationAwareBeanPostProcessors() {
+ return !getBeanPostProcessorCache().instantiationAware.isEmpty();
+ }
+
+ /**
+ * Return whether this factory holds a DestructionAwareBeanPostProcessor
+ * that will get applied to singleton beans on shutdown.
+ * @see #addBeanPostProcessor
+ * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
+ */
+ protected boolean hasDestructionAwareBeanPostProcessors() {
+ return !getBeanPostProcessorCache().destructionAware.isEmpty();
+ }
+
+ @Override
+ public void registerScope(String scopeName, Scope scope) {
+ Assert.notNull(scopeName, "Scope identifier must not be null");
+ Assert.notNull(scope, "Scope must not be null");
+ if (SCOPE_SINGLETON.equals(scopeName) || SCOPE_PROTOTYPE.equals(scopeName)) {
+ throw new IllegalArgumentException("Cannot replace existing scopes 'singleton' and 'prototype'");
+ }
+ Scope previous = this.scopes.put(scopeName, scope);
+ if (previous != null && previous != scope) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Replacing scope '" + scopeName + "' from [" + previous + "] to [" + scope + "]");
+ }
+ }
+ else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Registering scope '" + scopeName + "' with implementation [" + scope + "]");
+ }
+ }
+ }
+
+ @Override
+ public String[] getRegisteredScopeNames() {
+ return StringUtils.toStringArray(this.scopes.keySet());
+ }
+
+ @Override
+ @Nullable
+ public Scope getRegisteredScope(String scopeName) {
+ Assert.notNull(scopeName, "Scope identifier must not be null");
+ return this.scopes.get(scopeName);
+ }
+
+ /**
+ * Set the security context provider for this bean factory. If a security manager
+ * is set, interaction with the user code will be executed using the privileged
+ * of the provided security context.
+ */
+ public void setSecurityContextProvider(SecurityContextProvider securityProvider) {
+ this.securityContextProvider = securityProvider;
+ }
+
+ @Override
+ public void setApplicationStartup(ApplicationStartup applicationStartup) {
+ Assert.notNull(applicationStartup, "applicationStartup should not be null");
+ this.applicationStartup = applicationStartup;
+ }
+
+ @Override
+ public ApplicationStartup getApplicationStartup() {
+ return this.applicationStartup;
+ }
+
+ /**
+ * Delegate the creation of the access control context to the
+ * {@link #setSecurityContextProvider SecurityContextProvider}.
+ */
+ @Override
+ public AccessControlContext getAccessControlContext() {
+ return (this.securityContextProvider != null ?
+ this.securityContextProvider.getAccessControlContext() :
+ AccessController.getContext());
+ }
+
+ @Override
+ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
+ Assert.notNull(otherFactory, "BeanFactory must not be null");
+ setBeanClassLoader(otherFactory.getBeanClassLoader());
+ setCacheBeanMetadata(otherFactory.isCacheBeanMetadata());
+ setBeanExpressionResolver(otherFactory.getBeanExpressionResolver());
+ setConversionService(otherFactory.getConversionService());
+ if (otherFactory instanceof AbstractBeanFactory) {
+ AbstractBeanFactory otherAbstractFactory = (AbstractBeanFactory) otherFactory;
+ this.propertyEditorRegistrars.addAll(otherAbstractFactory.propertyEditorRegistrars);
+ this.customEditors.putAll(otherAbstractFactory.customEditors);
+ this.typeConverter = otherAbstractFactory.typeConverter;
+ this.beanPostProcessors.addAll(otherAbstractFactory.beanPostProcessors);
+ this.scopes.putAll(otherAbstractFactory.scopes);
+ this.securityContextProvider = otherAbstractFactory.securityContextProvider;
+ }
+ else {
+ setTypeConverter(otherFactory.getTypeConverter());
+ String[] otherScopeNames = otherFactory.getRegisteredScopeNames();
+ for (String scopeName : otherScopeNames) {
+ this.scopes.put(scopeName, otherFactory.getRegisteredScope(scopeName));
+ }
+ }
+ }
+
+ /**
+ * Return a 'merged' BeanDefinition for the given bean name,
+ * merging a child bean definition with its parent if necessary.
+ * This {@code getMergedBeanDefinition} considers bean definition
+ * in ancestors as well.
+ * @param name the name of the bean to retrieve the merged definition for
+ * (may be an alias)
+ * @return a (potentially merged) RootBeanDefinition for the given bean
+ * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+ * @throws BeanDefinitionStoreException in case of an invalid bean definition
+ */
+ @Override
+ public BeanDefinition getMergedBeanDefinition(String name) throws BeansException {
+ String beanName = transformedBeanName(name);
+ // Efficiently check whether bean definition exists in this factory.
+ if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
+ return ((ConfigurableBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName);
+ }
+ // Resolve merged bean definition locally.
+ return getMergedLocalBeanDefinition(beanName);
+ }
+
+ @Override
+ public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
+ String beanName = transformedBeanName(name);
+ Object beanInstance = getSingleton(beanName, false);
+ if (beanInstance != null) {
+ return (beanInstance instanceof FactoryBean);
+ }
+ // No singleton instance found -> check bean definition.
+ if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
+ // No bean definition found in this factory -> delegate to parent.
+ return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
+ }
+ return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
+ }
+
+ @Override
+ public boolean isActuallyInCreation(String beanName) {
+ return (isSingletonCurrentlyInCreation(beanName) || isPrototypeCurrentlyInCreation(beanName));
+ }
+
+ /**
+ * Return whether the specified prototype bean is currently in creation
+ * (within the current thread).
+ * @param beanName the name of the bean
+ */
+ protected boolean isPrototypeCurrentlyInCreation(String beanName) {
+ Object curVal = this.prototypesCurrentlyInCreation.get();
+ return (curVal != null &&
+ (curVal.equals(beanName) || (curVal instanceof Set && ((Set>) curVal).contains(beanName))));
+ }
+
+ /**
+ * Callback before prototype creation.
+ *
The default implementation register the prototype as currently in creation.
+ * @param beanName the name of the prototype about to be created
+ * @see #isPrototypeCurrentlyInCreation
+ */
+ @SuppressWarnings("unchecked")
+ protected void beforePrototypeCreation(String beanName) {
+ Object curVal = this.prototypesCurrentlyInCreation.get();
+ if (curVal == null) {
+ this.prototypesCurrentlyInCreation.set(beanName);
+ }
+ else if (curVal instanceof String) {
+ Set beanNameSet = new HashSet<>(2);
+ beanNameSet.add((String) curVal);
+ beanNameSet.add(beanName);
+ this.prototypesCurrentlyInCreation.set(beanNameSet);
+ }
+ else {
+ Set beanNameSet = (Set) curVal;
+ beanNameSet.add(beanName);
+ }
+ }
+
+ /**
+ * Callback after prototype creation.
+ * The default implementation marks the prototype as not in creation anymore.
+ * @param beanName the name of the prototype that has been created
+ * @see #isPrototypeCurrentlyInCreation
+ */
+ @SuppressWarnings("unchecked")
+ protected void afterPrototypeCreation(String beanName) {
+ Object curVal = this.prototypesCurrentlyInCreation.get();
+ if (curVal instanceof String) {
+ this.prototypesCurrentlyInCreation.remove();
+ }
+ else if (curVal instanceof Set) {
+ Set beanNameSet = (Set) curVal;
+ beanNameSet.remove(beanName);
+ if (beanNameSet.isEmpty()) {
+ this.prototypesCurrentlyInCreation.remove();
+ }
+ }
+ }
+
+ @Override
+ public void destroyBean(String beanName, Object beanInstance) {
+ destroyBean(beanName, beanInstance, getMergedLocalBeanDefinition(beanName));
+ }
+
+ /**
+ * Destroy the given bean instance (usually a prototype instance
+ * obtained from this factory) according to the given bean definition.
+ * @param beanName the name of the bean definition
+ * @param bean the bean instance to destroy
+ * @param mbd the merged bean definition
+ */
+ protected void destroyBean(String beanName, Object bean, RootBeanDefinition mbd) {
+ new DisposableBeanAdapter(
+ bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, getAccessControlContext()).destroy();
+ }
+
+ @Override
+ public void destroyScopedBean(String beanName) {
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ if (mbd.isSingleton() || mbd.isPrototype()) {
+ throw new IllegalArgumentException(
+ "Bean name '" + beanName + "' does not correspond to an object in a mutable scope");
+ }
+ String scopeName = mbd.getScope();
+ Scope scope = this.scopes.get(scopeName);
+ if (scope == null) {
+ throw new IllegalStateException("No Scope SPI registered for scope name '" + scopeName + "'");
+ }
+ Object bean = scope.remove(beanName);
+ if (bean != null) {
+ destroyBean(beanName, bean, mbd);
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation methods
+ //---------------------------------------------------------------------
+
+ /**
+ * Return the bean name, stripping out the factory dereference prefix if necessary,
+ * and resolving aliases to canonical names.
+ * @param name the user-specified name
+ * @return the transformed bean name
+ */
+ protected String transformedBeanName(String name) {
+ return canonicalName(BeanFactoryUtils.transformedBeanName(name));
+ }
+
+ /**
+ * Determine the original bean name, resolving locally defined aliases to canonical names.
+ * @param name the user-specified name
+ * @return the original bean name
+ */
+ protected String originalBeanName(String name) {
+ String beanName = transformedBeanName(name);
+ if (name.startsWith(FACTORY_BEAN_PREFIX)) {
+ beanName = FACTORY_BEAN_PREFIX + beanName;
+ }
+ return beanName;
+ }
+
+ /**
+ * Initialize the given BeanWrapper with the custom editors registered
+ * with this factory. To be called for BeanWrappers that will create
+ * and populate bean instances.
+ * The default implementation delegates to {@link #registerCustomEditors}.
+ * Can be overridden in subclasses.
+ * @param bw the BeanWrapper to initialize
+ */
+ protected void initBeanWrapper(BeanWrapper bw) {
+ bw.setConversionService(getConversionService());
+ registerCustomEditors(bw);
+ }
+
+ /**
+ * Initialize the given PropertyEditorRegistry with the custom editors
+ * that have been registered with this BeanFactory.
+ *
To be called for BeanWrappers that will create and populate bean
+ * instances, and for SimpleTypeConverter used for constructor argument
+ * and factory method type conversion.
+ * @param registry the PropertyEditorRegistry to initialize
+ */
+ protected void registerCustomEditors(PropertyEditorRegistry registry) {
+ if (registry instanceof PropertyEditorRegistrySupport) {
+ ((PropertyEditorRegistrySupport) registry).useConfigValueEditors();
+ }
+ if (!this.propertyEditorRegistrars.isEmpty()) {
+ for (PropertyEditorRegistrar registrar : this.propertyEditorRegistrars) {
+ try {
+ registrar.registerCustomEditors(registry);
+ }
+ catch (BeanCreationException ex) {
+ Throwable rootCause = ex.getMostSpecificCause();
+ if (rootCause instanceof BeanCurrentlyInCreationException) {
+ BeanCreationException bce = (BeanCreationException) rootCause;
+ String bceBeanName = bce.getBeanName();
+ if (bceBeanName != null && isCurrentlyInCreation(bceBeanName)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("PropertyEditorRegistrar [" + registrar.getClass().getName() +
+ "] failed because it tried to obtain currently created bean '" +
+ ex.getBeanName() + "': " + ex.getMessage());
+ }
+ onSuppressedException(ex);
+ continue;
+ }
+ }
+ throw ex;
+ }
+ }
+ }
+ if (!this.customEditors.isEmpty()) {
+ this.customEditors.forEach((requiredType, editorClass) ->
+ registry.registerCustomEditor(requiredType, BeanUtils.instantiateClass(editorClass)));
+ }
+ }
+
+
+ /**
+ * Return a merged RootBeanDefinition, traversing the parent bean definition
+ * if the specified bean corresponds to a child bean definition.
+ * @param beanName the name of the bean to retrieve the merged definition for
+ * @return a (potentially merged) RootBeanDefinition for the given bean
+ * @throws NoSuchBeanDefinitionException if there is no bean with the given name
+ * @throws BeanDefinitionStoreException in case of an invalid bean definition
+ */
+ protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
+ // Quick check on the concurrent map first, with minimal locking.
+ RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
+ if (mbd != null && !mbd.stale) {
+ return mbd;
+ }
+ return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
+ }
+
+ /**
+ * Return a RootBeanDefinition for the given top-level bean, by merging with
+ * the parent if the given bean's definition is a child bean definition.
+ * @param beanName the name of the bean definition
+ * @param bd the original bean definition (Root/ChildBeanDefinition)
+ * @return a (potentially merged) RootBeanDefinition for the given bean
+ * @throws BeanDefinitionStoreException in case of an invalid bean definition
+ */
+ protected RootBeanDefinition getMergedBeanDefinition(String beanName, BeanDefinition bd)
+ throws BeanDefinitionStoreException {
+
+ return getMergedBeanDefinition(beanName, bd, null);
+ }
+
+ /**
+ * Return a RootBeanDefinition for the given bean, by merging with the
+ * parent if the given bean's definition is a child bean definition.
+ * @param beanName the name of the bean definition
+ * @param bd the original bean definition (Root/ChildBeanDefinition)
+ * @param containingBd the containing bean definition in case of inner bean,
+ * or {@code null} in case of a top-level bean
+ * @return a (potentially merged) RootBeanDefinition for the given bean
+ * @throws BeanDefinitionStoreException in case of an invalid bean definition
+ */
+ protected RootBeanDefinition getMergedBeanDefinition(
+ String beanName, BeanDefinition bd, @Nullable BeanDefinition containingBd)
+ throws BeanDefinitionStoreException {
+
+ synchronized (this.mergedBeanDefinitions) {
+ RootBeanDefinition mbd = null;
+ RootBeanDefinition previous = null;
+
+ // Check with full lock now in order to enforce the same merged instance.
+ if (containingBd == null) {
+ mbd = this.mergedBeanDefinitions.get(beanName);
+ }
+
+ if (mbd == null || mbd.stale) {
+ previous = mbd;
+ if (bd.getParentName() == null) {
+ // Use copy of given root bean definition.
+ if (bd instanceof RootBeanDefinition) {
+ mbd = ((RootBeanDefinition) bd).cloneBeanDefinition();
+ }
+ else {
+ mbd = new RootBeanDefinition(bd);
+ }
+ }
+ else {
+ // Child bean definition: needs to be merged with parent.
+ BeanDefinition pbd;
+ try {
+ String parentBeanName = transformedBeanName(bd.getParentName());
+ if (!beanName.equals(parentBeanName)) {
+ pbd = getMergedBeanDefinition(parentBeanName);
+ }
+ else {
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof ConfigurableBeanFactory) {
+ pbd = ((ConfigurableBeanFactory) parent).getMergedBeanDefinition(parentBeanName);
+ }
+ else {
+ throw new NoSuchBeanDefinitionException(parentBeanName,
+ "Parent name '" + parentBeanName + "' is equal to bean name '" + beanName +
+ "': cannot be resolved without a ConfigurableBeanFactory parent");
+ }
+ }
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanName,
+ "Could not resolve parent bean definition '" + bd.getParentName() + "'", ex);
+ }
+ // Deep copy with overridden values.
+ mbd = new RootBeanDefinition(pbd);
+ mbd.overrideFrom(bd);
+ }
+
+ // Set default singleton scope, if not configured before.
+ if (!StringUtils.hasLength(mbd.getScope())) {
+ mbd.setScope(SCOPE_SINGLETON);
+ }
+
+ // A bean contained in a non-singleton bean cannot be a singleton itself.
+ // Let's correct this on the fly here, since this might be the result of
+ // parent-child merging for the outer bean, in which case the original inner bean
+ // definition will not have inherited the merged outer bean's singleton status.
+ if (containingBd != null && !containingBd.isSingleton() && mbd.isSingleton()) {
+ mbd.setScope(containingBd.getScope());
+ }
+
+ // Cache the merged bean definition for the time being
+ // (it might still get re-merged later on in order to pick up metadata changes)
+ if (containingBd == null && isCacheBeanMetadata()) {
+ this.mergedBeanDefinitions.put(beanName, mbd);
+ }
+ }
+ if (previous != null) {
+ copyRelevantMergedBeanDefinitionCaches(previous, mbd);
+ }
+ return mbd;
+ }
+ }
+
+ private void copyRelevantMergedBeanDefinitionCaches(RootBeanDefinition previous, RootBeanDefinition mbd) {
+ if (ObjectUtils.nullSafeEquals(mbd.getBeanClassName(), previous.getBeanClassName()) &&
+ ObjectUtils.nullSafeEquals(mbd.getFactoryBeanName(), previous.getFactoryBeanName()) &&
+ ObjectUtils.nullSafeEquals(mbd.getFactoryMethodName(), previous.getFactoryMethodName())) {
+ ResolvableType targetType = mbd.targetType;
+ ResolvableType previousTargetType = previous.targetType;
+ if (targetType == null || targetType.equals(previousTargetType)) {
+ mbd.targetType = previousTargetType;
+ mbd.isFactoryBean = previous.isFactoryBean;
+ mbd.resolvedTargetType = previous.resolvedTargetType;
+ mbd.factoryMethodReturnType = previous.factoryMethodReturnType;
+ mbd.factoryMethodToIntrospect = previous.factoryMethodToIntrospect;
+ }
+ }
+ }
+
+ /**
+ * Check the given merged bean definition,
+ * potentially throwing validation exceptions.
+ * @param mbd the merged bean definition to check
+ * @param beanName the name of the bean
+ * @param args the arguments for bean creation, if any
+ * @throws BeanDefinitionStoreException in case of validation failure
+ */
+ protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
+ throws BeanDefinitionStoreException {
+
+ if (mbd.isAbstract()) {
+ throw new BeanIsAbstractException(beanName);
+ }
+ }
+
+ /**
+ * Remove the merged bean definition for the specified bean,
+ * recreating it on next access.
+ * @param beanName the bean name to clear the merged definition for
+ */
+ protected void clearMergedBeanDefinition(String beanName) {
+ RootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
+ if (bd != null) {
+ bd.stale = true;
+ }
+ }
+
+ /**
+ * Clear the merged bean definition cache, removing entries for beans
+ * which are not considered eligible for full metadata caching yet.
+ *
Typically triggered after changes to the original bean definitions,
+ * e.g. after applying a {@code BeanFactoryPostProcessor}. Note that metadata
+ * for beans which have already been created at this point will be kept around.
+ * @since 4.2
+ */
+ public void clearMetadataCache() {
+ this.mergedBeanDefinitions.forEach((beanName, bd) -> {
+ if (!isBeanEligibleForMetadataCaching(beanName)) {
+ bd.stale = true;
+ }
+ });
+ }
+
+ /**
+ * Resolve the bean class for the specified bean definition,
+ * resolving a bean class name into a Class reference (if necessary)
+ * and storing the resolved Class in the bean definition for further use.
+ * @param mbd the merged bean definition to determine the class for
+ * @param beanName the name of the bean (for error handling purposes)
+ * @param typesToMatch the types to match in case of internal type matching purposes
+ * (also signals that the returned {@code Class} will never be exposed to application code)
+ * @return the resolved bean class (or {@code null} if none)
+ * @throws CannotLoadBeanClassException if we failed to load the class
+ */
+ @Nullable
+ protected Class> resolveBeanClass(RootBeanDefinition mbd, String beanName, Class>... typesToMatch)
+ throws CannotLoadBeanClassException {
+
+ try {
+ if (mbd.hasBeanClass()) {
+ return mbd.getBeanClass();
+ }
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged((PrivilegedExceptionAction>)
+ () -> doResolveBeanClass(mbd, typesToMatch), getAccessControlContext());
+ }
+ else {
+ return doResolveBeanClass(mbd, typesToMatch);
+ }
+ }
+ catch (PrivilegedActionException pae) {
+ ClassNotFoundException ex = (ClassNotFoundException) pae.getException();
+ throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), ex);
+ }
+ catch (LinkageError err) {
+ throw new CannotLoadBeanClassException(mbd.getResourceDescription(), beanName, mbd.getBeanClassName(), err);
+ }
+ }
+
+ @Nullable
+ private Class> doResolveBeanClass(RootBeanDefinition mbd, Class>... typesToMatch)
+ throws ClassNotFoundException {
+
+ ClassLoader beanClassLoader = getBeanClassLoader();
+ ClassLoader dynamicLoader = beanClassLoader;
+ boolean freshResolve = false;
+
+ if (!ObjectUtils.isEmpty(typesToMatch)) {
+ // When just doing type checks (i.e. not creating an actual instance yet),
+ // use the specified temporary class loader (e.g. in a weaving scenario).
+ ClassLoader tempClassLoader = getTempClassLoader();
+ if (tempClassLoader != null) {
+ dynamicLoader = tempClassLoader;
+ freshResolve = true;
+ if (tempClassLoader instanceof DecoratingClassLoader) {
+ DecoratingClassLoader dcl = (DecoratingClassLoader) tempClassLoader;
+ for (Class> typeToMatch : typesToMatch) {
+ dcl.excludeClass(typeToMatch.getName());
+ }
+ }
+ }
+ }
+
+ String className = mbd.getBeanClassName();
+ if (className != null) {
+ Object evaluated = evaluateBeanDefinitionString(className, mbd);
+ if (!className.equals(evaluated)) {
+ // A dynamically resolved expression, supported as of 4.2...
+ if (evaluated instanceof Class) {
+ return (Class>) evaluated;
+ }
+ else if (evaluated instanceof String) {
+ className = (String) evaluated;
+ freshResolve = true;
+ }
+ else {
+ throw new IllegalStateException("Invalid class name expression result: " + evaluated);
+ }
+ }
+ if (freshResolve) {
+ // When resolving against a temporary class loader, exit early in order
+ // to avoid storing the resolved Class in the bean definition.
+ if (dynamicLoader != null) {
+ try {
+ return dynamicLoader.loadClass(className);
+ }
+ catch (ClassNotFoundException ex) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Could not load class [" + className + "] from " + dynamicLoader + ": " + ex);
+ }
+ }
+ }
+ return ClassUtils.forName(className, dynamicLoader);
+ }
+ }
+
+ // Resolve regularly, caching the result in the BeanDefinition...
+ return mbd.resolveBeanClass(beanClassLoader);
+ }
+
+ /**
+ * Evaluate the given String as contained in a bean definition,
+ * potentially resolving it as an expression.
+ * @param value the value to check
+ * @param beanDefinition the bean definition that the value comes from
+ * @return the resolved value
+ * @see #setBeanExpressionResolver
+ */
+ @Nullable
+ protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
+ if (this.beanExpressionResolver == null) {
+ return value;
+ }
+
+ Scope scope = null;
+ if (beanDefinition != null) {
+ String scopeName = beanDefinition.getScope();
+ if (scopeName != null) {
+ scope = getRegisteredScope(scopeName);
+ }
+ }
+ return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
+ }
+
+
+ /**
+ * Predict the eventual bean type (of the processed bean instance) for the
+ * specified bean. Called by {@link #getType} and {@link #isTypeMatch}.
+ * Does not need to handle FactoryBeans specifically, since it is only
+ * supposed to operate on the raw bean type.
+ * This implementation is simplistic in that it is not able to
+ * handle factory methods and InstantiationAwareBeanPostProcessors.
+ * It only predicts the bean type correctly for a standard bean.
+ * To be overridden in subclasses, applying more sophisticated type detection.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition to determine the type for
+ * @param typesToMatch the types to match in case of internal type matching purposes
+ * (also signals that the returned {@code Class} will never be exposed to application code)
+ * @return the type of the bean, or {@code null} if not predictable
+ */
+ @Nullable
+ protected Class> predictBeanType(String beanName, RootBeanDefinition mbd, Class>... typesToMatch) {
+ Class> targetType = mbd.getTargetType();
+ if (targetType != null) {
+ return targetType;
+ }
+ if (mbd.getFactoryMethodName() != null) {
+ return null;
+ }
+ return resolveBeanClass(mbd, beanName, typesToMatch);
+ }
+
+ /**
+ * Check whether the given bean is defined as a {@link FactoryBean}.
+ * @param beanName the name of the bean
+ * @param mbd the corresponding bean definition
+ */
+ protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
+ Boolean result = mbd.isFactoryBean;
+ if (result == null) {
+ Class> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
+ result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
+ mbd.isFactoryBean = result;
+ }
+ return result;
+ }
+
+ /**
+ * Determine the bean type for the given FactoryBean definition, as far as possible.
+ * Only called if there is no singleton instance registered for the target bean
+ * already. The implementation is allowed to instantiate the target factory bean if
+ * {@code allowInit} is {@code true} and the type cannot be determined another way;
+ * otherwise it is restricted to introspecting signatures and related metadata.
+ *
If no {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} if set on the bean definition
+ * and {@code allowInit} is {@code true}, the default implementation will create
+ * the FactoryBean via {@code getBean} to call its {@code getObjectType} method.
+ * Subclasses are encouraged to optimize this, typically by inspecting the generic
+ * signature of the factory bean class or the factory method that creates it.
+ * If subclasses do instantiate the FactoryBean, they should consider trying the
+ * {@code getObjectType} method without fully populating the bean. If this fails,
+ * a full FactoryBean creation as performed by this implementation should be used
+ * as fallback.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @param allowInit if initialization of the FactoryBean is permitted if the type
+ * cannot be determined another way
+ * @return the type for the bean if determinable, otherwise {@code ResolvableType.NONE}
+ * @since 5.2
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ * @see #getBean(String)
+ */
+ protected ResolvableType getTypeForFactoryBean(String beanName, RootBeanDefinition mbd, boolean allowInit) {
+ ResolvableType result = getTypeForFactoryBeanFromAttributes(mbd);
+ if (result != ResolvableType.NONE) {
+ return result;
+ }
+
+ if (allowInit && mbd.isSingleton()) {
+ try {
+ FactoryBean> factoryBean = doGetBean(FACTORY_BEAN_PREFIX + beanName, FactoryBean.class, null, true);
+ Class> objectType = getTypeForFactoryBean(factoryBean);
+ return (objectType != null ? ResolvableType.forClass(objectType) : ResolvableType.NONE);
+ }
+ catch (BeanCreationException ex) {
+ if (ex.contains(BeanCurrentlyInCreationException.class)) {
+ logger.trace(LogMessage.format("Bean currently in creation on FactoryBean type check: %s", ex));
+ }
+ else if (mbd.isLazyInit()) {
+ logger.trace(LogMessage.format("Bean creation exception on lazy FactoryBean type check: %s", ex));
+ }
+ else {
+ logger.debug(LogMessage.format("Bean creation exception on eager FactoryBean type check: %s", ex));
+ }
+ onSuppressedException(ex);
+ }
+ }
+ return ResolvableType.NONE;
+ }
+
+ /**
+ * Determine the bean type for a FactoryBean by inspecting its attributes for a
+ * {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE} value.
+ * @param attributes the attributes to inspect
+ * @return a {@link ResolvableType} extracted from the attributes or
+ * {@code ResolvableType.NONE}
+ * @since 5.2
+ */
+ ResolvableType getTypeForFactoryBeanFromAttributes(AttributeAccessor attributes) {
+ Object attribute = attributes.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE);
+ if (attribute instanceof ResolvableType) {
+ return (ResolvableType) attribute;
+ }
+ if (attribute instanceof Class) {
+ return ResolvableType.forClass((Class>) attribute);
+ }
+ return ResolvableType.NONE;
+ }
+
+ /**
+ * Determine the bean type for the given FactoryBean definition, as far as possible.
+ * Only called if there is no singleton instance registered for the target bean already.
+ *
The default implementation creates the FactoryBean via {@code getBean}
+ * to call its {@code getObjectType} method. Subclasses are encouraged to optimize
+ * this, typically by just instantiating the FactoryBean but not populating it yet,
+ * trying whether its {@code getObjectType} method already returns a type.
+ * If no type found, a full FactoryBean creation as performed by this implementation
+ * should be used as fallback.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @return the type for the bean if determinable, or {@code null} otherwise
+ * @see org.springframework.beans.factory.FactoryBean#getObjectType()
+ * @see #getBean(String)
+ * @deprecated since 5.2 in favor of {@link #getTypeForFactoryBean(String, RootBeanDefinition, boolean)}
+ */
+ @Nullable
+ @Deprecated
+ protected Class> getTypeForFactoryBean(String beanName, RootBeanDefinition mbd) {
+ return getTypeForFactoryBean(beanName, mbd, true).resolve();
+ }
+
+ /**
+ * Mark the specified bean as already created (or about to be created).
+ *
This allows the bean factory to optimize its caching for repeated
+ * creation of the specified bean.
+ * @param beanName the name of the bean
+ */
+ protected void markBeanAsCreated(String beanName) {
+ if (!this.alreadyCreated.contains(beanName)) {
+ synchronized (this.mergedBeanDefinitions) {
+ if (!this.alreadyCreated.contains(beanName)) {
+ // Let the bean definition get re-merged now that we're actually creating
+ // the bean... just in case some of its metadata changed in the meantime.
+ clearMergedBeanDefinition(beanName);
+ this.alreadyCreated.add(beanName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Perform appropriate cleanup of cached metadata after bean creation failed.
+ * @param beanName the name of the bean
+ */
+ protected void cleanupAfterBeanCreationFailure(String beanName) {
+ synchronized (this.mergedBeanDefinitions) {
+ this.alreadyCreated.remove(beanName);
+ }
+ }
+
+ /**
+ * Determine whether the specified bean is eligible for having
+ * its bean definition metadata cached.
+ * @param beanName the name of the bean
+ * @return {@code true} if the bean's metadata may be cached
+ * at this point already
+ */
+ protected boolean isBeanEligibleForMetadataCaching(String beanName) {
+ return this.alreadyCreated.contains(beanName);
+ }
+
+ /**
+ * Remove the singleton instance (if any) for the given bean name,
+ * but only if it hasn't been used for other purposes than type checking.
+ * @param beanName the name of the bean
+ * @return {@code true} if actually removed, {@code false} otherwise
+ */
+ protected boolean removeSingletonIfCreatedForTypeCheckOnly(String beanName) {
+ if (!this.alreadyCreated.contains(beanName)) {
+ removeSingleton(beanName);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Check whether this factory's bean creation phase already started,
+ * i.e. whether any bean has been marked as created in the meantime.
+ * @since 4.2.2
+ * @see #markBeanAsCreated
+ */
+ protected boolean hasBeanCreationStarted() {
+ return !this.alreadyCreated.isEmpty();
+ }
+
+ /**
+ * Get the object for the given bean instance, either the bean
+ * instance itself or its created object in case of a FactoryBean.
+ * @param beanInstance the shared bean instance
+ * @param name the name that may include factory dereference prefix
+ * @param beanName the canonical bean name
+ * @param mbd the merged bean definition
+ * @return the object to expose for the bean
+ */
+ protected Object getObjectForBeanInstance(
+ 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)) {
+ if (beanInstance instanceof NullBean) {
+ return beanInstance;
+ }
+ if (!(beanInstance instanceof FactoryBean)) {
+ throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
+ }
+ if (mbd != null) {
+ mbd.isFactoryBean = true;
+ }
+ return beanInstance;
+ }
+
+ // Now we have the bean instance, which may be a normal bean or a FactoryBean.
+ // If it's a FactoryBean, we use it to create a bean instance, unless the
+ // caller actually wants a reference to the factory.
+ if (!(beanInstance instanceof FactoryBean)) {
+ return beanInstance;
+ }
+
+ Object object = null;
+ if (mbd != null) {
+ mbd.isFactoryBean = true;
+ }
+ else {
+ object = getCachedObjectForFactoryBean(beanName);
+ }
+ if (object == null) {
+ // Return bean instance from factory.
+ FactoryBean> factory = (FactoryBean>) beanInstance;
+ // Caches object obtained from FactoryBean if it is a singleton.
+ if (mbd == null && containsBeanDefinition(beanName)) {
+ mbd = getMergedLocalBeanDefinition(beanName);
+ }
+ boolean synthetic = (mbd != null && mbd.isSynthetic());
+ object = getObjectFromFactoryBean(factory, beanName, !synthetic);
+ }
+ return object;
+ }
+
+ /**
+ * Determine whether the given bean name is already in use within this factory,
+ * i.e. whether there is a local bean or alias registered under this name or
+ * an inner bean created with this name.
+ * @param beanName the name to check
+ */
+ public boolean isBeanNameInUse(String beanName) {
+ return isAlias(beanName) || containsLocalBean(beanName) || hasDependentBean(beanName);
+ }
+
+ /**
+ * Determine whether the given bean requires destruction on shutdown.
+ *
The default implementation checks the DisposableBean interface as well as
+ * a specified destroy method and registered DestructionAwareBeanPostProcessors.
+ * @param bean the bean instance to check
+ * @param mbd the corresponding bean definition
+ * @see org.springframework.beans.factory.DisposableBean
+ * @see AbstractBeanDefinition#getDestroyMethodName()
+ * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
+ */
+ protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
+ return (bean.getClass() != NullBean.class && (DisposableBeanAdapter.hasDestroyMethod(bean, mbd) ||
+ (hasDestructionAwareBeanPostProcessors() && DisposableBeanAdapter.hasApplicableProcessors(
+ bean, getBeanPostProcessorCache().destructionAware))));
+ }
+
+ /**
+ * Add the given bean to the list of disposable beans in this factory,
+ * registering its DisposableBean interface and/or the given destroy method
+ * to be called on factory shutdown (if applicable). Only applies to singletons.
+ * @param beanName the name of the bean
+ * @param bean the bean instance
+ * @param mbd the bean definition for the bean
+ * @see RootBeanDefinition#isSingleton
+ * @see RootBeanDefinition#getDependsOn
+ * @see #registerDisposableBean
+ * @see #registerDependentBean
+ */
+ protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
+ AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
+ if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
+ if (mbd.isSingleton()) {
+ // Register a DisposableBean implementation that performs all destruction
+ // work for the given bean: DestructionAwareBeanPostProcessors,
+ // DisposableBean interface, custom destroy method.
+ registerDisposableBean(beanName, new DisposableBeanAdapter(
+ bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
+ }
+ else {
+ // A bean with a custom scope...
+ Scope scope = this.scopes.get(mbd.getScope());
+ if (scope == null) {
+ throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
+ }
+ scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(
+ bean, beanName, mbd, getBeanPostProcessorCache().destructionAware, acc));
+ }
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Abstract methods to be implemented by subclasses
+ //---------------------------------------------------------------------
+
+ /**
+ * Check if this bean factory contains a bean definition with the given name.
+ * Does not consider any hierarchy this factory may participate in.
+ * Invoked by {@code containsBean} when no cached singleton instance is found.
+ *
Depending on the nature of the concrete bean factory implementation,
+ * this operation might be expensive (for example, because of directory lookups
+ * in external registries). However, for listable bean factories, this usually
+ * just amounts to a local hash lookup: The operation is therefore part of the
+ * public interface there. The same implementation can serve for both this
+ * template method and the public interface method in that case.
+ * @param beanName the name of the bean to look for
+ * @return if this bean factory contains a bean definition with the given name
+ * @see #containsBean
+ * @see org.springframework.beans.factory.ListableBeanFactory#containsBeanDefinition
+ */
+ protected abstract boolean containsBeanDefinition(String beanName);
+
+ /**
+ * Return the bean definition for the given bean name.
+ * Subclasses should normally implement caching, as this method is invoked
+ * by this class every time bean definition metadata is needed.
+ *
Depending on the nature of the concrete bean factory implementation,
+ * this operation might be expensive (for example, because of directory lookups
+ * in external registries). However, for listable bean factories, this usually
+ * just amounts to a local hash lookup: The operation is therefore part of the
+ * public interface there. The same implementation can serve for both this
+ * template method and the public interface method in that case.
+ * @param beanName the name of the bean to find a definition for
+ * @return the BeanDefinition for this prototype name (never {@code null})
+ * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
+ * if the bean definition cannot be resolved
+ * @throws BeansException in case of errors
+ * @see RootBeanDefinition
+ * @see ChildBeanDefinition
+ * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#getBeanDefinition
+ */
+ protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;
+
+ /**
+ * Create a bean instance for the given merged bean definition (and arguments).
+ * The bean definition will already have been merged with the parent definition
+ * in case of a child definition.
+ *
All bean retrieval methods delegate to this method for actual bean creation.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @param args explicit arguments to use for constructor or factory method invocation
+ * @return a new instance of the bean
+ * @throws BeanCreationException if the bean could not be created
+ */
+ protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
+ throws BeanCreationException;
+
+
+ /**
+ * CopyOnWriteArrayList which resets the beanPostProcessorCache field on modification.
+ *
+ * @since 5.3
+ */
+ private class BeanPostProcessorCacheAwareList extends CopyOnWriteArrayList {
+
+ @Override
+ public BeanPostProcessor set(int index, BeanPostProcessor element) {
+ BeanPostProcessor result = super.set(index, element);
+ beanPostProcessorCache = null;
+ return result;
+ }
+
+ @Override
+ public boolean add(BeanPostProcessor o) {
+ boolean success = super.add(o);
+ beanPostProcessorCache = null;
+ return success;
+ }
+
+ @Override
+ public void add(int index, BeanPostProcessor element) {
+ super.add(index, element);
+ beanPostProcessorCache = null;
+ }
+
+ @Override
+ public BeanPostProcessor remove(int index) {
+ BeanPostProcessor result = super.remove(index);
+ beanPostProcessorCache = null;
+ return result;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ boolean success = super.remove(o);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public boolean removeAll(Collection> c) {
+ boolean success = super.removeAll(c);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public boolean retainAll(Collection> c) {
+ boolean success = super.retainAll(c);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public boolean addAll(Collection extends BeanPostProcessor> c) {
+ boolean success = super.addAll(c);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public boolean addAll(int index, Collection extends BeanPostProcessor> c) {
+ boolean success = super.addAll(index, c);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public boolean removeIf(Predicate super BeanPostProcessor> filter) {
+ boolean success = super.removeIf(filter);
+ if (success) {
+ beanPostProcessorCache = null;
+ }
+ return success;
+ }
+
+ @Override
+ public void replaceAll(UnaryOperator operator) {
+ super.replaceAll(operator);
+ beanPostProcessorCache = null;
+ }
+ }
+
+
+ /**
+ * Internal cache of pre-filtered post-processors.
+ *
+ * @since 5.3
+ */
+ static class BeanPostProcessorCache {
+
+ final List instantiationAware = new ArrayList<>();
+
+ final List smartInstantiationAware = new ArrayList<>();
+
+ final List destructionAware = new ArrayList<>();
+
+ final List mergedDefinition = new ArrayList<>();
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java
new file mode 100644
index 0000000..405e35c
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateQualifier.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2002-2018 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.support;
+
+import org.springframework.beans.BeanMetadataAttributeAccessor;
+import org.springframework.util.Assert;
+
+/**
+ * Qualifier for resolving autowire candidates. A bean definition that
+ * includes one or more such qualifiers enables fine-grained matching
+ * against annotations on a field or parameter to be autowired.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see org.springframework.beans.factory.annotation.Qualifier
+ */
+@SuppressWarnings("serial")
+public class AutowireCandidateQualifier extends BeanMetadataAttributeAccessor {
+
+ /**
+ * The name of the key used to store the value.
+ */
+ public static final String VALUE_KEY = "value";
+
+ private final String typeName;
+
+
+ /**
+ * Construct a qualifier to match against an annotation of the
+ * given type.
+ * @param type the annotation type
+ */
+ public AutowireCandidateQualifier(Class> type) {
+ this(type.getName());
+ }
+
+ /**
+ * Construct a qualifier to match against an annotation of the
+ * given type name.
+ * The type name may match the fully-qualified class name of
+ * the annotation or the short class name (without the package).
+ * @param typeName the name of the annotation type
+ */
+ public AutowireCandidateQualifier(String typeName) {
+ Assert.notNull(typeName, "Type name must not be null");
+ this.typeName = typeName;
+ }
+
+ /**
+ * Construct a qualifier to match against an annotation of the
+ * given type whose {@code value} attribute also matches
+ * the specified value.
+ * @param type the annotation type
+ * @param value the annotation value to match
+ */
+ public AutowireCandidateQualifier(Class> type, Object value) {
+ this(type.getName(), value);
+ }
+
+ /**
+ * Construct a qualifier to match against an annotation of the
+ * given type name whose {@code value} attribute also matches
+ * the specified value.
+ *
The type name may match the fully-qualified class name of
+ * the annotation or the short class name (without the package).
+ * @param typeName the name of the annotation type
+ * @param value the annotation value to match
+ */
+ public AutowireCandidateQualifier(String typeName, Object value) {
+ Assert.notNull(typeName, "Type name must not be null");
+ this.typeName = typeName;
+ setAttribute(VALUE_KEY, value);
+ }
+
+
+ /**
+ * Retrieve the type name. This value will be the same as the
+ * type name provided to the constructor or the fully-qualified
+ * class name if a Class instance was provided to the constructor.
+ */
+ public String getTypeName() {
+ return this.typeName;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java
new file mode 100644
index 0000000..0a1f0a7
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireCandidateResolver.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.lang.Nullable;
+
+/**
+ * Strategy interface for determining whether a specific bean definition
+ * qualifies as an autowire candidate for a specific dependency.
+ *
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @since 2.5
+ */
+public interface AutowireCandidateResolver {
+
+ /**
+ * Determine whether the given bean definition qualifies as an
+ * autowire candidate for the given dependency.
+ *
The default implementation checks
+ * {@link org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()}.
+ * @param bdHolder the bean definition including bean name and aliases
+ * @param descriptor the descriptor for the target method parameter or field
+ * @return whether the bean definition qualifies as autowire candidate
+ * @see org.springframework.beans.factory.config.BeanDefinition#isAutowireCandidate()
+ */
+ default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
+ return bdHolder.getBeanDefinition().isAutowireCandidate();
+ }
+
+ /**
+ * Determine whether the given descriptor is effectively required.
+ *
The default implementation checks {@link DependencyDescriptor#isRequired()}.
+ * @param descriptor the descriptor for the target method parameter or field
+ * @return whether the descriptor is marked as required or possibly indicating
+ * non-required status some other way (e.g. through a parameter annotation)
+ * @since 5.0
+ * @see DependencyDescriptor#isRequired()
+ */
+ default boolean isRequired(DependencyDescriptor descriptor) {
+ return descriptor.isRequired();
+ }
+
+ /**
+ * Determine whether the given descriptor declares a qualifier beyond the type
+ * (typically - but not necessarily - a specific kind of annotation).
+ *
The default implementation returns {@code false}.
+ * @param descriptor the descriptor for the target method parameter or field
+ * @return whether the descriptor declares a qualifier, narrowing the candidate
+ * status beyond the type match
+ * @since 5.1
+ * @see org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#hasQualifier
+ */
+ default boolean hasQualifier(DependencyDescriptor descriptor) {
+ return false;
+ }
+
+ /**
+ * Determine whether a default value is suggested for the given dependency.
+ *
The default implementation simply returns {@code null}.
+ * @param descriptor the descriptor for the target method parameter or field
+ * @return the value suggested (typically an expression String),
+ * or {@code null} if none found
+ * @since 3.0
+ */
+ @Nullable
+ default Object getSuggestedValue(DependencyDescriptor descriptor) {
+ return null;
+ }
+
+ /**
+ * Build a proxy for lazy resolution of the actual dependency target,
+ * if demanded by the injection point.
+ *
The default implementation simply returns {@code null}.
+ * @param descriptor the descriptor for the target method parameter or field
+ * @param beanName the name of the bean that contains the injection point
+ * @return the lazy resolution proxy for the actual dependency target,
+ * or {@code null} if straight resolution is to be performed
+ * @since 4.0
+ */
+ @Nullable
+ default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
+ return null;
+ }
+
+ /**
+ * Return a clone of this resolver instance if necessary, retaining its local
+ * configuration and allowing for the cloned instance to get associated with
+ * a new bean factory, or this original instance if there is no such state.
+ *
The default implementation creates a separate instance via the default
+ * class constructor, assuming no specific configuration state to copy.
+ * Subclasses may override this with custom configuration state handling
+ * or with standard {@link Cloneable} support (as implemented by Spring's
+ * own configurable {@code AutowireCandidateResolver} variants), or simply
+ * return {@code this} (as in {@link SimpleAutowireCandidateResolver}).
+ * @since 5.2.7
+ * @see GenericTypeAwareAutowireCandidateResolver#cloneIfNecessary()
+ * @see DefaultListableBeanFactory#copyConfigurationFrom
+ */
+ default AutowireCandidateResolver cloneIfNecessary() {
+ return BeanUtils.instantiateClass(getClass());
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
new file mode 100644
index 0000000..d807cc4
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AutowireUtils.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Set;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.TypedStringValue;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Utility class that contains various methods useful for the implementation of
+ * autowire-capable bean factories.
+ *
+ * @author Juergen Hoeller
+ * @author Mark Fisher
+ * @author Sam Brannen
+ * @since 1.1.2
+ * @see AbstractAutowireCapableBeanFactory
+ */
+abstract class AutowireUtils {
+
+ public static final Comparator EXECUTABLE_COMPARATOR = (e1, e2) -> {
+ int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
+ return result != 0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());
+ };
+
+
+ /**
+ * Sort the given constructors, preferring public constructors and "greedy" ones with
+ * a maximum number of arguments. The result will contain public constructors first,
+ * with decreasing number of arguments, then non-public constructors, again with
+ * decreasing number of arguments.
+ * @param constructors the constructor array to sort
+ */
+ public static void sortConstructors(Constructor>[] constructors) {
+ Arrays.sort(constructors, EXECUTABLE_COMPARATOR);
+ }
+
+ /**
+ * Sort the given factory methods, preferring public methods and "greedy" ones
+ * with a maximum of arguments. The result will contain public methods first,
+ * with decreasing number of arguments, then non-public methods, again with
+ * decreasing number of arguments.
+ * @param factoryMethods the factory method array to sort
+ */
+ public static void sortFactoryMethods(Method[] factoryMethods) {
+ Arrays.sort(factoryMethods, EXECUTABLE_COMPARATOR);
+ }
+
+ /**
+ * Determine whether the given bean property is excluded from dependency checks.
+ * This implementation excludes properties defined by CGLIB.
+ * @param pd the PropertyDescriptor of the bean property
+ * @return whether the bean property is excluded
+ */
+ public static boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
+ Method wm = pd.getWriteMethod();
+ if (wm == null) {
+ return false;
+ }
+ if (!wm.getDeclaringClass().getName().contains("$$")) {
+ // Not a CGLIB method so it's OK.
+ return false;
+ }
+ // It was declared by CGLIB, but we might still want to autowire it
+ // if it was actually declared by the superclass.
+ Class> superclass = wm.getDeclaringClass().getSuperclass();
+ return !ClassUtils.hasMethod(superclass, wm);
+ }
+
+ /**
+ * Return whether the setter method of the given bean property is defined
+ * in any of the given interfaces.
+ * @param pd the PropertyDescriptor of the bean property
+ * @param interfaces the Set of interfaces (Class objects)
+ * @return whether the setter method is defined by an interface
+ */
+ public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set> interfaces) {
+ Method setter = pd.getWriteMethod();
+ if (setter != null) {
+ Class> targetClass = setter.getDeclaringClass();
+ for (Class> ifc : interfaces) {
+ if (ifc.isAssignableFrom(targetClass) && ClassUtils.hasMethod(ifc, setter)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Resolve the given autowiring value against the given required type,
+ * e.g. an {@link ObjectFactory} value to its actual object result.
+ * @param autowiringValue the value to resolve
+ * @param requiredType the type to assign the result to
+ * @return the resolved value
+ */
+ public static Object resolveAutowiringValue(Object autowiringValue, Class> requiredType) {
+ if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
+ ObjectFactory> factory = (ObjectFactory>) autowiringValue;
+ if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
+ autowiringValue = Proxy.newProxyInstance(requiredType.getClassLoader(),
+ new Class>[] {requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
+ }
+ else {
+ return factory.getObject();
+ }
+ }
+ return autowiringValue;
+ }
+
+ /**
+ * Determine the target type for the generic return type of the given
+ * generic factory method , where formal type variables are declared
+ * on the given method itself.
+ * For example, given a factory method with the following signature, if
+ * {@code resolveReturnTypeForFactoryMethod()} is invoked with the reflected
+ * method for {@code createProxy()} and an {@code Object[]} array containing
+ * {@code MyService.class}, {@code resolveReturnTypeForFactoryMethod()} will
+ * infer that the target return type is {@code MyService}.
+ *
{@code public static T createProxy(Class clazz)}
+ * Possible Return Values
+ *
+ * the target return type, if it can be inferred
+ * the {@linkplain Method#getReturnType() standard return type}, if
+ * the given {@code method} does not declare any {@linkplain
+ * Method#getTypeParameters() formal type variables}
+ * the {@linkplain Method#getReturnType() standard return type}, if the
+ * target return type cannot be inferred (e.g., due to type erasure)
+ * {@code null}, if the length of the given arguments array is shorter
+ * than the length of the {@linkplain
+ * Method#getGenericParameterTypes() formal argument list} for the given
+ * method
+ *
+ * @param method the method to introspect (never {@code null})
+ * @param args the arguments that will be supplied to the method when it is
+ * invoked (never {@code null})
+ * @param classLoader the ClassLoader to resolve class names against,
+ * if necessary (never {@code null})
+ * @return the resolved target return type or the standard method return type
+ * @since 3.2.5
+ */
+ public static Class> resolveReturnTypeForFactoryMethod(
+ Method method, Object[] args, @Nullable ClassLoader classLoader) {
+
+ Assert.notNull(method, "Method must not be null");
+ Assert.notNull(args, "Argument array must not be null");
+
+ TypeVariable[] declaredTypeVariables = method.getTypeParameters();
+ Type genericReturnType = method.getGenericReturnType();
+ Type[] methodParameterTypes = method.getGenericParameterTypes();
+ Assert.isTrue(args.length == methodParameterTypes.length, "Argument array does not match parameter count");
+
+ // Ensure that the type variable (e.g., T) is declared directly on the method
+ // itself (e.g., via ), not on the enclosing class or interface.
+ boolean locallyDeclaredTypeVariableMatchesReturnType = false;
+ for (TypeVariable currentTypeVariable : declaredTypeVariables) {
+ if (currentTypeVariable.equals(genericReturnType)) {
+ locallyDeclaredTypeVariableMatchesReturnType = true;
+ break;
+ }
+ }
+
+ if (locallyDeclaredTypeVariableMatchesReturnType) {
+ for (int i = 0; i < methodParameterTypes.length; i++) {
+ Type methodParameterType = methodParameterTypes[i];
+ Object arg = args[i];
+ if (methodParameterType.equals(genericReturnType)) {
+ if (arg instanceof TypedStringValue) {
+ TypedStringValue typedValue = ((TypedStringValue) arg);
+ if (typedValue.hasTargetType()) {
+ return typedValue.getTargetType();
+ }
+ try {
+ Class> resolvedType = typedValue.resolveTargetType(classLoader);
+ if (resolvedType != null) {
+ return resolvedType;
+ }
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Failed to resolve value type [" +
+ typedValue.getTargetTypeName() + "] for factory method argument", ex);
+ }
+ }
+ else if (arg != null && !(arg instanceof BeanMetadataElement)) {
+ // Only consider argument type if it is a simple value...
+ return arg.getClass();
+ }
+ return method.getReturnType();
+ }
+ else if (methodParameterType instanceof ParameterizedType) {
+ ParameterizedType parameterizedType = (ParameterizedType) methodParameterType;
+ Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+ for (Type typeArg : actualTypeArguments) {
+ if (typeArg.equals(genericReturnType)) {
+ if (arg instanceof Class) {
+ return (Class>) arg;
+ }
+ else {
+ String className = null;
+ if (arg instanceof String) {
+ className = (String) arg;
+ }
+ else if (arg instanceof TypedStringValue) {
+ TypedStringValue typedValue = ((TypedStringValue) arg);
+ String targetTypeName = typedValue.getTargetTypeName();
+ if (targetTypeName == null || Class.class.getName().equals(targetTypeName)) {
+ className = typedValue.getValue();
+ }
+ }
+ if (className != null) {
+ try {
+ return ClassUtils.forName(className, classLoader);
+ }
+ catch (ClassNotFoundException ex) {
+ throw new IllegalStateException("Could not resolve class name [" + arg +
+ "] for factory method argument", ex);
+ }
+ }
+ // Consider adding logic to determine the class of the typeArg, if possible.
+ // For now, just fall back...
+ return method.getReturnType();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Fall back...
+ return method.getReturnType();
+ }
+
+
+ /**
+ * Reflective {@link InvocationHandler} for lazy access to the current target object.
+ */
+ @SuppressWarnings("serial")
+ private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
+
+ private final ObjectFactory> objectFactory;
+
+ ObjectFactoryDelegatingInvocationHandler(ObjectFactory> objectFactory) {
+ this.objectFactory = objectFactory;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ switch (method.getName()) {
+ case "equals":
+ // Only consider equal when proxies are identical.
+ return (proxy == args[0]);
+ case "hashCode":
+ // Use hashCode of proxy.
+ return System.identityHashCode(proxy);
+ case "toString":
+ return this.objectFactory.toString();
+ }
+ try {
+ return method.invoke(this.objectFactory.getObject(), args);
+ }
+ catch (InvocationTargetException ex) {
+ throw ex.getTargetException();
+ }
+ }
+ }
+
+}
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
new file mode 100644
index 0000000..dab55e9
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionBuilder.java
@@ -0,0 +1,345 @@
+/*
+ * 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.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;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * Programmatic means of constructing
+ * {@link org.springframework.beans.factory.config.BeanDefinition BeanDefinitions}
+ * using the builder pattern. Intended primarily for use when implementing Spring 2.0
+ * {@link org.springframework.beans.factory.xml.NamespaceHandler NamespaceHandlers}.
+ *
+ * @author Rod Johnson
+ * @author Rob Harrop
+ * @author Juergen Hoeller
+ * @since 2.0
+ */
+public final class BeanDefinitionBuilder {
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
+ */
+ public static BeanDefinitionBuilder genericBeanDefinition() {
+ return new BeanDefinitionBuilder(new GenericBeanDefinition());
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
+ * @param beanClassName the class name for the bean that the definition is being created for
+ */
+ public static BeanDefinitionBuilder genericBeanDefinition(String beanClassName) {
+ BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
+ builder.beanDefinition.setBeanClassName(beanClassName);
+ return builder;
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
+ * @param beanClass the {@code Class} of the bean that the definition is being created for
+ */
+ public static BeanDefinitionBuilder genericBeanDefinition(Class> beanClass) {
+ BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
+ builder.beanDefinition.setBeanClass(beanClass);
+ return builder;
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link GenericBeanDefinition}.
+ * @param beanClass the {@code Class} of the bean that the definition is being created for
+ * @param instanceSupplier a callback for creating an instance of the bean
+ * @since 5.0
+ */
+ public static BeanDefinitionBuilder genericBeanDefinition(Class beanClass, Supplier instanceSupplier) {
+ BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
+ builder.beanDefinition.setBeanClass(beanClass);
+ builder.beanDefinition.setInstanceSupplier(instanceSupplier);
+ return builder;
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link RootBeanDefinition}.
+ * @param beanClassName the class name for the bean that the definition is being created for
+ */
+ public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName) {
+ return rootBeanDefinition(beanClassName, null);
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link RootBeanDefinition}.
+ * @param beanClassName the class name for the bean that the definition is being created for
+ * @param factoryMethodName the name of the method to use to construct the bean instance
+ */
+ public static BeanDefinitionBuilder rootBeanDefinition(String beanClassName, @Nullable String factoryMethodName) {
+ BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new RootBeanDefinition());
+ builder.beanDefinition.setBeanClassName(beanClassName);
+ builder.beanDefinition.setFactoryMethodName(factoryMethodName);
+ return builder;
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link RootBeanDefinition}.
+ * @param beanClass the {@code Class} of the bean that the definition is being created for
+ */
+ public static BeanDefinitionBuilder rootBeanDefinition(Class> beanClass) {
+ return rootBeanDefinition(beanClass, null);
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link RootBeanDefinition}.
+ * @param beanClass the {@code Class} of the bean that the definition is being created for
+ * @param factoryMethodName the name of the method to use to construct the bean instance
+ */
+ public static BeanDefinitionBuilder rootBeanDefinition(Class> beanClass, @Nullable String factoryMethodName) {
+ BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new RootBeanDefinition());
+ builder.beanDefinition.setBeanClass(beanClass);
+ builder.beanDefinition.setFactoryMethodName(factoryMethodName);
+ return builder;
+ }
+
+ /**
+ * Create a new {@code BeanDefinitionBuilder} used to construct a {@link ChildBeanDefinition}.
+ * @param parentName the name of the parent bean
+ */
+ public static BeanDefinitionBuilder childBeanDefinition(String parentName) {
+ return new BeanDefinitionBuilder(new ChildBeanDefinition(parentName));
+ }
+
+
+ /**
+ * The {@code BeanDefinition} instance we are creating.
+ */
+ private final AbstractBeanDefinition beanDefinition;
+
+ /**
+ * Our current position with respect to constructor args.
+ */
+ private int constructorArgIndex;
+
+
+ /**
+ * Enforce the use of factory methods.
+ */
+ private BeanDefinitionBuilder(AbstractBeanDefinition beanDefinition) {
+ this.beanDefinition = beanDefinition;
+ }
+
+ /**
+ * Return the current BeanDefinition object in its raw (unvalidated) form.
+ * @see #getBeanDefinition()
+ */
+ public AbstractBeanDefinition getRawBeanDefinition() {
+ return this.beanDefinition;
+ }
+
+ /**
+ * Validate and return the created BeanDefinition object.
+ */
+ public AbstractBeanDefinition getBeanDefinition() {
+ this.beanDefinition.validate();
+ return this.beanDefinition;
+ }
+
+
+ /**
+ * Set the name of the parent definition of this bean definition.
+ */
+ public BeanDefinitionBuilder setParentName(String parentName) {
+ this.beanDefinition.setParentName(parentName);
+ return this;
+ }
+
+ /**
+ * Set the name of a static factory method to use for this definition,
+ * to be called on this bean's class.
+ */
+ public BeanDefinitionBuilder setFactoryMethod(String factoryMethod) {
+ this.beanDefinition.setFactoryMethodName(factoryMethod);
+ return this;
+ }
+
+ /**
+ * Set the name of a non-static factory method to use for this definition,
+ * including the bean name of the factory instance to call the method on.
+ * @param factoryMethod the name of the factory method
+ * @param factoryBean the name of the bean to call the specified factory method on
+ * @since 4.3.6
+ */
+ public BeanDefinitionBuilder setFactoryMethodOnBean(String factoryMethod, String factoryBean) {
+ this.beanDefinition.setFactoryMethodName(factoryMethod);
+ this.beanDefinition.setFactoryBeanName(factoryBean);
+ return this;
+ }
+
+ /**
+ * Add an indexed constructor arg value. The current index is tracked internally
+ * and all additions are at the present point.
+ */
+ public BeanDefinitionBuilder addConstructorArgValue(@Nullable Object value) {
+ this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
+ this.constructorArgIndex++, value);
+ return this;
+ }
+
+ /**
+ * Add a reference to a named bean as a constructor arg.
+ * @see #addConstructorArgValue(Object)
+ */
+ public BeanDefinitionBuilder addConstructorArgReference(String beanName) {
+ this.beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(
+ this.constructorArgIndex++, new RuntimeBeanReference(beanName));
+ return this;
+ }
+
+ /**
+ * Add the supplied property value under the given property name.
+ */
+ public BeanDefinitionBuilder addPropertyValue(String name, @Nullable Object value) {
+ this.beanDefinition.getPropertyValues().add(name, value);
+ return this;
+ }
+
+ /**
+ * Add a reference to the specified bean name under the property specified.
+ * @param name the name of the property to add the reference to
+ * @param beanName the name of the bean being referenced
+ */
+ public BeanDefinitionBuilder addPropertyReference(String name, String beanName) {
+ this.beanDefinition.getPropertyValues().add(name, new RuntimeBeanReference(beanName));
+ 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.
+ */
+ public BeanDefinitionBuilder setInitMethodName(@Nullable String methodName) {
+ this.beanDefinition.setInitMethodName(methodName);
+ return this;
+ }
+
+ /**
+ * Set the destroy method for this definition.
+ */
+ public BeanDefinitionBuilder setDestroyMethodName(@Nullable String methodName) {
+ this.beanDefinition.setDestroyMethodName(methodName);
+ return this;
+ }
+
+
+ /**
+ * Set the scope of this definition.
+ * @see org.springframework.beans.factory.config.BeanDefinition#SCOPE_SINGLETON
+ * @see org.springframework.beans.factory.config.BeanDefinition#SCOPE_PROTOTYPE
+ */
+ public BeanDefinitionBuilder setScope(@Nullable String scope) {
+ this.beanDefinition.setScope(scope);
+ return this;
+ }
+
+ /**
+ * Set whether or not this definition is abstract.
+ */
+ public BeanDefinitionBuilder setAbstract(boolean flag) {
+ this.beanDefinition.setAbstract(flag);
+ return this;
+ }
+
+ /**
+ * Set whether beans for this definition should be lazily initialized or not.
+ */
+ public BeanDefinitionBuilder setLazyInit(boolean lazy) {
+ this.beanDefinition.setLazyInit(lazy);
+ return this;
+ }
+
+ /**
+ * Set the autowire mode for this definition.
+ */
+ public BeanDefinitionBuilder setAutowireMode(int autowireMode) {
+ this.beanDefinition.setAutowireMode(autowireMode);
+ return this;
+ }
+
+ /**
+ * Set the dependency check mode for this definition.
+ */
+ public BeanDefinitionBuilder setDependencyCheck(int dependencyCheck) {
+ this.beanDefinition.setDependencyCheck(dependencyCheck);
+ return this;
+ }
+
+ /**
+ * Append the specified bean name to the list of beans that this definition
+ * depends on.
+ */
+ public BeanDefinitionBuilder addDependsOn(String beanName) {
+ if (this.beanDefinition.getDependsOn() == null) {
+ this.beanDefinition.setDependsOn(beanName);
+ }
+ else {
+ String[] added = ObjectUtils.addObjectToArray(this.beanDefinition.getDependsOn(), beanName);
+ this.beanDefinition.setDependsOn(added);
+ }
+ return this;
+ }
+
+ /**
+ * Set whether this bean is a primary autowire candidate.
+ * @since 5.1.11
+ */
+ public BeanDefinitionBuilder setPrimary(boolean primary) {
+ this.beanDefinition.setPrimary(primary);
+ return this;
+ }
+
+ /**
+ * Set the role of this definition.
+ */
+ public BeanDefinitionBuilder setRole(int role) {
+ this.beanDefinition.setRole(role);
+ return this;
+ }
+
+ /**
+ * Apply the given customizers to the underlying bean definition.
+ * @since 5.0
+ */
+ public BeanDefinitionBuilder applyCustomizers(BeanDefinitionCustomizer... customizers) {
+ for (BeanDefinitionCustomizer customizer : customizers) {
+ customizer.customize(this.beanDefinition);
+ }
+ return this;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java
new file mode 100644
index 0000000..5da53bf
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionDefaults.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import org.springframework.lang.Nullable;
+import org.springframework.util.StringUtils;
+
+/**
+ * A simple holder for {@code BeanDefinition} property defaults.
+ *
+ * @author Mark Fisher
+ * @author Juergen Hoeller
+ * @since 2.5
+ * @see AbstractBeanDefinition#applyDefaults
+ */
+public class BeanDefinitionDefaults {
+
+ @Nullable
+ private Boolean lazyInit;
+
+ private int autowireMode = AbstractBeanDefinition.AUTOWIRE_NO;
+
+ private int dependencyCheck = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE;
+
+ @Nullable
+ private String initMethodName;
+
+ @Nullable
+ private String destroyMethodName;
+
+
+ /**
+ * Set whether beans should be lazily initialized by default.
+ * If {@code false}, the bean will get instantiated on startup by bean
+ * factories that perform eager initialization of singletons.
+ * @see AbstractBeanDefinition#setLazyInit
+ */
+ public void setLazyInit(boolean lazyInit) {
+ this.lazyInit = lazyInit;
+ }
+
+ /**
+ * Return whether beans should be lazily initialized by default, i.e. not
+ * eagerly instantiated on startup. Only applicable to singleton beans.
+ * @return whether to apply lazy-init semantics ({@code false} by default)
+ */
+ public boolean isLazyInit() {
+ return (this.lazyInit != null && this.lazyInit.booleanValue());
+ }
+
+ /**
+ * Return whether beans should be lazily initialized by default, i.e. not
+ * eagerly instantiated on startup. Only applicable to singleton beans.
+ * @return the lazy-init flag if explicitly set, or {@code null} otherwise
+ * @since 5.2
+ */
+ @Nullable
+ public Boolean getLazyInit() {
+ return this.lazyInit;
+ }
+
+ /**
+ * Set the autowire mode. This determines whether any automagical detection
+ * and setting of bean references will happen. Default is AUTOWIRE_NO
+ * which means there won't be convention-based autowiring by name or type
+ * (however, there may still be explicit annotation-driven autowiring).
+ * @param autowireMode the autowire mode to set.
+ * Must be one of the constants defined in {@link AbstractBeanDefinition}.
+ * @see AbstractBeanDefinition#setAutowireMode
+ */
+ public void setAutowireMode(int autowireMode) {
+ this.autowireMode = autowireMode;
+ }
+
+ /**
+ * Return the default autowire mode.
+ */
+ public int getAutowireMode() {
+ return this.autowireMode;
+ }
+
+ /**
+ * Set the dependency check code.
+ * @param dependencyCheck the code to set.
+ * Must be one of the constants defined in {@link AbstractBeanDefinition}.
+ * @see AbstractBeanDefinition#setDependencyCheck
+ */
+ public void setDependencyCheck(int dependencyCheck) {
+ this.dependencyCheck = dependencyCheck;
+ }
+
+ /**
+ * Return the default dependency check code.
+ */
+ public int getDependencyCheck() {
+ return this.dependencyCheck;
+ }
+
+ /**
+ * Set the name of the default initializer method.
+ *
Note that this method is not enforced on all affected bean definitions
+ * but rather taken as an optional callback, to be invoked if actually present.
+ * @see AbstractBeanDefinition#setInitMethodName
+ * @see AbstractBeanDefinition#setEnforceInitMethod
+ */
+ public void setInitMethodName(@Nullable String initMethodName) {
+ this.initMethodName = (StringUtils.hasText(initMethodName) ? initMethodName : null);
+ }
+
+ /**
+ * Return the name of the default initializer method.
+ */
+ @Nullable
+ public String getInitMethodName() {
+ return this.initMethodName;
+ }
+
+ /**
+ * Set the name of the default destroy method.
+ *
Note that this method is not enforced on all affected bean definitions
+ * but rather taken as an optional callback, to be invoked if actually present.
+ * @see AbstractBeanDefinition#setDestroyMethodName
+ * @see AbstractBeanDefinition#setEnforceDestroyMethod
+ */
+ public void setDestroyMethodName(@Nullable String destroyMethodName) {
+ this.destroyMethodName = (StringUtils.hasText(destroyMethodName) ? destroyMethodName : null);
+ }
+
+ /**
+ * Return the name of the default destroy method.
+ */
+ @Nullable
+ public String getDestroyMethodName() {
+ return this.destroyMethodName;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
new file mode 100644
index 0000000..b026877
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionReaderUtils.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.lang.Nullable;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Utility methods that are useful for bean definition reader implementations.
+ * Mainly intended for internal use.
+ *
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @since 1.1
+ * @see PropertiesBeanDefinitionReader
+ * @see org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader
+ */
+public abstract class BeanDefinitionReaderUtils {
+
+ /**
+ * Separator for generated bean names. If a class name or parent name is not
+ * unique, "#1", "#2" etc will be appended, until the name becomes unique.
+ */
+ public static final String GENERATED_BEAN_NAME_SEPARATOR = BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR;
+
+
+ /**
+ * Create a new GenericBeanDefinition for the given parent name and class name,
+ * eagerly loading the bean class if a ClassLoader has been specified.
+ * @param parentName the name of the parent bean, if any
+ * @param className the name of the bean class, if any
+ * @param classLoader the ClassLoader to use for loading bean classes
+ * (can be {@code null} to just register bean classes by name)
+ * @return the bean definition
+ * @throws ClassNotFoundException if the bean class could not be loaded
+ */
+ public static AbstractBeanDefinition createBeanDefinition(
+ @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
+
+ GenericBeanDefinition bd = new GenericBeanDefinition();
+ bd.setParentName(parentName);
+ if (className != null) {
+ if (classLoader != null) {
+ bd.setBeanClass(ClassUtils.forName(className, classLoader));
+ }
+ else {
+ bd.setBeanClassName(className);
+ }
+ }
+ return bd;
+ }
+
+ /**
+ * Generate a bean name for the given top-level bean definition,
+ * unique within the given bean factory.
+ * @param beanDefinition the bean definition to generate a bean name for
+ * @param registry the bean factory that the definition is going to be
+ * registered with (to check for existing bean names)
+ * @return the generated bean name
+ * @throws BeanDefinitionStoreException if no unique name can be generated
+ * for the given bean definition
+ * @see #generateBeanName(BeanDefinition, BeanDefinitionRegistry, boolean)
+ */
+ public static String generateBeanName(BeanDefinition beanDefinition, BeanDefinitionRegistry registry)
+ throws BeanDefinitionStoreException {
+
+ return generateBeanName(beanDefinition, registry, false);
+ }
+
+ /**
+ * Generate a bean name for the given bean definition, unique within the
+ * given bean factory.
+ * @param definition the bean definition to generate a bean name for
+ * @param registry the bean factory that the definition is going to be
+ * registered with (to check for existing bean names)
+ * @param isInnerBean whether the given bean definition will be registered
+ * as inner bean or as top-level bean (allowing for special name generation
+ * for inner beans versus top-level beans)
+ * @return the generated bean name
+ * @throws BeanDefinitionStoreException if no unique name can be generated
+ * for the given bean definition
+ */
+ public static String generateBeanName(
+ BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
+ throws BeanDefinitionStoreException {
+
+ String generatedBeanName = definition.getBeanClassName();
+ if (generatedBeanName == null) {
+ if (definition.getParentName() != null) {
+ generatedBeanName = definition.getParentName() + "$child";
+ }
+ else if (definition.getFactoryBeanName() != null) {
+ generatedBeanName = definition.getFactoryBeanName() + "$created";
+ }
+ }
+ if (!StringUtils.hasText(generatedBeanName)) {
+ throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
+ "'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
+ }
+
+ if (isInnerBean) {
+ // Inner bean: generate identity hashcode suffix.
+ return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
+ }
+
+ // Top-level bean: use plain class name with unique suffix if necessary.
+ return uniqueBeanName(generatedBeanName, registry);
+ }
+
+ /**
+ * Turn the given bean name into a unique bean name for the given bean factory,
+ * appending a unique counter as suffix if necessary.
+ * @param beanName the original bean name
+ * @param registry the bean factory that the definition is going to be
+ * registered with (to check for existing bean names)
+ * @return the unique bean name to use
+ * @since 5.1
+ */
+ public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
+ String id = beanName;
+ int counter = -1;
+
+ // Increase counter until the id is unique.
+ String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
+ while (counter == -1 || registry.containsBeanDefinition(id)) {
+ counter++;
+ id = prefix + counter;
+ }
+ return id;
+ }
+
+ /**
+ * Register the given bean definition with the given bean factory.
+ * @param definitionHolder the bean definition including name and aliases
+ * @param registry the bean factory to register with
+ * @throws BeanDefinitionStoreException if registration failed
+ */
+ public static void registerBeanDefinition(
+ BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
+ throws BeanDefinitionStoreException {
+
+ // Register bean definition under primary name.
+ String beanName = definitionHolder.getBeanName();
+ registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
+
+ // Register aliases for bean name, if any.
+ String[] aliases = definitionHolder.getAliases();
+ if (aliases != null) {
+ for (String alias : aliases) {
+ registry.registerAlias(beanName, alias);
+ }
+ }
+ }
+
+ /**
+ * Register the given bean definition with a generated name,
+ * unique within the given bean factory.
+ * @param definition the bean definition to generate a bean name for
+ * @param registry the bean factory to register with
+ * @return the generated bean name
+ * @throws BeanDefinitionStoreException if no unique name can be generated
+ * for the given bean definition or the definition cannot be registered
+ */
+ public static String registerWithGeneratedName(
+ AbstractBeanDefinition definition, BeanDefinitionRegistry registry)
+ throws BeanDefinitionStoreException {
+
+ String generatedName = generateBeanName(definition, registry, false);
+ registry.registerBeanDefinition(generatedName, definition);
+ return generatedName;
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java
new file mode 100644
index 0000000..a1f47e5
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistry.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2002-2018 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.support;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.core.AliasRegistry;
+
+/**
+ * Interface for registries that hold bean definitions, for example RootBeanDefinition
+ * and ChildBeanDefinition instances. Typically implemented by BeanFactories that
+ * internally work with the AbstractBeanDefinition hierarchy.
+ *
+ *
This is the only interface in Spring's bean factory packages that encapsulates
+ * registration of bean definitions. The standard BeanFactory interfaces
+ * only cover access to a fully configured factory instance .
+ *
+ *
Spring's bean definition readers expect to work on an implementation of this
+ * interface. Known implementors within the Spring core are DefaultListableBeanFactory
+ * and GenericApplicationContext.
+ *
+ * @author Juergen Hoeller
+ * @since 26.11.2003
+ * @see org.springframework.beans.factory.config.BeanDefinition
+ * @see AbstractBeanDefinition
+ * @see RootBeanDefinition
+ * @see ChildBeanDefinition
+ * @see DefaultListableBeanFactory
+ * @see org.springframework.context.support.GenericApplicationContext
+ * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
+ * @see PropertiesBeanDefinitionReader
+ */
+public interface BeanDefinitionRegistry extends AliasRegistry {
+
+ /**
+ * Register a new bean definition with this registry.
+ * Must support RootBeanDefinition and ChildBeanDefinition.
+ * @param beanName the name of the bean instance to register
+ * @param beanDefinition definition of the bean instance to register
+ * @throws BeanDefinitionStoreException if the BeanDefinition is invalid
+ * @throws BeanDefinitionOverrideException if there is already a BeanDefinition
+ * for the specified bean name and we are not allowed to override it
+ * @see GenericBeanDefinition
+ * @see RootBeanDefinition
+ * @see ChildBeanDefinition
+ */
+ void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
+ throws BeanDefinitionStoreException;
+
+ /**
+ * Remove the BeanDefinition for the given name.
+ * @param beanName the name of the bean instance to register
+ * @throws NoSuchBeanDefinitionException if there is no such bean definition
+ */
+ void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
+
+ /**
+ * Return the BeanDefinition for the given bean name.
+ * @param beanName name of the bean to find a definition for
+ * @return the BeanDefinition for the given name (never {@code null})
+ * @throws NoSuchBeanDefinitionException if there is no such bean definition
+ */
+ BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
+
+ /**
+ * Check if this registry contains a bean definition with the given name.
+ * @param beanName the name of the bean to look for
+ * @return if this registry contains a bean definition with the given name
+ */
+ boolean containsBeanDefinition(String beanName);
+
+ /**
+ * Return the names of all beans defined in this registry.
+ * @return the names of all beans defined in this registry,
+ * or an empty array if none defined
+ */
+ String[] getBeanDefinitionNames();
+
+ /**
+ * Return the number of beans defined in the registry.
+ * @return the number of beans defined in the registry
+ */
+ int getBeanDefinitionCount();
+
+ /**
+ * Determine whether the given bean name is already in use within this registry,
+ * i.e. whether there is a local bean or alias registered under this name.
+ * @param beanName the name to check
+ * @return whether the given bean name is already in use
+ */
+ boolean isBeanNameInUse(String beanName);
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistryPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistryPostProcessor.java
new file mode 100644
index 0000000..b94f1ab
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionRegistryPostProcessor.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2002-2010 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.support;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+
+/**
+ * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
+ * the registration of further bean definitions before regular
+ * BeanFactoryPostProcessor detection kicks in. In particular,
+ * BeanDefinitionRegistryPostProcessor may register further bean definitions
+ * which in turn define BeanFactoryPostProcessor instances.
+ *
+ * @author Juergen Hoeller
+ * @since 3.0.1
+ * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
+ */
+public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
+
+ /**
+ * Modify the application context's internal bean definition registry after its
+ * standard initialization. All regular bean definitions will have been loaded,
+ * but no beans will have been instantiated yet. This allows for adding further
+ * bean definitions before the next post-processing phase kicks in.
+ * @param registry the bean definition registry used by the application context
+ * @throws org.springframework.beans.BeansException in case of errors
+ */
+ void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionResource.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionResource.java
new file mode 100644
index 0000000..3a243bf
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionResource.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2002-2018 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.support;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.core.io.AbstractResource;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Descriptive {@link org.springframework.core.io.Resource} wrapper for
+ * a {@link org.springframework.beans.factory.config.BeanDefinition}.
+ *
+ * @author Juergen Hoeller
+ * @since 2.5.2
+ * @see org.springframework.core.io.DescriptiveResource
+ */
+class BeanDefinitionResource extends AbstractResource {
+
+ private final BeanDefinition beanDefinition;
+
+
+ /**
+ * Create a new BeanDefinitionResource.
+ * @param beanDefinition the BeanDefinition object to wrap
+ */
+ public BeanDefinitionResource(BeanDefinition beanDefinition) {
+ Assert.notNull(beanDefinition, "BeanDefinition must not be null");
+ this.beanDefinition = beanDefinition;
+ }
+
+ /**
+ * Return the wrapped BeanDefinition object.
+ */
+ public final BeanDefinition getBeanDefinition() {
+ return this.beanDefinition;
+ }
+
+
+ @Override
+ public boolean exists() {
+ return false;
+ }
+
+ @Override
+ public boolean isReadable() {
+ return false;
+ }
+
+ @Override
+ public InputStream getInputStream() throws IOException {
+ throw new FileNotFoundException(
+ "Resource cannot be opened because it points to " + getDescription());
+ }
+
+ @Override
+ public String getDescription() {
+ return "BeanDefinition defined in " + this.beanDefinition.getResourceDescription();
+ }
+
+
+ /**
+ * This implementation compares the underlying BeanDefinition.
+ */
+ @Override
+ public boolean equals(@Nullable Object other) {
+ return (this == other || (other instanceof BeanDefinitionResource &&
+ ((BeanDefinitionResource) other).beanDefinition.equals(this.beanDefinition)));
+ }
+
+ /**
+ * This implementation returns the hash code of the underlying BeanDefinition.
+ */
+ @Override
+ public int hashCode() {
+ return this.beanDefinition.hashCode();
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValidationException.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValidationException.java
new file mode 100644
index 0000000..88e0458
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanDefinitionValidationException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2002-2012 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.support;
+
+import org.springframework.beans.FatalBeanException;
+
+/**
+ * Exception thrown when the validation of a bean definition failed.
+ *
+ * @author Juergen Hoeller
+ * @since 21.11.2003
+ * @see AbstractBeanDefinition#validate()
+ */
+@SuppressWarnings("serial")
+public class BeanDefinitionValidationException extends FatalBeanException {
+
+ /**
+ * Create a new BeanDefinitionValidationException with the specified message.
+ * @param msg the detail message
+ */
+ public BeanDefinitionValidationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Create a new BeanDefinitionValidationException with the specified message
+ * and root cause.
+ * @param msg the detail message
+ * @param cause the root cause
+ */
+ public BeanDefinitionValidationException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanNameGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanNameGenerator.java
new file mode 100644
index 0000000..d7d3c9b
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/BeanNameGenerator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2002-2007 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.support;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+
+/**
+ * Strategy interface for generating bean names for bean definitions.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0.3
+ */
+public interface BeanNameGenerator {
+
+ /**
+ * Generate a bean name for the given bean definition.
+ * @param definition the bean definition to generate a name for
+ * @param registry the bean definition registry that the given definition
+ * is supposed to be registered with
+ * @return the generated bean name
+ */
+ String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry);
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
new file mode 100644
index 0000000..698a04d
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/CglibSubclassingInstantiationStrategy.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeanInstantiationException;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.cglib.core.ClassLoaderAwareGeneratorStrategy;
+import org.springframework.cglib.core.SpringNamingPolicy;
+import org.springframework.cglib.proxy.Callback;
+import org.springframework.cglib.proxy.CallbackFilter;
+import org.springframework.cglib.proxy.Enhancer;
+import org.springframework.cglib.proxy.Factory;
+import org.springframework.cglib.proxy.MethodInterceptor;
+import org.springframework.cglib.proxy.MethodProxy;
+import org.springframework.cglib.proxy.NoOp;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Default object instantiation strategy for use in BeanFactories.
+ *
+ *
Uses CGLIB to generate subclasses dynamically if methods need to be
+ * overridden by the container to implement Method Injection .
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @since 1.1
+ */
+public class CglibSubclassingInstantiationStrategy extends SimpleInstantiationStrategy {
+
+ /**
+ * Index in the CGLIB callback array for passthrough behavior,
+ * in which case the subclass won't override the original class.
+ */
+ private static final int PASSTHROUGH = 0;
+
+ /**
+ * Index in the CGLIB callback array for a method that should
+ * be overridden to provide method lookup .
+ */
+ private static final int LOOKUP_OVERRIDE = 1;
+
+ /**
+ * Index in the CGLIB callback array for a method that should
+ * be overridden using generic method replacer functionality.
+ */
+ private static final int METHOD_REPLACER = 2;
+
+
+ @Override
+ protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) {
+ return instantiateWithMethodInjection(bd, beanName, owner, null);
+ }
+
+ @Override
+ protected Object instantiateWithMethodInjection(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
+ @Nullable Constructor> ctor, Object... args) {
+
+ // Must generate CGLIB subclass...
+ return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);
+ }
+
+
+ /**
+ * An inner class created for historical reasons to avoid external CGLIB dependency
+ * in Spring versions earlier than 3.2.
+ */
+ private static class CglibSubclassCreator {
+
+ private static final Class>[] CALLBACK_TYPES = new Class>[]
+ {NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class};
+
+ private final RootBeanDefinition beanDefinition;
+
+ private final BeanFactory owner;
+
+ CglibSubclassCreator(RootBeanDefinition beanDefinition, BeanFactory owner) {
+ this.beanDefinition = beanDefinition;
+ this.owner = owner;
+ }
+
+ /**
+ * Create a new instance of a dynamically generated subclass implementing the
+ * required lookups.
+ * @param ctor constructor to use. If this is {@code null}, use the
+ * no-arg constructor (no parameterization, or Setter Injection)
+ * @param args arguments to use for the constructor.
+ * Ignored if the {@code ctor} parameter is {@code null}.
+ * @return new instance of the dynamically generated subclass
+ */
+ public Object instantiate(@Nullable Constructor> ctor, Object... args) {
+ Class> subclass = createEnhancedSubclass(this.beanDefinition);
+ Object instance;
+ if (ctor == null) {
+ instance = BeanUtils.instantiateClass(subclass);
+ }
+ else {
+ try {
+ Constructor> enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes());
+ instance = enhancedSubclassConstructor.newInstance(args);
+ }
+ catch (Exception ex) {
+ throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
+ "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex);
+ }
+ }
+ // SPR-10785: set callbacks directly on the instance instead of in the
+ // enhanced class (via the Enhancer) in order to avoid memory leaks.
+ Factory factory = (Factory) instance;
+ factory.setCallbacks(new Callback[] {NoOp.INSTANCE,
+ new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
+ new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)});
+ return instance;
+ }
+
+ /**
+ * Create an enhanced subclass of the bean class for the provided bean
+ * definition, using CGLIB.
+ */
+ private Class> createEnhancedSubclass(RootBeanDefinition beanDefinition) {
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(beanDefinition.getBeanClass());
+ enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
+ if (this.owner instanceof ConfigurableBeanFactory) {
+ ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
+ enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));
+ }
+ enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
+ enhancer.setCallbackTypes(CALLBACK_TYPES);
+ return enhancer.createClass();
+ }
+ }
+
+
+ /**
+ * Class providing hashCode and equals methods required by CGLIB to
+ * ensure that CGLIB doesn't generate a distinct class per bean.
+ * Identity is based on class and bean definition.
+ */
+ private static class CglibIdentitySupport {
+
+ private final RootBeanDefinition beanDefinition;
+
+ public CglibIdentitySupport(RootBeanDefinition beanDefinition) {
+ this.beanDefinition = beanDefinition;
+ }
+
+ public RootBeanDefinition getBeanDefinition() {
+ return this.beanDefinition;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ return (other != null && getClass() == other.getClass() &&
+ this.beanDefinition.equals(((CglibIdentitySupport) other).beanDefinition));
+ }
+
+ @Override
+ public int hashCode() {
+ return this.beanDefinition.hashCode();
+ }
+ }
+
+
+ /**
+ * CGLIB callback for filtering method interception behavior.
+ */
+ private static class MethodOverrideCallbackFilter extends CglibIdentitySupport implements CallbackFilter {
+
+ private static final Log logger = LogFactory.getLog(MethodOverrideCallbackFilter.class);
+
+ public MethodOverrideCallbackFilter(RootBeanDefinition beanDefinition) {
+ super(beanDefinition);
+ }
+
+ @Override
+ public int accept(Method method) {
+ MethodOverride methodOverride = getBeanDefinition().getMethodOverrides().getOverride(method);
+ if (logger.isTraceEnabled()) {
+ logger.trace("MethodOverride for " + method + ": " + methodOverride);
+ }
+ if (methodOverride == null) {
+ return PASSTHROUGH;
+ }
+ else if (methodOverride instanceof LookupOverride) {
+ return LOOKUP_OVERRIDE;
+ }
+ else if (methodOverride instanceof ReplaceOverride) {
+ return METHOD_REPLACER;
+ }
+ throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " +
+ methodOverride.getClass().getName());
+ }
+ }
+
+
+ /**
+ * CGLIB MethodInterceptor to override methods, replacing them with an
+ * implementation that returns a bean looked up in the container.
+ */
+ private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
+
+ private final BeanFactory owner;
+
+ public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
+ super(beanDefinition);
+ this.owner = owner;
+ }
+
+ @Override
+ public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
+ // Cast is safe, as CallbackFilter filters are used selectively.
+ LookupOverride lo = (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
+ Assert.state(lo != null, "LookupOverride not found");
+ Object[] argsToUse = (args.length > 0 ? args : null); // if no-arg, don't insist on args at all
+ if (StringUtils.hasText(lo.getBeanName())) {
+ Object bean = (argsToUse != null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
+ this.owner.getBean(lo.getBeanName()));
+ // Detect package-protected NullBean instance through equals(null) check
+ return (bean.equals(null) ? null : bean);
+ }
+ else {
+ return (argsToUse != null ? this.owner.getBean(method.getReturnType(), argsToUse) :
+ this.owner.getBean(method.getReturnType()));
+ }
+ }
+ }
+
+
+ /**
+ * CGLIB MethodInterceptor to override methods, replacing them with a call
+ * to a generic MethodReplacer.
+ */
+ private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor {
+
+ private final BeanFactory owner;
+
+ public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner) {
+ super(beanDefinition);
+ this.owner = owner;
+ }
+
+ @Override
+ public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable {
+ ReplaceOverride ro = (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
+ Assert.state(ro != null, "ReplaceOverride not found");
+ // TODO could cache if a singleton for minor performance optimization
+ MethodReplacer mr = this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
+ return mr.reimplement(obj, method, args);
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
new file mode 100644
index 0000000..7647f11
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java
@@ -0,0 +1,1009 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.beans.ConstructorProperties;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+
+import org.springframework.beans.BeanMetadataElement;
+import org.springframework.beans.BeanWrapper;
+import org.springframework.beans.BeanWrapperImpl;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.TypeMismatchException;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.InjectionPoint;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
+import org.springframework.beans.factory.UnsatisfiedDependencyException;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.core.CollectionFactory;
+import org.springframework.core.MethodParameter;
+import org.springframework.core.NamedThreadLocal;
+import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.MethodInvoker;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.ReflectionUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Delegate for resolving constructors and factory methods.
+ *
+ *
Performs constructor resolution through argument matching.
+ *
+ * @author Juergen Hoeller
+ * @author Rob Harrop
+ * @author Mark Fisher
+ * @author Costin Leau
+ * @author Sebastien Deleuze
+ * @author Sam Brannen
+ * @since 2.0
+ * @see #autowireConstructor
+ * @see #instantiateUsingFactoryMethod
+ * @see AbstractAutowireCapableBeanFactory
+ */
+class ConstructorResolver {
+
+ private static final Object[] EMPTY_ARGS = new Object[0];
+
+ /**
+ * Marker for autowired arguments in a cached argument array, to be replaced
+ * by a {@linkplain #resolveAutowiredArgument resolved autowired argument}.
+ */
+ private static final Object autowiredArgumentMarker = new Object();
+
+ private static final NamedThreadLocal currentInjectionPoint =
+ new NamedThreadLocal<>("Current injection point");
+
+
+ private final AbstractAutowireCapableBeanFactory beanFactory;
+
+ private final Log logger;
+
+
+ /**
+ * Create a new ConstructorResolver for the given factory and instantiation strategy.
+ * @param beanFactory the BeanFactory to work with
+ */
+ public ConstructorResolver(AbstractAutowireCapableBeanFactory beanFactory) {
+ this.beanFactory = beanFactory;
+ this.logger = beanFactory.getLogger();
+ }
+
+
+ /**
+ * "autowire constructor" (with constructor arguments by type) behavior.
+ * Also applied if explicit constructor argument values are specified,
+ * matching all remaining arguments with beans from the bean factory.
+ * This corresponds to constructor injection: In this mode, a Spring
+ * bean factory is able to host components that expect constructor-based
+ * dependency resolution.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @param chosenCtors chosen candidate constructors (or {@code null} if none)
+ * @param explicitArgs argument values passed in programmatically via the getBean method,
+ * or {@code null} if none (-> use constructor argument values from bean definition)
+ * @return a BeanWrapper for the new instance
+ */
+ public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd,
+ @Nullable Constructor>[] chosenCtors, @Nullable Object[] explicitArgs) {
+
+ BeanWrapperImpl bw = new BeanWrapperImpl();
+ this.beanFactory.initBeanWrapper(bw);
+
+ Constructor> constructorToUse = null;
+ ArgumentsHolder argsHolderToUse = null;
+ Object[] argsToUse = null;
+
+ if (explicitArgs != null) {
+ argsToUse = explicitArgs;
+ }
+ else {
+ Object[] argsToResolve = null;
+ synchronized (mbd.constructorArgumentLock) {
+ constructorToUse = (Constructor>) mbd.resolvedConstructorOrFactoryMethod;
+ if (constructorToUse != null && mbd.constructorArgumentsResolved) {
+ // Found a cached constructor...
+ argsToUse = mbd.resolvedConstructorArguments;
+ if (argsToUse == null) {
+ argsToResolve = mbd.preparedConstructorArguments;
+ }
+ }
+ }
+ if (argsToResolve != null) {
+ argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
+ }
+ }
+
+ if (constructorToUse == null || argsToUse == null) {
+ // Take specified constructors, if any.
+ Constructor>[] candidates = chosenCtors;
+ if (candidates == null) {
+ Class> beanClass = mbd.getBeanClass();
+ try {
+ candidates = (mbd.isNonPublicAccessAllowed() ?
+ beanClass.getDeclaredConstructors() : beanClass.getConstructors());
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Resolution of declared constructors on bean Class [" + beanClass.getName() +
+ "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
+ }
+ }
+
+ if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
+ Constructor> uniqueCandidate = candidates[0];
+ if (uniqueCandidate.getParameterCount() == 0) {
+ synchronized (mbd.constructorArgumentLock) {
+ mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
+ mbd.constructorArgumentsResolved = true;
+ mbd.resolvedConstructorArguments = EMPTY_ARGS;
+ }
+ bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
+ return bw;
+ }
+ }
+
+ // Need to resolve the constructor.
+ boolean autowiring = (chosenCtors != null ||
+ mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
+ ConstructorArgumentValues resolvedValues = null;
+
+ int minNrOfArgs;
+ if (explicitArgs != null) {
+ minNrOfArgs = explicitArgs.length;
+ }
+ else {
+ ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
+ resolvedValues = new ConstructorArgumentValues();
+ minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
+ }
+
+ AutowireUtils.sortConstructors(candidates);
+ int minTypeDiffWeight = Integer.MAX_VALUE;
+ Set> ambiguousConstructors = null;
+ Deque causes = null;
+
+ for (Constructor> candidate : candidates) {
+ int parameterCount = candidate.getParameterCount();
+
+ if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
+ // Already found greedy constructor that can be satisfied ->
+ // do not look any further, there are only less greedy constructors left.
+ break;
+ }
+ if (parameterCount < minNrOfArgs) {
+ continue;
+ }
+
+ ArgumentsHolder argsHolder;
+ Class>[] paramTypes = candidate.getParameterTypes();
+ if (resolvedValues != null) {
+ try {
+ String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
+ if (paramNames == null) {
+ ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
+ if (pnd != null) {
+ paramNames = pnd.getParameterNames(candidate);
+ }
+ }
+ argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
+ getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
+ }
+ catch (UnsatisfiedDependencyException ex) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
+ }
+ // Swallow and try next constructor.
+ if (causes == null) {
+ causes = new ArrayDeque<>(1);
+ }
+ causes.add(ex);
+ continue;
+ }
+ }
+ else {
+ // Explicit arguments given -> arguments length must match exactly.
+ if (parameterCount != explicitArgs.length) {
+ continue;
+ }
+ argsHolder = new ArgumentsHolder(explicitArgs);
+ }
+
+ int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
+ argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
+ // Choose this constructor if it represents the closest match.
+ if (typeDiffWeight < minTypeDiffWeight) {
+ constructorToUse = candidate;
+ argsHolderToUse = argsHolder;
+ argsToUse = argsHolder.arguments;
+ minTypeDiffWeight = typeDiffWeight;
+ ambiguousConstructors = null;
+ }
+ else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
+ if (ambiguousConstructors == null) {
+ ambiguousConstructors = new LinkedHashSet<>();
+ ambiguousConstructors.add(constructorToUse);
+ }
+ ambiguousConstructors.add(candidate);
+ }
+ }
+
+ if (constructorToUse == null) {
+ if (causes != null) {
+ UnsatisfiedDependencyException ex = causes.removeLast();
+ for (Exception cause : causes) {
+ this.beanFactory.onSuppressedException(cause);
+ }
+ throw ex;
+ }
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Could not resolve matching constructor " +
+ "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
+ }
+ else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Ambiguous constructor matches found in bean '" + beanName + "' " +
+ "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
+ ambiguousConstructors);
+ }
+
+ if (explicitArgs == null && argsHolderToUse != null) {
+ argsHolderToUse.storeCache(mbd, constructorToUse);
+ }
+ }
+
+ Assert.state(argsToUse != null, "Unresolved constructor arguments");
+ bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));
+ return bw;
+ }
+
+ private Object instantiate(
+ String beanName, RootBeanDefinition mbd, Constructor> constructorToUse, Object[] argsToUse) {
+
+ try {
+ InstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged((PrivilegedAction) () ->
+ strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse),
+ this.beanFactory.getAccessControlContext());
+ }
+ else {
+ return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
+ }
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Bean instantiation via constructor failed", ex);
+ }
+ }
+
+ /**
+ * Resolve the factory method in the specified bean definition, if possible.
+ * {@link RootBeanDefinition#getResolvedFactoryMethod()} can be checked for the result.
+ * @param mbd the bean definition to check
+ */
+ public void resolveFactoryMethodIfPossible(RootBeanDefinition mbd) {
+ Class> factoryClass;
+ boolean isStatic;
+ if (mbd.getFactoryBeanName() != null) {
+ factoryClass = this.beanFactory.getType(mbd.getFactoryBeanName());
+ isStatic = false;
+ }
+ else {
+ factoryClass = mbd.getBeanClass();
+ isStatic = true;
+ }
+ Assert.state(factoryClass != null, "Unresolvable factory class");
+ factoryClass = ClassUtils.getUserClass(factoryClass);
+
+ Method[] candidates = getCandidateMethods(factoryClass, mbd);
+ Method uniqueCandidate = null;
+ for (Method candidate : candidates) {
+ if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
+ if (uniqueCandidate == null) {
+ uniqueCandidate = candidate;
+ }
+ else if (isParamMismatch(uniqueCandidate, candidate)) {
+ uniqueCandidate = null;
+ break;
+ }
+ }
+ }
+ mbd.factoryMethodToIntrospect = uniqueCandidate;
+ }
+
+ private boolean isParamMismatch(Method uniqueCandidate, Method candidate) {
+ int uniqueCandidateParameterCount = uniqueCandidate.getParameterCount();
+ int candidateParameterCount = candidate.getParameterCount();
+ return (uniqueCandidateParameterCount != candidateParameterCount ||
+ !Arrays.equals(uniqueCandidate.getParameterTypes(), candidate.getParameterTypes()));
+ }
+
+ /**
+ * Retrieve all candidate methods for the given class, considering
+ * the {@link RootBeanDefinition#isNonPublicAccessAllowed()} flag.
+ * Called as the starting point for factory method determination.
+ */
+ private Method[] getCandidateMethods(Class> factoryClass, RootBeanDefinition mbd) {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged((PrivilegedAction) () ->
+ (mbd.isNonPublicAccessAllowed() ?
+ ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods()));
+ }
+ else {
+ return (mbd.isNonPublicAccessAllowed() ?
+ ReflectionUtils.getAllDeclaredMethods(factoryClass) : factoryClass.getMethods());
+ }
+ }
+
+ /**
+ * Instantiate the bean using a named factory method. The method may be static, if the
+ * bean definition parameter specifies a class, rather than a "factory-bean", or
+ * an instance variable on a factory object itself configured using Dependency Injection.
+ * Implementation requires iterating over the static or instance methods with the
+ * name specified in the RootBeanDefinition (the method may be overloaded) and trying
+ * to match with the parameters. We don't have the types attached to constructor args,
+ * so trial and error is the only way to go here. The explicitArgs array may contain
+ * argument values passed in programmatically via the corresponding getBean method.
+ * @param beanName the name of the bean
+ * @param mbd the merged bean definition for the bean
+ * @param explicitArgs argument values passed in programmatically via the getBean
+ * method, or {@code null} if none (-> use constructor argument values from bean definition)
+ * @return a BeanWrapper for the new instance
+ */
+ public BeanWrapper instantiateUsingFactoryMethod(
+ String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
+
+ BeanWrapperImpl bw = new BeanWrapperImpl();
+ this.beanFactory.initBeanWrapper(bw);
+
+ Object factoryBean;
+ Class> factoryClass;
+ boolean isStatic;
+
+ String factoryBeanName = mbd.getFactoryBeanName();
+ if (factoryBeanName != null) {
+ if (factoryBeanName.equals(beanName)) {
+ throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
+ "factory-bean reference points back to the same bean definition");
+ }
+ factoryBean = this.beanFactory.getBean(factoryBeanName);
+ if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
+ throw new ImplicitlyAppearedSingletonException();
+ }
+ this.beanFactory.registerDependentBean(factoryBeanName, beanName);
+ factoryClass = factoryBean.getClass();
+ isStatic = false;
+ }
+ else {
+ // It's a static factory method on the bean class.
+ if (!mbd.hasBeanClass()) {
+ throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
+ "bean definition declares neither a bean class nor a factory-bean reference");
+ }
+ factoryBean = null;
+ factoryClass = mbd.getBeanClass();
+ isStatic = true;
+ }
+
+ Method factoryMethodToUse = null;
+ ArgumentsHolder argsHolderToUse = null;
+ Object[] argsToUse = null;
+
+ if (explicitArgs != null) {
+ argsToUse = explicitArgs;
+ }
+ else {
+ Object[] argsToResolve = null;
+ synchronized (mbd.constructorArgumentLock) {
+ factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
+ if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
+ // Found a cached factory method...
+ argsToUse = mbd.resolvedConstructorArguments;
+ if (argsToUse == null) {
+ argsToResolve = mbd.preparedConstructorArguments;
+ }
+ }
+ }
+ if (argsToResolve != null) {
+ argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve);
+ }
+ }
+
+ if (factoryMethodToUse == null || argsToUse == null) {
+ // Need to determine the factory method...
+ // Try all methods with this name to see if they match the given arguments.
+ factoryClass = ClassUtils.getUserClass(factoryClass);
+
+ List candidates = null;
+ if (mbd.isFactoryMethodUnique) {
+ if (factoryMethodToUse == null) {
+ factoryMethodToUse = mbd.getResolvedFactoryMethod();
+ }
+ if (factoryMethodToUse != null) {
+ candidates = Collections.singletonList(factoryMethodToUse);
+ }
+ }
+ if (candidates == null) {
+ candidates = new ArrayList<>();
+ Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
+ for (Method candidate : rawCandidates) {
+ if (Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) {
+ candidates.add(candidate);
+ }
+ }
+ }
+
+ if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
+ Method uniqueCandidate = candidates.get(0);
+ if (uniqueCandidate.getParameterCount() == 0) {
+ mbd.factoryMethodToIntrospect = uniqueCandidate;
+ synchronized (mbd.constructorArgumentLock) {
+ mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
+ mbd.constructorArgumentsResolved = true;
+ mbd.resolvedConstructorArguments = EMPTY_ARGS;
+ }
+ bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
+ return bw;
+ }
+ }
+
+ if (candidates.size() > 1) { // explicitly skip immutable singletonList
+ candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
+ }
+
+ ConstructorArgumentValues resolvedValues = null;
+ boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
+ int minTypeDiffWeight = Integer.MAX_VALUE;
+ Set ambiguousFactoryMethods = null;
+
+ int minNrOfArgs;
+ if (explicitArgs != null) {
+ minNrOfArgs = explicitArgs.length;
+ }
+ else {
+ // We don't have arguments passed in programmatically, so we need to resolve the
+ // arguments specified in the constructor arguments held in the bean definition.
+ if (mbd.hasConstructorArgumentValues()) {
+ ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
+ resolvedValues = new ConstructorArgumentValues();
+ minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
+ }
+ else {
+ minNrOfArgs = 0;
+ }
+ }
+
+ Deque causes = null;
+
+ for (Method candidate : candidates) {
+ int parameterCount = candidate.getParameterCount();
+
+ if (parameterCount >= minNrOfArgs) {
+ ArgumentsHolder argsHolder;
+
+ Class>[] paramTypes = candidate.getParameterTypes();
+ if (explicitArgs != null) {
+ // Explicit arguments given -> arguments length must match exactly.
+ if (paramTypes.length != explicitArgs.length) {
+ continue;
+ }
+ argsHolder = new ArgumentsHolder(explicitArgs);
+ }
+ else {
+ // Resolved constructor arguments: type conversion and/or autowiring necessary.
+ try {
+ String[] paramNames = null;
+ ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
+ if (pnd != null) {
+ paramNames = pnd.getParameterNames(candidate);
+ }
+ argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
+ paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
+ }
+ catch (UnsatisfiedDependencyException ex) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "': " + ex);
+ }
+ // Swallow and try next overloaded factory method.
+ if (causes == null) {
+ causes = new ArrayDeque<>(1);
+ }
+ causes.add(ex);
+ continue;
+ }
+ }
+
+ int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
+ argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
+ // Choose this factory method if it represents the closest match.
+ if (typeDiffWeight < minTypeDiffWeight) {
+ factoryMethodToUse = candidate;
+ argsHolderToUse = argsHolder;
+ argsToUse = argsHolder.arguments;
+ minTypeDiffWeight = typeDiffWeight;
+ ambiguousFactoryMethods = null;
+ }
+ // Find out about ambiguity: In case of the same type difference weight
+ // for methods with the same number of parameters, collect such candidates
+ // and eventually raise an ambiguity exception.
+ // However, only perform that check in non-lenient constructor resolution mode,
+ // and explicitly ignore overridden methods (with the same parameter signature).
+ else if (factoryMethodToUse != null && typeDiffWeight == minTypeDiffWeight &&
+ !mbd.isLenientConstructorResolution() &&
+ paramTypes.length == factoryMethodToUse.getParameterCount() &&
+ !Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {
+ if (ambiguousFactoryMethods == null) {
+ ambiguousFactoryMethods = new LinkedHashSet<>();
+ ambiguousFactoryMethods.add(factoryMethodToUse);
+ }
+ ambiguousFactoryMethods.add(candidate);
+ }
+ }
+ }
+
+ if (factoryMethodToUse == null || argsToUse == null) {
+ if (causes != null) {
+ UnsatisfiedDependencyException ex = causes.removeLast();
+ for (Exception cause : causes) {
+ this.beanFactory.onSuppressedException(cause);
+ }
+ throw ex;
+ }
+ List argTypes = new ArrayList<>(minNrOfArgs);
+ if (explicitArgs != null) {
+ for (Object arg : explicitArgs) {
+ argTypes.add(arg != null ? arg.getClass().getSimpleName() : "null");
+ }
+ }
+ else if (resolvedValues != null) {
+ Set valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
+ valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
+ valueHolders.addAll(resolvedValues.getGenericArgumentValues());
+ for (ValueHolder value : valueHolders) {
+ String argType = (value.getType() != null ? ClassUtils.getShortName(value.getType()) :
+ (value.getValue() != null ? value.getValue().getClass().getSimpleName() : "null"));
+ argTypes.add(argType);
+ }
+ }
+ String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "No matching factory method found: " +
+ (mbd.getFactoryBeanName() != null ?
+ "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
+ "factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
+ "Check that a method with the specified name " +
+ (minNrOfArgs > 0 ? "and arguments " : "") +
+ "exists and that it is " +
+ (isStatic ? "static" : "non-static") + ".");
+ }
+ else if (void.class == factoryMethodToUse.getReturnType()) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Invalid factory method '" + mbd.getFactoryMethodName() +
+ "': needs to have a non-void return type!");
+ }
+ else if (ambiguousFactoryMethods != null) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Ambiguous factory method matches found in bean '" + beanName + "' " +
+ "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
+ ambiguousFactoryMethods);
+ }
+
+ if (explicitArgs == null && argsHolderToUse != null) {
+ mbd.factoryMethodToIntrospect = factoryMethodToUse;
+ argsHolderToUse.storeCache(mbd, factoryMethodToUse);
+ }
+ }
+
+ bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
+ return bw;
+ }
+
+ private Object instantiate(String beanName, RootBeanDefinition mbd,
+ @Nullable Object factoryBean, Method factoryMethod, Object[] args) {
+
+ try {
+ if (System.getSecurityManager() != null) {
+ return AccessController.doPrivileged((PrivilegedAction) () ->
+ this.beanFactory.getInstantiationStrategy().instantiate(
+ mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args),
+ this.beanFactory.getAccessControlContext());
+ }
+ else {
+ return this.beanFactory.getInstantiationStrategy().instantiate(
+ mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
+ }
+ }
+ catch (Throwable ex) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Bean instantiation via factory method failed", ex);
+ }
+ }
+
+ /**
+ * Resolve the constructor arguments for this bean into the resolvedValues object.
+ * This may involve looking up other beans.
+ * This method is also used for handling invocations of static factory methods.
+ */
+ private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
+ ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
+
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
+ BeanDefinitionValueResolver valueResolver =
+ new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
+
+ int minNrOfArgs = cargs.getArgumentCount();
+
+ for (Map.Entry entry : cargs.getIndexedArgumentValues().entrySet()) {
+ int index = entry.getKey();
+ if (index < 0) {
+ throw new BeanCreationException(mbd.getResourceDescription(), beanName,
+ "Invalid constructor argument index: " + index);
+ }
+ if (index + 1 > minNrOfArgs) {
+ minNrOfArgs = index + 1;
+ }
+ ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue();
+ if (valueHolder.isConverted()) {
+ resolvedValues.addIndexedArgumentValue(index, valueHolder);
+ }
+ else {
+ Object resolvedValue =
+ valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
+ ConstructorArgumentValues.ValueHolder resolvedValueHolder =
+ new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName());
+ resolvedValueHolder.setSource(valueHolder);
+ resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder);
+ }
+ }
+
+ for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
+ if (valueHolder.isConverted()) {
+ resolvedValues.addGenericArgumentValue(valueHolder);
+ }
+ else {
+ Object resolvedValue =
+ valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue());
+ ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(
+ resolvedValue, valueHolder.getType(), valueHolder.getName());
+ resolvedValueHolder.setSource(valueHolder);
+ resolvedValues.addGenericArgumentValue(resolvedValueHolder);
+ }
+ }
+
+ return minNrOfArgs;
+ }
+
+ /**
+ * Create an array of arguments to invoke a constructor or factory method,
+ * given the resolved constructor argument values.
+ */
+ private ArgumentsHolder createArgumentArray(
+ String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues,
+ BeanWrapper bw, Class>[] paramTypes, @Nullable String[] paramNames, Executable executable,
+ boolean autowiring, boolean fallback) throws UnsatisfiedDependencyException {
+
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
+
+ ArgumentsHolder args = new ArgumentsHolder(paramTypes.length);
+ Set usedValueHolders = new HashSet<>(paramTypes.length);
+ Set autowiredBeanNames = new LinkedHashSet<>(4);
+
+ for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
+ Class> paramType = paramTypes[paramIndex];
+ String paramName = (paramNames != null ? paramNames[paramIndex] : "");
+ // Try to find matching constructor argument value, either indexed or generic.
+ ConstructorArgumentValues.ValueHolder valueHolder = null;
+ if (resolvedValues != null) {
+ valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders);
+ // If we couldn't find a direct match and are not supposed to autowire,
+ // let's try the next generic, untyped argument value as fallback:
+ // it could match after type conversion (for example, String -> int).
+ if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) {
+ valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders);
+ }
+ }
+ if (valueHolder != null) {
+ // We found a potential match - let's give it a try.
+ // Do not consider the same value definition multiple times!
+ usedValueHolders.add(valueHolder);
+ Object originalValue = valueHolder.getValue();
+ Object convertedValue;
+ if (valueHolder.isConverted()) {
+ convertedValue = valueHolder.getConvertedValue();
+ args.preparedArguments[paramIndex] = convertedValue;
+ }
+ else {
+ MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
+ try {
+ convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam);
+ }
+ catch (TypeMismatchException ex) {
+ throw new UnsatisfiedDependencyException(
+ mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
+ "Could not convert argument value of type [" +
+ ObjectUtils.nullSafeClassName(valueHolder.getValue()) +
+ "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
+ }
+ Object sourceHolder = valueHolder.getSource();
+ if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) {
+ Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue();
+ args.resolveNecessary = true;
+ args.preparedArguments[paramIndex] = sourceValue;
+ }
+ }
+ args.arguments[paramIndex] = convertedValue;
+ args.rawArguments[paramIndex] = originalValue;
+ }
+ else {
+ MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex);
+ // No explicit match found: we're either supposed to autowire or
+ // have to fail creating an argument array for the given constructor.
+ if (!autowiring) {
+ throw new UnsatisfiedDependencyException(
+ mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
+ "Ambiguous argument values for parameter of type [" + paramType.getName() +
+ "] - did you specify the correct bean references as arguments?");
+ }
+ try {
+ Object autowiredArgument = resolveAutowiredArgument(
+ methodParam, beanName, autowiredBeanNames, converter, fallback);
+ args.rawArguments[paramIndex] = autowiredArgument;
+ args.arguments[paramIndex] = autowiredArgument;
+ args.preparedArguments[paramIndex] = autowiredArgumentMarker;
+ args.resolveNecessary = true;
+ }
+ catch (BeansException ex) {
+ throw new UnsatisfiedDependencyException(
+ mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex);
+ }
+ }
+ }
+
+ for (String autowiredBeanName : autowiredBeanNames) {
+ this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Autowiring by type from bean name '" + beanName +
+ "' via " + (executable instanceof Constructor ? "constructor" : "factory method") +
+ " to bean named '" + autowiredBeanName + "'");
+ }
+ }
+
+ return args;
+ }
+
+ /**
+ * Resolve the prepared arguments stored in the given bean definition.
+ */
+ private Object[] resolvePreparedArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
+ Executable executable, Object[] argsToResolve) {
+
+ TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
+ TypeConverter converter = (customConverter != null ? customConverter : bw);
+ BeanDefinitionValueResolver valueResolver =
+ new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter);
+ Class>[] paramTypes = executable.getParameterTypes();
+
+ Object[] resolvedArgs = new Object[argsToResolve.length];
+ for (int argIndex = 0; argIndex < argsToResolve.length; argIndex++) {
+ Object argValue = argsToResolve[argIndex];
+ MethodParameter methodParam = MethodParameter.forExecutable(executable, argIndex);
+ if (argValue == autowiredArgumentMarker) {
+ argValue = resolveAutowiredArgument(methodParam, beanName, null, converter, true);
+ }
+ else if (argValue instanceof BeanMetadataElement) {
+ argValue = valueResolver.resolveValueIfNecessary("constructor argument", argValue);
+ }
+ else if (argValue instanceof String) {
+ argValue = this.beanFactory.evaluateBeanDefinitionString((String) argValue, mbd);
+ }
+ Class> paramType = paramTypes[argIndex];
+ try {
+ resolvedArgs[argIndex] = converter.convertIfNecessary(argValue, paramType, methodParam);
+ }
+ catch (TypeMismatchException ex) {
+ throw new UnsatisfiedDependencyException(
+ mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam),
+ "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(argValue) +
+ "] to required type [" + paramType.getName() + "]: " + ex.getMessage());
+ }
+ }
+ return resolvedArgs;
+ }
+
+ protected Constructor> getUserDeclaredConstructor(Constructor> constructor) {
+ Class> declaringClass = constructor.getDeclaringClass();
+ Class> userClass = ClassUtils.getUserClass(declaringClass);
+ if (userClass != declaringClass) {
+ try {
+ return userClass.getDeclaredConstructor(constructor.getParameterTypes());
+ }
+ catch (NoSuchMethodException ex) {
+ // No equivalent constructor on user class (superclass)...
+ // Let's proceed with the given constructor as we usually would.
+ }
+ }
+ return constructor;
+ }
+
+ /**
+ * Template method for resolving the specified argument which is supposed to be autowired.
+ */
+ @Nullable
+ protected Object resolveAutowiredArgument(MethodParameter param, String beanName,
+ @Nullable Set autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {
+
+ Class> paramType = param.getParameterType();
+ if (InjectionPoint.class.isAssignableFrom(paramType)) {
+ InjectionPoint injectionPoint = currentInjectionPoint.get();
+ if (injectionPoint == null) {
+ throw new IllegalStateException("No current InjectionPoint available for " + param);
+ }
+ return injectionPoint;
+ }
+ try {
+ return this.beanFactory.resolveDependency(
+ new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
+ }
+ catch (NoUniqueBeanDefinitionException ex) {
+ throw ex;
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ if (fallback) {
+ // Single constructor or factory method -> let's return an empty array/collection
+ // for e.g. a vararg or a non-null List/Set/Map parameter.
+ if (paramType.isArray()) {
+ return Array.newInstance(paramType.getComponentType(), 0);
+ }
+ else if (CollectionFactory.isApproximableCollectionType(paramType)) {
+ return CollectionFactory.createCollection(paramType, 0);
+ }
+ else if (CollectionFactory.isApproximableMapType(paramType)) {
+ return CollectionFactory.createMap(paramType, 0);
+ }
+ }
+ throw ex;
+ }
+ }
+
+ static InjectionPoint setCurrentInjectionPoint(@Nullable InjectionPoint injectionPoint) {
+ InjectionPoint old = currentInjectionPoint.get();
+ if (injectionPoint != null) {
+ currentInjectionPoint.set(injectionPoint);
+ }
+ else {
+ currentInjectionPoint.remove();
+ }
+ return old;
+ }
+
+
+ /**
+ * Private inner class for holding argument combinations.
+ */
+ private static class ArgumentsHolder {
+
+ public final Object[] rawArguments;
+
+ public final Object[] arguments;
+
+ public final Object[] preparedArguments;
+
+ public boolean resolveNecessary = false;
+
+ public ArgumentsHolder(int size) {
+ this.rawArguments = new Object[size];
+ this.arguments = new Object[size];
+ this.preparedArguments = new Object[size];
+ }
+
+ public ArgumentsHolder(Object[] args) {
+ this.rawArguments = args;
+ this.arguments = args;
+ this.preparedArguments = args;
+ }
+
+ public int getTypeDifferenceWeight(Class>[] paramTypes) {
+ // If valid arguments found, determine type difference weight.
+ // Try type difference weight on both the converted arguments and
+ // the raw arguments. If the raw weight is better, use it.
+ // Decrease raw weight by 1024 to prefer it over equal converted weight.
+ int typeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.arguments);
+ int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024;
+ return Math.min(rawTypeDiffWeight, typeDiffWeight);
+ }
+
+ public int getAssignabilityWeight(Class>[] paramTypes) {
+ for (int i = 0; i < paramTypes.length; i++) {
+ if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) {
+ return Integer.MAX_VALUE;
+ }
+ }
+ for (int i = 0; i < paramTypes.length; i++) {
+ if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) {
+ return Integer.MAX_VALUE - 512;
+ }
+ }
+ return Integer.MAX_VALUE - 1024;
+ }
+
+ public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) {
+ synchronized (mbd.constructorArgumentLock) {
+ mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod;
+ mbd.constructorArgumentsResolved = true;
+ if (this.resolveNecessary) {
+ mbd.preparedConstructorArguments = this.preparedArguments;
+ }
+ else {
+ mbd.resolvedConstructorArguments = this.arguments;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Delegate for checking Java 6's {@link ConstructorProperties} annotation.
+ */
+ private static class ConstructorPropertiesChecker {
+
+ @Nullable
+ public static String[] evaluate(Constructor> candidate, int paramCount) {
+ ConstructorProperties cp = candidate.getAnnotation(ConstructorProperties.class);
+ if (cp != null) {
+ String[] names = cp.value();
+ if (names.length != paramCount) {
+ throw new IllegalStateException("Constructor annotated with @ConstructorProperties but not " +
+ "corresponding to actual number of parameters (" + paramCount + "): " + candidate);
+ }
+ return names;
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultBeanNameGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultBeanNameGenerator.java
new file mode 100644
index 0000000..9632f5a
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultBeanNameGenerator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.support;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+
+/**
+ * Default implementation of the {@link BeanNameGenerator} interface, delegating to
+ * {@link BeanDefinitionReaderUtils#generateBeanName(BeanDefinition, BeanDefinitionRegistry)}.
+ *
+ * @author Juergen Hoeller
+ * @since 2.0.3
+ */
+public class DefaultBeanNameGenerator implements BeanNameGenerator {
+
+ /**
+ * A convenient constant for a default {@code DefaultBeanNameGenerator} instance,
+ * as used for {@link AbstractBeanDefinitionReader} setup.
+ * @since 5.2
+ */
+ public static final DefaultBeanNameGenerator INSTANCE = new DefaultBeanNameGenerator();
+
+
+ @Override
+ public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
+ return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
+ }
+
+}
diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
new file mode 100644
index 0000000..dc79d3c
--- /dev/null
+++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultListableBeanFactory.java
@@ -0,0 +1,2163 @@
+/*
+ * Copyright 2002-2020 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.support;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import javax.inject.Provider;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.TypeConverter;
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.beans.factory.BeanCurrentlyInCreationException;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.BeanFactoryUtils;
+import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
+import org.springframework.beans.factory.CannotLoadBeanClassException;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.InjectionPoint;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.SmartFactoryBean;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.config.DependencyDescriptor;
+import org.springframework.beans.factory.config.NamedBeanHolder;
+import org.springframework.core.OrderComparator;
+import org.springframework.core.ResolvableType;
+import org.springframework.core.annotation.MergedAnnotation;
+import org.springframework.core.annotation.MergedAnnotations;
+import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
+import org.springframework.core.log.LogMessage;
+import org.springframework.core.metrics.StartupStep;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.CompositeIterator;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * Spring's default implementation of the {@link ConfigurableListableBeanFactory}
+ * and {@link BeanDefinitionRegistry} interfaces: a full-fledged bean factory
+ * based on bean definition metadata, extensible through post-processors.
+ *
+ * Typical usage is registering all bean definitions first (possibly read
+ * from a bean definition file), before accessing beans. Bean lookup by name
+ * is therefore an inexpensive operation in a local bean definition table,
+ * operating on pre-resolved bean definition metadata objects.
+ *
+ *
Note that readers for specific bean definition formats are typically
+ * implemented separately rather than as bean factory subclasses: see for example
+ * {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
+ *
+ *
For an alternative implementation of the
+ * {@link org.springframework.beans.factory.ListableBeanFactory} interface,
+ * have a look at {@link StaticListableBeanFactory}, which manages existing
+ * bean instances rather than creating new ones based on bean definitions.
+ *
+ * @author Rod Johnson
+ * @author Juergen Hoeller
+ * @author Sam Brannen
+ * @author Costin Leau
+ * @author Chris Beams
+ * @author Phillip Webb
+ * @author Stephane Nicoll
+ * @since 16 April 2001
+ * @see #registerBeanDefinition
+ * @see #addBeanPostProcessor
+ * @see #getBean
+ * @see #resolveDependency
+ */
+@SuppressWarnings("serial")
+public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
+ implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
+
+ @Nullable
+ private static Class> javaxInjectProviderClass;
+
+ static {
+ try {
+ javaxInjectProviderClass =
+ ClassUtils.forName("javax.inject.Provider", DefaultListableBeanFactory.class.getClassLoader());
+ }
+ catch (ClassNotFoundException ex) {
+ // JSR-330 API not available - Provider interface simply not supported then.
+ javaxInjectProviderClass = null;
+ }
+ }
+
+
+ /** Map from serialized id to factory instance. */
+ private static final Map> serializableFactories =
+ new ConcurrentHashMap<>(8);
+
+ /** Optional id for this factory, for serialization purposes. */
+ @Nullable
+ private String serializationId;
+
+ /** Whether to allow re-registration of a different definition with the same name. */
+ private boolean allowBeanDefinitionOverriding = true;
+
+ /** Whether to allow eager class loading even for lazy-init beans. */
+ private boolean allowEagerClassLoading = true;
+
+ /** Optional OrderComparator for dependency Lists and arrays. */
+ @Nullable
+ private Comparator dependencyComparator;
+
+ /** Resolver to use for checking if a bean definition is an autowire candidate. */
+ private AutowireCandidateResolver autowireCandidateResolver = SimpleAutowireCandidateResolver.INSTANCE;
+
+ /** Map from dependency type to corresponding autowired value. */
+ private final Map, Object> resolvableDependencies = new ConcurrentHashMap<>(16);
+
+ /** Map of bean definition objects, keyed by bean name. */
+ private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);
+
+ /** Map from bean name to merged BeanDefinitionHolder. */
+ private final Map mergedBeanDefinitionHolders = new ConcurrentHashMap<>(256);
+
+ /** Map of singleton and non-singleton bean names, keyed by dependency type. */
+ private final Map, String[]> allBeanNamesByType = new ConcurrentHashMap<>(64);
+
+ /** Map of singleton-only bean names, keyed by dependency type. */
+ private final Map, String[]> singletonBeanNamesByType = new ConcurrentHashMap<>(64);
+
+ /** List of bean definition names, in registration order. */
+ private volatile List beanDefinitionNames = new ArrayList<>(256);
+
+ /** List of names of manually registered singletons, in registration order. */
+ private volatile Set manualSingletonNames = new LinkedHashSet<>(16);
+
+ /** Cached array of bean definition names in case of frozen configuration. */
+ @Nullable
+ private volatile String[] frozenBeanDefinitionNames;
+
+ /** Whether bean definition metadata may be cached for all beans. */
+ private volatile boolean configurationFrozen;
+
+
+ /**
+ * Create a new DefaultListableBeanFactory.
+ */
+ public DefaultListableBeanFactory() {
+ super();
+ }
+
+ /**
+ * Create a new DefaultListableBeanFactory with the given parent.
+ * @param parentBeanFactory the parent BeanFactory
+ */
+ public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
+ super(parentBeanFactory);
+ }
+
+
+ /**
+ * Specify an id for serialization purposes, allowing this BeanFactory to be
+ * deserialized from this id back into the BeanFactory object, if needed.
+ */
+ public void setSerializationId(@Nullable String serializationId) {
+ if (serializationId != null) {
+ serializableFactories.put(serializationId, new WeakReference<>(this));
+ }
+ else if (this.serializationId != null) {
+ serializableFactories.remove(this.serializationId);
+ }
+ this.serializationId = serializationId;
+ }
+
+ /**
+ * Return an id for serialization purposes, if specified, allowing this BeanFactory
+ * to be deserialized from this id back into the BeanFactory object, if needed.
+ * @since 4.1.2
+ */
+ @Nullable
+ public String getSerializationId() {
+ return this.serializationId;
+ }
+
+ /**
+ * Set whether it should be allowed to override bean definitions by registering
+ * a different definition with the same name, automatically replacing the former.
+ * If not, an exception will be thrown. This also applies to overriding aliases.
+ * Default is "true".
+ * @see #registerBeanDefinition
+ */
+ public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
+ this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
+ }
+
+ /**
+ * Return whether it should be allowed to override bean definitions by registering
+ * a different definition with the same name, automatically replacing the former.
+ * @since 4.1.2
+ */
+ public boolean isAllowBeanDefinitionOverriding() {
+ return this.allowBeanDefinitionOverriding;
+ }
+
+ /**
+ * Set whether the factory is allowed to eagerly load bean classes
+ * even for bean definitions that are marked as "lazy-init".
+ *
Default is "true". Turn this flag off to suppress class loading
+ * for lazy-init beans unless such a bean is explicitly requested.
+ * In particular, by-type lookups will then simply ignore bean definitions
+ * without resolved class name, instead of loading the bean classes on
+ * demand just to perform a type check.
+ * @see AbstractBeanDefinition#setLazyInit
+ */
+ public void setAllowEagerClassLoading(boolean allowEagerClassLoading) {
+ this.allowEagerClassLoading = allowEagerClassLoading;
+ }
+
+ /**
+ * Return whether the factory is allowed to eagerly load bean classes
+ * even for bean definitions that are marked as "lazy-init".
+ * @since 4.1.2
+ */
+ public boolean isAllowEagerClassLoading() {
+ return this.allowEagerClassLoading;
+ }
+
+ /**
+ * Set a {@link java.util.Comparator} for dependency Lists and arrays.
+ * @since 4.0
+ * @see org.springframework.core.OrderComparator
+ * @see org.springframework.core.annotation.AnnotationAwareOrderComparator
+ */
+ public void setDependencyComparator(@Nullable Comparator dependencyComparator) {
+ this.dependencyComparator = dependencyComparator;
+ }
+
+ /**
+ * Return the dependency comparator for this BeanFactory (may be {@code null}.
+ * @since 4.0
+ */
+ @Nullable
+ public Comparator getDependencyComparator() {
+ return this.dependencyComparator;
+ }
+
+ /**
+ * Set a custom autowire candidate resolver for this BeanFactory to use
+ * when deciding whether a bean definition should be considered as a
+ * candidate for autowiring.
+ */
+ public void setAutowireCandidateResolver(AutowireCandidateResolver autowireCandidateResolver) {
+ Assert.notNull(autowireCandidateResolver, "AutowireCandidateResolver must not be null");
+ if (autowireCandidateResolver instanceof BeanFactoryAware) {
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(this);
+ return null;
+ }, getAccessControlContext());
+ }
+ else {
+ ((BeanFactoryAware) autowireCandidateResolver).setBeanFactory(this);
+ }
+ }
+ this.autowireCandidateResolver = autowireCandidateResolver;
+ }
+
+ /**
+ * Return the autowire candidate resolver for this BeanFactory (never {@code null}).
+ */
+ public AutowireCandidateResolver getAutowireCandidateResolver() {
+ return this.autowireCandidateResolver;
+ }
+
+
+ @Override
+ public void copyConfigurationFrom(ConfigurableBeanFactory otherFactory) {
+ super.copyConfigurationFrom(otherFactory);
+ if (otherFactory instanceof DefaultListableBeanFactory) {
+ DefaultListableBeanFactory otherListableFactory = (DefaultListableBeanFactory) otherFactory;
+ this.allowBeanDefinitionOverriding = otherListableFactory.allowBeanDefinitionOverriding;
+ this.allowEagerClassLoading = otherListableFactory.allowEagerClassLoading;
+ this.dependencyComparator = otherListableFactory.dependencyComparator;
+ // A clone of the AutowireCandidateResolver since it is potentially BeanFactoryAware
+ setAutowireCandidateResolver(otherListableFactory.getAutowireCandidateResolver().cloneIfNecessary());
+ // Make resolvable dependencies (e.g. ResourceLoader) available here as well
+ this.resolvableDependencies.putAll(otherListableFactory.resolvableDependencies);
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of remaining BeanFactory methods
+ //---------------------------------------------------------------------
+
+ @Override
+ public T getBean(Class requiredType) throws BeansException {
+ return getBean(requiredType, (Object[]) null);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T getBean(Class requiredType, @Nullable Object... args) throws BeansException {
+ Assert.notNull(requiredType, "Required type must not be null");
+ Object resolved = resolveBean(ResolvableType.forRawClass(requiredType), args, false);
+ if (resolved == null) {
+ throw new NoSuchBeanDefinitionException(requiredType);
+ }
+ return (T) resolved;
+ }
+
+ @Override
+ public ObjectProvider getBeanProvider(Class requiredType) {
+ Assert.notNull(requiredType, "Required type must not be null");
+ return getBeanProvider(ResolvableType.forRawClass(requiredType), true);
+ }
+
+ @Override
+ public ObjectProvider getBeanProvider(ResolvableType requiredType) {
+ return getBeanProvider(requiredType, true);
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ListableBeanFactory interface
+ //---------------------------------------------------------------------
+
+ @Override
+ public boolean containsBeanDefinition(String beanName) {
+ Assert.notNull(beanName, "Bean name must not be null");
+ return this.beanDefinitionMap.containsKey(beanName);
+ }
+
+ @Override
+ public int getBeanDefinitionCount() {
+ return this.beanDefinitionMap.size();
+ }
+
+ @Override
+ public String[] getBeanDefinitionNames() {
+ String[] frozenNames = this.frozenBeanDefinitionNames;
+ if (frozenNames != null) {
+ return frozenNames.clone();
+ }
+ else {
+ return StringUtils.toStringArray(this.beanDefinitionNames);
+ }
+ }
+
+ @Override
+ public ObjectProvider getBeanProvider(Class requiredType, boolean allowEagerInit) {
+ Assert.notNull(requiredType, "Required type must not be null");
+ return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit);
+ }
+
+ @Override
+ public ObjectProvider getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) {
+ return new BeanObjectProvider() {
+ @Override
+ public T getObject() throws BeansException {
+ T resolved = resolveBean(requiredType, null, false);
+ if (resolved == null) {
+ throw new NoSuchBeanDefinitionException(requiredType);
+ }
+ return resolved;
+ }
+ @Override
+ public T getObject(Object... args) throws BeansException {
+ T resolved = resolveBean(requiredType, args, false);
+ if (resolved == null) {
+ throw new NoSuchBeanDefinitionException(requiredType);
+ }
+ return resolved;
+ }
+ @Override
+ @Nullable
+ public T getIfAvailable() throws BeansException {
+ try {
+ return resolveBean(requiredType, null, false);
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope
+ return null;
+ }
+ }
+ @Override
+ public void ifAvailable(Consumer dependencyConsumer) throws BeansException {
+ T dependency = getIfAvailable();
+ if (dependency != null) {
+ try {
+ dependencyConsumer.accept(dependency);
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope, even on scoped proxy invocation
+ }
+ }
+ }
+ @Override
+ @Nullable
+ public T getIfUnique() throws BeansException {
+ try {
+ return resolveBean(requiredType, null, true);
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope
+ return null;
+ }
+ }
+ @Override
+ public void ifUnique(Consumer dependencyConsumer) throws BeansException {
+ T dependency = getIfUnique();
+ if (dependency != null) {
+ try {
+ dependencyConsumer.accept(dependency);
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope, even on scoped proxy invocation
+ }
+ }
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public Stream stream() {
+ return Arrays.stream(getBeanNamesForTypedStream(requiredType, allowEagerInit))
+ .map(name -> (T) getBean(name))
+ .filter(bean -> !(bean instanceof NullBean));
+ }
+ @SuppressWarnings("unchecked")
+ @Override
+ public Stream orderedStream() {
+ String[] beanNames = getBeanNamesForTypedStream(requiredType, allowEagerInit);
+ if (beanNames.length == 0) {
+ return Stream.empty();
+ }
+ Map matchingBeans = CollectionUtils.newLinkedHashMap(beanNames.length);
+ for (String beanName : beanNames) {
+ Object beanInstance = getBean(beanName);
+ if (!(beanInstance instanceof NullBean)) {
+ matchingBeans.put(beanName, (T) beanInstance);
+ }
+ }
+ Stream stream = matchingBeans.values().stream();
+ return stream.sorted(adaptOrderComparator(matchingBeans));
+ }
+ };
+ }
+
+ @Nullable
+ private T resolveBean(ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) {
+ NamedBeanHolder namedBean = resolveNamedBean(requiredType, args, nonUniqueAsNull);
+ if (namedBean != null) {
+ return namedBean.getBeanInstance();
+ }
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof DefaultListableBeanFactory) {
+ return ((DefaultListableBeanFactory) parent).resolveBean(requiredType, args, nonUniqueAsNull);
+ }
+ else if (parent != null) {
+ ObjectProvider parentProvider = parent.getBeanProvider(requiredType);
+ if (args != null) {
+ return parentProvider.getObject(args);
+ }
+ else {
+ return (nonUniqueAsNull ? parentProvider.getIfUnique() : parentProvider.getIfAvailable());
+ }
+ }
+ return null;
+ }
+
+ private String[] getBeanNamesForTypedStream(ResolvableType requiredType, boolean allowEagerInit) {
+ return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, allowEagerInit);
+ }
+
+ @Override
+ public String[] getBeanNamesForType(ResolvableType type) {
+ return getBeanNamesForType(type, true, true);
+ }
+
+ @Override
+ public String[] getBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
+ Class> resolved = type.resolve();
+ if (resolved != null && !type.hasGenerics()) {
+ return getBeanNamesForType(resolved, includeNonSingletons, allowEagerInit);
+ }
+ else {
+ return doGetBeanNamesForType(type, includeNonSingletons, allowEagerInit);
+ }
+ }
+
+ @Override
+ public String[] getBeanNamesForType(@Nullable Class> type) {
+ return getBeanNamesForType(type, true, true);
+ }
+
+ @Override
+ public String[] getBeanNamesForType(@Nullable Class> type, boolean includeNonSingletons, boolean allowEagerInit) {
+ if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
+ return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
+ }
+ Map, String[]> cache =
+ (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
+ String[] resolvedBeanNames = cache.get(type);
+ if (resolvedBeanNames != null) {
+ return resolvedBeanNames;
+ }
+ resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
+ if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
+ cache.put(type, resolvedBeanNames);
+ }
+ return resolvedBeanNames;
+ }
+
+ private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
+ List result = new ArrayList<>();
+
+ // Check all bean definitions.
+ for (String beanName : this.beanDefinitionNames) {
+ // Only consider bean as eligible if the bean name is not defined as alias for some other bean.
+ if (!isAlias(beanName)) {
+ try {
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ // Only check bean definition if it is complete.
+ if (!mbd.isAbstract() && (allowEagerInit ||
+ (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
+ !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
+ boolean isFactoryBean = isFactoryBean(beanName, mbd);
+ BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
+ boolean matchFound = false;
+ boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));
+ boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());
+ if (!isFactoryBean) {
+ if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {
+ matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
+ }
+ }
+ else {
+ if (includeNonSingletons || isNonLazyDecorated ||
+ (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {
+ matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
+ }
+ if (!matchFound) {
+ // In case of FactoryBean, try to match FactoryBean instance itself next.
+ beanName = FACTORY_BEAN_PREFIX + beanName;
+ matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);
+ }
+ }
+ if (matchFound) {
+ result.add(beanName);
+ }
+ }
+ }
+ catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {
+ if (allowEagerInit) {
+ throw ex;
+ }
+ // Probably a placeholder: let's ignore it for type matching purposes.
+ LogMessage message = (ex instanceof CannotLoadBeanClassException ?
+ LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :
+ LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName));
+ logger.trace(message, ex);
+ // Register exception, in case the bean was accidentally unresolvable.
+ onSuppressedException(ex);
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ // Bean definition got removed while we were iterating -> ignore.
+ }
+ }
+ }
+
+ // Check manually registered singletons too.
+ for (String beanName : this.manualSingletonNames) {
+ try {
+ // In case of FactoryBean, match object created by FactoryBean.
+ if (isFactoryBean(beanName)) {
+ if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
+ result.add(beanName);
+ // Match found for this bean: do not match FactoryBean itself anymore.
+ continue;
+ }
+ // In case of FactoryBean, try to match FactoryBean itself next.
+ beanName = FACTORY_BEAN_PREFIX + beanName;
+ }
+ // Match raw bean instance (might be raw FactoryBean).
+ if (isTypeMatch(beanName, type)) {
+ result.add(beanName);
+ }
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ // Shouldn't happen - probably a result of circular reference resolution...
+ logger.trace(LogMessage.format(
+ "Failed to check manually registered singleton with name '%s'", beanName), ex);
+ }
+ }
+
+ return StringUtils.toStringArray(result);
+ }
+
+ private boolean isSingleton(String beanName, RootBeanDefinition mbd, @Nullable BeanDefinitionHolder dbd) {
+ return (dbd != null ? mbd.isSingleton() : isSingleton(beanName));
+ }
+
+ /**
+ * Check whether the specified bean would need to be eagerly initialized
+ * in order to determine its type.
+ * @param factoryBeanName a factory-bean reference that the bean definition
+ * defines a factory method for
+ * @return whether eager initialization is necessary
+ */
+ private boolean requiresEagerInitForType(@Nullable String factoryBeanName) {
+ return (factoryBeanName != null && isFactoryBean(factoryBeanName) && !containsSingleton(factoryBeanName));
+ }
+
+ @Override
+ public Map getBeansOfType(@Nullable Class type) throws BeansException {
+ return getBeansOfType(type, true, true);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Map getBeansOfType(
+ @Nullable Class type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
+
+ String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
+ Map result = CollectionUtils.newLinkedHashMap(beanNames.length);
+ for (String beanName : beanNames) {
+ try {
+ Object beanInstance = getBean(beanName);
+ if (!(beanInstance instanceof NullBean)) {
+ result.put(beanName, (T) beanInstance);
+ }
+ }
+ catch (BeanCreationException ex) {
+ Throwable rootCause = ex.getMostSpecificCause();
+ if (rootCause instanceof BeanCurrentlyInCreationException) {
+ BeanCreationException bce = (BeanCreationException) rootCause;
+ String exBeanName = bce.getBeanName();
+ if (exBeanName != null && isCurrentlyInCreation(exBeanName)) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Ignoring match to currently created bean '" + exBeanName + "': " +
+ ex.getMessage());
+ }
+ onSuppressedException(ex);
+ // Ignore: indicates a circular reference when autowiring constructors.
+ // We want to find matches other than the currently created bean itself.
+ continue;
+ }
+ }
+ throw ex;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public String[] getBeanNamesForAnnotation(Class extends Annotation> annotationType) {
+ List result = new ArrayList<>();
+ for (String beanName : this.beanDefinitionNames) {
+ BeanDefinition bd = this.beanDefinitionMap.get(beanName);
+ if (bd != null && !bd.isAbstract() && findAnnotationOnBean(beanName, annotationType) != null) {
+ result.add(beanName);
+ }
+ }
+ for (String beanName : this.manualSingletonNames) {
+ if (!result.contains(beanName) && findAnnotationOnBean(beanName, annotationType) != null) {
+ result.add(beanName);
+ }
+ }
+ return StringUtils.toStringArray(result);
+ }
+
+ @Override
+ public Map getBeansWithAnnotation(Class extends Annotation> annotationType) {
+ String[] beanNames = getBeanNamesForAnnotation(annotationType);
+ Map result = CollectionUtils.newLinkedHashMap(beanNames.length);
+ for (String beanName : beanNames) {
+ Object beanInstance = getBean(beanName);
+ if (!(beanInstance instanceof NullBean)) {
+ result.put(beanName, beanInstance);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ @Nullable
+ public A findAnnotationOnBean(String beanName, Class annotationType)
+ throws NoSuchBeanDefinitionException {
+
+ return findMergedAnnotationOnBean(beanName, annotationType)
+ .synthesize(MergedAnnotation::isPresent).orElse(null);
+ }
+
+ private MergedAnnotation findMergedAnnotationOnBean(
+ String beanName, Class annotationType) {
+
+ Class> beanType = getType(beanName);
+ if (beanType != null) {
+ MergedAnnotation annotation =
+ MergedAnnotations.from(beanType, SearchStrategy.TYPE_HIERARCHY).get(annotationType);
+ if (annotation.isPresent()) {
+ return annotation;
+ }
+ }
+ if (containsBeanDefinition(beanName)) {
+ RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
+ // Check raw bean class, e.g. in case of a proxy.
+ if (bd.hasBeanClass()) {
+ Class> beanClass = bd.getBeanClass();
+ if (beanClass != beanType) {
+ MergedAnnotation annotation =
+ MergedAnnotations.from(beanClass, SearchStrategy.TYPE_HIERARCHY).get(annotationType);
+ if (annotation.isPresent()) {
+ return annotation;
+ }
+ }
+ }
+ // Check annotations declared on factory method, if any.
+ Method factoryMethod = bd.getResolvedFactoryMethod();
+ if (factoryMethod != null) {
+ MergedAnnotation annotation =
+ MergedAnnotations.from(factoryMethod, SearchStrategy.TYPE_HIERARCHY).get(annotationType);
+ if (annotation.isPresent()) {
+ return annotation;
+ }
+ }
+ }
+ return MergedAnnotation.missing();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of ConfigurableListableBeanFactory interface
+ //---------------------------------------------------------------------
+
+ @Override
+ public void registerResolvableDependency(Class> dependencyType, @Nullable Object autowiredValue) {
+ Assert.notNull(dependencyType, "Dependency type must not be null");
+ if (autowiredValue != null) {
+ if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
+ throw new IllegalArgumentException("Value [" + autowiredValue +
+ "] does not implement specified dependency type [" + dependencyType.getName() + "]");
+ }
+ this.resolvableDependencies.put(dependencyType, autowiredValue);
+ }
+ }
+
+ @Override
+ public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
+ throws NoSuchBeanDefinitionException {
+
+ return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver());
+ }
+
+ /**
+ * Determine whether the specified bean definition qualifies as an autowire candidate,
+ * to be injected into other beans which declare a dependency of matching type.
+ * @param beanName the name of the bean definition to check
+ * @param descriptor the descriptor of the dependency to resolve
+ * @param resolver the AutowireCandidateResolver to use for the actual resolution algorithm
+ * @return whether the bean should be considered as autowire candidate
+ */
+ protected boolean isAutowireCandidate(
+ String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
+ throws NoSuchBeanDefinitionException {
+
+ String bdName = BeanFactoryUtils.transformedBeanName(beanName);
+ if (containsBeanDefinition(bdName)) {
+ return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(bdName), descriptor, resolver);
+ }
+ else if (containsSingleton(beanName)) {
+ return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
+ }
+
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof DefaultListableBeanFactory) {
+ // No bean definition found in this factory -> delegate to parent.
+ return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver);
+ }
+ else if (parent instanceof ConfigurableListableBeanFactory) {
+ // If no DefaultListableBeanFactory, can't pass the resolver along.
+ return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor);
+ }
+ else {
+ return true;
+ }
+ }
+
+ /**
+ * Determine whether the specified bean definition qualifies as an autowire candidate,
+ * to be injected into other beans which declare a dependency of matching type.
+ * @param beanName the name of the bean definition to check
+ * @param mbd the merged bean definition to check
+ * @param descriptor the descriptor of the dependency to resolve
+ * @param resolver the AutowireCandidateResolver to use for the actual resolution algorithm
+ * @return whether the bean should be considered as autowire candidate
+ */
+ protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
+ DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {
+
+ String bdName = BeanFactoryUtils.transformedBeanName(beanName);
+ resolveBeanClass(mbd, bdName);
+ if (mbd.isFactoryMethodUnique && mbd.factoryMethodToIntrospect == null) {
+ new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
+ }
+ BeanDefinitionHolder holder = (beanName.equals(bdName) ?
+ this.mergedBeanDefinitionHolders.computeIfAbsent(beanName,
+ key -> new BeanDefinitionHolder(mbd, beanName, getAliases(bdName))) :
+ new BeanDefinitionHolder(mbd, beanName, getAliases(bdName)));
+ return resolver.isAutowireCandidate(holder, descriptor);
+ }
+
+ @Override
+ public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
+ BeanDefinition bd = this.beanDefinitionMap.get(beanName);
+ if (bd == null) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("No bean named '" + beanName + "' found in " + this);
+ }
+ throw new NoSuchBeanDefinitionException(beanName);
+ }
+ return bd;
+ }
+
+ @Override
+ public Iterator getBeanNamesIterator() {
+ CompositeIterator iterator = new CompositeIterator<>();
+ iterator.add(this.beanDefinitionNames.iterator());
+ iterator.add(this.manualSingletonNames.iterator());
+ return iterator;
+ }
+
+ @Override
+ protected void clearMergedBeanDefinition(String beanName) {
+ super.clearMergedBeanDefinition(beanName);
+ this.mergedBeanDefinitionHolders.remove(beanName);
+ }
+
+ @Override
+ public void clearMetadataCache() {
+ super.clearMetadataCache();
+ this.mergedBeanDefinitionHolders.clear();
+ clearByTypeCache();
+ }
+
+ @Override
+ public void freezeConfiguration() {
+ this.configurationFrozen = true;
+ this.frozenBeanDefinitionNames = StringUtils.toStringArray(this.beanDefinitionNames);
+ }
+
+ @Override
+ public boolean isConfigurationFrozen() {
+ return this.configurationFrozen;
+ }
+
+ /**
+ * Considers all beans as eligible for metadata caching
+ * if the factory's configuration has been marked as frozen.
+ * @see #freezeConfiguration()
+ */
+ @Override
+ protected boolean isBeanEligibleForMetadataCaching(String beanName) {
+ return (this.configurationFrozen || super.isBeanEligibleForMetadataCaching(beanName));
+ }
+
+ @Override
+ public void preInstantiateSingletons() throws BeansException {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Pre-instantiating singletons in " + this);
+ }
+
+ // Iterate over a copy to allow for init methods which in turn register new bean definitions.
+ // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
+ List beanNames = new ArrayList<>(this.beanDefinitionNames);
+
+ // Trigger initialization of all non-lazy singleton beans...
+ for (String beanName : beanNames) {
+ RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
+ if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
+ if (isFactoryBean(beanName)) {
+ Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
+ if (bean instanceof FactoryBean) {
+ FactoryBean> factory = (FactoryBean>) bean;
+ boolean isEagerInit;
+ if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
+ isEagerInit = AccessController.doPrivileged(
+ (PrivilegedAction) ((SmartFactoryBean>) factory)::isEagerInit,
+ getAccessControlContext());
+ }
+ else {
+ isEagerInit = (factory instanceof SmartFactoryBean &&
+ ((SmartFactoryBean>) factory).isEagerInit());
+ }
+ if (isEagerInit) {
+ getBean(beanName);
+ }
+ }
+ }
+ else {
+ getBean(beanName);
+ }
+ }
+ }
+
+ // Trigger post-initialization callback for all applicable beans...
+ for (String beanName : beanNames) {
+ Object singletonInstance = getSingleton(beanName);
+ if (singletonInstance instanceof SmartInitializingSingleton) {
+ StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
+ .tag("beanName", beanName);
+ SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
+ if (System.getSecurityManager() != null) {
+ AccessController.doPrivileged((PrivilegedAction) () -> {
+ smartSingleton.afterSingletonsInstantiated();
+ return null;
+ }, getAccessControlContext());
+ }
+ else {
+ smartSingleton.afterSingletonsInstantiated();
+ }
+ smartInitialize.end();
+ }
+ }
+ }
+
+
+ //---------------------------------------------------------------------
+ // Implementation of BeanDefinitionRegistry interface
+ //---------------------------------------------------------------------
+
+ @Override
+ public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
+ throws BeanDefinitionStoreException {
+
+ Assert.hasText(beanName, "Bean name must not be empty");
+ Assert.notNull(beanDefinition, "BeanDefinition must not be null");
+
+ if (beanDefinition instanceof AbstractBeanDefinition) {
+ try {
+ ((AbstractBeanDefinition) beanDefinition).validate();
+ }
+ catch (BeanDefinitionValidationException ex) {
+ throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
+ "Validation of bean definition failed", ex);
+ }
+ }
+
+ BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
+ if (existingDefinition != null) {
+ if (!isAllowBeanDefinitionOverriding()) {
+ throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
+ }
+ else if (existingDefinition.getRole() < beanDefinition.getRole()) {
+ // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
+ if (logger.isInfoEnabled()) {
+ logger.info("Overriding user-defined bean definition for bean '" + beanName +
+ "' with a framework-generated bean definition: replacing [" +
+ existingDefinition + "] with [" + beanDefinition + "]");
+ }
+ }
+ else if (!beanDefinition.equals(existingDefinition)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Overriding bean definition for bean '" + beanName +
+ "' with a different definition: replacing [" + existingDefinition +
+ "] with [" + beanDefinition + "]");
+ }
+ }
+ else {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Overriding bean definition for bean '" + beanName +
+ "' with an equivalent definition: replacing [" + existingDefinition +
+ "] with [" + beanDefinition + "]");
+ }
+ }
+ this.beanDefinitionMap.put(beanName, beanDefinition);
+ }
+ else {
+ if (hasBeanCreationStarted()) {
+ // Cannot modify startup-time collection elements anymore (for stable iteration)
+ synchronized (this.beanDefinitionMap) {
+ this.beanDefinitionMap.put(beanName, beanDefinition);
+ List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
+ updatedDefinitions.addAll(this.beanDefinitionNames);
+ updatedDefinitions.add(beanName);
+ this.beanDefinitionNames = updatedDefinitions;
+ removeManualSingletonName(beanName);
+ }
+ }
+ else {
+ // Still in startup registration phase
+ this.beanDefinitionMap.put(beanName, beanDefinition);
+ this.beanDefinitionNames.add(beanName);
+ removeManualSingletonName(beanName);
+ }
+ this.frozenBeanDefinitionNames = null;
+ }
+
+ if (existingDefinition != null || containsSingleton(beanName)) {
+ resetBeanDefinition(beanName);
+ }
+ else if (isConfigurationFrozen()) {
+ clearByTypeCache();
+ }
+ }
+
+ @Override
+ public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
+ Assert.hasText(beanName, "'beanName' must not be empty");
+
+ BeanDefinition bd = this.beanDefinitionMap.remove(beanName);
+ if (bd == null) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("No bean named '" + beanName + "' found in " + this);
+ }
+ throw new NoSuchBeanDefinitionException(beanName);
+ }
+
+ if (hasBeanCreationStarted()) {
+ // Cannot modify startup-time collection elements anymore (for stable iteration)
+ synchronized (this.beanDefinitionMap) {
+ List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames);
+ updatedDefinitions.remove(beanName);
+ this.beanDefinitionNames = updatedDefinitions;
+ }
+ }
+ else {
+ // Still in startup registration phase
+ this.beanDefinitionNames.remove(beanName);
+ }
+ this.frozenBeanDefinitionNames = null;
+
+ resetBeanDefinition(beanName);
+ }
+
+ /**
+ * Reset all bean definition caches for the given bean,
+ * including the caches of beans that are derived from it.
+ * Called after an existing bean definition has been replaced or removed,
+ * triggering {@link #clearMergedBeanDefinition}, {@link #destroySingleton}
+ * and {@link MergedBeanDefinitionPostProcessor#resetBeanDefinition} on the
+ * given bean and on all bean definitions that have the given bean as parent.
+ * @param beanName the name of the bean to reset
+ * @see #registerBeanDefinition
+ * @see #removeBeanDefinition
+ */
+ protected void resetBeanDefinition(String beanName) {
+ // Remove the merged bean definition for the given bean, if already created.
+ clearMergedBeanDefinition(beanName);
+
+ // Remove corresponding bean from singleton cache, if any. Shouldn't usually
+ // be necessary, rather just meant for overriding a context's default beans
+ // (e.g. the default StaticMessageSource in a StaticApplicationContext).
+ destroySingleton(beanName);
+
+ // Notify all post-processors that the specified bean definition has been reset.
+ for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
+ processor.resetBeanDefinition(beanName);
+ }
+
+ // Reset all bean definitions that have the given bean as parent (recursively).
+ for (String bdName : this.beanDefinitionNames) {
+ if (!beanName.equals(bdName)) {
+ BeanDefinition bd = this.beanDefinitionMap.get(bdName);
+ // Ensure bd is non-null due to potential concurrent modification of beanDefinitionMap.
+ if (bd != null && beanName.equals(bd.getParentName())) {
+ resetBeanDefinition(bdName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Only allows alias overriding if bean definition overriding is allowed.
+ */
+ @Override
+ protected boolean allowAliasOverriding() {
+ return isAllowBeanDefinitionOverriding();
+ }
+
+ /**
+ * Also checks for an alias overriding a bean definition of the same name.
+ */
+ @Override
+ protected void checkForAliasCircle(String name, String alias) {
+ super.checkForAliasCircle(name, alias);
+ if (!isAllowBeanDefinitionOverriding() && containsBeanDefinition(alias)) {
+ throw new IllegalStateException("Cannot register alias '" + alias +
+ "' for name '" + name + "': Alias would override bean definition '" + alias + "'");
+ }
+ }
+
+ @Override
+ public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
+ super.registerSingleton(beanName, singletonObject);
+ updateManualSingletonNames(set -> set.add(beanName), set -> !this.beanDefinitionMap.containsKey(beanName));
+ clearByTypeCache();
+ }
+
+ @Override
+ public void destroySingletons() {
+ super.destroySingletons();
+ updateManualSingletonNames(Set::clear, set -> !set.isEmpty());
+ clearByTypeCache();
+ }
+
+ @Override
+ public void destroySingleton(String beanName) {
+ super.destroySingleton(beanName);
+ removeManualSingletonName(beanName);
+ clearByTypeCache();
+ }
+
+ private void removeManualSingletonName(String beanName) {
+ updateManualSingletonNames(set -> set.remove(beanName), set -> set.contains(beanName));
+ }
+
+ /**
+ * Update the factory's internal set of manual singleton names.
+ * @param action the modification action
+ * @param condition a precondition for the modification action
+ * (if this condition does not apply, the action can be skipped)
+ */
+ private void updateManualSingletonNames(Consumer> action, Predicate> condition) {
+ if (hasBeanCreationStarted()) {
+ // Cannot modify startup-time collection elements anymore (for stable iteration)
+ synchronized (this.beanDefinitionMap) {
+ if (condition.test(this.manualSingletonNames)) {
+ Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
+ action.accept(updatedSingletons);
+ this.manualSingletonNames = updatedSingletons;
+ }
+ }
+ }
+ else {
+ // Still in startup registration phase
+ if (condition.test(this.manualSingletonNames)) {
+ action.accept(this.manualSingletonNames);
+ }
+ }
+ }
+
+ /**
+ * Remove any assumptions about by-type mappings.
+ */
+ private void clearByTypeCache() {
+ this.allBeanNamesByType.clear();
+ this.singletonBeanNamesByType.clear();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Dependency resolution functionality
+ //---------------------------------------------------------------------
+
+ @Override
+ public NamedBeanHolder resolveNamedBean(Class requiredType) throws BeansException {
+ Assert.notNull(requiredType, "Required type must not be null");
+ NamedBeanHolder namedBean = resolveNamedBean(ResolvableType.forRawClass(requiredType), null, false);
+ if (namedBean != null) {
+ return namedBean;
+ }
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof AutowireCapableBeanFactory) {
+ return ((AutowireCapableBeanFactory) parent).resolveNamedBean(requiredType);
+ }
+ throw new NoSuchBeanDefinitionException(requiredType);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private NamedBeanHolder resolveNamedBean(
+ ResolvableType requiredType, @Nullable Object[] args, boolean nonUniqueAsNull) throws BeansException {
+
+ Assert.notNull(requiredType, "Required type must not be null");
+ String[] candidateNames = getBeanNamesForType(requiredType);
+
+ if (candidateNames.length > 1) {
+ List autowireCandidates = new ArrayList<>(candidateNames.length);
+ for (String beanName : candidateNames) {
+ if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
+ autowireCandidates.add(beanName);
+ }
+ }
+ if (!autowireCandidates.isEmpty()) {
+ candidateNames = StringUtils.toStringArray(autowireCandidates);
+ }
+ }
+
+ if (candidateNames.length == 1) {
+ String beanName = candidateNames[0];
+ return new NamedBeanHolder<>(beanName, (T) getBean(beanName, requiredType.toClass(), args));
+ }
+ else if (candidateNames.length > 1) {
+ Map candidates = CollectionUtils.newLinkedHashMap(candidateNames.length);
+ for (String beanName : candidateNames) {
+ if (containsSingleton(beanName) && args == null) {
+ Object beanInstance = getBean(beanName);
+ candidates.put(beanName, (beanInstance instanceof NullBean ? null : beanInstance));
+ }
+ else {
+ candidates.put(beanName, getType(beanName));
+ }
+ }
+ String candidateName = determinePrimaryCandidate(candidates, requiredType.toClass());
+ if (candidateName == null) {
+ candidateName = determineHighestPriorityCandidate(candidates, requiredType.toClass());
+ }
+ if (candidateName != null) {
+ Object beanInstance = candidates.get(candidateName);
+ if (beanInstance == null || beanInstance instanceof Class) {
+ beanInstance = getBean(candidateName, requiredType.toClass(), args);
+ }
+ return new NamedBeanHolder<>(candidateName, (T) beanInstance);
+ }
+ if (!nonUniqueAsNull) {
+ throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
+ @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
+
+ descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
+ if (Optional.class == descriptor.getDependencyType()) {
+ return createOptionalDependency(descriptor, requestingBeanName);
+ }
+ else if (ObjectFactory.class == descriptor.getDependencyType() ||
+ ObjectProvider.class == descriptor.getDependencyType()) {
+ return new DependencyObjectProvider(descriptor, requestingBeanName);
+ }
+ else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
+ return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
+ }
+ else {
+ Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
+ descriptor, requestingBeanName);
+ if (result == null) {
+ result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
+ }
+ return result;
+ }
+ }
+
+ @Nullable
+ public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
+ @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
+
+ InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
+ try {
+ Object shortcut = descriptor.resolveShortcut(this);
+ if (shortcut != null) {
+ return shortcut;
+ }
+
+ Class> type = descriptor.getDependencyType();
+ Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
+ if (value != null) {
+ if (value instanceof String) {
+ String strVal = resolveEmbeddedValue((String) value);
+ BeanDefinition bd = (beanName != null && containsBean(beanName) ?
+ getMergedBeanDefinition(beanName) : null);
+ value = evaluateBeanDefinitionString(strVal, bd);
+ }
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
+ try {
+ return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
+ }
+ catch (UnsupportedOperationException ex) {
+ // A custom TypeConverter which does not support TypeDescriptor resolution...
+ return (descriptor.getField() != null ?
+ converter.convertIfNecessary(value, type, descriptor.getField()) :
+ converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
+ }
+ }
+
+ Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
+ if (multipleBeans != null) {
+ return multipleBeans;
+ }
+
+ Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
+ if (matchingBeans.isEmpty()) {
+ if (isRequired(descriptor)) {
+ raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
+ }
+ return null;
+ }
+
+ String autowiredBeanName;
+ Object instanceCandidate;
+
+ if (matchingBeans.size() > 1) {
+ autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
+ if (autowiredBeanName == null) {
+ if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
+ return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
+ }
+ else {
+ // In case of an optional Collection/Map, silently ignore a non-unique case:
+ // possibly it was meant to be an empty collection of multiple regular beans
+ // (before 4.3 in particular when we didn't even look for collection beans).
+ return null;
+ }
+ }
+ instanceCandidate = matchingBeans.get(autowiredBeanName);
+ }
+ else {
+ // We have exactly one match.
+ Map.Entry entry = matchingBeans.entrySet().iterator().next();
+ autowiredBeanName = entry.getKey();
+ instanceCandidate = entry.getValue();
+ }
+
+ if (autowiredBeanNames != null) {
+ autowiredBeanNames.add(autowiredBeanName);
+ }
+ 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 autowiredBeanNames, @Nullable TypeConverter typeConverter) {
+
+ Class> type = descriptor.getDependencyType();
+
+ if (descriptor instanceof StreamDependencyDescriptor) {
+ Map matchingBeans = findAutowireCandidates(beanName, type, descriptor);
+ if (autowiredBeanNames != null) {
+ autowiredBeanNames.addAll(matchingBeans.keySet());
+ }
+ Stream stream = matchingBeans.keySet().stream()
+ .map(name -> descriptor.resolveCandidate(name, type, this))
+ .filter(bean -> !(bean instanceof NullBean));
+ if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
+ stream = stream.sorted(adaptOrderComparator(matchingBeans));
+ }
+ return stream;
+ }
+ else if (type.isArray()) {
+ Class> componentType = type.getComponentType();
+ ResolvableType resolvableType = descriptor.getResolvableType();
+ Class> resolvedArrayType = resolvableType.resolve(type);
+ if (resolvedArrayType != type) {
+ componentType = resolvableType.getComponentType().resolve();
+ }
+ if (componentType == null) {
+ return null;
+ }
+ Map matchingBeans = findAutowireCandidates(beanName, componentType,
+ new MultiElementDescriptor(descriptor));
+ if (matchingBeans.isEmpty()) {
+ return null;
+ }
+ if (autowiredBeanNames != null) {
+ autowiredBeanNames.addAll(matchingBeans.keySet());
+ }
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
+ Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);
+ if (result instanceof Object[]) {
+ Comparator comparator = adaptDependencyComparator(matchingBeans);
+ if (comparator != null) {
+ Arrays.sort((Object[]) result, comparator);
+ }
+ }
+ return result;
+ }
+ else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
+ Class> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
+ if (elementType == null) {
+ return null;
+ }
+ Map matchingBeans = findAutowireCandidates(beanName, elementType,
+ new MultiElementDescriptor(descriptor));
+ if (matchingBeans.isEmpty()) {
+ return null;
+ }
+ if (autowiredBeanNames != null) {
+ autowiredBeanNames.addAll(matchingBeans.keySet());
+ }
+ TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
+ Object result = converter.convertIfNecessary(matchingBeans.values(), type);
+ if (result instanceof List) {
+ if (((List>) result).size() > 1) {
+ Comparator comparator = adaptDependencyComparator(matchingBeans);
+ if (comparator != null) {
+ ((List>) result).sort(comparator);
+ }
+ }
+ }
+ return result;
+ }
+ else if (Map.class == type) {
+ ResolvableType mapType = descriptor.getResolvableType().asMap();
+ Class> keyType = mapType.resolveGeneric(0);
+ if (String.class != keyType) {
+ return null;
+ }
+ Class> valueType = mapType.resolveGeneric(1);
+ if (valueType == null) {
+ return null;
+ }
+ Map matchingBeans = findAutowireCandidates(beanName, valueType,
+ new MultiElementDescriptor(descriptor));
+ if (matchingBeans.isEmpty()) {
+ return null;
+ }
+ if (autowiredBeanNames != null) {
+ autowiredBeanNames.addAll(matchingBeans.keySet());
+ }
+ return matchingBeans;
+ }
+ else {
+ return null;
+ }
+ }
+
+ private boolean isRequired(DependencyDescriptor descriptor) {
+ return getAutowireCandidateResolver().isRequired(descriptor);
+ }
+
+ private boolean indicatesMultipleBeans(Class> type) {
+ return (type.isArray() || (type.isInterface() &&
+ (Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type))));
+ }
+
+ @Nullable
+ private Comparator adaptDependencyComparator(Map matchingBeans) {
+ Comparator comparator = getDependencyComparator();
+ if (comparator instanceof OrderComparator) {
+ return ((OrderComparator) comparator).withSourceProvider(
+ createFactoryAwareOrderSourceProvider(matchingBeans));
+ }
+ else {
+ return comparator;
+ }
+ }
+
+ private Comparator adaptOrderComparator(Map matchingBeans) {
+ Comparator dependencyComparator = getDependencyComparator();
+ OrderComparator comparator = (dependencyComparator instanceof OrderComparator ?
+ (OrderComparator) dependencyComparator : OrderComparator.INSTANCE);
+ return comparator.withSourceProvider(createFactoryAwareOrderSourceProvider(matchingBeans));
+ }
+
+ private OrderComparator.OrderSourceProvider createFactoryAwareOrderSourceProvider(Map beans) {
+ IdentityHashMap instancesToBeanNames = new IdentityHashMap<>();
+ beans.forEach((beanName, instance) -> instancesToBeanNames.put(instance, beanName));
+ return new FactoryAwareOrderSourceProvider(instancesToBeanNames);
+ }
+
+ /**
+ * Find bean instances that match the required type.
+ * Called during autowiring for the specified bean.
+ * @param beanName the name of the bean that is about to be wired
+ * @param requiredType the actual type of bean to look for
+ * (may be an array component type or collection element type)
+ * @param descriptor the descriptor of the dependency to resolve
+ * @return a Map of candidate names and candidate instances that match
+ * the required type (never {@code null})
+ * @throws BeansException in case of errors
+ * @see #autowireByType
+ * @see #autowireConstructor
+ */
+ protected Map findAutowireCandidates(
+ @Nullable String beanName, Class> requiredType, DependencyDescriptor descriptor) {
+
+ String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
+ this, requiredType, true, descriptor.isEager());
+ Map result = CollectionUtils.newLinkedHashMap(candidateNames.length);
+ for (Map.Entry, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
+ Class> autowiringType = classObjectEntry.getKey();
+ if (autowiringType.isAssignableFrom(requiredType)) {
+ Object autowiringValue = classObjectEntry.getValue();
+ autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
+ if (requiredType.isInstance(autowiringValue)) {
+ result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
+ break;
+ }
+ }
+ }
+ for (String candidate : candidateNames) {
+ if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
+ addCandidateEntry(result, candidate, descriptor, requiredType);
+ }
+ }
+ if (result.isEmpty()) {
+ boolean multiple = indicatesMultipleBeans(requiredType);
+ // Consider fallback matches if the first pass failed to find anything...
+ DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
+ for (String candidate : candidateNames) {
+ if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&
+ (!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
+ addCandidateEntry(result, candidate, descriptor, requiredType);
+ }
+ }
+ if (result.isEmpty() && !multiple) {
+ // Consider self references as a final pass...
+ // but in the case of a dependency collection, not the very same bean itself.
+ for (String candidate : candidateNames) {
+ if (isSelfReference(beanName, candidate) &&
+ (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
+ isAutowireCandidate(candidate, fallbackDescriptor)) {
+ addCandidateEntry(result, candidate, descriptor, requiredType);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Add an entry to the candidate map: a bean instance if available or just the resolved
+ * type, preventing early bean initialization ahead of primary candidate selection.
+ */
+ private void addCandidateEntry(Map candidates, String candidateName,
+ DependencyDescriptor descriptor, Class> requiredType) {
+
+ if (descriptor instanceof MultiElementDescriptor) {
+ Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
+ if (!(beanInstance instanceof NullBean)) {
+ candidates.put(candidateName, beanInstance);
+ }
+ }
+ else if (containsSingleton(candidateName) || (descriptor instanceof StreamDependencyDescriptor &&
+ ((StreamDependencyDescriptor) descriptor).isOrdered())) {
+ Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
+ candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
+ }
+ else {
+ candidates.put(candidateName, getType(candidateName));
+ }
+ }
+
+ /**
+ * Determine the autowire candidate in the given set of beans.
+ * Looks for {@code @Primary} and {@code @Priority} (in that order).
+ * @param candidates a Map of candidate names and candidate instances
+ * that match the required type, as returned by {@link #findAutowireCandidates}
+ * @param descriptor the target dependency to match against
+ * @return the name of the autowire candidate, or {@code null} if none found
+ */
+ @Nullable
+ protected String determineAutowireCandidate(Map candidates, DependencyDescriptor descriptor) {
+ Class> requiredType = descriptor.getDependencyType();
+ String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
+ if (primaryCandidate != null) {
+ return primaryCandidate;
+ }
+ String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
+ if (priorityCandidate != null) {
+ return priorityCandidate;
+ }
+ // Fallback
+ for (Map.Entry entry : candidates.entrySet()) {
+ String candidateName = entry.getKey();
+ Object beanInstance = entry.getValue();
+ if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
+ matchesBeanName(candidateName, descriptor.getDependencyName())) {
+ return candidateName;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Determine the primary candidate in the given set of beans.
+ * @param candidates a Map of candidate names and candidate instances
+ * (or candidate classes if not created yet) that match the required type
+ * @param requiredType the target dependency type to match against
+ * @return the name of the primary candidate, or {@code null} if none found
+ * @see #isPrimary(String, Object)
+ */
+ @Nullable
+ protected String determinePrimaryCandidate(Map candidates, Class> requiredType) {
+ String primaryBeanName = null;
+ for (Map.Entry entry : candidates.entrySet()) {
+ String candidateBeanName = entry.getKey();
+ Object beanInstance = entry.getValue();
+ if (isPrimary(candidateBeanName, beanInstance)) {
+ if (primaryBeanName != null) {
+ boolean candidateLocal = containsBeanDefinition(candidateBeanName);
+ boolean primaryLocal = containsBeanDefinition(primaryBeanName);
+ if (candidateLocal && primaryLocal) {
+ throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
+ "more than one 'primary' bean found among candidates: " + candidates.keySet());
+ }
+ else if (candidateLocal) {
+ primaryBeanName = candidateBeanName;
+ }
+ }
+ else {
+ primaryBeanName = candidateBeanName;
+ }
+ }
+ }
+ return primaryBeanName;
+ }
+
+ /**
+ * Determine the candidate with the highest priority in the given set of beans.
+ * Based on {@code @javax.annotation.Priority}. As defined by the related
+ * {@link org.springframework.core.Ordered} interface, the lowest value has
+ * the highest priority.
+ * @param candidates a Map of candidate names and candidate instances
+ * (or candidate classes if not created yet) that match the required type
+ * @param requiredType the target dependency type to match against
+ * @return the name of the candidate with the highest priority,
+ * or {@code null} if none found
+ * @see #getPriority(Object)
+ */
+ @Nullable
+ protected String determineHighestPriorityCandidate(Map candidates, Class> requiredType) {
+ String highestPriorityBeanName = null;
+ Integer highestPriority = null;
+ for (Map.Entry entry : candidates.entrySet()) {
+ String candidateBeanName = entry.getKey();
+ Object beanInstance = entry.getValue();
+ if (beanInstance != null) {
+ Integer candidatePriority = getPriority(beanInstance);
+ if (candidatePriority != null) {
+ if (highestPriorityBeanName != null) {
+ if (candidatePriority.equals(highestPriority)) {
+ throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
+ "Multiple beans found with the same priority ('" + highestPriority +
+ "') among candidates: " + candidates.keySet());
+ }
+ else if (candidatePriority < highestPriority) {
+ highestPriorityBeanName = candidateBeanName;
+ highestPriority = candidatePriority;
+ }
+ }
+ else {
+ highestPriorityBeanName = candidateBeanName;
+ highestPriority = candidatePriority;
+ }
+ }
+ }
+ }
+ return highestPriorityBeanName;
+ }
+
+ /**
+ * Return whether the bean definition for the given bean name has been
+ * marked as a primary bean.
+ * @param beanName the name of the bean
+ * @param beanInstance the corresponding bean instance (can be null)
+ * @return whether the given bean qualifies as primary
+ */
+ protected boolean isPrimary(String beanName, Object beanInstance) {
+ String transformedBeanName = transformedBeanName(beanName);
+ if (containsBeanDefinition(transformedBeanName)) {
+ return getMergedLocalBeanDefinition(transformedBeanName).isPrimary();
+ }
+ BeanFactory parent = getParentBeanFactory();
+ return (parent instanceof DefaultListableBeanFactory &&
+ ((DefaultListableBeanFactory) parent).isPrimary(transformedBeanName, beanInstance));
+ }
+
+ /**
+ * Return the priority assigned for the given bean instance by
+ * the {@code javax.annotation.Priority} annotation.
+ * The default implementation delegates to the specified
+ * {@link #setDependencyComparator dependency comparator}, checking its
+ * {@link OrderComparator#getPriority method} if it is an extension of
+ * Spring's common {@link OrderComparator} - typically, an
+ * {@link org.springframework.core.annotation.AnnotationAwareOrderComparator}.
+ * If no such comparator is present, this implementation returns {@code null}.
+ * @param beanInstance the bean instance to check (can be {@code null})
+ * @return the priority assigned to that bean or {@code null} if none is set
+ */
+ @Nullable
+ protected Integer getPriority(Object beanInstance) {
+ Comparator comparator = getDependencyComparator();
+ if (comparator instanceof OrderComparator) {
+ return ((OrderComparator) comparator).getPriority(beanInstance);
+ }
+ return null;
+ }
+
+ /**
+ * Determine whether the given candidate name matches the bean name or the aliases
+ * stored in this bean definition.
+ */
+ protected boolean matchesBeanName(String beanName, @Nullable String candidateName) {
+ return (candidateName != null &&
+ (candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
+ }
+
+ /**
+ * Determine whether the given beanName/candidateName pair indicates a self reference,
+ * i.e. whether the candidate points back to the original bean or to a factory method
+ * on the original bean.
+ */
+ private boolean isSelfReference(@Nullable String beanName, @Nullable String candidateName) {
+ return (beanName != null && candidateName != null &&
+ (beanName.equals(candidateName) || (containsBeanDefinition(candidateName) &&
+ beanName.equals(getMergedLocalBeanDefinition(candidateName).getFactoryBeanName()))));
+ }
+
+ /**
+ * Raise a NoSuchBeanDefinitionException or BeanNotOfRequiredTypeException
+ * for an unresolvable dependency.
+ */
+ private void raiseNoMatchingBeanFound(
+ Class> type, ResolvableType resolvableType, DependencyDescriptor descriptor) throws BeansException {
+
+ checkBeanNotOfRequiredType(type, descriptor);
+
+ throw new NoSuchBeanDefinitionException(resolvableType,
+ "expected at least 1 bean which qualifies as autowire candidate. " +
+ "Dependency annotations: " + ObjectUtils.nullSafeToString(descriptor.getAnnotations()));
+ }
+
+ /**
+ * Raise a BeanNotOfRequiredTypeException for an unresolvable dependency, if applicable,
+ * i.e. if the target type of the bean would match but an exposed proxy doesn't.
+ */
+ private void checkBeanNotOfRequiredType(Class> type, DependencyDescriptor descriptor) {
+ for (String beanName : this.beanDefinitionNames) {
+ try {
+ RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
+ Class> targetType = mbd.getTargetType();
+ if (targetType != null && type.isAssignableFrom(targetType) &&
+ 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() != NullBean.class ?
+ beanInstance.getClass() : predictBeanType(beanName, mbd));
+ if (beanType != null && !type.isAssignableFrom(beanType)) {
+ throw new BeanNotOfRequiredTypeException(beanName, type, beanType);
+ }
+ }
+ }
+ catch (NoSuchBeanDefinitionException ex) {
+ // Bean definition got removed while we were iterating -> ignore.
+ }
+ }
+
+ BeanFactory parent = getParentBeanFactory();
+ if (parent instanceof DefaultListableBeanFactory) {
+ ((DefaultListableBeanFactory) parent).checkBeanNotOfRequiredType(type, descriptor);
+ }
+ }
+
+ /**
+ * Create an {@link Optional} wrapper for the specified dependency.
+ */
+ private Optional> createOptionalDependency(
+ DependencyDescriptor descriptor, @Nullable String beanName, final Object... args) {
+
+ DependencyDescriptor descriptorToUse = new NestedDependencyDescriptor(descriptor) {
+ @Override
+ public boolean isRequired() {
+ return false;
+ }
+ @Override
+ public Object resolveCandidate(String beanName, Class> requiredType, BeanFactory beanFactory) {
+ return (!ObjectUtils.isEmpty(args) ? beanFactory.getBean(beanName, args) :
+ super.resolveCandidate(beanName, requiredType, beanFactory));
+ }
+ };
+ Object result = doResolveDependency(descriptorToUse, beanName, null, null);
+ return (result instanceof Optional ? (Optional>) result : Optional.ofNullable(result));
+ }
+
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(ObjectUtils.identityToString(this));
+ sb.append(": defining beans [");
+ sb.append(StringUtils.collectionToCommaDelimitedString(this.beanDefinitionNames));
+ sb.append("]; ");
+ BeanFactory parent = getParentBeanFactory();
+ if (parent == null) {
+ sb.append("root of factory hierarchy");
+ }
+ else {
+ sb.append("parent: ").append(ObjectUtils.identityToString(parent));
+ }
+ return sb.toString();
+ }
+
+
+ //---------------------------------------------------------------------
+ // Serialization support
+ //---------------------------------------------------------------------
+
+ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+ throw new NotSerializableException("DefaultListableBeanFactory itself is not deserializable - " +
+ "just a SerializedBeanFactoryReference is");
+ }
+
+ protected Object writeReplace() throws ObjectStreamException {
+ if (this.serializationId != null) {
+ return new SerializedBeanFactoryReference(this.serializationId);
+ }
+ else {
+ throw new NotSerializableException("DefaultListableBeanFactory has no serialization id");
+ }
+ }
+
+
+ /**
+ * Minimal id reference to the factory.
+ * Resolved to the actual factory instance on deserialization.
+ */
+ private static class SerializedBeanFactoryReference implements Serializable {
+
+ private final String id;
+
+ public SerializedBeanFactoryReference(String id) {
+ this.id = id;
+ }
+
+ private Object readResolve() {
+ Reference> ref = serializableFactories.get(this.id);
+ if (ref != null) {
+ Object result = ref.get();
+ if (result != null) {
+ return result;
+ }
+ }
+ // Lenient fallback: dummy factory in case of original factory not found...
+ DefaultListableBeanFactory dummyFactory = new DefaultListableBeanFactory();
+ dummyFactory.serializationId = this.id;
+ return dummyFactory;
+ }
+ }
+
+
+ /**
+ * A dependency descriptor marker for nested elements.
+ */
+ private static class NestedDependencyDescriptor extends DependencyDescriptor {
+
+ public NestedDependencyDescriptor(DependencyDescriptor original) {
+ super(original);
+ increaseNestingLevel();
+ }
+ }
+
+
+ /**
+ * A dependency descriptor for a multi-element declaration with nested elements.
+ */
+ private static class MultiElementDescriptor extends NestedDependencyDescriptor {
+
+ public MultiElementDescriptor(DependencyDescriptor original) {
+ super(original);
+ }
+ }
+
+
+ /**
+ * A dependency descriptor marker for stream access to multiple elements.
+ */
+ private static class StreamDependencyDescriptor extends DependencyDescriptor {
+
+ private final boolean ordered;
+
+ public StreamDependencyDescriptor(DependencyDescriptor original, boolean ordered) {
+ super(original);
+ this.ordered = ordered;
+ }
+
+ public boolean isOrdered() {
+ return this.ordered;
+ }
+ }
+
+
+ private interface BeanObjectProvider extends ObjectProvider, Serializable {
+ }
+
+
+ /**
+ * Serializable ObjectFactory/ObjectProvider for lazy resolution of a dependency.
+ */
+ private class DependencyObjectProvider implements BeanObjectProvider {
+
+ private final DependencyDescriptor descriptor;
+
+ private final boolean optional;
+
+ @Nullable
+ private final String beanName;
+
+ public DependencyObjectProvider(DependencyDescriptor descriptor, @Nullable String beanName) {
+ this.descriptor = new NestedDependencyDescriptor(descriptor);
+ this.optional = (this.descriptor.getDependencyType() == Optional.class);
+ this.beanName = beanName;
+ }
+
+ @Override
+ public Object getObject() throws BeansException {
+ if (this.optional) {
+ return createOptionalDependency(this.descriptor, this.beanName);
+ }
+ else {
+ Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
+ if (result == null) {
+ throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public Object getObject(final Object... args) throws BeansException {
+ if (this.optional) {
+ return createOptionalDependency(this.descriptor, this.beanName, args);
+ }
+ else {
+ DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
+ @Override
+ public Object resolveCandidate(String beanName, Class> requiredType, BeanFactory beanFactory) {
+ return beanFactory.getBean(beanName, args);
+ }
+ };
+ Object result = doResolveDependency(descriptorToUse, this.beanName, null, null);
+ if (result == null) {
+ throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
+ }
+ return result;
+ }
+ }
+
+ @Override
+ @Nullable
+ public Object getIfAvailable() throws BeansException {
+ try {
+ if (this.optional) {
+ return createOptionalDependency(this.descriptor, this.beanName);
+ }
+ else {
+ DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
+ @Override
+ public boolean isRequired() {
+ return false;
+ }
+ };
+ return doResolveDependency(descriptorToUse, this.beanName, null, null);
+ }
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope
+ return null;
+ }
+ }
+
+ @Override
+ public void ifAvailable(Consumer dependencyConsumer) throws BeansException {
+ Object dependency = getIfAvailable();
+ if (dependency != null) {
+ try {
+ dependencyConsumer.accept(dependency);
+ }
+ catch (ScopeNotActiveException ex) {
+ // Ignore resolved bean in non-active scope, even on scoped proxy invocation
+ }
+ }
+ }
+
+ @Override
+ @Nullable
+ public Object getIfUnique() throws BeansException {
+ DependencyDescriptor descriptorToUse = new DependencyDescriptor(this.descriptor) {
+ @Override
+ public boolean isRequired() {
+ return false;
+ }
+
+ @Override
+ @Nullable
+ public Object resolveNotUnique(ResolvableType type, Map