Skip to content

Commit 17ad8b2

Browse files
committed
Extend @PostConstruct to all scopes
1 parent 257cb92 commit 17ad8b2

File tree

30 files changed

+179
-69
lines changed

30 files changed

+179
-69
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# Version 3.14.0 (2021-09-30)
2+
3+
* [new] `@PostConstruct` support was extended to non-singletons. `@PreDestroy` is still limited to singletons per design
4+
(only singletons instances are known and tracked by the lifecycle manager).
5+
16
# Version 3.13.0 (2021-07-31)
27

38
* [new] Support JSON and YAML expansion in configuration. Use a `myKey|json` or `myKey|yaml` as key syntax. The suffix

cli/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.seedstack.seed</groupId>
1616
<artifactId>seed</artifactId>
17-
<version>3.13.0-SNAPSHOT</version>
17+
<version>3.14.0-SNAPSHOT</version>
1818
</parent>
1919

2020
<artifactId>seed-cli</artifactId>

core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.seedstack.seed</groupId>
1616
<artifactId>seed</artifactId>
17-
<version>3.13.0-SNAPSHOT</version>
17+
<version>3.14.0-SNAPSHOT</version>
1818
</parent>
1919

2020
<artifactId>seed-core</artifactId>

core/src/main/java/org/seedstack/seed/core/internal/CorePlugin.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,26 @@
77
*/
88
package org.seedstack.seed.core.internal;
99

10-
import static org.seedstack.shed.misc.PriorityUtils.sortByPriority;
11-
1210
import com.google.common.base.Strings;
1311
import com.google.inject.Module;
1412
import io.nuun.kernel.api.plugin.InitState;
1513
import io.nuun.kernel.api.plugin.context.InitContext;
1614
import io.nuun.kernel.api.plugin.request.ClasspathScanRequest;
15+
import org.seedstack.seed.SeedInterceptor;
16+
import org.seedstack.shed.misc.PriorityUtils;
17+
import org.seedstack.shed.reflect.Classes;
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
21+
import javax.inject.Provider;
1722
import java.util.ArrayList;
1823
import java.util.Collection;
1924
import java.util.HashSet;
2025
import java.util.List;
2126
import java.util.Set;
2227
import java.util.stream.Collectors;
23-
import javax.inject.Provider;
24-
import org.seedstack.seed.SeedInterceptor;
25-
import org.seedstack.shed.misc.PriorityUtils;
26-
import org.seedstack.shed.reflect.Classes;
27-
import org.slf4j.Logger;
28-
import org.slf4j.LoggerFactory;
28+
29+
import static org.seedstack.shed.misc.PriorityUtils.sortByPriority;
2930

3031
/**
3132
* Core plugin that configures base package roots and detects diagnostic collectors, dependency providers, Guice modules
@@ -120,6 +121,8 @@ private void detectBindings(InitContext initContext) {
120121
@SuppressWarnings("unchecked")
121122
private void detectProviders(InitContext initContext) {
122123
initContext.scannedTypesByPredicate().get(ProvideResolver.INSTANCE)
124+
.stream()
125+
.filter(Provider.class::isAssignableFrom)
123126
.forEach(candidate -> ProvideResolver.INSTANCE.apply(candidate).ifPresent(annotation -> {
124127
if (annotation.override()) {
125128
overridingBindings.add(new ProviderDefinition<>((Class<Provider<Object>>) candidate));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.Binding;
11+
import com.google.inject.matcher.AbstractMatcher;
12+
import org.seedstack.shed.reflect.Classes;
13+
14+
import javax.annotation.PostConstruct;
15+
16+
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
17+
18+
class ConstructionMatcher extends AbstractMatcher<Binding<?>> {
19+
@Override
20+
public boolean matches(Binding<?> binding) {
21+
return hasPostConstruct(binding.getKey().getTypeLiteral().getRawType());
22+
}
23+
24+
private boolean hasPostConstruct(Class<?> rawType) {
25+
return Classes.from(rawType)
26+
.traversingInterfaces()
27+
.traversingSuperclasses()
28+
.methods()
29+
.anyMatch(elementAnnotatedWith(PostConstruct.class, true));
30+
}
31+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.spi.ProvisionListener;
11+
import org.seedstack.shed.reflect.Classes;
12+
13+
import javax.annotation.PostConstruct;
14+
15+
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
16+
import static org.seedstack.shed.reflect.ReflectUtils.invoke;
17+
import static org.seedstack.shed.reflect.ReflectUtils.makeAccessible;
18+
19+
class ConstructionProvisionListener implements ProvisionListener {
20+
@Override
21+
public <T> void onProvision(ProvisionInvocation<T> provisionInvocation) {
22+
T provision = provisionInvocation.provision();
23+
Classes.from(provision.getClass())
24+
.traversingInterfaces()
25+
.traversingSuperclasses()
26+
.methods()
27+
.forEach(m -> {
28+
if (elementAnnotatedWith(PostConstruct.class, true).test(m)) {
29+
invoke(makeAccessible(m), provision);
30+
}
31+
});
32+
}
33+
}

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,42 @@
77
*/
88
package org.seedstack.seed.core.internal.lifecycle;
99

10-
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
11-
1210
import com.google.inject.Binding;
1311
import com.google.inject.Scope;
1412
import com.google.inject.Scopes;
1513
import com.google.inject.matcher.AbstractMatcher;
1614
import com.google.inject.spi.BindingScopingVisitor;
17-
import java.lang.annotation.Annotation;
18-
import javax.annotation.PostConstruct;
15+
import org.seedstack.shed.reflect.Classes;
16+
1917
import javax.annotation.PreDestroy;
2018
import javax.inject.Singleton;
21-
import org.seedstack.shed.reflect.Classes;
19+
import java.lang.annotation.Annotation;
20+
21+
import static org.seedstack.shed.reflect.AnnotationPredicates.elementAnnotatedWith;
2222

23-
class LifecycleMatcher extends AbstractMatcher<Binding<?>> {
24-
private final PreDestroyScopingVisitor preDestroyScopingVisitor = new PreDestroyScopingVisitor();
23+
class DestructionMatcher extends AbstractMatcher<Binding<?>> {
24+
private final SingletonScopingVisitor singletonScopingVisitor = new SingletonScopingVisitor();
2525

2626
@Override
2727
public boolean matches(Binding<?> binding) {
2828
Class<?> rawType = binding.getKey().getTypeLiteral().getRawType();
29-
return binding.acceptScopingVisitor(preDestroyScopingVisitor) &&
30-
(isAutoCloseable(rawType) || hasJsr250Methods(rawType));
29+
return binding.acceptScopingVisitor(singletonScopingVisitor) &&
30+
(isAutoCloseable(rawType) || hasPreDestroy(rawType));
3131
}
3232

33-
private boolean hasJsr250Methods(Class<?> rawType) {
33+
private boolean hasPreDestroy(Class<?> rawType) {
3434
return Classes.from(rawType)
3535
.traversingInterfaces()
3636
.traversingSuperclasses()
3737
.methods()
38-
.anyMatch(elementAnnotatedWith(PreDestroy.class, true)
39-
.or(elementAnnotatedWith(PostConstruct.class, true)));
38+
.anyMatch(elementAnnotatedWith(PreDestroy.class, true));
4039
}
4140

4241
private boolean isAutoCloseable(Class<?> rawType) {
4342
return AutoCloseable.class.isAssignableFrom(rawType);
4443
}
4544

46-
private static class PreDestroyScopingVisitor implements BindingScopingVisitor<Boolean> {
45+
private static class SingletonScopingVisitor implements BindingScopingVisitor<Boolean> {
4746
@Override
4847
public Boolean visitEagerSingleton() {
4948
return true;
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@
1212
import static org.seedstack.shed.reflect.ReflectUtils.makeAccessible;
1313

1414
import com.google.inject.spi.ProvisionListener;
15+
1516
import javax.annotation.PostConstruct;
1617
import javax.annotation.PreDestroy;
18+
1719
import org.seedstack.shed.reflect.Classes;
1820

19-
class LifecycleProvisionListener implements ProvisionListener {
21+
class DestructionProvisionListener implements ProvisionListener {
2022
private final LifecycleManager lifecycleManager;
2123

22-
LifecycleProvisionListener(LifecycleManager lifecycleManager) {
24+
DestructionProvisionListener(LifecycleManager lifecycleManager) {
2325
this.lifecycleManager = lifecycleManager;
2426
}
2527

@@ -31,9 +33,6 @@ public <T> void onProvision(ProvisionInvocation<T> provisionInvocation) {
3133
.traversingSuperclasses()
3234
.methods()
3335
.forEach(m -> {
34-
if (elementAnnotatedWith(PostConstruct.class, true).test(m)) {
35-
invoke(makeAccessible(m), provision);
36-
}
3736
if (elementAnnotatedWith(PreDestroy.class, true).test(m)) {
3837
lifecycleManager.registerPreDestroy(provision, m);
3938
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@
99

1010
import com.google.inject.AbstractModule;
1111
import com.google.inject.multibindings.Multibinder;
12-
import java.util.Set;
1312
import org.seedstack.seed.LifecycleListener;
1413

14+
import java.util.Set;
15+
1516
class LifecycleModule extends AbstractModule {
1617
private final Set<Class<? extends LifecycleListener>> lifecycleListenerClasses;
1718
private final LifecycleManager lifecycleManager;
1819

1920
LifecycleModule(Set<Class<? extends LifecycleListener>> lifecycleListenerClasses,
20-
LifecycleManager lifecycleManager) {
21+
LifecycleManager lifecycleManager) {
2122
this.lifecycleListenerClasses = lifecycleListenerClasses;
2223
this.lifecycleManager = lifecycleManager;
2324
}
@@ -31,7 +32,8 @@ protected void configure() {
3132
lifecycleListenerMultibinder.addBinding().to(lifecycleListenerClass);
3233
}
3334

34-
// Listen for singletons having lifecycle annotations or interfaces
35-
bindListener(new LifecycleMatcher(), new LifecycleProvisionListener(lifecycleManager));
35+
// Bind lifecycle provision listeners
36+
bindListener(new ConstructionMatcher(), new ConstructionProvisionListener());
37+
bindListener(new DestructionMatcher(), new DestructionProvisionListener(lifecycleManager));
3638
}
3739
}

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

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,18 @@
77
*/
88
package org.seedstack.seed.core;
99

10-
import static org.assertj.core.api.Assertions.assertThat;
11-
import static org.assertj.core.api.Assertions.fail;
12-
1310
import com.google.inject.ConfigurationException;
1411
import com.google.inject.CreationException;
1512
import com.google.inject.Injector;
1613
import com.google.inject.Module;
17-
import java.lang.annotation.Retention;
18-
import java.lang.annotation.RetentionPolicy;
19-
import java.lang.reflect.Method;
20-
import java.util.function.Predicate;
21-
import javax.inject.Inject;
22-
import javax.inject.Named;
2314
import org.aopalliance.intercept.MethodInvocation;
15+
import org.assertj.core.util.Lists;
2416
import org.junit.Test;
2517
import org.junit.runner.RunWith;
2618
import org.seedstack.seed.Bind;
2719
import org.seedstack.seed.Logging;
2820
import org.seedstack.seed.Nullable;
21+
import org.seedstack.seed.Provide;
2922
import org.seedstack.seed.SeedException;
3023
import org.seedstack.seed.SeedInterceptor;
3124
import org.seedstack.seed.core.fixtures.BoundFromInterface;
@@ -49,6 +42,18 @@
4942
import org.slf4j.LoggerFactory;
5043
import some.other.pkg.ForeignClass;
5144

45+
import javax.inject.Inject;
46+
import javax.inject.Named;
47+
import javax.inject.Provider;
48+
import java.lang.annotation.Retention;
49+
import java.lang.annotation.RetentionPolicy;
50+
import java.lang.reflect.Method;
51+
import java.util.List;
52+
import java.util.function.Predicate;
53+
54+
import static org.assertj.core.api.Assertions.assertThat;
55+
import static org.assertj.core.api.Assertions.fail;
56+
5257
@RunWith(SeedITRunner.class)
5358
public class CoreIT {
5459
@Inject
@@ -143,6 +148,21 @@ public void methodInterceptorsAreWorking() {
143148
assertThat(SomeSeedInterceptor.invokedTimes).isEqualTo(1);
144149
}
145150

151+
@Test
152+
public void annotatedProviderIsWorking() {
153+
HolderNominal instance = injector.getInstance(HolderNominal.class);
154+
assertThat(instance.testValue).isEqualTo(Lists.newArrayList("test"));
155+
}
156+
157+
@Provide
158+
@Named("test")
159+
private static class ValueProvider implements Provider<List<String>> {
160+
@Override
161+
public List<String> get() {
162+
return Lists.newArrayList("test");
163+
}
164+
}
165+
146166
@Bind
147167
private static class LoggerHolder {
148168
private static final Logger logger = LoggerFactory.getLogger(LoggerHolder.class);
@@ -203,6 +223,9 @@ private static class HolderNominal {
203223
@Inject
204224
@Dummy
205225
ProvidedInterface<Integer> providedFromInterfaceWithAnnotation;
226+
@Inject
227+
@Named("test")
228+
List<String> testValue;
206229
}
207230

208231
private static class HolderException {

0 commit comments

Comments
 (0)