DisposableBeanAdapter.java 9.0 KB
Newer Older
1
/*
2
 * Copyright 2002-2009 the original author or authors.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.support;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
J
Juergen Hoeller 已提交
30
import org.springframework.beans.factory.config.BeanPostProcessor;
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

/**
 * Adapter that implements the {@link DisposableBean} interface
 * performing various destruction steps on a given bean instance:
 * <ul>
 * <li>DestructionAwareBeanPostProcessors
 * <li>the bean implementing DisposableBean itself
 * <li>a custom destroy method specified on the bean definition
 * </ul>
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see AbstractBeanFactory
 * @see org.springframework.beans.factory.DisposableBean
 * @see org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor
 * @see AbstractBeanDefinition#getDestroyMethodName()
 */
class DisposableBeanAdapter implements DisposableBean, Runnable, Serializable {

	private static final Log logger = LogFactory.getLog(DisposableBeanAdapter.class);

	private final Object bean;

	private final String beanName;

	private final boolean invokeDisposableBean;

61
	private String destroyMethodName;
62

63
	private transient Method destroyMethod;
64

J
Juergen Hoeller 已提交
65
	private List<DestructionAwareBeanPostProcessor> beanPostProcessors;
66 67 68 69 70 71 72 73 74 75


	/**
	 * Create a new DisposableBeanAdapter for the given bean.
	 * @param bean the bean instance (never <code>null</code>)
	 * @param beanName the name of the bean
	 * @param beanDefinition the merged bean definition
	 * @param postProcessors the List of BeanPostProcessors
	 * (potentially DestructionAwareBeanPostProcessor), if any
	 */
J
Juergen Hoeller 已提交
76 77
	public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition beanDefinition,
			List<BeanPostProcessor> postProcessors) {
78 79 80 81

		Assert.notNull(bean, "Bean must not be null");
		this.bean = bean;
		this.beanName = beanName;
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
		this.invokeDisposableBean =
				(this.bean instanceof DisposableBean && !beanDefinition.isExternallyManagedDestroyMethod("destroy"));
		String destroyMethodName = beanDefinition.getDestroyMethodName();
		if (destroyMethodName != null && !(this.invokeDisposableBean && "destroy".equals(destroyMethodName)) &&
				!beanDefinition.isExternallyManagedDestroyMethod(destroyMethodName)) {
			this.destroyMethodName = destroyMethodName;
			try {
				this.destroyMethod = BeanUtils.findMethodWithMinimalParameters(bean.getClass(), destroyMethodName);
			}
			catch (IllegalArgumentException ex) {
				throw new BeanDefinitionValidationException("Couldn't find a unique destroy method on bean with name '" +
						this.beanName + ": " + ex.getMessage());
			}
			if (this.destroyMethod == null) {
				if (beanDefinition.isEnforceDestroyMethod()) {
					throw new BeanDefinitionValidationException("Couldn't find a destroy method named '" +
							destroyMethodName + "' on bean with name '" + beanName + "'");
				}
			}
			else {
				Class[] paramTypes = this.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 && !paramTypes[0].equals(boolean.class)) {
					throw new BeanDefinitionValidationException("Method '" + destroyMethodName + "' of bean '" +
							beanName + "' has a non-boolean parameter - not supported as destroy method");
				}
			}
		}
113 114 115 116 117 118 119
		this.beanPostProcessors = filterPostProcessors(postProcessors);
	}

	/**
	 * Create a new DisposableBeanAdapter for the given bean.
	 * @param bean the bean instance (never <code>null</code>)
	 * @param beanName the name of the bean
120 121
	 * @param invokeDisposableBean whether to actually invoke DisposableBean's destroy method here
	 * @param destroyMethodName the name of the custom destroy method (<code>null</code> if there is none)
122 123 124
	 * @param postProcessors the List of DestructionAwareBeanPostProcessors, if any
	 */
	private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDisposableBean,
125
			String destroyMethodName, List<DestructionAwareBeanPostProcessor> postProcessors) {
126 127 128 129 130 131 132 133

		this.bean = bean;
		this.beanName = beanName;
		this.invokeDisposableBean = invokeDisposableBean;
		this.destroyMethodName = destroyMethodName;
		this.beanPostProcessors = postProcessors;
	}

J
Juergen Hoeller 已提交
134

135 136 137 138 139
	/**
	 * Search for all DestructionAwareBeanPostProcessors in the List.
	 * @param postProcessors the List to search
	 * @return the filtered List of DestructionAwareBeanPostProcessors
	 */
J
Juergen Hoeller 已提交
140 141
	private List<DestructionAwareBeanPostProcessor> filterPostProcessors(List<BeanPostProcessor> postProcessors) {
		List<DestructionAwareBeanPostProcessor> filteredPostProcessors = null;
142
		if (postProcessors != null && !postProcessors.isEmpty()) {
J
Juergen Hoeller 已提交
143 144
			filteredPostProcessors = new ArrayList<DestructionAwareBeanPostProcessor>(postProcessors.size());
			for (BeanPostProcessor postProcessor : postProcessors) {
145
				if (postProcessor instanceof DestructionAwareBeanPostProcessor) {
J
Juergen Hoeller 已提交
146
					filteredPostProcessors.add((DestructionAwareBeanPostProcessor) postProcessor);
147 148 149 150 151 152 153 154 155 156 157 158 159 160
				}
			}
		}
		return filteredPostProcessors;
	}


	public void run() {
		destroy();
	}

	public void destroy() {
		if (this.beanPostProcessors != null && !this.beanPostProcessors.isEmpty()) {
			for (int i = this.beanPostProcessors.size() - 1; i >= 0; i--) {
J
Juergen Hoeller 已提交
161
				this.beanPostProcessors.get(i).postProcessBeforeDestruction(this.bean, this.beanName);
162 163 164
			}
		}

165
		if (this.invokeDisposableBean) {
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
			if (logger.isDebugEnabled()) {
				logger.debug("Invoking destroy() on bean with name '" + this.beanName + "'");
			}
			try {
				((DisposableBean) this.bean).destroy();
			}
			catch (Throwable ex) {
				String msg = "Invocation of destroy method failed on bean with name '" + this.beanName + "'";
				if (logger.isDebugEnabled()) {
					logger.warn(msg, ex);
				}
				else {
					logger.warn(msg + ": " + ex);
				}
			}
		}

183 184 185 186 187 188 189
		if (this.destroyMethod != null) {
			invokeCustomDestroyMethod(this.destroyMethod);
		}
		else if (this.destroyMethodName != null) {
			Method destroyMethod =
					BeanUtils.findMethodWithMinimalParameters(this.bean.getClass(), this.destroyMethodName);
			invokeCustomDestroyMethod(destroyMethod);
190 191 192 193 194 195 196 197 198
		}
	}

	/**
	 * Invoke the specified custom destroy method on the given bean.
	 * <p>This implementation invokes a no-arg method if found, else checking
	 * for a method with a single boolean argument (passing in "true",
	 * assuming a "force" parameter), else logging an error.
	 */
199 200 201 202 203 204 205 206 207 208 209
	private void invokeCustomDestroyMethod(Method destroyMethod) {
		Class[] paramTypes = destroyMethod.getParameterTypes();
		Object[] args = new Object[paramTypes.length];
		if (paramTypes.length == 1) {
			args[0] = Boolean.TRUE;
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Invoking destroy method '" + this.destroyMethodName +
					"' on bean with name '" + this.beanName + "'");
		}
		ReflectionUtils.makeAccessible(destroyMethod);
210
		try {
211 212 213 214 215 216 217
			destroyMethod.invoke(this.bean, args);
		}
		catch (InvocationTargetException ex) {
			String msg = "Invocation of destroy method '" + this.destroyMethodName +
					"' failed on bean with name '" + this.beanName + "'";
			if (logger.isDebugEnabled()) {
				logger.warn(msg, ex.getTargetException());
218 219
			}
			else {
220
				logger.warn(msg + ": " + ex.getTargetException());
221 222
			}
		}
223 224 225
		catch (Throwable ex) {
			logger.error("Couldn't invoke destroy method '" + this.destroyMethodName +
					"' on bean with name '" + this.beanName + "'", ex);
226 227 228 229 230 231 232 233 234
		}
	}


	/**
	 * Serializes a copy of the state of this class,
	 * filtering out non-serializable BeanPostProcessors.
	 */
	protected Object writeReplace() {
J
Juergen Hoeller 已提交
235
		List<DestructionAwareBeanPostProcessor> serializablePostProcessors = null;
236
		if (this.beanPostProcessors != null) {
J
Juergen Hoeller 已提交
237 238
			serializablePostProcessors = new ArrayList<DestructionAwareBeanPostProcessor>();
			for (DestructionAwareBeanPostProcessor postProcessor : this.beanPostProcessors) {
239 240 241 242 243 244
				if (postProcessor instanceof Serializable) {
					serializablePostProcessors.add(postProcessor);
				}
			}
		}
		return new DisposableBeanAdapter(this.bean, this.beanName, this.invokeDisposableBean,
245
				this.destroyMethodName, serializablePostProcessors);
246 247 248
	}

}