spring源码

master
msb_16686 3 years ago
parent 120fc301b3
commit 945abaf19e

@ -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.
*
* <p>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.
*
* <p>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 <T> the bean type
* @see #setSingleton
* @see #createInstance()
*/
public abstract class AbstractFactoryBean<T>
implements FactoryBean<T>, 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 <i>not</i> thread-safe.
* <p>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.
* <p>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.
* <p>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.
* <p>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();
}
}
}
}

@ -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.
*
* <p>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();
}

@ -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.
* <p>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.
* <p>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.
* <p>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();
}
}

@ -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.
*
* <p>Note that as it is expected to be used mostly for accessing factory methods,
* this factory by default operates in a <b>singleton</b> 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.
*
* <p><b>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.</b>
*
* <p>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.
*
* <p>This class depends on {@link #afterPropertiesSet()} being called once
* all properties have been set, as per the InitializingBean contract.
*
* <p>An example (in an XML based bean factory definition) of a bean definition
* which uses this class to call a static factory method:
*
* <pre class="code">
* &lt;bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
* &lt;property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
* &lt;/bean></pre>
*
* <p>An example of calling a static method then an instance method to get at a
* Java system property. Somewhat verbose, but it works.
*
* <pre class="code">
* &lt;bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
* &lt;property name="targetClass" value="java.lang.System"/>
* &lt;property name="targetMethod" value="getProperties"/>
* &lt;/bean>
*
* &lt;bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
* &lt;property name="targetObject" ref="sysProps"/>
* &lt;property name="targetMethod" value="getProperty"/>
* &lt;property name="arguments" value="java.version"/>
* &lt;/bean></pre>
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @since 21.11.2003
* @see MethodInvokingBean
* @see org.springframework.util.MethodInvoker
*/
public class MethodInvokingFactoryBean extends MethodInvokingBean implements FactoryBean<Object> {
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;
}
}

@ -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}.
*
* <p>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).
*
* <p>A sample config in an XML-based
* {@link org.springframework.beans.factory.BeanFactory} might look as follows:
*
* <pre class="code">&lt;beans&gt;
*
* &lt;!-- Prototype bean since we have state --&gt;
* &lt;bean id="myService" class="a.b.c.MyService" scope="prototype"/&gt;
*
* &lt;bean id="myServiceFactory"
* class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"&gt;
* &lt;property name="targetBeanName"&gt;&lt;idref local="myService"/&gt;&lt;/property&gt;
* &lt;/bean&gt;
*
* &lt;bean id="clientBean" class="a.b.c.MyClientBean"&gt;
* &lt;property name="myServiceFactory" ref="myServiceFactory"/&gt;
* &lt;/bean&gt;
*
*&lt;/beans&gt;</pre>
*
* <p>The attendant {@code MyClientBean} class implementation might look
* something like this:
*
* <pre class="code">package a.b.c;
*
* import org.springframework.beans.factory.ObjectFactory;
*
* public class MyClientBean {
*
* private ObjectFactory&lt;MyService&gt; myServiceFactory;
*
* public void setMyServiceFactory(ObjectFactory&lt;MyService&gt; 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...
* }
* }</pre>
*
* <p>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<ObjectFactory<Object>> {
@Nullable
private String targetBeanName;
/**
* Set the name of the target bean.
* <p>The target does not <i>have</i> 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<Object> 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<Object>, 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);
}
}
}

@ -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}).
*
* <p>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.
*
* <p>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);
}
}
}

@ -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.
* <p>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() + '>';
}
}

@ -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}.
*
* <p>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
* <b>prototype beans</b>, 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. <b>For singleton beans, direct
* setter or constructor injection of the target bean is preferable.</b>
*
* <p>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
* <b>one</b> 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.
*
* <p>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.
*
* <p>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.
*
* <p>By way of an example, consider the following service locator interface.
* Note that this interface is not dependent on any Spring APIs.
*
* <pre class="code">package a.b.c;
*
*public interface ServiceFactory {
*
* public MyService getService();
*}</pre>
*
* <p>A sample config in an XML-based
* {@link org.springframework.beans.factory.BeanFactory} might look as follows:
*
* <pre class="code">&lt;beans>
*
* &lt;!-- Prototype bean since we have state -->
* &lt;bean id="myService" class="a.b.c.MyService" singleton="false"/>
*
* &lt;!-- will lookup the above 'myService' bean by *TYPE* -->
* &lt;bean id="myServiceFactory"
* class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
* &lt;property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
* &lt;/bean>
*
* &lt;bean id="clientBean" class="a.b.c.MyClientBean">
* &lt;property name="myServiceFactory" ref="myServiceFactory"/>
* &lt;/bean>
*
*&lt;/beans></pre>
*
* <p>The attendant {@code MyClientBean} class implementation might then
* look something like this:
*
* <pre class="code">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...
* }
*}</pre>
*
* <p>By way of an example that looks up a bean <b>by name</b>, consider
* the following service locator interface. Again, note that this
* interface is not dependent on any Spring APIs.
*
* <pre class="code">package a.b.c;
*
*public interface ServiceFactory {
*
* public MyService getService (String serviceName);
*}</pre>
*
* <p>A sample config in an XML-based
* {@link org.springframework.beans.factory.BeanFactory} might look as follows:
*
* <pre class="code">&lt;beans>
*
* &lt;!-- Prototype beans since we have state (both extend MyService) -->
* &lt;bean id="specialService" class="a.b.c.SpecialService" singleton="false"/>
* &lt;bean id="anotherService" class="a.b.c.AnotherService" singleton="false"/>
*
* &lt;bean id="myServiceFactory"
* class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
* &lt;property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
* &lt;/bean>
*
* &lt;bean id="clientBean" class="a.b.c.MyClientBean">
* &lt;property name="myServiceFactory" ref="myServiceFactory"/>
* &lt;/bean>
*
*&lt;/beans></pre>
*
* <p>The attendant {@code MyClientBean} class implementation might then
* look something like this:
*
* <pre class="code">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...
* }
*}</pre>
*
* <p>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<Object>, BeanFactoryAware, InitializingBean {
@Nullable
private Class<?> serviceLocatorInterface;
@Nullable
private Constructor<Exception> 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)}.
* <p>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.
* <p>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.
* <p>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<Exception> determineServiceLocatorExceptionConstructor(Class<? extends Exception> exceptionClass) {
try {
return (Constructor<Exception>) exceptionClass.getConstructor(String.class, Throwable.class);
}
catch (NoSuchMethodException ex) {
try {
return (Constructor<Exception>) exceptionClass.getConstructor(Throwable.class);
}
catch (NoSuchMethodException ex2) {
try {
return (Constructor<Exception>) 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.
* <p>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<Exception> 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 '<type> xxx()' or '<type> xxx(<idtype> id)' " +
"on factory interface, but tried to call: " + interfaceMethod);
}
return serviceLocatorReturnType;
}
}
}

@ -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<Set<Object>> {
@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.
* <p>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<Set> getObjectType() {
return Set.class;
}
@Override
@SuppressWarnings("unchecked")
protected Set<Object> createInstance() {
if (this.sourceSet == null) {
throw new IllegalArgumentException("'sourceSet' is required");
}
Set<Object> 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;
}
}

@ -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.
*
* <p>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.
* <p>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.
* <p>When running within a full BeanFactory: <b>Register a bean definition
* instead of an existing instance if your bean is supposed to receive
* initialization and/or destruction callbacks.</b>
* <p>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.
* <p>Only checks already instantiated singletons; does not return an Object
* for singleton bean definitions which have not been instantiated yet.
* <p>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.
* <p><b>NOTE:</b> 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.
* <p>Only checks already instantiated singletons; does not return {@code true}
* for singleton bean definitions which have not been instantiated yet.
* <p>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.
* <p>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.
* <p>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.
* <p><b>NOTE:</b> 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.
* <p>Only checks already instantiated singletons; does not return names
* for singleton bean definitions which have not been instantiated yet.
* <p>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.
* <p>Only checks already instantiated singletons; does not count
* singleton bean definitions which have not been instantiated yet.
* <p>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();
}

@ -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.
*
* <p>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.
* <p>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.
* <p>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.
* <p>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 + "]";
}
}

@ -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.
*
* <p>This bean definition reader also understands XML bean definition files,
* allowing for seamless mixing and matching with Groovy bean definition files.
*
* <p>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.
*
* <h3>Example Syntax</h3>
* <pre class="code">
* 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
* }
* }
* }</pre>
*
* <p>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.
*
* <pre class="code">
* 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
* }
* }
* }</pre>
*
* @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<String, String> namespaces = new HashMap<>();
private final Map<String, DeferredProperty> 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.
* <p>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.
* <p>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.
* <p>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<Object> beans = new Closure<Object>(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<Object> 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<String, String> definition) {
if (!definition.isEmpty()) {
for (Map.Entry<String,String> 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<Object> 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<Object> 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<Object> 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<Object, Object> 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<Object> 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:
* <ul>
* <li>Retrieve a variable from the bean builder's binding if it exists
* <li>Retrieve a RuntimeBeanReference for a specific bean if it exists
* <li>Otherwise just delegate to MetaClass.getProperty which will resolve
* properties from the {@code GroovyBeanDefinitionReader} itself
* </ul>
*/
@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));
}
}
}
}
}

@ -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<String> 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);
}
}
}
}

@ -0,0 +1,4 @@
/**
* Support package for Groovy-based bean definitions.
*/
package org.springframework.beans.factory.groovy;

@ -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();
}
}

@ -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;
}
}

@ -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<BeanDefinition> innerBeans = new ArrayList<>();
List<BeanReference> 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)));
}
}

@ -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());
}
}

@ -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 + "'";
}
}

@ -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.
*
* <p>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.
*
* <p>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.
*
* <p>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.
*
* <p>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 <strong>not</strong> 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}.
* <p>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.
* <p>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}.
* <p>It should be noted that a {@code ComponentDefinition} may well be related with
* other {@link BeanDefinition BeanDefinitions} via {@link BeanReference references},
* however these are <strong>not</strong> 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.
* <p>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}.
* <p>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();
}

@ -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<ComponentDefinition> 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]);
}
}

@ -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 : "");
}
}

@ -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.
*
* <p>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 {
}

@ -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
}
}

@ -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.
*
* <p>The first error encountered results in a {@link BeanDefinitionParsingException}
* being thrown.
*
* <p>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.
* <p>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());
}
}

@ -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;
}
}

@ -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}.
*
* <p>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}).
* <p>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;
}
}

@ -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.
*
* <p>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;
}
}

@ -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.
*
* <p>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<Entry> 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 {
}
}

@ -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.
*
* <p>Using this implementation means that tools will get raw access to the
* underlying configuration source metadata provided by the tool.
*
* <p>This implementation <strong>should not</strong> 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;
}
}

@ -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}.
*
* <p>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();
}
}

@ -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.
* <p>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.
* <p>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.
* <p>Warnings are <strong>never</strong> considered to be fatal.
* @param problem the source of the warning (never {@code null})
*/
void warning(Problem problem);
}

@ -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 + "'";
}
}

@ -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 + "'";
}
}

@ -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);
}
}

@ -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);
}

@ -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.
*
* <p>Configuration parsers <strong>may</strong> 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);
}

@ -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;

@ -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.
*
* <p>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.
* <p>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.
* <p>If given a plain BeanDefinitionRegistry, the default ResourceLoader will be a
* {@link org.springframework.core.io.support.PathMatchingResourcePatternResolver}.
* <p>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.
* <p>Default is PathMatchingResourcePatternResolver, also capable of
* resource pattern resolving through the ResourcePatternResolver interface.
* <p>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.
* <p>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).
* <p>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.
* <p>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<Resource> 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;
}
}

@ -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.
* <p>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.
* <p>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;
}
}

@ -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.
* <p>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.
* <p>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).
* <p>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.
* <p>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.
* <p>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.
* <p>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());
}
}

@ -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> 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.
* <p>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<Class<?>> 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
* <em>generic factory method</em>, where formal type variables are declared
* on the given method itself.
* <p>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}.
* <pre class="code">{@code public static <T> T createProxy(Class<T> clazz)}</pre>
* <h4>Possible Return Values</h4>
* <ul>
* <li>the target return type, if it can be inferred</li>
* <li>the {@linkplain Method#getReturnType() standard return type}, if
* the given {@code method} does not declare any {@linkplain
* Method#getTypeParameters() formal type variables}</li>
* <li>the {@linkplain Method#getReturnType() standard return type}, if the
* target return type cannot be inferred (e.g., due to type erasure)</li>
* <li>{@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</li>
* </ul>
* @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<Method>[] 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 <T>), not on the enclosing class or interface.
boolean locallyDeclaredTypeVariableMatchesReturnType = false;
for (TypeVariable<Method> 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();
}
}
}
}

@ -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 <T> BeanDefinitionBuilder genericBeanDefinition(Class<T> beanClass, Supplier<T> 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;
}
}

@ -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.
* <p>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.
* <p>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.
* <p>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;
}
}

@ -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;
}
}

@ -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.
*
* <p>This is the only interface in Spring's bean factory packages that encapsulates
* <i>registration</i> of bean definitions. The standard BeanFactory interfaces
* only cover access to a <i>fully configured factory instance</i>.
*
* <p>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);
}

@ -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 <i>before</i> 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;
}

@ -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();
}
}

@ -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);
}
}

@ -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);
}

@ -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.
*
* <p>Uses CGLIB to generate subclasses dynamically if methods need to be
* overridden by the container to implement <em>Method Injection</em>.
*
* @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 <em>method lookup</em>.
*/
private static final int LOOKUP_OVERRIDE = 1;
/**
* Index in the CGLIB callback array for a method that should
* be overridden using generic <em>method replacer</em> 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);
}
}
}

@ -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);
}
}

@ -0,0 +1,636 @@
/*
* 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.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCreationNotAllowedException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.core.SimpleAliasRegistry;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Generic registry for shared bean instances, implementing the
* {@link org.springframework.beans.factory.config.SingletonBeanRegistry}.
* Allows for registering singleton instances that should be shared
* for all callers of the registry, to be obtained via bean name.
*
* <p>Also supports registration of
* {@link org.springframework.beans.factory.DisposableBean} instances,
* (which might or might not correspond to registered singletons),
* to be destroyed on shutdown of the registry. Dependencies between
* beans can be registered to enforce an appropriate shutdown order.
*
* <p>This class mainly serves as base class for
* {@link org.springframework.beans.factory.BeanFactory} implementations,
* factoring out the common management of singleton bean instances. Note that
* the {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
* interface extends the {@link SingletonBeanRegistry} interface.
*
* <p>Note that this class assumes neither a bean definition concept
* nor a specific creation process for bean instances, in contrast to
* {@link AbstractBeanFactory} and {@link DefaultListableBeanFactory}
* (which inherit from it). Can alternatively also be used as a nested
* helper to delegate to.
*
* @author Juergen Hoeller
* @since 2.0
* @see #registerSingleton
* @see #registerDisposableBean
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory
*/
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** Maximum number of suppressed exceptions to preserve. */
private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
/** Set of registered singletons, containing the bean names in registration order. */
private final Set<String> registeredSingletons = new LinkedHashSet<>(256);
/** Names of beans that are currently in creation. */
private final Set<String> singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Names of beans currently excluded from in creation checks. */
private final Set<String> inCreationCheckExclusions =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/** Collection of suppressed Exceptions, available for associating related causes. */
@Nullable
private Set<Exception> suppressedExceptions;
/** Flag that indicates whether we're currently within destroySingletons. */
private boolean singletonsCurrentlyInDestruction = false;
/** Disposable bean instances: bean name to disposable instance. */
private final Map<String, Object> disposableBeans = new LinkedHashMap<>();
/** Map between containing bean names: bean name to Set of bean names that the bean contains. */
private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);
/** Map between dependent bean names: bean name to Set of dependent bean names. */
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
/** Map between depending bean names: bean name to Set of bean names for the bean's dependencies. */
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
@Override
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "Bean name must not be null");
Assert.notNull(singletonObject, "Singleton object must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
/**
* Add the given singleton factory for building the specified singleton
* if necessary.
* <p>To be called for eager registration of singletons, e.g. to be able to
* resolve circular references.
* @param beanName the name of the bean
* @param singletonFactory the factory for the singleton object
*/
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
@Override
@Nullable
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
/**
* Return the (raw) singleton object registered under the given name.
* <p>Checks already instantiated singletons and also allows for an early
* reference to a currently created singleton (resolving a circular reference).
* @param beanName the name of the bean to look for
* @param allowEarlyReference whether early references should be created or not
* @return the registered singleton object, or {@code null} if none found
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
/**
* Return the (raw) singleton object registered under the given name,
* creating and registering a new one if none registered yet.
* @param beanName the name of the bean
* @param singletonFactory the ObjectFactory to lazily create the singleton
* with, if necessary
* @return the registered singleton object
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
if (this.singletonsCurrentlyInDestruction) {
throw new BeanCreationNotAllowedException(beanName,
"Singleton bean creation not allowed while singletons of this factory are in destruction " +
"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
}
if (logger.isDebugEnabled()) {
logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
catch (IllegalStateException ex) {
// Has the singleton object implicitly appeared in the meantime ->
// if yes, proceed with it since the exception indicates that state.
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
throw ex;
}
}
catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) {
ex.addRelatedCause(suppressedException);
}
}
throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
/**
* Register an exception that happened to get suppressed during the creation of a
* singleton bean instance, e.g. a temporary circular reference resolution problem.
* <p>The default implementation preserves any given exception in this registry's
* collection of suppressed exceptions, up to a limit of 100 exceptions, adding
* them as related causes to an eventual top-level {@link BeanCreationException}.
* @param ex the Exception to register
* @see BeanCreationException#getRelatedCauses()
*/
protected void onSuppressedException(Exception ex) {
synchronized (this.singletonObjects) {
if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) {
this.suppressedExceptions.add(ex);
}
}
}
/**
* Remove the bean with the given name from the singleton cache of this factory,
* to be able to clean up eager registration of a singleton if creation failed.
* @param beanName the name of the bean
* @see #getSingletonMutex()
*/
protected void removeSingleton(String beanName) {
synchronized (this.singletonObjects) {
this.singletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.remove(beanName);
}
}
@Override
public boolean containsSingleton(String beanName) {
return this.singletonObjects.containsKey(beanName);
}
@Override
public String[] getSingletonNames() {
synchronized (this.singletonObjects) {
return StringUtils.toStringArray(this.registeredSingletons);
}
}
@Override
public int getSingletonCount() {
synchronized (this.singletonObjects) {
return this.registeredSingletons.size();
}
}
public void setCurrentlyInCreation(String beanName, boolean inCreation) {
Assert.notNull(beanName, "Bean name must not be null");
if (!inCreation) {
this.inCreationCheckExclusions.add(beanName);
}
else {
this.inCreationCheckExclusions.remove(beanName);
}
}
public boolean isCurrentlyInCreation(String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
}
protected boolean isActuallyInCreation(String beanName) {
return isSingletonCurrentlyInCreation(beanName);
}
/**
* Return whether the specified singleton bean is currently in creation
* (within the entire factory).
* @param beanName the name of the bean
*/
public boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
/**
* Callback before singleton creation.
* <p>The default implementation register the singleton as currently in creation.
* @param beanName the name of the singleton about to be created
* @see #isSingletonCurrentlyInCreation
*/
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
/**
* Callback after singleton creation.
* <p>The default implementation marks the singleton as not in creation anymore.
* @param beanName the name of the singleton that has been created
* @see #isSingletonCurrentlyInCreation
*/
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
}
}
/**
* Add the given bean to the list of disposable beans in this registry.
* <p>Disposable beans usually correspond to registered singletons,
* matching the bean name but potentially being a different instance
* (for example, a DisposableBean adapter for a singleton that does not
* naturally implement Spring's DisposableBean interface).
* @param beanName the name of the bean
* @param bean the bean instance
*/
public void registerDisposableBean(String beanName, DisposableBean bean) {
synchronized (this.disposableBeans) {
this.disposableBeans.put(beanName, bean);
}
}
/**
* Register a containment relationship between two beans,
* e.g. between an inner bean and its containing outer bean.
* <p>Also registers the containing bean as dependent on the contained bean
* in terms of destruction order.
* @param containedBeanName the name of the contained (inner) bean
* @param containingBeanName the name of the containing (outer) bean
* @see #registerDependentBean
*/
public void registerContainedBean(String containedBeanName, String containingBeanName) {
synchronized (this.containedBeanMap) {
Set<String> containedBeans =
this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
if (!containedBeans.add(containedBeanName)) {
return;
}
}
registerDependentBean(containedBeanName, containingBeanName);
}
/**
* Register a dependent bean for the given bean,
* to be destroyed before the given bean is destroyed.
* @param beanName the name of the bean
* @param dependentBeanName the name of the dependent bean
*/
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if (!dependentBeans.add(dependentBeanName)) {
return;
}
}
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
dependenciesForBean.add(canonicalName);
}
}
/**
* Determine whether the specified dependent bean has been registered as
* dependent on the given bean or on any of its transitive dependencies.
* @param beanName the name of the bean to check
* @param dependentBeanName the name of the dependent bean
* @since 4.0
*/
protected boolean isDependent(String beanName, String dependentBeanName) {
synchronized (this.dependentBeanMap) {
return isDependent(beanName, dependentBeanName, null);
}
}
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
if (alreadySeen != null && alreadySeen.contains(beanName)) {
return false;
}
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
return false;
}
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true;
}
}
return false;
}
/**
* Determine whether a dependent bean has been registered for the given name.
* @param beanName the name of the bean to check
*/
protected boolean hasDependentBean(String beanName) {
return this.dependentBeanMap.containsKey(beanName);
}
/**
* Return the names of all beans which depend on the specified bean, if any.
* @param beanName the name of the bean
* @return the array of dependent bean names, or an empty array if none
*/
public String[] getDependentBeans(String beanName) {
Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
if (dependentBeans == null) {
return new String[0];
}
synchronized (this.dependentBeanMap) {
return StringUtils.toStringArray(dependentBeans);
}
}
/**
* Return the names of all beans that the specified bean depends on, if any.
* @param beanName the name of the bean
* @return the array of names of beans which the bean depends on,
* or an empty array if none
*/
public String[] getDependenciesForBean(String beanName) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(beanName);
if (dependenciesForBean == null) {
return new String[0];
}
synchronized (this.dependenciesForBeanMap) {
return StringUtils.toStringArray(dependenciesForBean);
}
}
public void destroySingletons() {
if (logger.isTraceEnabled()) {
logger.trace("Destroying singletons in " + this);
}
synchronized (this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}
String[] disposableBeanNames;
synchronized (this.disposableBeans) {
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}
this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();
clearSingletonCache();
}
/**
* Clear all cached singleton instances in this registry.
* @since 4.3.15
*/
protected void clearSingletonCache() {
synchronized (this.singletonObjects) {
this.singletonObjects.clear();
this.singletonFactories.clear();
this.earlySingletonObjects.clear();
this.registeredSingletons.clear();
this.singletonsCurrentlyInDestruction = false;
}
}
/**
* Destroy the given bean. Delegates to {@code destroyBean}
* if a corresponding disposable bean instance is found.
* @param beanName the name of the bean
* @see #destroyBean
*/
public void destroySingleton(String beanName) {
// Remove a registered singleton of the given name, if any.
removeSingleton(beanName);
// Destroy the corresponding DisposableBean instance.
DisposableBean disposableBean;
synchronized (this.disposableBeans) {
disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
}
destroyBean(beanName, disposableBean);
}
/**
* Destroy the given bean. Must destroy beans that depend on the given
* bean before the bean itself. Should not throw any exceptions.
* @param beanName the name of the bean
* @param bean the bean instance to destroy
*/
protected void destroyBean(String beanName, @Nullable DisposableBean bean) {
// Trigger destruction of dependent beans first...
Set<String> dependencies;
synchronized (this.dependentBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
dependencies = this.dependentBeanMap.remove(beanName);
}
if (dependencies != null) {
if (logger.isTraceEnabled()) {
logger.trace("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName);
}
}
// Actually destroy the bean now...
if (bean != null) {
try {
bean.destroy();
}
catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Destruction of bean with name '" + beanName + "' threw an exception", ex);
}
}
}
// Trigger destruction of contained beans...
Set<String> containedBeans;
synchronized (this.containedBeanMap) {
// Within full synchronization in order to guarantee a disconnected Set
containedBeans = this.containedBeanMap.remove(beanName);
}
if (containedBeans != null) {
for (String containedBeanName : containedBeans) {
destroySingleton(containedBeanName);
}
}
// Remove destroyed bean from other beans' dependencies.
synchronized (this.dependentBeanMap) {
for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, Set<String>> entry = it.next();
Set<String> dependenciesToClean = entry.getValue();
dependenciesToClean.remove(beanName);
if (dependenciesToClean.isEmpty()) {
it.remove();
}
}
}
// Remove destroyed bean's prepared dependency information.
this.dependenciesForBeanMap.remove(beanName);
}
/**
* Exposes the singleton mutex to subclasses and external collaborators.
* <p>Subclasses should synchronize on the given Object if they perform
* any sort of extended singleton creation phase. In particular, subclasses
* should <i>not</i> have their own mutexes involved in singleton creation,
* to avoid the potential for deadlocks in lazy-init situations.
*/
@Override
public final Object getSingletonMutex() {
return this.singletonObjects;
}
}

@ -0,0 +1,411 @@
/*
* 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.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Adapter that implements the {@link DisposableBean} and {@link Runnable}
* interfaces performing various destruction steps on a given bean instance:
* <ul>
* <li>DestructionAwareBeanPostProcessors;
* <li>the bean implementing DisposableBean itself;
* <li>a custom destroy method specified on the bean definition.
* </ul>
*
* @author Juergen Hoeller
* @author Costin Leau
* @author Stephane Nicoll
* @since 2.0
* @see AbstractBeanFactory
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
* @see AbstractBeanDefinition#getDestroyMethodName()
*/
@SuppressWarnings("serial")
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
private static final String CLOSE_METHOD_NAME = "close";
private static final String SHUTDOWN_METHOD_NAME = "shutdown";
private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);
private final Object bean;
private final String beanName;
private final boolean invokeDisposableBean;
private final boolean nonPublicAccessAllowed;
@Nullable
private final AccessControlContext acc;
@Nullable
private String destroyMethodName;
@Nullable
private transient Method destroyMethod;
@Nullable
private final List<DestructionAwareBeanPostProcessor> beanPostProcessors;
/**
* Create a new DisposableBeanAdapter for the given bean.
* @param bean the bean instance (never {@code null})
* @param beanName the name of the bean
* @param beanDefinition the merged bean definition
* @param postProcessors the List of BeanPostProcessors
* (potentially DestructionAwareBeanPostProcessor), if any
*/
public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
List<DestructionAwareBeanPostProcessor> postProcessors, @Nullable AccessControlContext acc) {
Assert.notNull(bean, "Disposable bean must not be null");
this.bean = bean;
this.beanName = beanName;
this.invokeDisposableBean =
(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
this.nonPublicAccessAllowed = beanDefinition.isNonPublicAccessAllowed();
this.acc = acc;
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
this.destroyMethodName = destroyMethodName;
Method destroyMethod = determineDestroyMethod(destroyMethodName);
if (destroyMethod == null) {
if (beanDefinition.isEnforceDestroyMethod()) {
throw new BeanDefinitionValidationException("Could not find a destroy method named '" +
destroyMethodName + "' on bean with name '" + beanName + "'");
}
}
else {
Class<?>[] paramTypes = destroyMethod.getParameterTypes();
if (paramTypes.length > 1) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has more than one parameter - not supported as destroy method");
}
else if (paramTypes.length == 1 && boolean.class != paramTypes[0]) {
throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
beanName + "' has a non-boolean parameter - not supported as destroy method");
}
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod);
}
this.destroyMethod = destroyMethod;
}
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
}
/**
* Create a new DisposableBeanAdapter for the given bean.
* @param bean the bean instance (never {@code null})
* @param postProcessors the List of BeanPostProcessors
* (potentially DestructionAwareBeanPostProcessor), if any
*/
public DisposableBeanAdapter(
Object bean, List<DestructionAwareBeanPostProcessor> postProcessors, AccessControlContext acc) {
Assert.notNull(bean, "Disposable bean must not be null");
this.bean = bean;
this.beanName = bean.getClass().getName();
this.invokeDisposableBean = (this.bean instanceof DisposableBean);
this.nonPublicAccessAllowed = true;
this.acc = acc;
this.beanPostProcessors = filterPostProcessors(postProcessors, bean);
}
/**
* Create a new DisposableBeanAdapter for the given bean.
*/
private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDisposableBean,
boolean nonPublicAccessAllowed, @Nullable String destroyMethodName,
@Nullable List<DestructionAwareBeanPostProcessor> postProcessors) {
this.bean = bean;
this.beanName = beanName;
this.invokeDisposableBean = invokeDisposableBean;
this.nonPublicAccessAllowed = nonPublicAccessAllowed;
this.acc = null;
this.destroyMethodName = destroyMethodName;
this.beanPostProcessors = postProcessors;
}
/**
* If the current value of the given beanDefinition's "destroyMethodName" property is
* {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
* Candidate methods are currently limited to public, no-arg methods named "close" or
* "shutdown" (whether declared locally or inherited). The given BeanDefinition's
* "destroyMethodName" is updated to be null if no such method is found, otherwise set
* to the name of the inferred method. This constant serves as the default for the
* {@code @Bean#destroyMethod} attribute and the value of the constant may also be
* used in XML within the {@code <bean destroy-method="">} or {@code
* <beans default-destroy-method="">} attributes.
* <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
* interfaces, reflectively calling the "close" method on implementing beans as well.
*/
@Nullable
private String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName) ||
(destroyMethodName == null && bean instanceof AutoCloseable)) {
// Only perform destroy method inference or Closeable detection
// in case of the bean not explicitly implementing DisposableBean
if (!(bean instanceof DisposableBean)) {
try {
return bean.getClass().getMethod(CLOSE_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex) {
try {
return bean.getClass().getMethod(SHUTDOWN_METHOD_NAME).getName();
}
catch (NoSuchMethodException ex2) {
// no candidate destroy method found
}
}
}
return null;
}
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
}
/**
* Search for all DestructionAwareBeanPostProcessors in the List.
* @param processors the List to search
* @return the filtered List of DestructionAwareBeanPostProcessors
*/
@Nullable
private List<DestructionAwareBeanPostProcessor> filterPostProcessors(
List<DestructionAwareBeanPostProcessor> processors, Object bean) {
List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
if (!CollectionUtils.isEmpty(processors)) {
filteredPostProcessors = new ArrayList<>(processors.size());
for (DestructionAwareBeanPostProcessor processor : processors) {
if (processor.requiresDestruction(bean)) {
filteredPostProcessors.add(processor);
}
}
}
return filteredPostProcessors;
}
@Override
public void run() {
destroy();
}
@Override
public void destroy() {
if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
processor.postProcessBeforeDestruction(this.bean, this.beanName);
}
}
if (this.invokeDisposableBean) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking destroy() on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((DisposableBean) this.bean).destroy();
return null;
}, this.acc);
}
else {
((DisposableBean) this.bean).destroy();
}
}
catch (Throwable ex) {
String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex);
}
else {
logger.warn(msg + ": " + ex);
}
}
}
if (this.destroyMethod != null) {
invokeCustomDestroyMethod(this.destroyMethod);
}
else if (this.destroyMethodName != null) {
Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
if (methodToInvoke != null) {
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
}
}
}
@Nullable
private Method determineDestroyMethod(String name) {
try {
if (System.getSecurityManager() != null) {
return AccessController.doPrivileged((PrivilegedAction<Method>) () -> findDestroyMethod(name));
}
else {
return findDestroyMethod(name);
}
}
catch (IllegalArgumentException ex) {
throw new BeanDefinitionValidationException("Could not find unique destroy method on bean with name '" +
this.beanName + ": " + ex.getMessage());
}
}
@Nullable
private Method findDestroyMethod(String name) {
return (this.nonPublicAccessAllowed ?
BeanUtils.findMethodWithMinimalParameters(this.bean.getClass(), name) :
BeanUtils.findMethodWithMinimalParameters(this.bean.getClass().getMethods(), name));
}
/**
* Invoke the specified custom destroy method on the given bean.
* <p>This implementation invokes a no-arg method if found, else checking
* for a method with a single boolean argument (passing in "true",
* assuming a "force" parameter), else logging an error.
*/
private void invokeCustomDestroyMethod(final Method destroyMethod) {
int paramCount = destroyMethod.getParameterCount();
final Object[] args = new Object[paramCount];
if (paramCount == 1) {
args[0] = Boolean.TRUE;
}
if (logger.isTraceEnabled()) {
logger.trace("Invoking destroy method '" + this.destroyMethodName +
"' on bean with name '" + this.beanName + "'");
}
try {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
ReflectionUtils.makeAccessible(destroyMethod);
return null;
});
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () ->
destroyMethod.invoke(this.bean, args), this.acc);
}
catch (PrivilegedActionException pax) {
throw (InvocationTargetException) pax.getException();
}
}
else {
ReflectionUtils.makeAccessible(destroyMethod);
destroyMethod.invoke(this.bean, args);
}
}
catch (InvocationTargetException ex) {
String msg = "Destroy method '" + this.destroyMethodName + "' on bean with name '" +
this.beanName + "' threw an exception";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.warn("Failed to invoke destroy method '" + this.destroyMethodName +
"' on bean with name '" + this.beanName + "'", ex);
}
}
/**
* Serializes a copy of the state of this class,
* filtering out non-serializable BeanPostProcessors.
*/
protected Object writeReplace() {
List<DestructionAwareBeanPostProcessor> serializablePostProcessors = null;
if (this.beanPostProcessors != null) {
serializablePostProcessors = new ArrayList<>();
for (DestructionAwareBeanPostProcessor postProcessor : this.beanPostProcessors) {
if (postProcessor instanceof Serializable) {
serializablePostProcessors.add(postProcessor);
}
}
}
return new DisposableBeanAdapter(this.bean, this.beanName, this.invokeDisposableBean,
this.nonPublicAccessAllowed, this.destroyMethodName, serializablePostProcessors);
}
/**
* Check whether the given bean has any kind of destroy method to call.
* @param bean the bean instance
* @param beanDefinition the corresponding bean definition
*/
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
if (bean instanceof DisposableBean || bean instanceof AutoCloseable) {
return true;
}
String destroyMethodName = beanDefinition.getDestroyMethodName();
if (AbstractBeanDefinition.INFER_METHOD.equals(destroyMethodName)) {
return (ClassUtils.hasMethod(bean.getClass(), CLOSE_METHOD_NAME) ||
ClassUtils.hasMethod(bean.getClass(), SHUTDOWN_METHOD_NAME));
}
return StringUtils.hasLength(destroyMethodName);
}
/**
* Check whether the given bean has destruction-aware post-processors applying to it.
* @param bean the bean instance
* @param postProcessors the post-processor candidates
*/
public static boolean hasApplicableProcessors(Object bean, List<DestructionAwareBeanPostProcessor> postProcessors) {
if (!CollectionUtils.isEmpty(postProcessors)) {
for (DestructionAwareBeanPostProcessor processor : postProcessors) {
if (processor.requiresDestruction(bean)) {
return true;
}
}
}
return false;
}
}

@ -0,0 +1,106 @@
/*
* 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;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* GenericBeanDefinition is a one-stop shop for standard bean definition purposes.
* Like any bean definition, it allows for specifying a class plus optionally
* constructor argument values and property values. Additionally, deriving from a
* parent bean definition can be flexibly configured through the "parentName" property.
*
* <p>In general, use this {@code GenericBeanDefinition} class for the purpose of
* registering user-visible bean definitions (which a post-processor might operate on,
* potentially even reconfiguring the parent name). Use {@code RootBeanDefinition} /
* {@code ChildBeanDefinition} where parent/child relationships happen to be pre-determined.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setParentName
* @see RootBeanDefinition
* @see ChildBeanDefinition
*/
@SuppressWarnings("serial")
public class GenericBeanDefinition extends AbstractBeanDefinition {
@Nullable
private String parentName;
/**
* Create a new GenericBeanDefinition, to be configured through its bean
* properties and configuration methods.
* @see #setBeanClass
* @see #setScope
* @see #setConstructorArgumentValues
* @see #setPropertyValues
*/
public GenericBeanDefinition() {
super();
}
/**
* Create a new GenericBeanDefinition as deep copy of the given
* bean definition.
* @param original the original bean definition to copy from
*/
public GenericBeanDefinition(BeanDefinition original) {
super(original);
}
@Override
public void setParentName(@Nullable String parentName) {
this.parentName = parentName;
}
@Override
@Nullable
public String getParentName() {
return this.parentName;
}
@Override
public AbstractBeanDefinition cloneBeanDefinition() {
return new GenericBeanDefinition(this);
}
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof GenericBeanDefinition)) {
return false;
}
GenericBeanDefinition that = (GenericBeanDefinition) other;
return (ObjectUtils.nullSafeEquals(this.parentName, that.parentName) && super.equals(other));
}
@Override
public String toString() {
if (this.parentName != null) {
return "Generic bean with parent '" + this.parentName + "': " + super.toString();
}
return "Generic bean: " + super.toString();
}
}

@ -0,0 +1,197 @@
/*
* 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.Method;
import java.util.Properties;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* Basic {@link AutowireCandidateResolver} that performs a full generic type
* match with the candidate's type if the dependency is declared as a generic type
* (e.g. Repository&lt;Customer&gt;).
*
* <p>This is the base class for
* {@link org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver},
* providing an implementation all non-annotation-based resolution steps at this level.
*
* @author Juergen Hoeller
* @since 4.0
*/
public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver
implements BeanFactoryAware, Cloneable {
@Nullable
private BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Nullable
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
@Override
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!super.isAutowireCandidate(bdHolder, descriptor)) {
// If explicitly false, do not proceed with any other checks...
return false;
}
return checkGenericTypeMatch(bdHolder, descriptor);
}
/**
* Match the given dependency type with its generic type information against the given
* candidate bean definition.
*/
protected boolean checkGenericTypeMatch(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
ResolvableType dependencyType = descriptor.getResolvableType();
if (dependencyType.getType() instanceof Class) {
// No generic type -> we know it's a Class type-match, so no need to check again.
return true;
}
ResolvableType targetType = null;
boolean cacheType = false;
RootBeanDefinition rbd = null;
if (bdHolder.getBeanDefinition() instanceof RootBeanDefinition) {
rbd = (RootBeanDefinition) bdHolder.getBeanDefinition();
}
if (rbd != null) {
targetType = rbd.targetType;
if (targetType == null) {
cacheType = true;
// First, check factory method return type, if applicable
targetType = getReturnTypeForFactoryMethod(rbd, descriptor);
if (targetType == null) {
RootBeanDefinition dbd = getResolvedDecoratedDefinition(rbd);
if (dbd != null) {
targetType = dbd.targetType;
if (targetType == null) {
targetType = getReturnTypeForFactoryMethod(dbd, descriptor);
}
}
}
}
}
if (targetType == null) {
// Regular case: straight bean instance, with BeanFactory available.
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanType));
}
}
// Fallback: no BeanFactory set, or no type resolvable through it
// -> best-effort match against the target class if applicable.
if (targetType == null && rbd != null && rbd.hasBeanClass() && rbd.getFactoryMethodName() == null) {
Class<?> beanClass = rbd.getBeanClass();
if (!FactoryBean.class.isAssignableFrom(beanClass)) {
targetType = ResolvableType.forClass(ClassUtils.getUserClass(beanClass));
}
}
}
if (targetType == null) {
return true;
}
if (cacheType) {
rbd.targetType = targetType;
}
if (descriptor.fallbackMatchAllowed() &&
(targetType.hasUnresolvableGenerics() || targetType.resolve() == Properties.class)) {
// Fallback matches allow unresolvable generics, e.g. plain HashMap to Map<String,String>;
// and pragmatically also java.util.Properties to any Map (since despite formally being a
// Map<Object,Object>, java.util.Properties is usually perceived as a Map<String,String>).
return true;
}
// Full check for complex generic type match...
return dependencyType.isAssignableFrom(targetType);
}
@Nullable
protected RootBeanDefinition getResolvedDecoratedDefinition(RootBeanDefinition rbd) {
BeanDefinitionHolder decDef = rbd.getDecoratedDefinition();
if (decDef != null && this.beanFactory instanceof ConfigurableListableBeanFactory) {
ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory;
if (clbf.containsBeanDefinition(decDef.getBeanName())) {
BeanDefinition dbd = clbf.getMergedBeanDefinition(decDef.getBeanName());
if (dbd instanceof RootBeanDefinition) {
return (RootBeanDefinition) dbd;
}
}
}
return null;
}
@Nullable
protected ResolvableType getReturnTypeForFactoryMethod(RootBeanDefinition rbd, DependencyDescriptor descriptor) {
// Should typically be set for any kind of factory method, since the BeanFactory
// pre-resolves them before reaching out to the AutowireCandidateResolver...
ResolvableType returnType = rbd.factoryMethodReturnType;
if (returnType == null) {
Method factoryMethod = rbd.getResolvedFactoryMethod();
if (factoryMethod != null) {
returnType = ResolvableType.forMethodReturnType(factoryMethod);
}
}
if (returnType != null) {
Class<?> resolvedClass = returnType.resolve();
if (resolvedClass != null && descriptor.getDependencyType().isAssignableFrom(resolvedClass)) {
// Only use factory method metadata if the return type is actually expressive enough
// for our dependency. Otherwise, the returned instance type may have matched instead
// in case of a singleton instance having been registered with the container already.
return returnType;
}
}
return null;
}
/**
* This implementation clones all instance fields through standard
* {@link Cloneable} support, allowing for subsequent reconfiguration
* of the cloned instance through a fresh {@link #setBeanFactory} call.
* @see #clone()
*/
@Override
public AutowireCandidateResolver cloneIfNecessary() {
try {
return (AutowireCandidateResolver) clone();
}
catch (CloneNotSupportedException ex) {
throw new IllegalStateException(ex);
}
}
}

@ -0,0 +1,35 @@
/*
* 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.support;
/**
* Internal exception to be propagated from {@link ConstructorResolver},
* passed through to the initiating {@link DefaultSingletonBeanRegistry}
* (without wrapping in a {@code BeanCreationException}).
*
* @author Juergen Hoeller
* @since 5.0
*/
@SuppressWarnings("serial")
class ImplicitlyAppearedSingletonException extends IllegalStateException {
public ImplicitlyAppearedSingletonException() {
super("About-to-be-created singleton instance implicitly appeared through the " +
"creation of the factory bean that its bean definition points to");
}
}

@ -0,0 +1,115 @@
/*
* 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.support;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;
/**
* Represents an override of a method that looks up an object in the same IoC context.
*
* <p>Methods eligible for lookup override must not have arguments.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 1.1
*/
public class LookupOverride extends MethodOverride {
@Nullable
private final String beanName;
@Nullable
private Method method;
/**
* Construct a new LookupOverride.
* @param methodName the name of the method to override
* @param beanName the name of the bean in the current {@code BeanFactory}
* that the overridden method should return (may be {@code null})
*/
public LookupOverride(String methodName, @Nullable String beanName) {
super(methodName);
this.beanName = beanName;
}
/**
* Construct a new LookupOverride.
* @param method the method to override
* @param beanName the name of the bean in the current {@code BeanFactory}
* that the overridden method should return (may be {@code null})
*/
public LookupOverride(Method method, @Nullable String beanName) {
super(method.getName());
this.method = method;
this.beanName = beanName;
}
/**
* Return the name of the bean that should be returned by this method.
*/
@Nullable
public String getBeanName() {
return this.beanName;
}
/**
* Match the specified method by {@link Method} reference or method name.
* <p>For backwards compatibility reasons, in a scenario with overloaded
* non-abstract methods of the given name, only the no-arg variant of a
* method will be turned into a container-driven lookup method.
* <p>In case of a provided {@link Method}, only straight matches will
* be considered, usually demarcated by the {@code @Lookup} annotation.
*/
@Override
public boolean matches(Method method) {
if (this.method != null) {
return method.equals(this.method);
}
else {
return (method.getName().equals(getMethodName()) && (!isOverloaded() ||
Modifier.isAbstract(method.getModifiers()) || method.getParameterCount() == 0));
}
}
@Override
public boolean equals(@Nullable Object other) {
if (!(other instanceof LookupOverride) || !super.equals(other)) {
return false;
}
LookupOverride that = (LookupOverride) other;
return (ObjectUtils.nullSafeEquals(this.method, that.method) &&
ObjectUtils.nullSafeEquals(this.beanName, that.beanName));
}
@Override
public int hashCode() {
return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.beanName));
}
@Override
public String toString() {
return "LookupOverride for method '" + getMethodName() + "'";
}
}

@ -0,0 +1,116 @@
/*
* 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.util.ArrayList;
import java.util.List;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.lang.Nullable;
/**
* Tag collection class used to hold managed List elements, which may
* include runtime bean references (to be resolved into bean objects).
*
* @author Rod Johnson
* @author Rob Harrop
* @author Juergen Hoeller
* @since 27.05.2003
* @param <E> the element type
*/
@SuppressWarnings("serial")
public class ManagedList<E> extends ArrayList<E> implements Mergeable, BeanMetadataElement {
@Nullable
private Object source;
@Nullable
private String elementTypeName;
private boolean mergeEnabled;
public ManagedList() {
}
public ManagedList(int initialCapacity) {
super(initialCapacity);
}
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>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 default element type name (class name) to be used for this list.
*/
public void setElementTypeName(String elementTypeName) {
this.elementTypeName = elementTypeName;
}
/**
* Return the default element type name (class name) to be used for this list.
*/
@Nullable
public String getElementTypeName() {
return this.elementTypeName;
}
/**
* Set whether merging should be enabled for this collection,
* in case of a 'parent' collection value being present.
*/
public void setMergeEnabled(boolean mergeEnabled) {
this.mergeEnabled = mergeEnabled;
}
@Override
public boolean isMergeEnabled() {
return this.mergeEnabled;
}
@Override
@SuppressWarnings("unchecked")
public List<E> merge(@Nullable Object parent) {
if (!this.mergeEnabled) {
throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
}
if (parent == null) {
return this;
}
if (!(parent instanceof List)) {
throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
}
List<E> merged = new ManagedList<>();
merged.addAll((List<E>) parent);
merged.addAll(this);
return merged;
}
}

@ -0,0 +1,134 @@
/*
* 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.util.LinkedHashMap;
import java.util.Map;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.lang.Nullable;
/**
* Tag collection class used to hold managed Map values, which may
* include runtime bean references (to be resolved into bean objects).
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 27.05.2003
* @param <K> the key type
* @param <V> the value type
*/
@SuppressWarnings("serial")
public class ManagedMap<K, V> extends LinkedHashMap<K, V> implements Mergeable, BeanMetadataElement {
@Nullable
private Object source;
@Nullable
private String keyTypeName;
@Nullable
private String valueTypeName;
private boolean mergeEnabled;
public ManagedMap() {
}
public ManagedMap(int initialCapacity) {
super(initialCapacity);
}
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>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 default key type name (class name) to be used for this map.
*/
public void setKeyTypeName(@Nullable String keyTypeName) {
this.keyTypeName = keyTypeName;
}
/**
* Return the default key type name (class name) to be used for this map.
*/
@Nullable
public String getKeyTypeName() {
return this.keyTypeName;
}
/**
* Set the default value type name (class name) to be used for this map.
*/
public void setValueTypeName(@Nullable String valueTypeName) {
this.valueTypeName = valueTypeName;
}
/**
* Return the default value type name (class name) to be used for this map.
*/
@Nullable
public String getValueTypeName() {
return this.valueTypeName;
}
/**
* Set whether merging should be enabled for this collection,
* in case of a 'parent' collection value being present.
*/
public void setMergeEnabled(boolean mergeEnabled) {
this.mergeEnabled = mergeEnabled;
}
@Override
public boolean isMergeEnabled() {
return this.mergeEnabled;
}
@Override
@SuppressWarnings("unchecked")
public Object merge(@Nullable Object parent) {
if (!this.mergeEnabled) {
throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
}
if (parent == null) {
return this;
}
if (!(parent instanceof Map)) {
throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
}
Map<K, V> merged = new ManagedMap<>();
merged.putAll((Map<K, V>) parent);
merged.putAll(this);
return merged;
}
}

@ -0,0 +1,87 @@
/*
* 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.support;
import java.util.Properties;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.lang.Nullable;
/**
* Tag class which represents a Spring-managed {@link Properties} instance
* that supports merging of parent/child definitions.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
@SuppressWarnings("serial")
public class ManagedProperties extends Properties implements Mergeable, BeanMetadataElement {
@Nullable
private Object source;
private boolean mergeEnabled;
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>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 whether merging should be enabled for this collection,
* in case of a 'parent' collection value being present.
*/
public void setMergeEnabled(boolean mergeEnabled) {
this.mergeEnabled = mergeEnabled;
}
@Override
public boolean isMergeEnabled() {
return this.mergeEnabled;
}
@Override
public Object merge(@Nullable Object parent) {
if (!this.mergeEnabled) {
throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
}
if (parent == null) {
return this;
}
if (!(parent instanceof Properties)) {
throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
}
Properties merged = new ManagedProperties();
merged.putAll((Properties) parent);
merged.putAll(this);
return merged;
}
}

@ -0,0 +1,115 @@
/*
* 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.util.LinkedHashSet;
import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.lang.Nullable;
/**
* Tag collection class used to hold managed Set values, which may
* include runtime bean references (to be resolved into bean objects).
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 21.01.2004
* @param <E> the element type
*/
@SuppressWarnings("serial")
public class ManagedSet<E> extends LinkedHashSet<E> implements Mergeable, BeanMetadataElement {
@Nullable
private Object source;
@Nullable
private String elementTypeName;
private boolean mergeEnabled;
public ManagedSet() {
}
public ManagedSet(int initialCapacity) {
super(initialCapacity);
}
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>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 default element type name (class name) to be used for this set.
*/
public void setElementTypeName(@Nullable String elementTypeName) {
this.elementTypeName = elementTypeName;
}
/**
* Return the default element type name (class name) to be used for this set.
*/
@Nullable
public String getElementTypeName() {
return this.elementTypeName;
}
/**
* Set whether merging should be enabled for this collection,
* in case of a 'parent' collection value being present.
*/
public void setMergeEnabled(boolean mergeEnabled) {
this.mergeEnabled = mergeEnabled;
}
@Override
public boolean isMergeEnabled() {
return this.mergeEnabled;
}
@Override
@SuppressWarnings("unchecked")
public Set<E> merge(@Nullable Object parent) {
if (!this.mergeEnabled) {
throw new IllegalStateException("Not allowed to merge when the 'mergeEnabled' property is set to 'false'");
}
if (parent == null) {
return this;
}
if (!(parent instanceof Set)) {
throw new IllegalArgumentException("Cannot merge with object of type [" + parent.getClass() + "]");
}
Set<E> merged = new ManagedSet<>();
merged.addAll((Set<E>) parent);
merged.addAll(this);
return merged;
}
}

@ -0,0 +1,60 @@
/*
* 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.config.BeanPostProcessor;
/**
* Post-processor callback interface for <i>merged</i> bean definitions at runtime.
* {@link BeanPostProcessor} implementations may implement this sub-interface in order
* to post-process the merged bean definition (a processed copy of the original bean
* definition) that the Spring {@code BeanFactory} uses to create a bean instance.
*
* <p>The {@link #postProcessMergedBeanDefinition} method may for example introspect
* the bean definition in order to prepare some cached metadata before post-processing
* actual instances of a bean. It is also allowed to modify the bean definition but
* <i>only</i> for definition properties which are actually intended for concurrent
* modification. Essentially, this only applies to operations defined on the
* {@link RootBeanDefinition} itself but not to the properties of its base classes.
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#getMergedBeanDefinition
*/
public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
/**
* Post-process the given merged bean definition for the specified bean.
* @param beanDefinition 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 AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors
*/
void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
/**
* A notification that the bean definition for the specified name has been reset,
* and that this post-processor should clear any metadata for the affected bean.
* <p>The default implementation is empty.
* @param beanName the name of the bean
* @since 5.1
* @see DefaultListableBeanFactory#resetBeanDefinition
*/
default void resetBeanDefinition(String beanName) {
}
}

@ -0,0 +1,127 @@
/*
* 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.support;
import java.lang.reflect.Method;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Object representing the override of a method on a managed object by the IoC
* container.
*
* <p>Note that the override mechanism is <em>not</em> intended as a generic
* means of inserting crosscutting code: use AOP for that.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.1
*/
public abstract class MethodOverride implements BeanMetadataElement {
private final String methodName;
private boolean overloaded = true;
@Nullable
private Object source;
/**
* Construct a new override for the given method.
* @param methodName the name of the method to override
*/
protected MethodOverride(String methodName) {
Assert.notNull(methodName, "Method name must not be null");
this.methodName = methodName;
}
/**
* Return the name of the method to be overridden.
*/
public String getMethodName() {
return this.methodName;
}
/**
* Set whether the overridden method is <em>overloaded</em> (i.e., whether argument
* type matching needs to occur to disambiguate methods of the same name).
* <p>Default is {@code true}; can be switched to {@code false} to optimize
* runtime performance.
*/
protected void setOverloaded(boolean overloaded) {
this.overloaded = overloaded;
}
/**
* Return whether the overridden method is <em>overloaded</em> (i.e., whether argument
* type matching needs to occur to disambiguate methods of the same name).
*/
protected boolean isOverloaded() {
return this.overloaded;
}
/**
* Set the configuration source {@code Object} for this metadata element.
* <p>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;
}
/**
* Subclasses must override this to indicate whether they <em>match</em> the
* given method. This allows for argument list checking as well as method
* name checking.
* @param method the method to check
* @return whether this override matches the given method
*/
public abstract boolean matches(Method method);
@Override
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MethodOverride)) {
return false;
}
MethodOverride that = (MethodOverride) other;
return (ObjectUtils.nullSafeEquals(this.methodName, that.methodName) &&
ObjectUtils.nullSafeEquals(this.source, that.source));
}
@Override
public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.methodName);
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.source);
return hashCode;
}
}

@ -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.support;
import java.lang.reflect.Method;
/**
* Interface to be implemented by classes that can reimplement any method
* on an IoC-managed object: the <b>Method Injection</b> form of
* Dependency Injection.
*
* <p>Such methods may be (but need not be) abstract, in which case the
* container will create a concrete subclass to instantiate.
*
* @author Rod Johnson
* @since 1.1
*/
public interface MethodReplacer {
/**
* Reimplement the given method.
* @param obj the instance we're reimplementing the method for
* @param method the method to reimplement
* @param args arguments to the method
* @return return value for the method
*/
Object reimplement(Object obj, Method method, Object[] args) throws Throwable;
}

@ -0,0 +1,57 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.FactoryBean;
import org.springframework.lang.Nullable;
/**
* Internal representation of a null bean instance, e.g. for a {@code null} value
* returned from {@link FactoryBean#getObject()} or from a factory method.
*
* <p>Each such null bean is represented by a dedicated {@code NullBean} instance
* which are not equal to each other, uniquely differentiating each bean as returned
* from all variants of {@link org.springframework.beans.factory.BeanFactory#getBean}.
* However, each such instance will return {@code true} for {@code #equals(null)}
* and returns "null" from {@code #toString()}, which is how they can be tested
* externally (since this class itself is not public).
*
* @author Juergen Hoeller
* @since 5.0
*/
final class NullBean {
NullBean() {
}
@Override
public boolean equals(@Nullable Object obj) {
return (this == obj || obj == null);
}
@Override
public int hashCode() {
return NullBean.class.hashCode();
}
@Override
public String toString() {
return "null";
}
}

@ -0,0 +1,485 @@
/*
* 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.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A root bean definition represents the merged bean definition that backs
* a specific bean in a Spring BeanFactory at runtime. It might have been created
* from multiple original bean definitions that inherit from each other,
* typically registered as {@link GenericBeanDefinition GenericBeanDefinitions}.
* A root bean definition is essentially the 'unified' bean definition view at runtime.
*
* <p>Root bean definitions may also be used for registering individual bean definitions
* in the configuration phase. However, since Spring 2.5, the preferred way to register
* bean definitions programmatically is the {@link GenericBeanDefinition} class.
* GenericBeanDefinition has the advantage that it allows to dynamically define
* parent dependencies, not 'hard-coding' the role as a root bean definition.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see GenericBeanDefinition
* @see ChildBeanDefinition
*/
@SuppressWarnings("serial")
public class RootBeanDefinition extends AbstractBeanDefinition {
@Nullable
private BeanDefinitionHolder decoratedDefinition;
@Nullable
private AnnotatedElement qualifiedElement;
/** Determines if the definition needs to be re-merged. */
volatile boolean stale;
boolean allowCaching = true;
boolean isFactoryMethodUnique;
@Nullable
volatile ResolvableType targetType;
/** Package-visible field for caching the determined Class of a given bean definition. */
@Nullable
volatile Class<?> resolvedTargetType;
/** Package-visible field for caching if the bean is a factory bean. */
@Nullable
volatile Boolean isFactoryBean;
/** Package-visible field for caching the return type of a generically typed factory method. */
@Nullable
volatile ResolvableType factoryMethodReturnType;
/** Package-visible field for caching a unique factory method candidate for introspection. */
@Nullable
volatile Method factoryMethodToIntrospect;
/** Common lock for the four constructor fields below. */
final Object constructorArgumentLock = new Object();
/** Package-visible field for caching the resolved constructor or factory method. */
@Nullable
Executable resolvedConstructorOrFactoryMethod;
/** Package-visible field that marks the constructor arguments as resolved. */
boolean constructorArgumentsResolved = false;
/** Package-visible field for caching fully resolved constructor arguments. */
@Nullable
Object[] resolvedConstructorArguments;
/** Package-visible field for caching partly prepared constructor arguments. */
@Nullable
Object[] preparedConstructorArguments;
/** Common lock for the two post-processing fields below. */
final Object postProcessingLock = new Object();
/** Package-visible field that indicates MergedBeanDefinitionPostProcessor having been applied. */
boolean postProcessed = false;
/** Package-visible field that indicates a before-instantiation post-processor having kicked in. */
@Nullable
volatile Boolean beforeInstantiationResolved;
@Nullable
private Set<Member> externallyManagedConfigMembers;
@Nullable
private Set<String> externallyManagedInitMethods;
@Nullable
private Set<String> externallyManagedDestroyMethods;
/**
* Create a new RootBeanDefinition, to be configured through its bean
* properties and configuration methods.
* @see #setBeanClass
* @see #setScope
* @see #setConstructorArgumentValues
* @see #setPropertyValues
*/
public RootBeanDefinition() {
super();
}
/**
* Create a new RootBeanDefinition for a singleton.
* @param beanClass the class of the bean to instantiate
* @see #setBeanClass
*/
public RootBeanDefinition(@Nullable Class<?> beanClass) {
super();
setBeanClass(beanClass);
}
/**
* Create a new RootBeanDefinition for a singleton bean, constructing each instance
* through calling the given supplier (possibly a lambda or method reference).
* @param beanClass the class of the bean to instantiate
* @param instanceSupplier the supplier to construct a bean instance,
* as an alternative to a declaratively specified factory method
* @since 5.0
* @see #setInstanceSupplier
*/
public <T> RootBeanDefinition(@Nullable Class<T> beanClass, @Nullable Supplier<T> instanceSupplier) {
super();
setBeanClass(beanClass);
setInstanceSupplier(instanceSupplier);
}
/**
* Create a new RootBeanDefinition for a scoped bean, constructing each instance
* through calling the given supplier (possibly a lambda or method reference).
* @param beanClass the class of the bean to instantiate
* @param scope the name of the corresponding scope
* @param instanceSupplier the supplier to construct a bean instance,
* as an alternative to a declaratively specified factory method
* @since 5.0
* @see #setInstanceSupplier
*/
public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) {
super();
setBeanClass(beanClass);
setScope(scope);
setInstanceSupplier(instanceSupplier);
}
/**
* Create a new RootBeanDefinition for a singleton,
* using the given autowire mode.
* @param beanClass the class of the bean to instantiate
* @param autowireMode by name or type, using the constants in this interface
* @param dependencyCheck whether to perform a dependency check for objects
* (not applicable to autowiring a constructor, thus ignored there)
*/
public RootBeanDefinition(@Nullable Class<?> beanClass, int autowireMode, boolean dependencyCheck) {
super();
setBeanClass(beanClass);
setAutowireMode(autowireMode);
if (dependencyCheck && getResolvedAutowireMode() != AUTOWIRE_CONSTRUCTOR) {
setDependencyCheck(DEPENDENCY_CHECK_OBJECTS);
}
}
/**
* Create a new RootBeanDefinition for a singleton,
* providing constructor arguments and property values.
* @param beanClass the class of the bean to instantiate
* @param cargs the constructor argument values to apply
* @param pvs the property values to apply
*/
public RootBeanDefinition(@Nullable Class<?> beanClass, @Nullable ConstructorArgumentValues cargs,
@Nullable MutablePropertyValues pvs) {
super(cargs, pvs);
setBeanClass(beanClass);
}
/**
* Create a new RootBeanDefinition for a singleton,
* providing constructor arguments and property values.
* <p>Takes a bean class name to avoid eager loading of the bean class.
* @param beanClassName the name of the class to instantiate
*/
public RootBeanDefinition(String beanClassName) {
setBeanClassName(beanClassName);
}
/**
* Create a new RootBeanDefinition for a singleton,
* providing constructor arguments and property values.
* <p>Takes a bean class name to avoid eager loading of the bean class.
* @param beanClassName the name of the class to instantiate
* @param cargs the constructor argument values to apply
* @param pvs the property values to apply
*/
public RootBeanDefinition(String beanClassName, ConstructorArgumentValues cargs, MutablePropertyValues pvs) {
super(cargs, pvs);
setBeanClassName(beanClassName);
}
/**
* Create a new RootBeanDefinition as deep copy of the given
* bean definition.
* @param original the original bean definition to copy from
*/
public RootBeanDefinition(RootBeanDefinition original) {
super(original);
this.decoratedDefinition = original.decoratedDefinition;
this.qualifiedElement = original.qualifiedElement;
this.allowCaching = original.allowCaching;
this.isFactoryMethodUnique = original.isFactoryMethodUnique;
this.targetType = original.targetType;
this.factoryMethodToIntrospect = original.factoryMethodToIntrospect;
}
/**
* Create a new RootBeanDefinition as deep copy of the given
* bean definition.
* @param original the original bean definition to copy from
*/
RootBeanDefinition(BeanDefinition original) {
super(original);
}
@Override
public String getParentName() {
return null;
}
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
/**
* Register a target definition that is being decorated by this bean definition.
*/
public void setDecoratedDefinition(@Nullable BeanDefinitionHolder decoratedDefinition) {
this.decoratedDefinition = decoratedDefinition;
}
/**
* Return the target definition that is being decorated by this bean definition, if any.
*/
@Nullable
public BeanDefinitionHolder getDecoratedDefinition() {
return this.decoratedDefinition;
}
/**
* Specify the {@link AnnotatedElement} defining qualifiers,
* to be used instead of the target class or factory method.
* @since 4.3.3
* @see #setTargetType(ResolvableType)
* @see #getResolvedFactoryMethod()
*/
public void setQualifiedElement(@Nullable AnnotatedElement qualifiedElement) {
this.qualifiedElement = qualifiedElement;
}
/**
* Return the {@link AnnotatedElement} defining qualifiers, if any.
* Otherwise, the factory method and target class will be checked.
* @since 4.3.3
*/
@Nullable
public AnnotatedElement getQualifiedElement() {
return this.qualifiedElement;
}
/**
* Specify a generics-containing target type of this bean definition, if known in advance.
* @since 4.3.3
*/
public void setTargetType(ResolvableType targetType) {
this.targetType = targetType;
}
/**
* Specify the target type of this bean definition, if known in advance.
* @since 3.2.2
*/
public void setTargetType(@Nullable Class<?> targetType) {
this.targetType = (targetType != null ? ResolvableType.forClass(targetType) : null);
}
/**
* Return the target type of this bean definition, if known
* (either specified in advance or resolved on first instantiation).
* @since 3.2.2
*/
@Nullable
public Class<?> getTargetType() {
if (this.resolvedTargetType != null) {
return this.resolvedTargetType;
}
ResolvableType targetType = this.targetType;
return (targetType != null ? targetType.resolve() : null);
}
/**
* Return a {@link ResolvableType} for this bean definition,
* either from runtime-cached type information or from configuration-time
* {@link #setTargetType(ResolvableType)} or {@link #setBeanClass(Class)},
* also considering resolved factory method definitions.
* @since 5.1
* @see #setTargetType(ResolvableType)
* @see #setBeanClass(Class)
* @see #setResolvedFactoryMethod(Method)
*/
@Override
public ResolvableType getResolvableType() {
ResolvableType targetType = this.targetType;
if (targetType != null) {
return targetType;
}
ResolvableType returnType = this.factoryMethodReturnType;
if (returnType != null) {
return returnType;
}
Method factoryMethod = this.factoryMethodToIntrospect;
if (factoryMethod != null) {
return ResolvableType.forMethodReturnType(factoryMethod);
}
return super.getResolvableType();
}
/**
* Determine preferred constructors to use for default construction, if any.
* Constructor arguments will be autowired if necessary.
* @return one or more preferred constructors, or {@code null} if none
* (in which case the regular no-arg default constructor will be called)
* @since 5.1
*/
@Nullable
public Constructor<?>[] getPreferredConstructors() {
return null;
}
/**
* Specify a factory method name that refers to a non-overloaded method.
*/
public void setUniqueFactoryMethodName(String name) {
Assert.hasText(name, "Factory method name must not be empty");
setFactoryMethodName(name);
this.isFactoryMethodUnique = true;
}
/**
* Specify a factory method name that refers to an overloaded method.
* @since 5.2
*/
public void setNonUniqueFactoryMethodName(String name) {
Assert.hasText(name, "Factory method name must not be empty");
setFactoryMethodName(name);
this.isFactoryMethodUnique = false;
}
/**
* Check whether the given candidate qualifies as a factory method.
*/
public boolean isFactoryMethod(Method candidate) {
return candidate.getName().equals(getFactoryMethodName());
}
/**
* Set a resolved Java Method for the factory method on this bean definition.
* @param method the resolved factory method, or {@code null} to reset it
* @since 5.2
*/
public void setResolvedFactoryMethod(@Nullable Method method) {
this.factoryMethodToIntrospect = method;
}
/**
* Return the resolved factory method as a Java Method object, if available.
* @return the factory method, or {@code null} if not found or not resolved yet
*/
@Nullable
public Method getResolvedFactoryMethod() {
return this.factoryMethodToIntrospect;
}
public void registerExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
if (this.externallyManagedConfigMembers == null) {
this.externallyManagedConfigMembers = new HashSet<>(1);
}
this.externallyManagedConfigMembers.add(configMember);
}
}
public boolean isExternallyManagedConfigMember(Member configMember) {
synchronized (this.postProcessingLock) {
return (this.externallyManagedConfigMembers != null &&
this.externallyManagedConfigMembers.contains(configMember));
}
}
public void registerExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
if (this.externallyManagedInitMethods == null) {
this.externallyManagedInitMethods = new HashSet<>(1);
}
this.externallyManagedInitMethods.add(initMethod);
}
}
public boolean isExternallyManagedInitMethod(String initMethod) {
synchronized (this.postProcessingLock) {
return (this.externallyManagedInitMethods != null &&
this.externallyManagedInitMethods.contains(initMethod));
}
}
public void registerExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
if (this.externallyManagedDestroyMethods == null) {
this.externallyManagedDestroyMethods = new HashSet<>(1);
}
this.externallyManagedDestroyMethods.add(destroyMethod);
}
}
public boolean isExternallyManagedDestroyMethod(String destroyMethod) {
synchronized (this.postProcessingLock) {
return (this.externallyManagedDestroyMethods != null &&
this.externallyManagedDestroyMethods.contains(destroyMethod));
}
}
@Override
public RootBeanDefinition cloneBeanDefinition() {
return new RootBeanDefinition(this);
}
@Override
public boolean equals(@Nullable Object other) {
return (this == other || (other instanceof RootBeanDefinition && super.equals(other)));
}
@Override
public String toString() {
return "Root bean: " + super.toString();
}
}

@ -0,0 +1,35 @@
/*
* 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 java.security.AccessControlContext;
/**
* Provider of the security context of the code running inside the bean factory.
*
* @author Costin Leau
* @since 3.0
*/
public interface SecurityContextProvider {
/**
* Provides a security access control context relevant to a bean factory.
* @return bean factory security control context
*/
AccessControlContext getAccessControlContext();
}

@ -0,0 +1,88 @@
/*
* 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.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.SimpleAliasRegistry;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Simple implementation of the {@link BeanDefinitionRegistry} interface.
* Provides registry capabilities only, with no factory capabilities built in.
* Can for example be used for testing bean definition readers.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class SimpleBeanDefinitionRegistry extends SimpleAliasRegistry implements BeanDefinitionRegistry {
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "'beanName' must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
this.beanDefinitionMap.put(beanName, beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
if (this.beanDefinitionMap.remove(beanName) == null) {
throw new NoSuchBeanDefinitionException(beanName);
}
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (bd == null) {
throw new NoSuchBeanDefinitionException(beanName);
}
return bd;
}
@Override
public boolean containsBeanDefinition(String beanName) {
return this.beanDefinitionMap.containsKey(beanName);
}
@Override
public String[] getBeanDefinitionNames() {
return StringUtils.toStringArray(this.beanDefinitionMap.keySet());
}
@Override
public int getBeanDefinitionCount() {
return this.beanDefinitionMap.size();
}
@Override
public boolean isBeanNameInUse(String beanName) {
return isAlias(beanName) || containsBeanDefinition(beanName);
}
}

@ -0,0 +1,62 @@
/*
* 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.security.AccessControlContext;
import java.security.AccessController;
import org.springframework.lang.Nullable;
/**
* Simple {@link SecurityContextProvider} implementation.
*
* @author Costin Leau
* @since 3.0
*/
public class SimpleSecurityContextProvider implements SecurityContextProvider {
@Nullable
private final AccessControlContext acc;
/**
* Construct a new {@code SimpleSecurityContextProvider} instance.
* <p>The security context will be retrieved on each call from the current
* thread.
*/
public SimpleSecurityContextProvider() {
this(null);
}
/**
* Construct a new {@code SimpleSecurityContextProvider} instance.
* <p>If the given control context is null, the security context will be
* retrieved on each call from the current thread.
* @param acc access control context (can be {@code null})
* @see AccessController#getContext()
*/
public SimpleSecurityContextProvider(@Nullable AccessControlContext acc) {
this.acc = acc;
}
@Override
public AccessControlContext getAccessControlContext() {
return (this.acc != null ? this.acc : AccessController.getContext());
}
}

@ -0,0 +1,466 @@
/*
* 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.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.BeanIsNotAFactoryException;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.core.OrderComparator;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Static {@link org.springframework.beans.factory.BeanFactory} implementation
* which allows one to register existing singleton instances programmatically.
*
* <p>Does not have support for prototype beans or aliases.
*
* <p>Serves as an example for a simple implementation of the
* {@link org.springframework.beans.factory.ListableBeanFactory} interface,
* managing existing bean instances rather than creating new ones based on bean
* definitions, and not implementing any extended SPI interfaces (such as
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}).
*
* <p>For a full-fledged factory based on bean definitions, have a look at
* {@link DefaultListableBeanFactory}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Sam Brannen
* @since 06.01.2003
* @see DefaultListableBeanFactory
*/
public class StaticListableBeanFactory implements ListableBeanFactory {
/** Map from bean name to bean instance. */
private final Map<String, Object> beans;
/**
* Create a regular {@code StaticListableBeanFactory}, to be populated
* with singleton bean instances through {@link #addBean} calls.
*/
public StaticListableBeanFactory() {
this.beans = new LinkedHashMap<>();
}
/**
* Create a {@code StaticListableBeanFactory} wrapping the given {@code Map}.
* <p>Note that the given {@code Map} may be pre-populated with beans;
* or new, still allowing for beans to be registered via {@link #addBean};
* or {@link java.util.Collections#emptyMap()} for a dummy factory which
* enforces operating against an empty set of beans.
* @param beans a {@code Map} for holding this factory's beans, with the
* bean name as key and the corresponding singleton object as value
* @since 4.3
*/
public StaticListableBeanFactory(Map<String, Object> beans) {
Assert.notNull(beans, "Beans Map must not be null");
this.beans = beans;
}
/**
* Add a new singleton bean.
* <p>Will overwrite any existing instance for the given name.
* @param name the name of the bean
* @param bean the bean instance
*/
public void addBean(String name, Object bean) {
this.beans.put(name, bean);
}
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------
@Override
public Object getBean(String name) throws BeansException {
String beanName = BeanFactoryUtils.transformedBeanName(name);
Object bean = this.beans.get(beanName);
if (bean == null) {
throw new NoSuchBeanDefinitionException(beanName,
"Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]");
}
// Don't let calling code try to dereference the
// bean factory if the bean isn't a factory
if (BeanFactoryUtils.isFactoryDereference(name) && !(bean instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, bean.getClass());
}
if (bean instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
try {
Object exposedObject = ((FactoryBean<?>) bean).getObject();
if (exposedObject == null) {
throw new BeanCreationException(beanName, "FactoryBean exposed null object");
}
return exposedObject;
}
catch (Exception ex) {
throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
}
}
else {
return bean;
}
}
@Override
@SuppressWarnings("unchecked")
public <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException {
Object bean = getBean(name);
if (requiredType != null && !requiredType.isInstance(bean)) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return (T) bean;
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
if (!ObjectUtils.isEmpty(args)) {
throw new UnsupportedOperationException(
"StaticListableBeanFactory does not support explicit bean creation arguments");
}
return getBean(name);
}
@Override
public <T> T getBean(Class<T> requiredType) throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return getBean(beanNames[0], requiredType);
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
}
else {
throw new NoSuchBeanDefinitionException(requiredType);
}
}
@Override
public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
if (!ObjectUtils.isEmpty(args)) {
throw new UnsupportedOperationException(
"StaticListableBeanFactory does not support explicit bean creation arguments");
}
return getBean(requiredType);
}
@Override
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType) throws BeansException {
return getBeanProvider(ResolvableType.forRawClass(requiredType), true);
}
@Override
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType) {
return getBeanProvider(requiredType, true);
}
@Override
public boolean containsBean(String name) {
return this.beans.containsKey(name);
}
@Override
public boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
Object bean = getBean(name);
// In case of FactoryBean, return singleton status of created object.
if (bean instanceof FactoryBean) {
return ((FactoryBean<?>) bean).isSingleton();
}
return true;
}
@Override
public boolean isPrototype(String name) throws NoSuchBeanDefinitionException {
Object bean = getBean(name);
// In case of FactoryBean, return prototype status of created object.
return ((bean instanceof SmartFactoryBean && ((SmartFactoryBean<?>) bean).isPrototype()) ||
(bean instanceof FactoryBean && !((FactoryBean<?>) bean).isSingleton()));
}
@Override
public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
Class<?> type = getType(name);
return (type != null && typeToMatch.isAssignableFrom(type));
}
@Override
public boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException {
Class<?> type = getType(name);
return (typeToMatch == null || (type != null && typeToMatch.isAssignableFrom(type)));
}
@Override
public Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getType(name, true);
}
@Override
public Class<?> getType(String name, boolean allowFactoryBeanInit) throws NoSuchBeanDefinitionException {
String beanName = BeanFactoryUtils.transformedBeanName(name);
Object bean = this.beans.get(beanName);
if (bean == null) {
throw new NoSuchBeanDefinitionException(beanName,
"Defined beans are [" + StringUtils.collectionToCommaDelimitedString(this.beans.keySet()) + "]");
}
if (bean instanceof FactoryBean && !BeanFactoryUtils.isFactoryDereference(name)) {
// If it's a FactoryBean, we want to look at what it creates, not the factory class.
return ((FactoryBean<?>) bean).getObjectType();
}
return bean.getClass();
}
@Override
public String[] getAliases(String name) {
return new String[0];
}
//---------------------------------------------------------------------
// Implementation of ListableBeanFactory interface
//---------------------------------------------------------------------
@Override
public boolean containsBeanDefinition(String name) {
return this.beans.containsKey(name);
}
@Override
public int getBeanDefinitionCount() {
return this.beans.size();
}
@Override
public String[] getBeanDefinitionNames() {
return StringUtils.toStringArray(this.beans.keySet());
}
@Override
public <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType, boolean allowEagerInit) {
return getBeanProvider(ResolvableType.forRawClass(requiredType), allowEagerInit);
}
@SuppressWarnings("unchecked")
@Override
public <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType, boolean allowEagerInit) {
return new ObjectProvider<T>() {
@Override
public T getObject() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0], requiredType);
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
}
else {
throw new NoSuchBeanDefinitionException(requiredType);
}
}
@Override
public T getObject(Object... args) throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0], args);
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
}
else {
throw new NoSuchBeanDefinitionException(requiredType);
}
}
@Override
@Nullable
public T getIfAvailable() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0]);
}
else if (beanNames.length > 1) {
throw new NoUniqueBeanDefinitionException(requiredType, beanNames);
}
else {
return null;
}
}
@Override
@Nullable
public T getIfUnique() throws BeansException {
String[] beanNames = getBeanNamesForType(requiredType);
if (beanNames.length == 1) {
return (T) getBean(beanNames[0]);
}
else {
return null;
}
}
@Override
public Stream<T> stream() {
return Arrays.stream(getBeanNamesForType(requiredType)).map(name -> (T) getBean(name));
}
@Override
public Stream<T> orderedStream() {
return stream().sorted(OrderComparator.INSTANCE);
}
};
}
@Override
public String[] getBeanNamesForType(@Nullable ResolvableType type) {
return getBeanNamesForType(type, true, true);
}
@Override
public String[] getBeanNamesForType(@Nullable ResolvableType type,
boolean includeNonSingletons, boolean allowEagerInit) {
Class<?> resolved = (type != null ? type.resolve() : null);
boolean isFactoryType = resolved != null && FactoryBean.class.isAssignableFrom(resolved);
List<String> matches = new ArrayList<>();
for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
String beanName = entry.getKey();
Object beanInstance = entry.getValue();
if (beanInstance instanceof FactoryBean && !isFactoryType) {
FactoryBean<?> factoryBean = (FactoryBean<?>) beanInstance;
Class<?> objectType = factoryBean.getObjectType();
if ((includeNonSingletons || factoryBean.isSingleton()) &&
objectType != null && (type == null || type.isAssignableFrom(objectType))) {
matches.add(beanName);
}
}
else {
if (type == null || type.isInstance(beanInstance)) {
matches.add(beanName);
}
}
}
return StringUtils.toStringArray(matches);
}
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type) {
return getBeanNamesForType(ResolvableType.forClass(type));
}
@Override
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
return getBeanNamesForType(ResolvableType.forClass(type), includeNonSingletons, allowEagerInit);
}
@Override
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type) throws BeansException {
return getBeansOfType(type, true, true);
}
@Override
@SuppressWarnings("unchecked")
public <T> Map<String, T> getBeansOfType(@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
boolean isFactoryType = (type != null && FactoryBean.class.isAssignableFrom(type));
Map<String, T> matches = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : this.beans.entrySet()) {
String beanName = entry.getKey();
Object beanInstance = entry.getValue();
// Is bean a FactoryBean?
if (beanInstance instanceof FactoryBean && !isFactoryType) {
// Match object created by FactoryBean.
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
Class<?> objectType = factory.getObjectType();
if ((includeNonSingletons || factory.isSingleton()) &&
objectType != null && (type == null || type.isAssignableFrom(objectType))) {
matches.put(beanName, getBean(beanName, type));
}
}
else {
if (type == null || type.isInstance(beanInstance)) {
// If type to match is FactoryBean, return FactoryBean itself.
// Else, return bean instance.
if (isFactoryType) {
beanName = FACTORY_BEAN_PREFIX + beanName;
}
matches.put(beanName, (T) beanInstance);
}
}
}
return matches;
}
@Override
public String[] getBeanNamesForAnnotation(Class<? extends Annotation> annotationType) {
List<String> results = new ArrayList<>();
for (String beanName : this.beans.keySet()) {
if (findAnnotationOnBean(beanName, annotationType) != null) {
results.add(beanName);
}
}
return StringUtils.toStringArray(results);
}
@Override
public Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
throws BeansException {
Map<String, Object> results = new LinkedHashMap<>();
for (String beanName : this.beans.keySet()) {
if (findAnnotationOnBean(beanName, annotationType) != null) {
results.put(beanName, getBean(beanName));
}
}
return results;
}
@Override
@Nullable
public <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType)
throws NoSuchBeanDefinitionException {
Class<?> beanType = getType(beanName);
return (beanType != null ? AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType) : null);
}
}

@ -0,0 +1,10 @@
/**
* Classes supporting the {@code org.springframework.beans.factory} package.
* Contains abstract base classes for {@code BeanFactory} implementations.
*/
@NonNullApi
@NonNullFields
package org.springframework.beans.factory.support;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;

@ -0,0 +1,178 @@
/*
* 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.wiring;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Convenient base class for bean configurers that can perform Dependency Injection
* on objects (however they may be created). Typically subclassed by AspectJ aspects.
*
* <p>Subclasses may also need a custom metadata resolution strategy, in the
* {@link BeanWiringInfoResolver} interface. The default implementation looks for
* a bean with the same name as the fully-qualified class name. (This is the default
* name of the bean in a Spring XML file if the '{@code id}' attribute is not used.)
* @author Rob Harrop
* @author Rod Johnson
* @author Juergen Hoeller
* @author Adrian Colyer
* @since 2.0
* @see #setBeanWiringInfoResolver
* @see ClassNameBeanWiringInfoResolver
*/
public class BeanConfigurerSupport implements BeanFactoryAware, InitializingBean, DisposableBean {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private volatile BeanWiringInfoResolver beanWiringInfoResolver;
@Nullable
private volatile ConfigurableListableBeanFactory beanFactory;
/**
* Set the {@link BeanWiringInfoResolver} to use.
* <p>The default behavior is to look for a bean with the same name as the class.
* As an alternative, consider using annotation-driven bean wiring.
* @see ClassNameBeanWiringInfoResolver
* @see org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver
*/
public void setBeanWiringInfoResolver(BeanWiringInfoResolver beanWiringInfoResolver) {
Assert.notNull(beanWiringInfoResolver, "BeanWiringInfoResolver must not be null");
this.beanWiringInfoResolver = beanWiringInfoResolver;
}
/**
* Set the {@link BeanFactory} in which this aspect must configure beans.
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"Bean configurer aspect needs to run in a ConfigurableListableBeanFactory: " + beanFactory);
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
if (this.beanWiringInfoResolver == null) {
this.beanWiringInfoResolver = createDefaultBeanWiringInfoResolver();
}
}
/**
* Create the default BeanWiringInfoResolver to be used if none was
* specified explicitly.
* <p>The default implementation builds a {@link ClassNameBeanWiringInfoResolver}.
* @return the default BeanWiringInfoResolver (never {@code null})
*/
@Nullable
protected BeanWiringInfoResolver createDefaultBeanWiringInfoResolver() {
return new ClassNameBeanWiringInfoResolver();
}
/**
* Check that a {@link BeanFactory} has been set.
*/
@Override
public void afterPropertiesSet() {
Assert.notNull(this.beanFactory, "BeanFactory must be set");
}
/**
* Release references to the {@link BeanFactory} and
* {@link BeanWiringInfoResolver} when the container is destroyed.
*/
@Override
public void destroy() {
this.beanFactory = null;
this.beanWiringInfoResolver = null;
}
/**
* Configure the bean instance.
* <p>Subclasses can override this to provide custom configuration logic.
* Typically called by an aspect, for all bean instances matched by a pointcut.
* @param beanInstance the bean instance to configure (must <b>not</b> be {@code null})
*/
public void configureBean(Object beanInstance) {
if (this.beanFactory == null) {
if (logger.isDebugEnabled()) {
logger.debug("BeanFactory has not been set on " + ClassUtils.getShortName(getClass()) + ": " +
"Make sure this configurer runs in a Spring container. Unable to configure bean of type [" +
ClassUtils.getDescriptiveType(beanInstance) + "]. Proceeding without injection.");
}
return;
}
BeanWiringInfoResolver bwiResolver = this.beanWiringInfoResolver;
Assert.state(bwiResolver != null, "No BeanWiringInfoResolver available");
BeanWiringInfo bwi = bwiResolver.resolveWiringInfo(beanInstance);
if (bwi == null) {
// Skip the bean if no wiring info given.
return;
}
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(beanFactory != null, "No BeanFactory available");
try {
String beanName = bwi.getBeanName();
if (bwi.indicatesAutowiring() || (bwi.isDefaultBeanName() && beanName != null &&
!beanFactory.containsBean(beanName))) {
// Perform autowiring (also applying standard factory / post-processor callbacks).
beanFactory.autowireBeanProperties(beanInstance, bwi.getAutowireMode(), bwi.getDependencyCheck());
beanFactory.initializeBean(beanInstance, (beanName != null ? beanName : ""));
}
else {
// Perform explicit wiring based on the specified bean definition.
beanFactory.configureBean(beanInstance, (beanName != null ? beanName : ""));
}
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to create target bean '" + bce.getBeanName() +
"' while configuring object of type [" + beanInstance.getClass().getName() +
"] - probably due to a circular reference. This is a common startup situation " +
"and usually not fatal. Proceeding without injection. Original exception: " + ex);
}
return;
}
}
throw ex;
}
}
}

@ -0,0 +1,152 @@
/*
* 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.wiring;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Holder for bean wiring metadata information about a particular class. Used in
* conjunction with the {@link org.springframework.beans.factory.annotation.Configurable}
* annotation and the AspectJ {@code AnnotationBeanConfigurerAspect}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see BeanWiringInfoResolver
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory
* @see org.springframework.beans.factory.annotation.Configurable
*/
public class BeanWiringInfo {
/**
* Constant that indicates autowiring bean properties by name.
* @see #BeanWiringInfo(int, boolean)
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_NAME
*/
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
/**
* Constant that indicates autowiring bean properties by type.
* @see #BeanWiringInfo(int, boolean)
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory#AUTOWIRE_BY_TYPE
*/
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;
@Nullable
private String beanName;
private boolean isDefaultBeanName = false;
private int autowireMode = AutowireCapableBeanFactory.AUTOWIRE_NO;
private boolean dependencyCheck = false;
/**
* Create a default BeanWiringInfo that suggests plain initialization of
* factory and post-processor callbacks that the bean class may expect.
*/
public BeanWiringInfo() {
}
/**
* Create a new BeanWiringInfo that points to the given bean name.
* @param beanName the name of the bean definition to take the property values from
* @throws IllegalArgumentException if the supplied beanName is {@code null},
* is empty, or consists wholly of whitespace
*/
public BeanWiringInfo(String beanName) {
this(beanName, false);
}
/**
* Create a new BeanWiringInfo that points to the given bean name.
* @param beanName the name of the bean definition to take the property values from
* @param isDefaultBeanName whether the given bean name is a suggested
* default bean name, not necessarily matching an actual bean definition
* @throws IllegalArgumentException if the supplied beanName is {@code null},
* is empty, or consists wholly of whitespace
*/
public BeanWiringInfo(String beanName, boolean isDefaultBeanName) {
Assert.hasText(beanName, "'beanName' must not be empty");
this.beanName = beanName;
this.isDefaultBeanName = isDefaultBeanName;
}
/**
* Create a new BeanWiringInfo that indicates autowiring.
* @param autowireMode one of the constants {@link #AUTOWIRE_BY_NAME} /
* {@link #AUTOWIRE_BY_TYPE}
* @param dependencyCheck whether to perform a dependency check for object
* references in the bean instance (after autowiring)
* @throws IllegalArgumentException if the supplied {@code autowireMode}
* is not one of the allowed values
* @see #AUTOWIRE_BY_NAME
* @see #AUTOWIRE_BY_TYPE
*/
public BeanWiringInfo(int autowireMode, boolean dependencyCheck) {
if (autowireMode != AUTOWIRE_BY_NAME && autowireMode != AUTOWIRE_BY_TYPE) {
throw new IllegalArgumentException("Only constants AUTOWIRE_BY_NAME and AUTOWIRE_BY_TYPE supported");
}
this.autowireMode = autowireMode;
this.dependencyCheck = dependencyCheck;
}
/**
* Return whether this BeanWiringInfo indicates autowiring.
*/
public boolean indicatesAutowiring() {
return (this.beanName == null);
}
/**
* Return the specific bean name that this BeanWiringInfo points to, if any.
*/
@Nullable
public String getBeanName() {
return this.beanName;
}
/**
* Return whether the specific bean name is a suggested default bean name,
* not necessarily matching an actual bean definition in the factory.
*/
public boolean isDefaultBeanName() {
return this.isDefaultBeanName;
}
/**
* Return one of the constants {@link #AUTOWIRE_BY_NAME} /
* {@link #AUTOWIRE_BY_TYPE}, if autowiring is indicated.
*/
public int getAutowireMode() {
return this.autowireMode;
}
/**
* Return whether to perform a dependency check for object references
* in the bean instance (after autowiring).
*/
public boolean getDependencyCheck() {
return this.dependencyCheck;
}
}

@ -0,0 +1,47 @@
/*
* 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.wiring;
import org.springframework.lang.Nullable;
/**
* Strategy interface to be implemented by objects than can resolve bean name
* information, given a newly instantiated bean object. Invocations to the
* {@link #resolveWiringInfo} method on this interface will be driven by
* the AspectJ pointcut in the relevant concrete aspect.
*
* <p>Metadata resolution strategy can be pluggable. A good default is
* {@link ClassNameBeanWiringInfoResolver}, which uses the fully-qualified
* class name as bean name.
*
* @author Rod Johnson
* @since 2.0
* @see BeanWiringInfo
* @see ClassNameBeanWiringInfoResolver
* @see org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver
*/
public interface BeanWiringInfoResolver {
/**
* Resolve the BeanWiringInfo for the given bean instance.
* @param beanInstance the bean instance to resolve info for
* @return the BeanWiringInfo, or {@code null} if not found
*/
@Nullable
BeanWiringInfo resolveWiringInfo(Object beanInstance);
}

@ -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.wiring;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Simple default implementation of the {@link BeanWiringInfoResolver} interface,
* looking for a bean with the same name as the fully-qualified class name.
* This matches the default name of the bean in a Spring XML file if the
* bean tag's "id" attribute is not used.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public class ClassNameBeanWiringInfoResolver implements BeanWiringInfoResolver {
@Override
public BeanWiringInfo resolveWiringInfo(Object beanInstance) {
Assert.notNull(beanInstance, "Bean instance must not be null");
return new BeanWiringInfo(ClassUtils.getUserClass(beanInstance).getName(), true);
}
}

@ -0,0 +1,10 @@
/**
* Mechanism to determine bean wiring metadata from a bean instance.
* Foundation for aspect-driven bean configuration.
*/
@NonNullApi
@NonNullFields
package org.springframework.beans.factory.wiring;
import org.springframework.lang.NonNullApi;
import org.springframework.lang.NonNullFields;
Loading…
Cancel
Save