Skip to content

Commit de8c0be

Browse files
committed
Optimize JSR250 lifecycle annotation detection
1 parent 17ad8b2 commit de8c0be

File tree

7 files changed

+102
-129
lines changed

7 files changed

+102
-129
lines changed

core/src/main/java/org/seedstack/seed/core/internal/lifecycle/ConstructionProvisionListener.java

Lines changed: 0 additions & 33 deletions
This file was deleted.

core/src/main/java/org/seedstack/seed/core/internal/lifecycle/DestructionMatcher.java

Lines changed: 0 additions & 67 deletions
This file was deleted.
Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,25 @@
1212
import org.seedstack.shed.reflect.Classes;
1313

1414
import javax.annotation.PostConstruct;
15+
import javax.annotation.PreDestroy;
1516

1617
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
1718

18-
class ConstructionMatcher extends AbstractMatcher<Binding<?>> {
19+
class LifecycleMatcher extends AbstractMatcher<Binding<?>> {
1920
@Override
2021
public boolean matches(Binding<?> binding) {
21-
return hasPostConstruct(binding.getKey().getTypeLiteral().getRawType());
22+
// Singletons being auto-closeable or having at least one @PreDestroy method match
23+
// Any binding (any scope) having at least one @PostConstruct method match
24+
Class<?> rawType = binding.getKey().getTypeLiteral().getRawType();
25+
return (binding.acceptScopingVisitor(SingletonScopingVisitor.INSTANCE) && (isAutoCloseable(rawType) || hasPreDestroy(rawType))) || hasPostConstruct(rawType);
26+
}
27+
28+
private boolean hasPreDestroy(Class<?> rawType) {
29+
return Classes.from(rawType)
30+
.traversingInterfaces()
31+
.traversingSuperclasses()
32+
.methods()
33+
.anyMatch(elementAnnotatedWith(PreDestroy.class, true));
2234
}
2335

2436
private boolean hasPostConstruct(Class<?> rawType) {
@@ -28,4 +40,8 @@ private boolean hasPostConstruct(Class<?> rawType) {
2840
.methods()
2941
.anyMatch(elementAnnotatedWith(PostConstruct.class, true));
3042
}
43+
44+
private boolean isAutoCloseable(Class<?> rawType) {
45+
return AutoCloseable.class.isAssignableFrom(rawType);
46+
}
3147
}

core/src/main/java/org/seedstack/seed/core/internal/lifecycle/LifecycleModule.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ protected void configure() {
3232
lifecycleListenerMultibinder.addBinding().to(lifecycleListenerClass);
3333
}
3434

35-
// Bind lifecycle provision listeners
36-
bindListener(new ConstructionMatcher(), new ConstructionProvisionListener());
37-
bindListener(new DestructionMatcher(), new DestructionProvisionListener(lifecycleManager));
35+
// Bind lifecycle provision listener
36+
bindListener(new LifecycleMatcher(), new LifecycleProvisionListener(lifecycleManager));
3837
}
3938
}
Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,20 @@
77
*/
88
package org.seedstack.seed.core.internal.lifecycle;
99

10-
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
11-
import static org.seedstack.shed.reflect.ReflectUtils.invoke;
12-
import static org.seedstack.shed.reflect.ReflectUtils.makeAccessible;
13-
1410
import com.google.inject.spi.ProvisionListener;
11+
import org.seedstack.shed.reflect.Classes;
1512

1613
import javax.annotation.PostConstruct;
1714
import javax.annotation.PreDestroy;
1815

19-
import org.seedstack.shed.reflect.Classes;
16+
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
17+
import static org.seedstack.shed.reflect.ReflectUtils.invoke;
18+
import static org.seedstack.shed.reflect.ReflectUtils.makeAccessible;
2019

21-
class DestructionProvisionListener implements ProvisionListener {
20+
class LifecycleProvisionListener implements ProvisionListener {
2221
private final LifecycleManager lifecycleManager;
2322

24-
DestructionProvisionListener(LifecycleManager lifecycleManager) {
23+
LifecycleProvisionListener(LifecycleManager lifecycleManager) {
2524
this.lifecycleManager = lifecycleManager;
2625
}
2726

@@ -33,11 +32,18 @@ public <T> void onProvision(ProvisionInvocation<T> provisionInvocation) {
3332
.traversingSuperclasses()
3433
.methods()
3534
.forEach(m -> {
36-
if (elementAnnotatedWith(PreDestroy.class, true).test(m)) {
37-
lifecycleManager.registerPreDestroy(provision, m);
35+
if (elementAnnotatedWith(PostConstruct.class, true).test(m)) {
36+
invoke(makeAccessible(m), provision);
3837
}
39-
if (provision instanceof AutoCloseable) {
40-
lifecycleManager.registerAutoCloseable((AutoCloseable) provision);
38+
39+
// End-of-life management are limited to singletons (in-line with the matching condition in LifecycleMatcher)
40+
if (provisionInvocation.getBinding().acceptScopingVisitor(SingletonScopingVisitor.INSTANCE)) {
41+
if (elementAnnotatedWith(PreDestroy.class, true).test(m)) {
42+
lifecycleManager.registerPreDestroy(provision, m);
43+
}
44+
if (provision instanceof AutoCloseable) {
45+
lifecycleManager.registerAutoCloseable((AutoCloseable) provision);
46+
}
4147
}
4248
});
4349
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright © 2013-2021, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
package org.seedstack.seed.core.internal.lifecycle;
9+
10+
import com.google.inject.Scope;
11+
import com.google.inject.Scopes;
12+
import com.google.inject.spi.BindingScopingVisitor;
13+
14+
import javax.inject.Singleton;
15+
import java.lang.annotation.Annotation;
16+
17+
final class SingletonScopingVisitor implements BindingScopingVisitor<Boolean> {
18+
static final SingletonScopingVisitor INSTANCE = new SingletonScopingVisitor();
19+
20+
private SingletonScopingVisitor() {
21+
}
22+
23+
@Override
24+
public Boolean visitEagerSingleton() {
25+
return true;
26+
}
27+
28+
@Override
29+
public Boolean visitScope(Scope scope) {
30+
return scope == Scopes.SINGLETON;
31+
}
32+
33+
@Override
34+
public Boolean visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
35+
return scopeAnnotation.isAssignableFrom(Singleton.class) || scopeAnnotation.isAssignableFrom(
36+
com.google.inject.Singleton.class);
37+
}
38+
39+
@Override
40+
public Boolean visitNoScoping() {
41+
return false;
42+
}
43+
}

core/src/test/java/org/seedstack/seed/core/LifecycleIT.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public class LifecycleIT implements LifecycleListener {
3535
private static boolean ignoredClosedWasCalled;
3636
private static boolean proxyClosedWasCalled;
3737
private static boolean classProxyClosedWasCalled;
38+
private static boolean preDestroySingletonCalled;
3839
private static boolean preDestroyCalled;
3940
private static boolean postConstructSingletonCalled;
4041
private static boolean postConstructCalled;
@@ -54,16 +55,16 @@ public static void tearDownClass() {
5455
assertThat(stoppingWasCalled).isTrue();
5556
assertThat(closedWasCalled).isTrue();
5657
assertThat(ignoredClosedWasCalled).isFalse();
57-
assertThat(preDestroyCalled).isTrue();
58+
assertThat(preDestroySingletonCalled).isTrue();
5859
}
5960

6061
@Before
6162
public void setUp() {
6263
kernel = Seed.createKernel();
6364
childInjector = kernel.objectGraph().as(Injector.class).createChildInjector(binder -> {
6465
binder.bind(PreDestroyFixture.class);
65-
binder.bind(PostConstructFixture.class);
66-
binder.bind(PostConstructSingletonFixture.class);
66+
binder.bind(StatelessFixture.class);
67+
binder.bind(SingletonFixture.class);
6768
binder.bind(AutoCloseableFixture.class);
6869
binder.bind(IgnoredAutoCloseableFixture.class);
6970
binder.bind(AutoCloseable.class)
@@ -79,7 +80,7 @@ public void setUp() {
7980
assertThat(closedWasCalled).isFalse();
8081
assertThat(ignoredClosedWasCalled).isFalse();
8182
assertThat(postConstructSingletonCalled).isTrue();
82-
assertThat(preDestroyCalled).isFalse();
83+
assertThat(preDestroySingletonCalled).isFalse();
8384
}
8485

8586
@After
@@ -88,7 +89,7 @@ public void tearDown() {
8889
assertThat(stoppingWasCalled).isFalse();
8990
assertThat(closedWasCalled).isFalse();
9091
assertThat(ignoredClosedWasCalled).isFalse();
91-
assertThat(preDestroyCalled).isFalse();
92+
assertThat(preDestroySingletonCalled).isFalse();
9293
Seed.disposeKernel(kernel);
9394
}
9495

@@ -103,10 +104,12 @@ public void lifecycle_callbacks_are_invoked() {
103104
assertThat(postConstructSingletonCalled).isTrue();
104105

105106
assertThat(postConstructCalled).isFalse();
106-
childInjector.getInstance(PostConstructFixture.class);
107+
assertThat(preDestroyCalled).isFalse();
108+
childInjector.getInstance(StatelessFixture.class);
107109
assertThat(postConstructCalled).isTrue();
108-
109110
assertThat(preDestroyCalled).isFalse();
111+
112+
assertThat(preDestroySingletonCalled).isFalse();
110113
}
111114

112115
@Override
@@ -157,24 +160,30 @@ public void close() {
157160

158161
@Singleton
159162
private static class PreDestroyFixture {
160-
@PreDestroy
161-
public void preDestroy() {
162-
preDestroyCalled = true;
163-
}
164163
}
165164

166165
@Singleton
167-
private static class PostConstructSingletonFixture {
166+
private static class SingletonFixture {
168167
@PostConstruct
169168
public void postConstruct() {
170169
postConstructSingletonCalled = true;
171170
}
171+
172+
@PreDestroy
173+
public void preDestroy() {
174+
preDestroySingletonCalled = true;
175+
}
172176
}
173177

174-
private static class PostConstructFixture {
178+
private static class StatelessFixture {
175179
@PostConstruct
176180
public void postConstruct() {
177181
postConstructCalled = true;
178182
}
183+
184+
@PreDestroy
185+
public void preDestroy() {
186+
preDestroyCalled = true;
187+
}
179188
}
180189
}

0 commit comments

Comments
 (0)