1
1
/*
2
- * Copyright 2002-2010 the original author or authors.
2
+ * Copyright 2002-2012 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
16
16
17
17
package org .springframework .beans .factory .support ;
18
18
19
+ import java .io .Closeable ;
19
20
import java .io .Serializable ;
20
21
import java .lang .reflect .InvocationTargetException ;
21
22
import java .lang .reflect .Method ;
22
- import java .lang .reflect .Modifier ;
23
23
import java .security .AccessControlContext ;
24
24
import java .security .AccessController ;
25
25
import java .security .PrivilegedAction ;
36
36
import org .springframework .beans .factory .config .BeanPostProcessor ;
37
37
import org .springframework .beans .factory .config .DestructionAwareBeanPostProcessor ;
38
38
import org .springframework .util .Assert ;
39
+ import org .springframework .util .ClassUtils ;
39
40
import org .springframework .util .ReflectionUtils ;
40
41
41
42
/**
58
59
@ SuppressWarnings ("serial" )
59
60
class DisposableBeanAdapter implements DisposableBean , Runnable , Serializable {
60
61
62
+ private static final String CLOSE_METHOD_NAME = "close" ;
63
+
61
64
private static final Log logger = LogFactory .getLog (DisposableBeanAdapter .class );
62
65
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
+
63
78
private final Object bean ;
64
79
65
80
private final String beanName ;
@@ -95,8 +110,7 @@ public DisposableBeanAdapter(Object bean, String beanName, RootBeanDefinition be
95
110
(this .bean instanceof DisposableBean && !beanDefinition .isExternallyManagedDestroyMethod ("destroy" ));
96
111
this .nonPublicAccessAllowed = beanDefinition .isNonPublicAccessAllowed ();
97
112
this .acc = acc ;
98
- inferDestroyMethodIfNecessary (beanDefinition );
99
- final String destroyMethodName = beanDefinition .getDestroyMethodName ();
113
+ String destroyMethodName = inferDestroyMethodIfNecessary (bean , beanDefinition );
100
114
if (destroyMethodName != null && !(this .invokeDisposableBean && "destroy" .equals (destroyMethodName )) &&
101
115
!beanDefinition .isExternallyManagedDestroyMethod (destroyMethodName )) {
102
116
this .destroyMethodName = destroyMethodName ;
@@ -122,31 +136,6 @@ else if (paramTypes.length == 1 && !paramTypes[0].equals(boolean.class)) {
122
136
this .beanPostProcessors = filterPostProcessors (postProcessors );
123
137
}
124
138
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
-
150
139
/**
151
140
* Create a new DisposableBeanAdapter for the given bean.
152
141
*/
@@ -164,6 +153,37 @@ private DisposableBeanAdapter(Object bean, String beanName, boolean invokeDispos
164
153
}
165
154
166
155
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
+
167
187
/**
168
188
* Search for all DestructionAwareBeanPostProcessors in the List.
169
189
* @param postProcessors the List to search
@@ -335,4 +355,21 @@ protected Object writeReplace() {
335
355
this .nonPublicAccessAllowed , this .destroyMethodName , serializablePostProcessors );
336
356
}
337
357
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
+
338
375
}
0 commit comments