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">
|
||||
* <bean id="myObject" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
||||
* <property name="staticMethod" value="com.whatever.MyClassFactory.getInstance"/>
|
||||
* </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">
|
||||
* <bean id="sysProps" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
||||
* <property name="targetClass" value="java.lang.System"/>
|
||||
* <property name="targetMethod" value="getProperties"/>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="javaVersion" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
|
||||
* <property name="targetObject" ref="sysProps"/>
|
||||
* <property name="targetMethod" value="getProperty"/>
|
||||
* <property name="arguments" value="java.version"/>
|
||||
* </bean></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"><beans>
|
||||
*
|
||||
* <!-- Prototype bean since we have state -->
|
||||
* <bean id="myService" class="a.b.c.MyService" scope="prototype"/>
|
||||
*
|
||||
* <bean id="myServiceFactory"
|
||||
* class="org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean">
|
||||
* <property name="targetBeanName"><idref local="myService"/></property>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="clientBean" class="a.b.c.MyClientBean">
|
||||
* <property name="myServiceFactory" ref="myServiceFactory"/>
|
||||
* </bean>
|
||||
*
|
||||
*</beans></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<MyService> myServiceFactory;
|
||||
*
|
||||
* public void setMyServiceFactory(ObjectFactory<MyService> myServiceFactory) {
|
||||
* this.myServiceFactory = myServiceFactory;
|
||||
* }
|
||||
*
|
||||
* public void someBusinessMethod() {
|
||||
* // get a 'fresh', brand new MyService instance
|
||||
* MyService service = this.myServiceFactory.getObject();
|
||||
* // use the service object to effect the business logic...
|
||||
* }
|
||||
* }</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"><beans>
|
||||
*
|
||||
* <!-- Prototype bean since we have state -->
|
||||
* <bean id="myService" class="a.b.c.MyService" singleton="false"/>
|
||||
*
|
||||
* <!-- will lookup the above 'myService' bean by *TYPE* -->
|
||||
* <bean id="myServiceFactory"
|
||||
* class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
|
||||
* <property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="clientBean" class="a.b.c.MyClientBean">
|
||||
* <property name="myServiceFactory" ref="myServiceFactory"/>
|
||||
* </bean>
|
||||
*
|
||||
*</beans></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"><beans>
|
||||
*
|
||||
* <!-- Prototype beans since we have state (both extend MyService) -->
|
||||
* <bean id="specialService" class="a.b.c.SpecialService" singleton="false"/>
|
||||
* <bean id="anotherService" class="a.b.c.AnotherService" singleton="false"/>
|
||||
*
|
||||
* <bean id="myServiceFactory"
|
||||
* class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
|
||||
* <property name="serviceLocatorInterface" value="a.b.c.ServiceFactory"/>
|
||||
* </bean>
|
||||
*
|
||||
* <bean id="clientBean" class="a.b.c.MyClientBean">
|
||||
* <property name="myServiceFactory" ref="myServiceFactory"/>
|
||||
* </bean>
|
||||
*
|
||||
*</beans></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;
|
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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<Customer>).
|
||||
*
|
||||
* <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…
Reference in new issue