Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private init/destroy method may be invoked twice #28083

Closed
levitin opened this issue Feb 20, 2022 · 9 comments
Closed

Private init/destroy method may be invoked twice #28083

levitin opened this issue Feb 20, 2022 · 9 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@levitin
Copy link

levitin commented Feb 20, 2022

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() {
        ...
    }
}
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 20, 2022
@mroccyen

This comment was marked as resolved.

@mroccyen

This comment was marked as resolved.

@snicoll
Copy link
Member

snicoll commented Feb 21, 2022

@mroccyen thanks but please do not share screenshots as it bloats the issue report. I've hidden those for now.

@mroccyen
Copy link

@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?

@snicoll
Copy link
Member

snicoll commented Feb 21, 2022

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.

@rstoyanchev rstoyanchev added the in: core Issues in core modules (aop, beans, core, context, expression) label Feb 25, 2022
vikeychen added a commit to vikeychen/spring-framework that referenced this issue Feb 28, 2022
@sbrannen sbrannen linked a pull request Feb 28, 2022 that will close this issue
@sbrannen sbrannen changed the title Multiple invocation of private init method Private init method is invoked multiple times Feb 28, 2022
@sbrannen
Copy link
Member

sbrannen commented Feb 28, 2022

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.

@sbrannen
Copy link
Member

Superseded by #28113

@sbrannen sbrannen added status: superseded An issue that has been superseded by another type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 28, 2022
@sbrannen sbrannen changed the title Private init method is invoked multiple times Private lifecycle method may be invoked twice Mar 1, 2022
@sbrannen sbrannen self-assigned this Mar 1, 2022
@sbrannen sbrannen removed the status: superseded An issue that has been superseded by another label Mar 1, 2022
@sbrannen sbrannen added this to the 5.3.17 milestone Mar 1, 2022
@sbrannen sbrannen changed the title Private lifecycle method may be invoked twice Private init/destroy method may be invoked twice Mar 1, 2022
@sbrannen
Copy link
Member

sbrannen commented Mar 1, 2022

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.

@sbrannen sbrannen reopened this Mar 1, 2022
sbrannen pushed a commit that referenced this issue Mar 1, 2022
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Mar 1, 2022
sbrannen added a commit to sbrannen/spring-framework that referenced this issue Mar 1, 2022
sbrannen added a commit that referenced this issue Mar 1, 2022
This commit introduces Javadoc to explain the difference between
init/destroy method names when such methods are private, namely that a
private method is registered via its qualified method name; whereas, a
non-private method is registered via its simple name.

See gh-28083
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants