Imagine, you have a bean with an init
method, annotated with @PostConstruct
and a configuration class, referencing the same init
method on its own.
public class MyBean {
@PostConstruct
public void init() {
...
}
}
@Configuration
public class MyConfiguration {
@Bean(initMethod = "init")
public MyBean myBean() {
return new MyBean();
}
}
The init
method will be invoked only once (what is pretty well).
This is true for all method modifiers except private
.
As soon as you change the method modifier to private
the init
method is invoked twice.
public class MyBean {
@PostConstruct
- public void init() {
+ private void init() {
...
}
}
Comment From: mroccyen
Hi, I test it and maked a same result, I found the problem.
1、when it is private
externallyManagedInitMethods value is 'org.springframework.issues.issue_28083.MyBean.init', but initMethod value is 'init', invokeCustomInitMethod method will be invoked
2、when it is not private
externallyManagedInitMethods value is 'init', initMethod value is 'init', invokeCustomInitMethod method will not be invoked
I found that the value of the location setting is as follows:
Is there a problem here?
Comment From: mroccyen
Both of the above modifications can get the correct answer.
Comment From: snicoll
@MrocCyen thanks but please do not share screenshots as it bloats the issue report. I've hidden those for now.
Comment From: mroccyen
@MrocCyen thanks but please do not share screenshots as it bloats the issue report. I've hidden those for now.
Sorry, I'll pay attention next time.
Excuse me, is it correct for me to fix it like that above?
Comment From: snicoll
Excuse me, is it correct for me to fix it like that above?
I haven't investigated this in detail so I can't say for sure.
Comment From: sbrannen
I reproduced the described behavior with the following test class.
@SpringJUnitConfig
class InitMethodTests {
@Test
@DirtiesContext
void test() {
assertSoftly(softly -> {
softly.assertThat(PublicLifecycleMethodBean.initCounter).as("public init-method").hasValue(1);
softly.assertThat(PrivateLifecycleMethodBean.initCounter).as("private init-method").hasValue(1);
});
}
@AfterAll
static void afterAll() {
assertSoftly(softly -> {
softly.assertThat(PublicLifecycleMethodBean.destroyCounter).as("public destroy-method").hasValue(1);
softly.assertThat(PrivateLifecycleMethodBean.destroyCounter).as("private destroy-method").hasValue(1);
});
}
@Configuration
static class Config {
@Bean(initMethod = "publicInit", destroyMethod = "publicDestroy")
Object publicLifecycleMethodBean() {
return new PublicLifecycleMethodBean();
}
@Bean(initMethod = "privateInit", destroyMethod = "privateDestroy")
Object privateLifecycleMethodBean() {
return new PrivateLifecycleMethodBean();
}
}
static class PublicLifecycleMethodBean {
static final AtomicInteger initCounter = new AtomicInteger();
static final AtomicInteger destroyCounter = new AtomicInteger();
@PostConstruct
public void publicInit() {
initCounter.incrementAndGet();
}
@PreDestroy
public void publicDestroy() {
destroyCounter.incrementAndGet();
}
}
static class PrivateLifecycleMethodBean {
static final AtomicInteger initCounter = new AtomicInteger();
static final AtomicInteger destroyCounter = new AtomicInteger();
@PostConstruct
private void privateInit() {
initCounter.incrementAndGet();
}
@PreDestroy
private void privateDestroy() {
destroyCounter.incrementAndGet();
}
}
}
The "private init-method" and "private destroy-method" assertions fail due to 2 invocations.
Comment From: sbrannen
Superseded by #28113
Comment From: sbrannen
Related Issues
-
8455
-
10762
-
27449
Comment From: sbrannen
Excuse me, is it correct for me to fix it like that above?
No, that would constitute a breaking change in behavior for private
init/destroy methods with the same name declared at multiple levels in a class hierarchy.
We have decided to approach the fix differently, and I have therefore assigned this issue to myself.