Clarify destroy method suppression for DisposableBean vs (Auto)Closeable

Issue: SPR-16078
This commit is contained in:
Juergen Hoeller 2017-10-16 23:11:19 +02:00
parent 17fb4fed09
commit dff2c84ed1
2 changed files with 52 additions and 15 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -157,8 +157,8 @@ import org.springframework.core.annotation.AliasFor;
* *
* <pre class="code"> * <pre class="code">
* &#064;Bean * &#064;Bean
* public static PropertyPlaceholderConfigurer ppc() { * public static PropertySourcesPlaceholderConfigurer pspc() {
* // instantiate, configure and return ppc ... * // instantiate, configure and return pspc ...
* } * }
* </pre> * </pre>
* *
@ -228,6 +228,8 @@ public @interface Bean {
* Not commonly used, given that the method may be called programmatically directly * Not commonly used, given that the method may be called programmatically directly
* within the body of a Bean-annotated method. * within the body of a Bean-annotated method.
* <p>The default value is {@code ""}, indicating no init method to be called. * <p>The default value is {@code ""}, indicating no init method to be called.
* @see org.springframework.beans.factory.InitializingBean
* @see org.springframework.context.ConfigurableApplicationContext#refresh()
*/ */
String initMethod() default ""; String initMethod() default "";
@ -248,12 +250,14 @@ public @interface Bean {
* creation time). * creation time).
* <p>To disable destroy method inference for a particular {@code @Bean}, specify an * <p>To disable destroy method inference for a particular {@code @Bean}, specify an
* empty string as the value, e.g. {@code @Bean(destroyMethod="")}. Note that the * empty string as the value, e.g. {@code @Bean(destroyMethod="")}. Note that the
* {@link org.springframework.beans.factory.DisposableBean} and the * {@link org.springframework.beans.factory.DisposableBean} callback interface will
* {@link java.io.Closeable}/{@link java.lang.AutoCloseable} interfaces will * nevertheless get detected and the corresponding destroy method invoked: In other
* nevertheless get detected and the corresponding destroy/close method invoked. * words, {@code destroyMethod=""} only affects custom close/shutdown methods and
* {@link java.io.Closeable}/{@link java.lang.AutoCloseable} declared close methods.
* <p>Note: Only invoked on beans whose lifecycle is under the full control of the * <p>Note: Only invoked on beans whose lifecycle is under the full control of the
* factory, which is always the case for singletons but not guaranteed for any * factory, which is always the case for singletons but not guaranteed for any
* other scope. * other scope.
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.context.ConfigurableApplicationContext#close() * @see org.springframework.context.ConfigurableApplicationContext#close()
*/ */
String destroyMethod() default AbstractBeanDefinition.INFER_METHOD; String destroyMethod() default AbstractBeanDefinition.INFER_METHOD;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2015 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,10 +17,10 @@
package org.springframework.context.annotation; package org.springframework.context.annotation;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.context.support.GenericXmlApplicationContext;
@ -36,8 +36,7 @@ public class DestroyMethodInferenceTests {
@Test @Test
public void beanMethods() { public void beanMethods() {
ConfigurableApplicationContext ctx = ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
new AnnotationConfigApplicationContext(Config.class);
WithExplicitDestroyMethod c0 = ctx.getBean(WithExplicitDestroyMethod.class); WithExplicitDestroyMethod c0 = ctx.getBean(WithExplicitDestroyMethod.class);
WithLocalCloseMethod c1 = ctx.getBean("c1", WithLocalCloseMethod.class); WithLocalCloseMethod c1 = ctx.getBean("c1", WithLocalCloseMethod.class);
WithLocalCloseMethod c2 = ctx.getBean("c2", WithLocalCloseMethod.class); WithLocalCloseMethod c2 = ctx.getBean("c2", WithLocalCloseMethod.class);
@ -47,6 +46,7 @@ public class DestroyMethodInferenceTests {
WithNoCloseMethod c6 = ctx.getBean("c6", WithNoCloseMethod.class); WithNoCloseMethod c6 = ctx.getBean("c6", WithNoCloseMethod.class);
WithLocalShutdownMethod c7 = ctx.getBean("c7", WithLocalShutdownMethod.class); WithLocalShutdownMethod c7 = ctx.getBean("c7", WithLocalShutdownMethod.class);
WithInheritedCloseMethod c8 = ctx.getBean("c8", WithInheritedCloseMethod.class); WithInheritedCloseMethod c8 = ctx.getBean("c8", WithInheritedCloseMethod.class);
WithDisposableBean c9 = ctx.getBean("c9", WithDisposableBean.class);
assertThat(c0.closed, is(false)); assertThat(c0.closed, is(false));
assertThat(c1.closed, is(false)); assertThat(c1.closed, is(false));
@ -57,6 +57,7 @@ public class DestroyMethodInferenceTests {
assertThat(c6.closed, is(false)); assertThat(c6.closed, is(false));
assertThat(c7.closed, is(false)); assertThat(c7.closed, is(false));
assertThat(c8.closed, is(false)); assertThat(c8.closed, is(false));
assertThat(c9.closed, is(false));
ctx.close(); ctx.close();
assertThat("c0", c0.closed, is(true)); assertThat("c0", c0.closed, is(true));
assertThat("c1", c1.closed, is(true)); assertThat("c1", c1.closed, is(true));
@ -67,6 +68,7 @@ public class DestroyMethodInferenceTests {
assertThat("c6", c6.closed, is(false)); assertThat("c6", c6.closed, is(false));
assertThat("c7", c7.closed, is(true)); assertThat("c7", c7.closed, is(true));
assertThat("c8", c8.closed, is(false)); assertThat("c8", c8.closed, is(false));
assertThat("c9", c9.closed, is(true));
} }
@Test @Test
@ -91,9 +93,11 @@ public class DestroyMethodInferenceTests {
assertThat(x8.closed, is(false)); assertThat(x8.closed, is(false));
} }
@Configuration @Configuration
static class Config { static class Config {
@Bean(destroyMethod="explicitClose")
@Bean(destroyMethod = "explicitClose")
public WithExplicitDestroyMethod c0() { public WithExplicitDestroyMethod c0() {
return new WithExplicitDestroyMethod(); return new WithExplicitDestroyMethod();
} }
@ -118,12 +122,12 @@ public class DestroyMethodInferenceTests {
return new WithInheritedCloseMethod(); return new WithInheritedCloseMethod();
} }
@Bean(destroyMethod="other") @Bean(destroyMethod = "other")
public WithInheritedCloseMethod c5() { public WithInheritedCloseMethod c5() {
return new WithInheritedCloseMethod() { return new WithInheritedCloseMethod() {
@Override @Override
public void close() throws IOException { public void close() {
throw new RuntimeException("close() should not be called"); throw new IllegalStateException("close() should not be called");
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
public void other() { public void other() {
@ -146,37 +150,66 @@ public class DestroyMethodInferenceTests {
public WithInheritedCloseMethod c8() { public WithInheritedCloseMethod c8() {
return new WithInheritedCloseMethod(); return new WithInheritedCloseMethod();
} }
@Bean(destroyMethod = "")
public WithDisposableBean c9() {
return new WithDisposableBean();
}
} }
static class WithExplicitDestroyMethod { static class WithExplicitDestroyMethod {
boolean closed = false; boolean closed = false;
public void explicitClose() { public void explicitClose() {
closed = true; closed = true;
} }
} }
static class WithLocalCloseMethod { static class WithLocalCloseMethod {
boolean closed = false; boolean closed = false;
public void close() { public void close() {
closed = true; closed = true;
} }
} }
static class WithInheritedCloseMethod implements Closeable { static class WithInheritedCloseMethod implements Closeable {
boolean closed = false; boolean closed = false;
@Override @Override
public void close() throws IOException { public void close() {
closed = true; closed = true;
} }
} }
static class WithDisposableBean implements DisposableBean {
boolean closed = false;
@Override
public void destroy() {
closed = true;
}
}
static class WithNoCloseMethod { static class WithNoCloseMethod {
boolean closed = false; boolean closed = false;
} }
static class WithLocalShutdownMethod { static class WithLocalShutdownMethod {
boolean closed = false; boolean closed = false;
public void shutdown() { public void shutdown() {
closed = true; closed = true;
} }