From 41ee23345d72623d18accb9484ce5119403c39d5 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 17 Feb 2022 18:14:09 +0100 Subject: [PATCH] Support for registering multiple init/destroy methods on AbstractBeanDefinition Closes gh-28013 --- .../AbstractAutowireCapableBeanFactory.java | 19 +-- .../support/AbstractBeanDefinition.java | 94 ++++++++++----- .../support/DisposableBeanAdapter.java | 105 ++++++++++------- .../DefaultListableBeanFactoryTests.java | 53 ++++++++- .../support/ScriptFactoryPostProcessor.java | 4 +- .../Spr3775InitDestroyLifecycleTests.java | 108 ++++++++---------- 6 files changed, 238 insertions(+), 145 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 2fb13145124..a013c2bdce1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -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)); diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index 847574dbd2e..0176af001a9 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -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. - *

The default is {@code null} in which case there is no initializer method. + * Specify the names of multiple initializer methods. + *

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. + *

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. - *

The default is {@code null} in which case there is no destroy method. + * Specify the names of multiple destroy methods. + *

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. + *

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()); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java index 0f1b5ecd467..23354b6cccb 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java @@ -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 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 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); } /** diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index db7454073cf..accf86ef058 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -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 initMethods = new ArrayList<>(); + final List 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; diff --git a/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java b/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java index 39772475c05..949da723710 100644 --- a/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java +++ b/spring-context/src/main/java/org/springframework/scripting/support/ScriptFactoryPostProcessor.java @@ -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]); } diff --git a/spring-context/src/test/java/org/springframework/context/annotation/Spr3775InitDestroyLifecycleTests.java b/spring-context/src/test/java/org/springframework/context/annotation/Spr3775InitDestroyLifecycleTests.java index 82caf5254e9..821fffffcf5 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/Spr3775InitDestroyLifecycleTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/Spr3775InitDestroyLifecycleTests.java @@ -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; /** - *

- * JUnit-3.8-based unit test which verifies expected init and - * destroy bean lifecycle behavior as requested in init and destroy + * bean lifecycle behavior as requested in + * SPR-3775. - *

- *

- * Specifically, combinations of the following are tested: - *

+ * + *

Specifically, combinations of the following are tested: *