Skip to content

Commit dcdea98

Browse files
committed
Polish init/destroy lifecycle method tests
See gh-28083
1 parent a524857 commit dcdea98

File tree

2 files changed

+279
-325
lines changed

2 files changed

+279
-325
lines changed
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
/*
2+
* Copyright 2002-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
import javax.annotation.PostConstruct;
23+
import javax.annotation.PreDestroy;
24+
25+
import org.junit.jupiter.api.Test;
26+
27+
import org.springframework.beans.factory.DisposableBean;
28+
import org.springframework.beans.factory.InitializingBean;
29+
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
30+
import org.springframework.beans.factory.support.RootBeanDefinition;
31+
32+
import static org.assertj.core.api.Assertions.assertThat;
33+
34+
/**
35+
* Tests which verify expected <em>init</em> and <em>destroy</em> bean lifecycle
36+
* behavior as requested in
37+
* <a href="https://github.com/spring-projects/spring-framework/issues/8455" target="_blank">SPR-3775</a>.
38+
*
39+
* <p>Specifically, combinations of the following are tested:
40+
* <ul>
41+
* <li>{@link InitializingBean} &amp; {@link DisposableBean} interfaces</li>
42+
* <li>Custom {@link RootBeanDefinition#getInitMethodName() init} &amp;
43+
* {@link RootBeanDefinition#getDestroyMethodName() destroy} methods</li>
44+
* <li>JSR 250's {@link javax.annotation.PostConstruct @PostConstruct} &amp;
45+
* {@link javax.annotation.PreDestroy @PreDestroy} annotations</li>
46+
* </ul>
47+
*
48+
* @author Sam Brannen
49+
* @since 2.5
50+
*/
51+
class InitDestroyMethodLifecycleTests {
52+
53+
@Test
54+
void initDestroyMethods() {
55+
Class<?> beanClass = InitDestroyBean.class;
56+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
57+
InitDestroyBean bean = beanFactory.getBean(InitDestroyBean.class);
58+
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet");
59+
beanFactory.destroySingletons();
60+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy");
61+
}
62+
63+
@Test
64+
void initializingDisposableInterfaces() {
65+
Class<?> beanClass = CustomInitializingDisposableBean.class;
66+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
67+
CustomInitializingDisposableBean bean = beanFactory.getBean(CustomInitializingDisposableBean.class);
68+
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet", "customInit");
69+
beanFactory.destroySingletons();
70+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy", "customDestroy");
71+
}
72+
73+
@Test
74+
void initializingDisposableInterfacesWithShadowedMethods() {
75+
Class<?> beanClass = InitializingDisposableWithShadowedMethodsBean.class;
76+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
77+
InitializingDisposableWithShadowedMethodsBean bean = beanFactory.getBean(InitializingDisposableWithShadowedMethodsBean.class);
78+
assertThat(bean.initMethods).as("init-methods").containsExactly("InitializingBean.afterPropertiesSet");
79+
beanFactory.destroySingletons();
80+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("DisposableBean.destroy");
81+
}
82+
83+
@Test
84+
void jsr250Annotations() {
85+
Class<?> beanClass = CustomAnnotatedInitDestroyBean.class;
86+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
87+
CustomAnnotatedInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedInitDestroyBean.class);
88+
assertThat(bean.initMethods).as("init-methods").containsExactly("postConstruct", "afterPropertiesSet", "customInit");
89+
beanFactory.destroySingletons();
90+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("preDestroy", "destroy", "customDestroy");
91+
}
92+
93+
@Test
94+
void jsr250AnnotationsWithShadowedMethods() {
95+
Class<?> beanClass = CustomAnnotatedInitDestroyWithShadowedMethodsBean.class;
96+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit", "customDestroy");
97+
CustomAnnotatedInitDestroyWithShadowedMethodsBean bean = beanFactory.getBean(CustomAnnotatedInitDestroyWithShadowedMethodsBean.class);
98+
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.afterPropertiesSet", "customInit");
99+
beanFactory.destroySingletons();
100+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.destroy", "customDestroy");
101+
}
102+
103+
@Test
104+
void jsr250AnnotationsWithCustomPrivateInitDestroyMethods() {
105+
Class<?> beanClass = CustomAnnotatedPrivateInitDestroyBean.class;
106+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit1", "customDestroy1");
107+
CustomAnnotatedPrivateInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedPrivateInitDestroyBean.class);
108+
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.privateCustomInit1", "afterPropertiesSet");
109+
beanFactory.destroySingletons();
110+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.privateCustomDestroy1", "destroy");
111+
}
112+
113+
@Test
114+
void jsr250AnnotationsWithCustomSameMethodNames() {
115+
Class<?> beanClass = CustomAnnotatedPrivateSameNameInitDestroyBean.class;
116+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "customInit1", "customDestroy1");
117+
CustomAnnotatedPrivateSameNameInitDestroyBean bean = beanFactory.getBean(CustomAnnotatedPrivateSameNameInitDestroyBean.class);
118+
assertThat(bean.initMethods).as("init-methods").containsExactly("@PostConstruct.privateCustomInit1", "@PostConstruct.sameNameCustomInit1", "afterPropertiesSet");
119+
beanFactory.destroySingletons();
120+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("@PreDestroy.sameNameCustomDestroy1", "@PreDestroy.privateCustomDestroy1", "destroy");
121+
}
122+
123+
@Test
124+
void allLifecycleMechanismsAtOnce() {
125+
Class<?> beanClass = AllInOneBean.class;
126+
DefaultListableBeanFactory beanFactory = createBeanFactoryAndRegisterBean(beanClass, "afterPropertiesSet", "destroy");
127+
AllInOneBean bean = beanFactory.getBean(AllInOneBean.class);
128+
assertThat(bean.initMethods).as("init-methods").containsExactly("afterPropertiesSet");
129+
beanFactory.destroySingletons();
130+
assertThat(bean.destroyMethods).as("destroy-methods").containsExactly("destroy");
131+
}
132+
133+
134+
private static DefaultListableBeanFactory createBeanFactoryAndRegisterBean(Class<?> beanClass,
135+
String initMethodName, String destroyMethodName) {
136+
137+
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
138+
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
139+
beanDefinition.setInitMethodName(initMethodName);
140+
beanDefinition.setDestroyMethodName(destroyMethodName);
141+
beanFactory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor());
142+
beanFactory.registerBeanDefinition("lifecycleTestBean", beanDefinition);
143+
return beanFactory;
144+
}
145+
146+
147+
static class InitDestroyBean {
148+
149+
final List<String> initMethods = new ArrayList<>();
150+
final List<String> destroyMethods = new ArrayList<>();
151+
152+
153+
public void afterPropertiesSet() throws Exception {
154+
this.initMethods.add("afterPropertiesSet");
155+
}
156+
157+
public void destroy() throws Exception {
158+
this.destroyMethods.add("destroy");
159+
}
160+
}
161+
162+
static class InitializingDisposableWithShadowedMethodsBean extends InitDestroyBean implements
163+
InitializingBean, DisposableBean {
164+
165+
@Override
166+
public void afterPropertiesSet() throws Exception {
167+
this.initMethods.add("InitializingBean.afterPropertiesSet");
168+
}
169+
170+
@Override
171+
public void destroy() throws Exception {
172+
this.destroyMethods.add("DisposableBean.destroy");
173+
}
174+
}
175+
176+
177+
static class CustomInitDestroyBean {
178+
179+
final List<String> initMethods = new ArrayList<>();
180+
final List<String> destroyMethods = new ArrayList<>();
181+
182+
public void customInit() throws Exception {
183+
this.initMethods.add("customInit");
184+
}
185+
186+
public void customDestroy() throws Exception {
187+
this.destroyMethods.add("customDestroy");
188+
}
189+
}
190+
191+
static class CustomAnnotatedPrivateInitDestroyBean extends CustomInitializingDisposableBean {
192+
193+
@PostConstruct
194+
private void customInit1() throws Exception {
195+
this.initMethods.add("@PostConstruct.privateCustomInit1");
196+
}
197+
198+
@PreDestroy
199+
private void customDestroy1() throws Exception {
200+
this.destroyMethods.add("@PreDestroy.privateCustomDestroy1");
201+
}
202+
}
203+
204+
static class CustomAnnotatedPrivateSameNameInitDestroyBean extends CustomAnnotatedPrivateInitDestroyBean {
205+
206+
@PostConstruct
207+
@SuppressWarnings("unused")
208+
private void customInit1() throws Exception {
209+
this.initMethods.add("@PostConstruct.sameNameCustomInit1");
210+
}
211+
212+
@PreDestroy
213+
@SuppressWarnings("unused")
214+
private void customDestroy1() throws Exception {
215+
this.destroyMethods.add("@PreDestroy.sameNameCustomDestroy1");
216+
}
217+
}
218+
219+
static class CustomInitializingDisposableBean extends CustomInitDestroyBean
220+
implements InitializingBean, DisposableBean {
221+
222+
@Override
223+
public void afterPropertiesSet() throws Exception {
224+
this.initMethods.add("afterPropertiesSet");
225+
}
226+
227+
@Override
228+
public void destroy() throws Exception {
229+
this.destroyMethods.add("destroy");
230+
}
231+
}
232+
233+
static class CustomAnnotatedInitDestroyBean extends CustomInitializingDisposableBean {
234+
235+
@PostConstruct
236+
public void postConstruct() throws Exception {
237+
this.initMethods.add("postConstruct");
238+
}
239+
240+
@PreDestroy
241+
public void preDestroy() throws Exception {
242+
this.destroyMethods.add("preDestroy");
243+
}
244+
}
245+
246+
static class CustomAnnotatedInitDestroyWithShadowedMethodsBean extends CustomInitializingDisposableBean {
247+
248+
@PostConstruct
249+
@Override
250+
public void afterPropertiesSet() throws Exception {
251+
this.initMethods.add("@PostConstruct.afterPropertiesSet");
252+
}
253+
254+
@PreDestroy
255+
@Override
256+
public void destroy() throws Exception {
257+
this.destroyMethods.add("@PreDestroy.destroy");
258+
}
259+
}
260+
261+
static class AllInOneBean implements InitializingBean, DisposableBean {
262+
263+
final List<String> initMethods = new ArrayList<>();
264+
final List<String> destroyMethods = new ArrayList<>();
265+
266+
@PostConstruct
267+
@Override
268+
public void afterPropertiesSet() throws Exception {
269+
this.initMethods.add("afterPropertiesSet");
270+
}
271+
272+
@PreDestroy
273+
@Override
274+
public void destroy() throws Exception {
275+
this.destroyMethods.add("destroy");
276+
}
277+
}
278+
279+
}

0 commit comments

Comments
 (0)