Skip to content

Commit 4c6a5e5

Browse files
committed
BeanScope PostContruct
1 parent 16f682c commit 4c6a5e5

File tree

6 files changed

+72
-32
lines changed

6 files changed

+72
-32
lines changed

blackbox-test-inject/src/main/java/org/example/myapp/HelloService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.example.myapp;
22

3+
import io.avaje.inject.BeanScope;
34
import io.avaje.inject.PostConstruct;
45
import io.avaje.inject.PreDestroy;
56
import jakarta.inject.Singleton;
@@ -86,7 +87,7 @@ public String justRunResult() {
8687
}
8788

8889
@PostConstruct
89-
void postCon() {
90+
void postCon(BeanScope scope) {
9091

9192
}
9293

inject/src/main/java/io/avaje/inject/BeanScopeBuilder.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,13 @@ default <D> BeanScopeBuilder provideDefault(Type type, Supplier<D> provider) {
220220
*/
221221
BeanScopeBuilder addPostConstructHooks(Runnable... runnables);
222222

223+
/**
224+
* Adds hook that will execute after this scope is built.
225+
*
226+
* @param consumer the PostConstruct hook to add to the bean scope
227+
*/
228+
BeanScopeBuilder addPostConstructConsumerHook(Consumer<BeanScope> consumer);
229+
223230
/**
224231
* Adds hooks that will execute before this scope is destroyed.
225232
*
@@ -429,5 +436,4 @@ interface ForTesting extends BeanScopeBuilder {
429436
*/
430437
<D> BeanScopeBuilder.ForTesting spy(Class<D> type, Consumer<D> consumer);
431438
}
432-
433439
}

inject/src/main/java/io/avaje/inject/DBeanScopeBuilder.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ final class DBeanScopeBuilder implements BeanScopeBuilder.ForTesting {
2727
private final List<EnrichBean> enrichBeans = new ArrayList<>();
2828
private final Set<Module> includeModules = new LinkedHashSet<>();
2929
private final List<Runnable> postConstructList = new ArrayList<>();
30+
private final List<Consumer<BeanScope>> postConstructConsumerList = new ArrayList<>();
3031
private final List<AutoCloseable> preDestroyList = new ArrayList<>();
3132
private BeanScope parent;
3233
private boolean parentOverride = true;
@@ -73,7 +74,7 @@ public PropertyRequiresPlugin propertyPlugin() {
7374

7475
@Override
7576
public BeanScopeBuilder beans(Object... beans) {
76-
for (Object bean : beans) {
77+
for (final Object bean : beans) {
7778
suppliedBeans.add(SuppliedBean.of(superOf(bean.getClass()), bean));
7879
}
7980
return this;
@@ -114,6 +115,12 @@ public BeanScopeBuilder addPostConstructHooks(Runnable... postConstructRunnable)
114115
return this;
115116
}
116117

118+
@Override
119+
public BeanScopeBuilder addPostConstructConsumerHook(Consumer<BeanScope> postConstructConsumer) {
120+
this.postConstructConsumerList.add(postConstructConsumer);
121+
return this;
122+
}
123+
117124
@Override
118125
public BeanScopeBuilder addPreDestroyHooks(AutoCloseable... closables) {
119126
Collections.addAll(this.preDestroyList, closables);
@@ -200,14 +207,14 @@ private boolean detectAvajeConfig() {
200207
try {
201208
Class.forName("io.avaje.config.Configuration", false, classLoader);
202209
return true;
203-
} catch (ClassNotFoundException e) {
210+
} catch (final ClassNotFoundException e) {
204211
return false;
205212
}
206213
}
207214

208215
@Override
209216
public BeanScope build() {
210-
var start = System.currentTimeMillis();
217+
final var start = System.currentTimeMillis();
211218
// load and apply plugins first
212219
initClassLoader();
213220
if (propertyRequiresPlugin == null) {
@@ -216,12 +223,12 @@ public BeanScope build() {
216223

217224
ServiceLoader.load(Plugin.class, classLoader).forEach(plugin -> plugin.apply(this));
218225
// sort factories by dependsOn
219-
FactoryOrder factoryOrder = new FactoryOrder(parent, includeModules, !suppliedBeans.isEmpty());
226+
final FactoryOrder factoryOrder = new FactoryOrder(parent, includeModules, !suppliedBeans.isEmpty());
220227
if (factoryOrder.isEmpty()) {
221228
ServiceLoader.load(Module.class, classLoader).forEach(factoryOrder::add);
222229
}
223230

224-
Set<String> moduleNames = factoryOrder.orderFactories();
231+
final Set<String> moduleNames = factoryOrder.orderFactories();
225232
if (moduleNames.isEmpty()) {
226233
throw new IllegalStateException("No modules found. When using java module system we need an explicit provides clause in module-info like:\n\n" +
227234
" provides io.avaje.inject.spi.Module with org.example.ExampleModule;\n\n" +
@@ -231,12 +238,13 @@ public BeanScope build() {
231238
}
232239
log.log(DEBUG, "building with modules {0}", moduleNames);
233240

234-
Builder builder = Builder.newBuilder(propertyRequiresPlugin, suppliedBeans, enrichBeans, parent, parentOverride);
235-
for (Module factory : factoryOrder.factories()) {
241+
final Builder builder = Builder.newBuilder(propertyRequiresPlugin, suppliedBeans, enrichBeans, parent, parentOverride);
242+
for (final Module factory : factoryOrder.factories()) {
236243
factory.build(builder);
237244
}
238245

239246
postConstructList.forEach(builder::addPostConstruct);
247+
postConstructConsumerList.forEach(builder::addPostConstruct);
240248
preDestroyList.forEach(builder::addPreDestroy);
241249
return builder.build(shutdownHook, start);
242250
}
@@ -245,7 +253,7 @@ public BeanScope build() {
245253
* Return the type that we map the supplied bean to.
246254
*/
247255
private static Class<?> superOf(Class<?> suppliedClass) {
248-
Class<?> suppliedSuper = suppliedClass.getSuperclass();
256+
final Class<?> suppliedSuper = suppliedClass.getSuperclass();
249257
if (Object.class.equals(suppliedSuper)) {
250258
return suppliedClass;
251259
} else {
@@ -272,13 +280,13 @@ static class FactoryOrder {
272280
this.parent = parent;
273281
this.factories.addAll(includeModules);
274282
this.suppliedBeans = suppliedBeans;
275-
for (Module includeModule : includeModules) {
283+
for (final Module includeModule : includeModules) {
276284
moduleNames.add(includeModule.getClass().getName());
277285
}
278286
}
279287

280288
void add(Module module) {
281-
FactoryState factoryState = new FactoryState(module);
289+
final FactoryState factoryState = new FactoryState(module);
282290
providesMap.computeIfAbsent(module.getClass().getTypeName(), s -> new FactoryList()).add(factoryState);
283291
addFactoryProvides(factoryState, module.provides());
284292
addFactoryProvides(factoryState, module.autoProvides());
@@ -299,7 +307,7 @@ void add(Module module) {
299307
}
300308

301309
private void addFactoryProvides(FactoryState factoryState, Class<?>[] provides) {
302-
for (Class<?> feature : provides) {
310+
for (final Class<?> feature : provides) {
303311
providesMap.computeIfAbsent(feature.getTypeName(), s -> new FactoryList()).add(factoryState);
304312
}
305313
}
@@ -319,7 +327,7 @@ private void push(FactoryState factory) {
319327
Set<String> orderFactories() {
320328
// push the 'no dependency' modules after the 'provides only' ones
321329
// as this is more intuitive for the simple (only provides modules case)
322-
for (FactoryState factoryState : queueNoDependencies) {
330+
for (final FactoryState factoryState : queueNoDependencies) {
323331
push(factoryState);
324332
}
325333
processQueue();
@@ -345,12 +353,12 @@ private void processQueue() {
345353
if (suppliedBeans) {
346354
// just push everything left assuming supplied beans
347355
// will satisfy the required dependencies
348-
for (FactoryState factoryState : queue) {
356+
for (final FactoryState factoryState : queue) {
349357
push(factoryState);
350358
}
351359
} else if (!queue.isEmpty()) {
352-
StringBuilder sb = new StringBuilder();
353-
for (FactoryState factory : queue) {
360+
final StringBuilder sb = new StringBuilder();
361+
for (final FactoryState factory : queue) {
354362
sb.append("Module [").append(factory).append("] has unsatisfied");
355363
unsatisfiedRequires(sb, factory.requires(), "requires");
356364
unsatisfiedRequires(sb, factory.requiresPackages(), "requiresPackages");
@@ -366,7 +374,7 @@ private void processQueue() {
366374
}
367375

368376
private void unsatisfiedRequires(StringBuilder sb, Class<?>[] requiredType, String requires) {
369-
for (Class<?> depModuleName : requiredType) {
377+
for (final Class<?> depModuleName : requiredType) {
370378
if (notProvided(depModuleName.getTypeName())) {
371379
sb.append(String.format(" %s [%s]", requires, depModuleName.getTypeName()));
372380
}
@@ -377,7 +385,7 @@ private boolean notProvided(String dependency) {
377385
if (parent != null && parent.contains(dependency)) {
378386
return false;
379387
}
380-
FactoryList factories = providesMap.get(dependency);
388+
final FactoryList factories = providesMap.get(dependency);
381389
return (factories == null || !factories.allPushed());
382390
}
383391

@@ -389,9 +397,9 @@ private boolean notProvided(String dependency) {
389397
*/
390398
private int processQueuedFactories() {
391399
int count = 0;
392-
Iterator<FactoryState> it = queue.iterator();
400+
final Iterator<FactoryState> it = queue.iterator();
393401
while (it.hasNext()) {
394-
FactoryState factory = it.next();
402+
final FactoryState factory = it.next();
395403
if (satisfiedDependencies(factory)) {
396404
// push the factory onto the build order
397405
it.remove();
@@ -503,7 +511,7 @@ void add(FactoryState factory) {
503511
* Return true if all factories here have been pushed onto the build order.
504512
*/
505513
boolean allPushed() {
506-
for (FactoryState factory : factories) {
514+
for (final FactoryState factory : factories) {
507515
if (!factory.isPushed()) {
508516
return false;
509517
}

inject/src/main/java/io/avaje/inject/spi/Builder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ static Builder newBuilder(PropertyRequiresPlugin plugin, List<SuppliedBean> supp
9595
*/
9696
void addPostConstruct(Runnable runnable);
9797

98+
/**
99+
* Add lifecycle PostConstruct method.
100+
*/
101+
void addPostConstruct(Consumer<BeanScope> consumer);
102+
98103
/**
99104
* Add lifecycle PreDestroy method.
100105
*/

inject/src/main/java/io/avaje/inject/spi/DBeanScope.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.lang.reflect.Type;
1313
import java.util.*;
1414
import java.util.concurrent.locks.ReentrantLock;
15+
import java.util.function.Consumer;
1516
import java.util.stream.Collectors;
1617

1718
import static java.lang.System.Logger.Level.INFO;
@@ -24,16 +25,24 @@ final class DBeanScope implements BeanScope {
2425

2526
private final ReentrantLock lock = new ReentrantLock();
2627
private final List<Runnable> postConstruct;
28+
private final List<Consumer<BeanScope>> postConstructConsumers;
2729
private final List<AutoCloseable> preDestroy;
2830
private final DBeanMap beans;
2931
private final ShutdownHook shutdownHook;
3032
private final BeanScope parent;
3133
private boolean shutdown;
3234
private boolean closed;
3335

34-
DBeanScope(boolean withShutdownHook, List<AutoCloseable> preDestroy, List<Runnable> postConstruct, DBeanMap beans, BeanScope parent) {
36+
DBeanScope(
37+
boolean withShutdownHook,
38+
List<AutoCloseable> preDestroy,
39+
List<Runnable> postConstruct,
40+
List<Consumer<BeanScope>> postConstructConsumers,
41+
DBeanMap beans,
42+
BeanScope parent) {
3543
this.preDestroy = preDestroy;
3644
this.postConstruct = postConstruct;
45+
this.postConstructConsumers = postConstructConsumers;
3746
this.beans = beans;
3847
this.parent = parent;
3948
if (withShutdownHook) {
@@ -51,7 +60,7 @@ public String toString() {
5160

5261
@Override
5362
public List<BeanEntry> all() {
54-
IdentityHashMap<DContextEntryBean, DEntry> map = new IdentityHashMap<>();
63+
final IdentityHashMap<DContextEntryBean, DEntry> map = new IdentityHashMap<>();
5564
if (parent != null) {
5665
((DBeanScope) parent).addAll(map);
5766
}
@@ -224,9 +233,12 @@ DBeanScope start(long start) {
224233
lock.lock();
225234
try {
226235
log.log(TRACE, "firing postConstruct");
227-
for (Runnable invoke : postConstruct) {
236+
for (final var invoke : postConstruct) {
228237
invoke.run();
229238
}
239+
for (final var consumer : postConstructConsumers) {
240+
consumer.accept(this);
241+
}
230242
} finally {
231243
lock.unlock();
232244
}
@@ -245,10 +257,10 @@ public void close() {
245257
// we only allow one call to preDestroy
246258
closed = true;
247259
log.log(TRACE, "firing preDestroy");
248-
for (AutoCloseable closeable : preDestroy) {
260+
for (final AutoCloseable closeable : preDestroy) {
249261
try {
250262
closeable.close();
251-
} catch (Exception e) {
263+
} catch (final Exception e) {
252264
log.log(Level.ERROR, "Error during PreDestroy lifecycle method", e);
253265
}
254266
}
@@ -297,9 +309,9 @@ private static class SortBean<T> implements Comparable<SortBean<T>> {
297309
int initPriority(Class<? extends Annotation> priorityAnnotation) {
298310
// Avoid adding hard dependency on javax.annotation-api by using reflection
299311
try {
300-
Annotation ann = bean.getClass().getDeclaredAnnotation(priorityAnnotation);
312+
final Annotation ann = bean.getClass().getDeclaredAnnotation(priorityAnnotation);
301313
if (ann != null) {
302-
int priority = (Integer) priorityAnnotation.getMethod("value").invoke(ann);
314+
final int priority = (Integer) priorityAnnotation.getMethod("value").invoke(ann);
303315
priorityDefined = true;
304316
return priority;
305317
}

inject/src/main/java/io/avaje/inject/spi/DBuilder.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class DBuilder implements Builder {
1717
* List of Lifecycle methods.
1818
*/
1919
private final List<Runnable> postConstruct = new ArrayList<>();
20+
private final List<Consumer<BeanScope>> postConstructConsumers = new ArrayList<>();
2021
private final List<AutoCloseable> preDestroy = new ArrayList<>();
2122
/**
2223
* List of field injection closures.
@@ -62,7 +63,7 @@ public boolean isAddBeanFor(String name, Type... types) {
6263
}
6364
if (parent instanceof DBeanScope) {
6465
// effectively looking for a match in the test scope
65-
DBeanScope dParent = (DBeanScope) parent;
66+
final DBeanScope dParent = (DBeanScope) parent;
6667
parentMatch = dParent.getStrict(name, removeAnnotations(types));
6768
return parentMatch == null;
6869
}
@@ -120,7 +121,7 @@ public final <T> List<T> list(Type type) {
120121

121122
@SuppressWarnings({"unchecked"})
122123
private <T> List<T> listOf(Type type) {
123-
List<T> values = (List<T>) beanMap.all(type);
124+
final List<T> values = (List<T>) beanMap.all(type);
124125
if (parent == null) {
125126
return values;
126127
}
@@ -201,6 +202,11 @@ public final void addPostConstruct(Runnable invoke) {
201202
postConstruct.add(invoke);
202203
}
203204

205+
@Override
206+
public void addPostConstruct(Consumer<BeanScope> consumer) {
207+
postConstructConsumers.add(consumer);
208+
}
209+
204210
@Override
205211
public final void addPreDestroy(AutoCloseable invoke) {
206212
preDestroy.add(invoke);
@@ -399,7 +405,9 @@ private void runInjectors() {
399405
@Override
400406
public final BeanScope build(boolean withShutdownHook, long start) {
401407
runInjectors();
402-
var scope = new DBeanScope(withShutdownHook, preDestroy, postConstruct, beanMap, parent);
408+
final var scope =
409+
new DBeanScope(
410+
withShutdownHook, preDestroy, postConstruct, postConstructConsumers, beanMap, parent);
403411
if (beanScopeProxy != null) {
404412
beanScopeProxy.inject(scope);
405413
}

0 commit comments

Comments
 (0)