Browse Source

Make sure inferred destroy method is set on the original bean definition

This commit updates InitDestroyAnnotationBeanPostProcessor to mutate
the original bean definition rather than the merged one that can be
recreated without it if the cache gets stale.

See gh-28215
pull/28944/head
Stephane Nicoll 2 years ago
parent
commit
2e1538a20b
  1. 32
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java
  2. 8
      spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java
  3. 9
      spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java
  4. 9
      spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java
  5. 13
      spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java

32
spring-beans/src/main/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.java

@ -41,7 +41,9 @@ import org.springframework.beans.BeansException; @@ -41,7 +41,9 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
@ -158,20 +160,30 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB @@ -158,20 +160,30 @@ public class InitDestroyAnnotationBeanPostProcessor implements DestructionAwareB
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
RootBeanDefinition beanDefinition = registeredBean.getMergedBeanDefinition();
beanDefinition.resolveDestroyMethodIfNecessary();
LifecycleMetadata metadata = findInjectionMetadata(beanDefinition, registeredBean.getBeanClass());
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
beanDefinition.setInitMethodNames(initMethodNames);
}
if (!CollectionUtils.isEmpty(metadata.destroyMethods)) {
String[] destroyMethodNames = safeMerge(beanDefinition.getDestroyMethodNames(), metadata.destroyMethods);
beanDefinition.setDestroyMethodNames(destroyMethodNames);
AbstractBeanDefinition beanDefinition = getOriginalBeanDefinition(registeredBean);
if (beanDefinition != null) {
RootBeanDefinition mergedBeanDefinition = registeredBean.getMergedBeanDefinition();
beanDefinition.resolveDestroyMethodIfNecessary();
LifecycleMetadata metadata = findInjectionMetadata(mergedBeanDefinition, registeredBean.getBeanClass());
if (!CollectionUtils.isEmpty(metadata.initMethods)) {
String[] initMethodNames = safeMerge(beanDefinition.getInitMethodNames(), metadata.initMethods);
beanDefinition.setInitMethodNames(initMethodNames);
}
if (!CollectionUtils.isEmpty(metadata.destroyMethods)) {
String[] destroyMethodNames = safeMerge(beanDefinition.getDestroyMethodNames(), metadata.destroyMethods);
beanDefinition.setDestroyMethodNames(destroyMethodNames);
}
registeredBean.getBeanFactory().clearMetadataCache();
}
return null;
}
@Nullable
private AbstractBeanDefinition getOriginalBeanDefinition(RegisteredBean registeredBean) {
BeanDefinition beanDefinition = registeredBean.getBeanFactory().getBeanDefinition(registeredBean.getBeanName());
return (beanDefinition instanceof AbstractBeanDefinition abd ? abd : null);
}
private LifecycleMetadata findInjectionMetadata(RootBeanDefinition beanDefinition, Class<?> beanType) {
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);

8
spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java

@ -1186,6 +1186,14 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess @@ -1186,6 +1186,14 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
}
}
/**
* Resolve the inferred destroy method if necessary.
* @since 6.0
*/
public void resolveDestroyMethodIfNecessary() {
setDestroyMethodNames(DisposableBeanAdapter
.inferDestroyMethodsIfNecessary(getResolvableType().toClass(), this));
}
/**
* Public declaration of Object's {@code clone()} method.

9
spring-beans/src/main/java/org/springframework/beans/factory/support/DisposableBeanAdapter.java

@ -344,13 +344,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { @@ -344,13 +344,14 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
* interfaces, reflectively calling the "close" method on implementing beans as well.
*/
@Nullable
static String[] inferDestroyMethodsIfNecessary(Class<?> target, RootBeanDefinition beanDefinition) {
static String[] inferDestroyMethodsIfNecessary(Class<?> target, AbstractBeanDefinition beanDefinition) {
String[] destroyMethodNames = beanDefinition.getDestroyMethodNames();
if (destroyMethodNames != null && destroyMethodNames.length > 1) {
return destroyMethodNames;
}
String destroyMethodName = beanDefinition.resolvedDestroyMethodName;
String destroyMethodName = (beanDefinition instanceof RootBeanDefinition rbd
? rbd.resolvedDestroyMethodName : null);
if (destroyMethodName == null) {
destroyMethodName = beanDefinition.getDestroyMethodName();
boolean autoCloseable = (AutoCloseable.class.isAssignableFrom(target));
@ -378,7 +379,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { @@ -378,7 +379,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {
}
}
}
beanDefinition.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
if (beanDefinition instanceof RootBeanDefinition rbd) {
rbd.resolvedDestroyMethodName = (destroyMethodName != null ? destroyMethodName : "");
}
}
return (StringUtils.hasLength(destroyMethodName) ? new String[] {destroyMethodName} : null);
}

9
spring-beans/src/main/java/org/springframework/beans/factory/support/RootBeanDefinition.java

@ -550,15 +550,6 @@ public class RootBeanDefinition extends AbstractBeanDefinition { @@ -550,15 +550,6 @@ public class RootBeanDefinition extends AbstractBeanDefinition {
}
}
/**
* Resolve the inferred destroy method if necessary.
* @since 6.0
*/
public void resolveDestroyMethodIfNecessary() {
setDestroyMethodNames(DisposableBeanAdapter
.inferDestroyMethodsIfNecessary(getResolvableType().toClass(), this));
}
/**
* Register an externally managed configuration destruction method &mdash;
* for example, a method annotated with JSR-250's

13
spring-beans/src/test/java/org/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessorTests.java

@ -90,6 +90,19 @@ class InitDestroyAnnotationBeanPostProcessorTests { @@ -90,6 +90,19 @@ class InitDestroyAnnotationBeanPostProcessorTests {
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("close");
}
@Test
void processAheadOfTimeWhenHasInferredDestroyMethodIsRetainedIfMergedBeanDefinitionIsStale() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(InferredDestroyBean.class);
beanDefinition.setDestroyMethodNames(AbstractBeanDefinition.INFER_METHOD);
processAheadOfTime(beanDefinition);
RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition();
assertThat(mergedBeanDefinition.getInitMethodNames()).isNull();
assertThat(mergedBeanDefinition.getDestroyMethodNames()).containsExactly("close");
RootBeanDefinition originalBeanDefinition = (RootBeanDefinition) this.beanFactory.getBeanDefinition("test");
assertThat(originalBeanDefinition.getInitMethodNames()).isNull();
assertThat(originalBeanDefinition.getDestroyMethodNames()).containsExactly("close");
}
@Test
void processAheadOfTimeWhenHasInferredDestroyMethodAndNoCandidateDoesNotMutateRootBeanDefinition() {
RootBeanDefinition beanDefinition = new RootBeanDefinition(NoInitDestroyBean.class);

Loading…
Cancel
Save