11/*
2- * Copyright 2002-2010 the original author or authors.
2+ * Copyright 2002-2012 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1616
1717package org .springframework .beans .factory .support ;
1818
19+ import java .io .Closeable ;
1920import java .io .Serializable ;
2021import java .lang .reflect .InvocationTargetException ;
2122import java .lang .reflect .Method ;
22- import java .lang .reflect .Modifier ;
2323import java .security .AccessControlContext ;
2424import java .security .AccessController ;
2525import java .security .PrivilegedAction ;
3636import org .springframework .beans .factory .config .BeanPostProcessor ;
3737import org .springframework .beans .factory .config .DestructionAwareBeanPostProcessor ;
3838import org .springframework .util .Assert ;
39+ import org .springframework .util .ClassUtils ;
3940import org .springframework .util .ReflectionUtils ;
4041
4142/**
5859@ SuppressWarnings ("serial" )
5960class DisposableBeanAdapter implements DisposableBean , Runnable , Serializable {
6061
62+ private static final String CLOSE_METHOD_NAME = "close" ;
63+
6164 private static final Log logger = LogFactory .getLog (DisposableBeanAdapter .class );
6265
66+ private static Class closeableInterface ;
67+
68+ static {
69+ try {
70+ closeableInterface = DisposableBeanAdapter .class .getClassLoader ().loadClass ("java.lang.AutoCloseable" );
71+ }
72+ catch (ClassNotFoundException ex ) {
73+ closeableInterface = Closeable .class ;
74+ }
75+ }
76+
77+
6378 private final Object bean ;
6479
6580 private final String beanName ;
@@ -95,8 +110,7 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be
95110 (this .bean instanceof DisposableBean && !beanDefinition .isExternallyManagedDestroyMethod ("destroy" ));
96111 this .nonPublicAccessAllowed = beanDefinition .isNonPublicAccessAllowed ();
97112 this .acc = acc ;
98- inferDestroyMethodIfNecessary (beanDefinition );
99- final String destroyMethodName = beanDefinition .getDestroyMethodName ();
113+ String destroyMethodName = inferDestroyMethodIfNecessary (bean , beanDefinition );
100114 if (destroyMethodName != null && !(this .invokeDisposableBean && "destroy" .equals (destroyMethodName )) &&
101115 !beanDefinition .isExternallyManagedDestroyMethod (destroyMethodName )) {
102116 this .destroyMethodName = destroyMethodName ;
@@ -122,31 +136,6 @@ else if (paramTypes.length == 1 && !paramTypes[0].equals(boolean.class)) {
122136 this .beanPostProcessors = filterPostProcessors (postProcessors );
123137 }
124138
125- /**
126- * If the current value of the given beanDefinition's destroyMethodName property is
127- * {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
128- * Candidate methods are currently limited to public, no-arg methods named 'close'
129- * (whether declared locally or inherited). The given beanDefinition's
130- * destroyMethodName is updated to be null if no such method is found, otherwise set
131- * to the name of the inferred method. This constant serves as the default for the
132- * {@code @Bean#destroyMethod} attribute and the value of the constant may also be
133- * used in XML within the {@code <bean destroy-method="">} or {@code
134- * <beans default-destroy-method="">} attributes.
135- */
136- private void inferDestroyMethodIfNecessary (RootBeanDefinition beanDefinition ) {
137- if ("(inferred)" .equals (beanDefinition .getDestroyMethodName ())) {
138- try {
139- Method candidate = bean .getClass ().getMethod ("close" );
140- if (Modifier .isPublic (candidate .getModifiers ())) {
141- beanDefinition .setDestroyMethodName (candidate .getName ());
142- }
143- } catch (NoSuchMethodException ex ) {
144- // no candidate destroy method found
145- beanDefinition .setDestroyMethodName (null );
146- }
147- }
148- }
149-
150139 /**
151140 * Create a new DisposableBeanAdapter for the given bean.
152141 */
@@ -164,6 +153,37 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDispos
164153 }
165154
166155
156+ /**
157+ * If the current value of the given beanDefinition's "destroyMethodName" property is
158+ * {@link AbstractBeanDefinition#INFER_METHOD}, then attempt to infer a destroy method.
159+ * Candidate methods are currently limited to public, no-arg methods named "close"
160+ * (whether declared locally or inherited). The given BeanDefinition's
161+ * "destroyMethodName" is updated to be null if no such method is found, otherwise set
162+ * to the name of the inferred method. This constant serves as the default for the
163+ * {@code @Bean#destroyMethod} attribute and the value of the constant may also be
164+ * used in XML within the {@code <bean destroy-method="">} or {@code
165+ * <beans default-destroy-method="">} attributes.
166+ * <p>Also processes the {@link java.io.Closeable} and {@link java.lang.AutoCloseable}
167+ * interfaces, reflectively calling the "close" method on implementing beans as well.
168+ */
169+ private String inferDestroyMethodIfNecessary (Object bean , RootBeanDefinition beanDefinition ) {
170+ if (AbstractBeanDefinition .INFER_METHOD .equals (beanDefinition .getDestroyMethodName ()) ||
171+ (beanDefinition .getDestroyMethodName () == null && closeableInterface .isInstance (bean ))) {
172+ // Only perform destroy method inference or Closeable detection
173+ // in case of the bean not explicitly implementing DisposableBean
174+ if (!(bean instanceof DisposableBean )) {
175+ try {
176+ return bean .getClass ().getMethod (CLOSE_METHOD_NAME ).getName ();
177+ }
178+ catch (NoSuchMethodException ex ) {
179+ // no candidate destroy method found
180+ }
181+ }
182+ return null ;
183+ }
184+ return beanDefinition .getDestroyMethodName ();
185+ }
186+
167187 /**
168188 * Search for all DestructionAwareBeanPostProcessors in the List.
169189 * @param postProcessors the List to search
@@ -335,4 +355,21 @@ protected Object writeReplace() {
335355 this .nonPublicAccessAllowed , this .destroyMethodName , serializablePostProcessors );
336356 }
337357
358+
359+ /**
360+ * Check whether the given bean has any kind of destroy method to call.
361+ * @param bean the bean instance
362+ * @param beanDefinition the corresponding bean definition
363+ */
364+ public static boolean hasDestroyMethod (Object bean , RootBeanDefinition beanDefinition ) {
365+ if (bean instanceof DisposableBean || closeableInterface .isInstance (bean )) {
366+ return true ;
367+ }
368+ String destroyMethodName = beanDefinition .getDestroyMethodName ();
369+ if (AbstractBeanDefinition .INFER_METHOD .equals (destroyMethodName )) {
370+ return ClassUtils .hasMethod (bean .getClass (), CLOSE_METHOD_NAME );
371+ }
372+ return (destroyMethodName != null );
373+ }
374+
338375}
0 commit comments