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(); | ||||
| 			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); | ||||
| 						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,14 +104,18 @@ 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; | ||||
| 				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()) { | ||||
|  | @ -132,7 +137,9 @@ class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable { | |||
| 						} | ||||
| 						destroyMethod = ClassUtils.getInterfaceMethodIfPossible(destroyMethod, bean.getClass()); | ||||
| 					} | ||||
| 				this.destroyMethod = destroyMethod; | ||||
| 					destroyMethods[i] = 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); | ||||
| 		} | ||||
| 		else if (this.destroyMethodNames != null) { | ||||
| 			for (String destroyMethodName: this.destroyMethodNames) { | ||||
| 				Method destroyMethod = determineDestroyMethod(destroyMethodName); | ||||
| 				if (destroyMethod != null) { | ||||
| 				invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(destroyMethod, this.bean.getClass())); | ||||
| 					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