Support for registering multiple init/destroy methods on AbstractBeanDefinition
Closes gh-28013
This commit is contained in:
parent
8506778608
commit
41ee23345d
|
|
@ -74,7 +74,6 @@ import org.springframework.core.ParameterNameDiscoverer;
|
|||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
|
@ -1789,11 +1788,15 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
}
|
||||
|
||||
if (mbd != null && bean.getClass() != NullBean.class) {
|
||||
String initMethodName = mbd.getInitMethodName();
|
||||
if (StringUtils.hasLength(initMethodName) &&
|
||||
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
|
||||
!mbd.isExternallyManagedInitMethod(initMethodName)) {
|
||||
invokeCustomInitMethod(beanName, bean, mbd);
|
||||
String[] initMethodNames = mbd.getInitMethodNames();
|
||||
if (initMethodNames != null) {
|
||||
for (String initMethodName : initMethodNames) {
|
||||
if (StringUtils.hasLength(initMethodName) &&
|
||||
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
|
||||
!mbd.isExternallyManagedInitMethod(initMethodName)) {
|
||||
invokeCustomInitMethod(beanName, bean, mbd, initMethodName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1805,11 +1808,9 @@ public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFac
|
|||
* methods with arguments.
|
||||
* @see #invokeInitMethods
|
||||
*/
|
||||
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd)
|
||||
protected void invokeCustomInitMethod(String beanName, Object bean, RootBeanDefinition mbd, String initMethodName)
|
||||
throws Throwable {
|
||||
|
||||
String initMethodName = mbd.getInitMethodName();
|
||||
Assert.state(initMethodName != null, "No init method set");
|
||||
Method initMethod = (mbd.isNonPublicAccessAllowed() ?
|
||||
BeanUtils.findMethod(bean.getClass(), initMethodName) :
|
||||
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
|
@ -184,10 +184,10 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
private MethodOverrides methodOverrides = new MethodOverrides();
|
||||
|
||||
@Nullable
|
||||
private String initMethodName;
|
||||
private String[] initMethodNames;
|
||||
|
||||
@Nullable
|
||||
private String destroyMethodName;
|
||||
private String[] destroyMethodNames;
|
||||
|
||||
private boolean enforceInitMethod = true;
|
||||
|
||||
|
|
@ -262,9 +262,9 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
setInstanceSupplier(originalAbd.getInstanceSupplier());
|
||||
setNonPublicAccessAllowed(originalAbd.isNonPublicAccessAllowed());
|
||||
setLenientConstructorResolution(originalAbd.isLenientConstructorResolution());
|
||||
setInitMethodName(originalAbd.getInitMethodName());
|
||||
setInitMethodNames(originalAbd.getInitMethodNames());
|
||||
setEnforceInitMethod(originalAbd.isEnforceInitMethod());
|
||||
setDestroyMethodName(originalAbd.getDestroyMethodName());
|
||||
setDestroyMethodNames(originalAbd.getDestroyMethodNames());
|
||||
setEnforceDestroyMethod(originalAbd.isEnforceDestroyMethod());
|
||||
setSynthetic(originalAbd.isSynthetic());
|
||||
setResource(originalAbd.getResource());
|
||||
|
|
@ -338,12 +338,12 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
setInstanceSupplier(otherAbd.getInstanceSupplier());
|
||||
setNonPublicAccessAllowed(otherAbd.isNonPublicAccessAllowed());
|
||||
setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
|
||||
if (otherAbd.getInitMethodName() != null) {
|
||||
setInitMethodName(otherAbd.getInitMethodName());
|
||||
if (otherAbd.getInitMethodNames() != null) {
|
||||
setInitMethodNames(otherAbd.getInitMethodNames());
|
||||
setEnforceInitMethod(otherAbd.isEnforceInitMethod());
|
||||
}
|
||||
if (otherAbd.getDestroyMethodName() != null) {
|
||||
setDestroyMethodName(otherAbd.getDestroyMethodName());
|
||||
if (otherAbd.getDestroyMethodNames() != null) {
|
||||
setDestroyMethodNames(otherAbd.getDestroyMethodNames());
|
||||
setEnforceDestroyMethod(otherAbd.isEnforceDestroyMethod());
|
||||
}
|
||||
setSynthetic(otherAbd.isSynthetic());
|
||||
|
|
@ -919,21 +919,41 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the name of the initializer method.
|
||||
* <p>The default is {@code null} in which case there is no initializer method.
|
||||
* Specify the names of multiple initializer methods.
|
||||
* <p>The default is {@code null} in which case there are no initializer methods.
|
||||
* @since 6.0
|
||||
* @see #setInitMethodName
|
||||
*/
|
||||
@Override
|
||||
public void setInitMethodName(@Nullable String initMethodName) {
|
||||
this.initMethodName = initMethodName;
|
||||
public void setInitMethodNames(@Nullable String... initMethodNames) {
|
||||
this.initMethodNames = initMethodNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the initializer method.
|
||||
* Return the names of the initializer methods.
|
||||
* @since 6.0
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getInitMethodNames() {
|
||||
return this.initMethodNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the initializer method.
|
||||
* <p>The default is {@code null} in which case there is no initializer method.
|
||||
* @see #setInitMethodNames
|
||||
*/
|
||||
@Override
|
||||
public void setInitMethodName(@Nullable String initMethodName) {
|
||||
this.initMethodNames = (initMethodName != null ? new String[] {initMethodName} : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the initializer method (the first one in case of multiple methods).
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public String getInitMethodName() {
|
||||
return this.initMethodName;
|
||||
return (!ObjectUtils.isEmpty(this.initMethodNames) ? this.initMethodNames[0] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -958,21 +978,41 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the name of the destroy method.
|
||||
* <p>The default is {@code null} in which case there is no destroy method.
|
||||
* Specify the names of multiple destroy methods.
|
||||
* <p>The default is {@code null} in which case there are no destroy methods.
|
||||
* @since 6.0
|
||||
* @see #setDestroyMethodName
|
||||
*/
|
||||
@Override
|
||||
public void setDestroyMethodName(@Nullable String destroyMethodName) {
|
||||
this.destroyMethodName = destroyMethodName;
|
||||
public void setDestroyMethodNames(@Nullable String... destroyMethodNames) {
|
||||
this.destroyMethodNames = destroyMethodNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the destroy method.
|
||||
* Return the names of the destroy methods.
|
||||
* @since 6.0
|
||||
*/
|
||||
@Nullable
|
||||
public String[] getDestroyMethodNames() {
|
||||
return this.destroyMethodNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the destroy method.
|
||||
* <p>The default is {@code null} in which case there is no destroy method.
|
||||
* @see #setDestroyMethodNames
|
||||
*/
|
||||
@Override
|
||||
public void setDestroyMethodName(@Nullable String destroyMethodName) {
|
||||
this.destroyMethodNames = (destroyMethodName != null ? new String[] {destroyMethodName} : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the destroy method (the first one in case of multiple methods).
|
||||
*/
|
||||
@Override
|
||||
@Nullable
|
||||
public String getDestroyMethodName() {
|
||||
return this.destroyMethodName;
|
||||
return (!ObjectUtils.isEmpty(this.destroyMethodNames) ? this.destroyMethodNames[0] : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1189,9 +1229,9 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
ObjectUtils.nullSafeEquals(this.methodOverrides, that.methodOverrides) &&
|
||||
ObjectUtils.nullSafeEquals(this.factoryBeanName, that.factoryBeanName) &&
|
||||
ObjectUtils.nullSafeEquals(this.factoryMethodName, that.factoryMethodName) &&
|
||||
ObjectUtils.nullSafeEquals(this.initMethodName, that.initMethodName) &&
|
||||
ObjectUtils.nullSafeEquals(this.initMethodNames, that.initMethodNames) &&
|
||||
this.enforceInitMethod == that.enforceInitMethod &&
|
||||
ObjectUtils.nullSafeEquals(this.destroyMethodName, that.destroyMethodName) &&
|
||||
ObjectUtils.nullSafeEquals(this.destroyMethodNames, that.destroyMethodNames) &&
|
||||
this.enforceDestroyMethod == that.enforceDestroyMethod &&
|
||||
this.synthetic == that.synthetic &&
|
||||
this.role == that.role &&
|
||||
|
|
@ -1241,8 +1281,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
|
|||
sb.append("; primary=").append(this.primary);
|
||||
sb.append("; factoryBeanName=").append(this.factoryBeanName);
|
||||
sb.append("; factoryMethodName=").append(this.factoryMethodName);
|
||||
sb.append("; initMethodName=").append(this.initMethodName);
|
||||
sb.append("; destroyMethodName=").append(this.destroyMethodName);
|
||||
sb.append("; initMethodNames=").append(this.initMethodNames);
|
||||
sb.append("; destroyMethodNames=").append(this.destroyMethodNames);
|
||||
if (this.resource != null) {
|
||||
sb.append("; defined in ").append(this.resource.getDescription());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.springframework.lang.Nullable;
|
|||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ import org.springframework.util.StringUtils;
|
|||
* @see AbstractBeanFactory
|
||||
* @see org.springframework.beans.factory.DisposableBean
|
||||
* @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
|
||||
* @see AbstractBeanDefinition#getDestroyMethodName()
|
||||
* @see AbstractBeanDefinition#getDestroyMethodNames()
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
||||
|
|
@ -76,10 +77,10 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
private boolean invokeAutoCloseable;
|
||||
|
||||
@Nullable
|
||||
private String destroyMethodName;
|
||||
private String[] destroyMethodNames;
|
||||
|
||||
@Nullable
|
||||
private transient Method destroyMethod;
|
||||
private transient Method[] destroyMethods;
|
||||
|
||||
@Nullable
|
||||
private final List<DestructionAwareBeanPostProcessor> beanPostProcessors;
|
||||
|
|
@ -103,36 +104,42 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
this.invokeDisposableBean = (bean instanceof DisposableBean &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(DESTROY_METHOD_NAME));
|
||||
|
||||
String destroyMethodName = inferDestroyMethodIfNecessary(bean, beanDefinition);
|
||||
if (destroyMethodName != null &&
|
||||
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodName)) &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
|
||||
String[] destroyMethodNames = inferDestroyMethodsIfNecessary(bean, beanDefinition);
|
||||
if (!ObjectUtils.isEmpty(destroyMethodNames) &&
|
||||
!(this.invokeDisposableBean && DESTROY_METHOD_NAME.equals(destroyMethodNames[0])) &&
|
||||
!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodNames[0])) {
|
||||
|
||||
this.invokeAutoCloseable = (bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodName));
|
||||
this.invokeAutoCloseable =
|
||||
(bean instanceof AutoCloseable && CLOSE_METHOD_NAME.equals(destroyMethodNames[0]));
|
||||
if (!this.invokeAutoCloseable) {
|
||||
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 {
|
||||
if (destroyMethod.getParameterCount() > 0) {
|
||||
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");
|
||||
this.destroyMethodNames = destroyMethodNames;
|
||||
Method[] destroyMethods = new Method[destroyMethodNames.length];
|
||||
for (int i = 0; i < destroyMethodNames.length; i++) {
|
||||
String destroyMethodName = destroyMethodNames[i];
|
||||
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 + "'");
|
||||
}
|
||||
}
|
||||
destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod, bean.getClass());
|
||||
else {
|
||||
if (destroyMethod.getParameterCount() > 0) {
|
||||
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, bean.getClass());
|
||||
}
|
||||
destroyMethods[i] = destroyMethod;
|
||||
}
|
||||
this.destroyMethod = destroyMethod;
|
||||
this.destroyMethods = destroyMethods;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +165,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
* Create a new DisposableBeanAdapter for the given bean.
|
||||
*/
|
||||
private DisposableBeanAdapter(Object bean, String beanName, boolean nonPublicAccessAllowed,
|
||||
boolean invokeDisposableBean, boolean invokeAutoCloseable, @Nullable String destroyMethodName,
|
||||
boolean invokeDisposableBean, boolean invokeAutoCloseable, @Nullable String[] destroyMethodNames,
|
||||
@Nullable List<DestructionAwareBeanPostProcessor> postProcessors) {
|
||||
|
||||
this.bean = bean;
|
||||
|
|
@ -166,7 +173,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
this.nonPublicAccessAllowed = nonPublicAccessAllowed;
|
||||
this.invokeDisposableBean = invokeDisposableBean;
|
||||
this.invokeAutoCloseable = invokeAutoCloseable;
|
||||
this.destroyMethodName = destroyMethodName;
|
||||
this.destroyMethodNames = destroyMethodNames;
|
||||
this.beanPostProcessors = postProcessors;
|
||||
}
|
||||
|
||||
|
|
@ -219,13 +226,18 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (this.destroyMethod != null) {
|
||||
invokeCustomDestroyMethod(this.destroyMethod);
|
||||
else if (this.destroyMethods != null) {
|
||||
for (Method destroyMethod : this.destroyMethods) {
|
||||
invokeCustomDestroyMethod(destroyMethod);
|
||||
}
|
||||
}
|
||||
else if (this.destroyMethodName != null) {
|
||||
Method destroyMethod = determineDestroyMethod(this.destroyMethodName);
|
||||
if (destroyMethod != null) {
|
||||
invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass()));
|
||||
else if (this.destroyMethodNames != null) {
|
||||
for (String destroyMethodName: this.destroyMethodNames) {
|
||||
Method destroyMethod = determineDestroyMethod(destroyMethodName);
|
||||
if (destroyMethod != null) {
|
||||
invokeCustomDestroyMethod(
|
||||
ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -255,14 +267,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
* 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) {
|
||||
private void invokeCustomDestroyMethod(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 custom destroy method '" + this.destroyMethodName +
|
||||
logger.trace("Invoking custom destroy method '" + destroyMethod.getName() +
|
||||
"' on bean with name '" + this.beanName + "'");
|
||||
}
|
||||
try {
|
||||
|
|
@ -270,7 +282,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
destroyMethod.invoke(this.bean, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
String msg = "Custom destroy method '" + this.destroyMethodName + "' on bean with name '" +
|
||||
String msg = "Custom destroy method '" + destroyMethod.getName() + "' on bean with name '" +
|
||||
this.beanName + "' threw an exception";
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(msg, ex.getTargetException());
|
||||
|
|
@ -280,7 +292,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.warn("Failed to invoke custom destroy method '" + this.destroyMethodName +
|
||||
logger.warn("Failed to invoke custom destroy method '" + destroyMethod.getName() +
|
||||
"' on bean with name '" + this.beanName + "'", ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -302,7 +314,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
}
|
||||
return new DisposableBeanAdapter(
|
||||
this.bean, this.beanName, this.nonPublicAccessAllowed, this.invokeDisposableBean,
|
||||
this.invokeAutoCloseable, this.destroyMethodName, serializablePostProcessors);
|
||||
this.invokeAutoCloseable, this.destroyMethodNames, serializablePostProcessors);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -312,7 +324,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
* @param beanDefinition the corresponding bean definition
|
||||
*/
|
||||
public static boolean hasDestroyMethod(Object bean, RootBeanDefinition beanDefinition) {
|
||||
return (bean instanceof DisposableBean || inferDestroyMethodIfNecessary(bean, beanDefinition) != null);
|
||||
return (bean instanceof DisposableBean || inferDestroyMethodsIfNecessary(bean, beanDefinition) != null);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -330,7 +342,12 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
* interfaces, reflectively calling the "close" method on implementing beans as well.
|
||||
*/
|
||||
@Nullable
|
||||
private static String inferDestroyMethodIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
private static String[] inferDestroyMethodsIfNecessary(Object bean, RootBeanDefinition beanDefinition) {
|
||||
String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
|
||||
if (destroyMethodNames != null && destroyMethodNames.length > 1) {
|
||||
return destroyMethodNames;
|
||||
}
|
||||
|
||||
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
|
||||
if (destroyMethodName == null) {
|
||||
destroyMethodName = beanDefinition.getDestroyMethodName();
|
||||
|
|
@ -361,7 +378,7 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
|
|||
}
|
||||
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
|
||||
}
|
||||
return (StringUtils.hasLength(destroyMethodName) ? destroyMethodName : null);
|
||||
return (StringUtils.hasLength(destroyMethodName) ? new String[] {destroyMethodName} : null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.lang.reflect.Field;
|
|||
import java.net.MalformedURLException;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
|
@ -2333,6 +2334,19 @@ class DefaultListableBeanFactoryTests {
|
|||
assertThat(tb2.getBeanName()).isEqualTo("myBeanName");
|
||||
}
|
||||
|
||||
@Test
|
||||
void multipleInitAndDestroyMethods() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(BeanWithInitAndDestroyMethods.class);
|
||||
bd.setInitMethodNames("init1", "init2");
|
||||
bd.setDestroyMethodNames("destroy2", "destroy1");
|
||||
lbf.registerBeanDefinition("test", bd);
|
||||
BeanWithInitAndDestroyMethods bean = lbf.getBean("test", BeanWithInitAndDestroyMethods.class);
|
||||
assertThat(bean.initMethods).containsExactly("init", "init1", "init2");
|
||||
assertThat(bean.destroyMethods).isEmpty();
|
||||
lbf.destroySingletons();
|
||||
assertThat(bean.destroyMethods).containsExactly("destroy", "destroy2", "destroy1");
|
||||
}
|
||||
|
||||
@Test
|
||||
void beanPostProcessorWithWrappedObjectAndDisposableBean() {
|
||||
RootBeanDefinition bd = new RootBeanDefinition(BeanWithDisposableBean.class);
|
||||
|
|
@ -2758,9 +2772,42 @@ class DefaultListableBeanFactoryTests {
|
|||
}
|
||||
|
||||
|
||||
static class BeanWithInitAndDestroyMethods implements InitializingBean, DisposableBean {
|
||||
|
||||
final List<String> initMethods = new ArrayList<>();
|
||||
final List<String> destroyMethods = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
initMethods.add("init");
|
||||
}
|
||||
|
||||
void init1() {
|
||||
initMethods.add("init1");
|
||||
}
|
||||
|
||||
void init2() {
|
||||
initMethods.add("init2");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
destroyMethods.add("destroy");
|
||||
}
|
||||
|
||||
void destroy1() {
|
||||
destroyMethods.add("destroy1");
|
||||
}
|
||||
|
||||
void destroy2() {
|
||||
destroyMethods.add("destroy2");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class BeanWithDisposableBean implements DisposableBean {
|
||||
|
||||
private static boolean closed;
|
||||
static boolean closed;
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
|
@ -2771,7 +2818,7 @@ class DefaultListableBeanFactoryTests {
|
|||
|
||||
public static class BeanWithCloseable implements Closeable {
|
||||
|
||||
private static boolean closed;
|
||||
static boolean closed;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
|
@ -2788,7 +2835,7 @@ class DefaultListableBeanFactoryTests {
|
|||
|
||||
public static class BeanWithDestroyMethod extends BaseClassWithDestroyMethod {
|
||||
|
||||
private static int closeCount = 0;
|
||||
static int closeCount = 0;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BeanWithDestroyMethod inner;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
|
@ -510,7 +510,7 @@ public class ScriptFactoryPostProcessor implements SmartInstantiationAwareBeanPo
|
|||
Signature signature = new Signature(setterName, Type.VOID_TYPE, new Type[] {Type.getType(propertyType)});
|
||||
maker.add(signature, new Type[0]);
|
||||
}
|
||||
if (bd.getInitMethodName() != null) {
|
||||
if (StringUtils.hasText(bd.getInitMethodName())) {
|
||||
Signature signature = new Signature(bd.getInitMethodName(), Type.VOID_TYPE, new Type[0]);
|
||||
maker.add(signature, new Type[0]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
|
@ -22,8 +22,6 @@ import java.util.List;
|
|||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
|
|
@ -35,15 +33,12 @@ import org.springframework.util.ObjectUtils;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* JUnit-3.8-based unit test which verifies expected <em>init</em> and
|
||||
* <em>destroy</em> bean lifecycle behavior as requested in <a
|
||||
* href="https://opensource.atlassian.com/projects/spring/browse/SPR-3775"
|
||||
* Unit test which verifies expected <em>init</em> and <em>destroy</em>
|
||||
* bean lifecycle behavior as requested in
|
||||
* <a href="https://opensource.atlassian.com/projects/spring/browse/SPR-3775"
|
||||
* target="_blank">SPR-3775</a>.
|
||||
* </p>
|
||||
* <p>
|
||||
* Specifically, combinations of the following are tested:
|
||||
* </p>
|
||||
*
|
||||
* <p>Specifically, combinations of the following are tested:
|
||||
* <ul>
|
||||
* <li>{@link InitializingBean} & {@link DisposableBean} interfaces</li>
|
||||
* <li>Custom {@link RootBeanDefinition#getInitMethodName() init} &
|
||||
|
|
@ -57,26 +52,17 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
public class Spr3775InitDestroyLifecycleTests {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(Spr3775InitDestroyLifecycleTests.class);
|
||||
|
||||
/** LIFECYCLE_TEST_BEAN. */
|
||||
private static final String LIFECYCLE_TEST_BEAN = "lifecycleTestBean";
|
||||
|
||||
|
||||
private void debugMethods(Class<?> clazz, String category, List<String> methodNames) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(clazz.getSimpleName() + ": " + category + ": " + methodNames);
|
||||
}
|
||||
private void assertMethodOrdering(String category, List<String> expectedMethods, List<String> actualMethods) {
|
||||
assertThat(ObjectUtils.nullSafeEquals(expectedMethods, actualMethods)).
|
||||
as("Verifying " + category + ": expected<" + expectedMethods + "> but got<" + actualMethods + ">.").isTrue();
|
||||
}
|
||||
|
||||
private void assertMethodOrdering(Class<?> clazz, String category, List<String> expectedMethods,
|
||||
List<String> actualMethods) {
|
||||
debugMethods(clazz, category, actualMethods);
|
||||
assertThat(ObjectUtils.nullSafeEquals(expectedMethods, actualMethods)).as("Verifying " + category + ": expected<" + expectedMethods + "> but got<" + actualMethods + ">.").isTrue();
|
||||
}
|
||||
private DefaultListableBeanFactory createBeanFactoryAndRegisterBean(
|
||||
Class<?> beanClass, String initMethodName, String destroyMethodName) {
|
||||
|
||||
private DefaultListableBeanFactory createBeanFactoryAndRegisterBean(final Class<?> beanClass,
|
||||
final String initMethodName, final String destroyMethodName) {
|
||||
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
|
||||
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
|
||||
beanDefinition.setInitMethodName(initMethodName);
|
||||
|
|
@ -88,75 +74,77 @@ public class Spr3775InitDestroyLifecycleTests {
|
|||
|
||||
@Test
|
||||
public void testInitDestroyMethods() {
|
||||
final Class<?> beanClass = InitDestroyBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
Class<?> beanClass = InitDestroyBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
"afterPropertiesSet", "destroy");
|
||||
final InitDestroyBean bean = (InitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
|
||||
InitDestroyBean bean = (InitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializingDisposableInterfaces() {
|
||||
final Class<?> beanClass = CustomInitializingDisposableBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
Class<?> beanClass = CustomInitializingDisposableBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
"customDestroy");
|
||||
final CustomInitializingDisposableBean bean = (CustomInitializingDisposableBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet", "customInit"),
|
||||
CustomInitializingDisposableBean bean = (CustomInitializingDisposableBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods", Arrays.asList("afterPropertiesSet", "customInit"),
|
||||
bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy", "customDestroy"),
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("destroy", "customDestroy"),
|
||||
bean.destroyMethods);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializingDisposableInterfacesWithShadowedMethods() {
|
||||
final Class<?> beanClass = InitializingDisposableWithShadowedMethodsBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
Class<?> beanClass = InitializingDisposableWithShadowedMethodsBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
"afterPropertiesSet", "destroy");
|
||||
final InitializingDisposableWithShadowedMethodsBean bean = (InitializingDisposableWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("InitializingBean.afterPropertiesSet"),
|
||||
InitializingDisposableWithShadowedMethodsBean bean =
|
||||
(InitializingDisposableWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods", Arrays.asList("InitializingBean.afterPropertiesSet"),
|
||||
bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("DisposableBean.destroy"), bean.destroyMethods);
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("DisposableBean.destroy"), bean.destroyMethods);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsr250Annotations() {
|
||||
final Class<?> beanClass = CustomAnnotatedInitDestroyBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
Class<?> beanClass = CustomAnnotatedInitDestroyBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
"customDestroy");
|
||||
final CustomAnnotatedInitDestroyBean bean = (CustomAnnotatedInitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("postConstruct", "afterPropertiesSet",
|
||||
CustomAnnotatedInitDestroyBean bean = (CustomAnnotatedInitDestroyBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods", Arrays.asList("postConstruct", "afterPropertiesSet",
|
||||
"customInit"), bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("preDestroy", "destroy", "customDestroy"),
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("preDestroy", "destroy", "customDestroy"),
|
||||
bean.destroyMethods);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsr250AnnotationsWithShadowedMethods() {
|
||||
final Class<?> beanClass = CustomAnnotatedInitDestroyWithShadowedMethodsBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
Class<?> beanClass = CustomAnnotatedInitDestroyWithShadowedMethodsBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit",
|
||||
"customDestroy");
|
||||
final CustomAnnotatedInitDestroyWithShadowedMethodsBean bean = (CustomAnnotatedInitDestroyWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods",
|
||||
CustomAnnotatedInitDestroyWithShadowedMethodsBean bean =
|
||||
(CustomAnnotatedInitDestroyWithShadowedMethodsBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods",
|
||||
Arrays.asList("@PostConstruct.afterPropertiesSet", "customInit"), bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("@PreDestroy.destroy", "customDestroy"),
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("@PreDestroy.destroy", "customDestroy"),
|
||||
bean.destroyMethods);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllLifecycleMechanismsAtOnce() {
|
||||
final Class<?> beanClass = AllInOneBean.class;
|
||||
final DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
Class<?> beanClass = AllInOneBean.class;
|
||||
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass,
|
||||
"afterPropertiesSet", "destroy");
|
||||
final AllInOneBean bean = (AllInOneBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering(beanClass, "init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
|
||||
AllInOneBean bean = (AllInOneBean) beanFactory.getBean(LIFECYCLE_TEST_BEAN);
|
||||
assertMethodOrdering("init-methods", Arrays.asList("afterPropertiesSet"), bean.initMethods);
|
||||
beanFactory.destroySingletons();
|
||||
assertMethodOrdering(beanClass, "destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
|
||||
assertMethodOrdering("destroy-methods", Arrays.asList("destroy"), bean.destroyMethods);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -165,7 +153,6 @@ public class Spr3775InitDestroyLifecycleTests {
|
|||
final List<String> initMethods = new ArrayList<>();
|
||||
final List<String> destroyMethods = new ArrayList<>();
|
||||
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.initMethods.add("afterPropertiesSet");
|
||||
}
|
||||
|
|
@ -175,8 +162,9 @@ public class Spr3775InitDestroyLifecycleTests {
|
|||
}
|
||||
}
|
||||
|
||||
public static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
|
||||
InitializingBean, DisposableBean {
|
||||
|
||||
public static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean
|
||||
implements InitializingBean, DisposableBean {
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
|
@ -255,14 +243,14 @@ public class Spr3775InitDestroyLifecycleTests {
|
|||
final List<String> initMethods = new ArrayList<>();
|
||||
final List<String> destroyMethods = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
@PostConstruct
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
this.initMethods.add("afterPropertiesSet");
|
||||
}
|
||||
|
||||
@Override
|
||||
@PreDestroy
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
this.destroyMethods.add("destroy");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue