From 1f7f05e836537820238d45f1a8a38460a86afdba Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Sat, 4 Jan 2025 20:51:17 -0500 Subject: [PATCH 1/5] can inject directly into postconstruct methods --- .../io/avaje/inject/generator/BeanReader.java | 69 ++++++++++++++++--- .../generator/TypeExtendsInjection.java | 6 +- .../inject/generator/TypeExtendsReader.java | 3 +- .../io/avaje/inject/generator/TypeReader.java | 12 ++-- .../models/valid/lifecycle/Minos.java | 18 +++++ .../models/valid/lifecycle/Serpent.java | 15 ++++ 6 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Minos.java create mode 100644 inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Serpent.java diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index d4daf460..94667221 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -29,7 +29,7 @@ final class BeanReader { private final List injectMethods; private final List factoryMethods; private final List observerMethods; - private final Element postConstructMethod; + private final Optional postConstructMethod; private final Element preDestroyMethod; private final ImportTypeMap importTypes = new ImportTypeMap(); @@ -159,6 +159,8 @@ BeanReader read() { factoryMethod.addImports(importTypes); } + postConstructMethod.ifPresent(m -> m.addImports(importTypes)); + conditions.addImports(importTypes); return this; } @@ -236,6 +238,13 @@ Set allGenericTypes() { for (MethodReader factoryMethod : factoryMethods()) { factoryMethod.addDependsOnGeneric(allUTypes); } + + postConstructMethod.ifPresent( + m -> + m.params().stream() + .filter(MethodParam::isGenericParam) + .map(MethodParam::getFullUType) + .forEach(allUTypes::add)); return allUTypes; } @@ -260,7 +269,7 @@ String metaKey() { * Return true if lifecycle via annotated methods is required. */ boolean hasLifecycleMethods() { - return (postConstructMethod != null || preDestroyMethod != null || typeReader.isClosable()); + return (postConstructMethod.isPresent() || preDestroyMethod != null || typeReader.isClosable()); } List createFactoryMethodMeta() { @@ -322,9 +331,10 @@ void buildRegister(Append writer) { void addLifecycleCallbacks(Append writer, String indent) { - if (postConstructMethod != null && !registerProvider()) { - writer.indent(indent).append(" builder.addPostConstruct($bean::%s);", postConstructMethod.getSimpleName()).eol(); + if (postConstructMethod.isPresent() && !registerProvider()) { + writePostConstruct(writer, indent, postConstructMethod.get()); } + if (preDestroyMethod != null) { lifeCycleNotSupported("@PreDestroy"); var priority = preDestroyPriority == null || preDestroyPriority == 1000 ? "" : ", " + preDestroyPriority; @@ -334,17 +344,54 @@ void addLifecycleCallbacks(Append writer, String indent) { } } + private void writePostConstruct(Append writer, String indent, MethodReader postConstruct) { + + writer.indent(indent).append(" builder.addPostConstruct("); + final var simplename = postConstruct.name(); + + final var params = postConstruct.params(); + if (params.isEmpty() || Constants.BEANSCOPE.equals(params.get(0).getFullUType().shortType())) { + writer.append("$bean::%s);", simplename).eol(); + } else { + writer.append("b -> $bean.%s(", simplename); + + writeLifeCycleGet(writer, params, "b", "b"); + writer.append(");").eol(); + } + } + void prototypePostConstruct(Append writer, String indent) { - if (postConstructMethod != null) { - var postConstruct = (ExecutableElement) postConstructMethod; - writer.indent(indent).append(" bean.%s(", postConstructMethod.getSimpleName()); - if (postConstruct.getParameters().isEmpty()) { - writer.append(");").eol(); + postConstructMethod.ifPresent( + m -> { + writer.indent(indent).append(" bean.%s(", m.name()); + if (m.params().isEmpty()) { + writer.append(");").eol(); + } else { + writeLifeCycleGet( + writer, m.params(), "builder", "builder.get(io.avaje.inject.BeanScope.class)"); + writer.append(";").eol(); + } + writer.eol(); + }); + } + + private void writeLifeCycleGet( + Append writer, final List params, String builderName, String beanScopeString) { + final var size = params.size(); + for (int i = 0; i < size; i++) { + final var param = params.get(i); + + if (Constants.BEANSCOPE.equals(param.getFullUType().fullWithoutAnnotations())) { + writer.append(beanScopeString); } else { - writer.append("builder.get(io.avaje.inject.BeanScope.class));").eol(); + param.builderGetDependency(writer, builderName); + } + + if (i + 1 != size) { + writer.append(", "); } - writer.eol(); } + writer.append(")"); } private void lifeCycleNotSupported(String lifecycle) { diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java index 5f3e6718..073cd15b 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsInjection.java @@ -30,7 +30,7 @@ final class TypeExtendsInjection { private final TypeElement baseType; private final boolean factory; private final List typeAspects; - private Element postConstructMethod; + private Optional postConstructMethod = Optional.empty(); private Element preDestroyMethod; private Integer preDestroyPriority; @@ -125,7 +125,7 @@ private void readMethod(Element element, TypeElement type) { notInjectMethods.add(methodKey); } if (AnnotationUtil.hasAnnotationWithName(element, "PostConstruct")) { - postConstructMethod = element; + postConstructMethod = Optional.of(new MethodReader(methodElement, type, importTypes).read()); checkAspect = false; } if (AnnotationUtil.hasAnnotationWithName(element, "PreDestroy")) { @@ -206,7 +206,7 @@ List observerMethods() { return observerMethods; } - Element postConstructMethod() { + Optional postConstructMethod() { return postConstructMethod; } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsReader.java index 732e216a..f1cb9166 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/TypeExtendsReader.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -101,7 +102,7 @@ List observerMethods() { return extendsInjection.observerMethods(); } - Element postConstructMethod() { + Optional postConstructMethod() { return extendsInjection.postConstructMethod(); } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java index ab90f310..e7e14975 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/TypeReader.java @@ -1,14 +1,14 @@ package io.avaje.inject.generator; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; +import static java.util.stream.Collectors.toList; import java.util.List; import java.util.Optional; import java.util.Set; -import static java.util.stream.Collectors.toList; + +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeKind; final class TypeReader { @@ -107,7 +107,7 @@ List observerMethods() { return extendsReader.observerMethods(); } - Element postConstructMethod() { + Optional postConstructMethod() { return extendsReader.postConstructMethod(); } diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Minos.java b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Minos.java new file mode 100644 index 00000000..8b811862 --- /dev/null +++ b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Minos.java @@ -0,0 +1,18 @@ +package io.avaje.inject.generator.models.valid.lifecycle; + +import java.util.function.Consumer; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.PostConstruct; +import jakarta.inject.Singleton; + +@Singleton +public class Minos { + + @PostConstruct + void prepareThyself(Serpent serpent, Consumer c, BeanScope b) {} + + // @PreDestroy + // void thyEndIsNow() { + // } +} diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Serpent.java b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Serpent.java new file mode 100644 index 00000000..05eda842 --- /dev/null +++ b/inject-generator/src/test/java/io/avaje/inject/generator/models/valid/lifecycle/Serpent.java @@ -0,0 +1,15 @@ +package io.avaje.inject.generator.models.valid.lifecycle; + +import java.util.function.Consumer; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.PostConstruct; +import io.avaje.inject.Prototype; + +@Prototype +public class Serpent { + + @PostConstruct + void hiss(Consumer c, BeanScope b) {} + +} From 502f08487a44b439cddd079d746a281ab1ff29ea Mon Sep 17 00:00:00 2001 From: Josiah Noel <32279667+SentryMan@users.noreply.github.com> Date: Mon, 6 Jan 2025 17:30:01 -0500 Subject: [PATCH 2/5] Update BeanReader.java --- .../main/java/io/avaje/inject/generator/BeanReader.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index 94667221..ae413d4a 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -54,10 +54,15 @@ final class BeanReader { this.beanType = beanType; this.type = beanType.getQualifiedName().toString(); this.shortName = shortName(beanType); - this.prototype = PrototypePrism.isPresent(beanType); + this.prototype = + PrototypePrism.isPresent(beanType) + || importedComponent && ProcessingContext.isImportedPrototype(beanType); this.primary = PrimaryPrism.isPresent(beanType); this.secondary = !primary && SecondaryPrism.isPresent(beanType); - this.lazy = !FactoryPrism.isPresent(beanType) && LazyPrism.isPresent(beanType); + this.lazy = + !FactoryPrism.isPresent(beanType) + && (LazyPrism.isPresent(beanType) + || importedComponent && ProcessingContext.isImportedLazy(beanType)); final var beantypes = BeanTypesPrism.getOptionalOn(beanType); beantypes.ifPresent(p -> Util.validateBeanTypes(beanType, p.value())); this.typeReader = From e78b8ffef70fd22b4d99187bbb1941e2a40bc244 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 7 Jan 2025 12:33:44 +1300 Subject: [PATCH 3/5] Add tests for @PostConstruct argument options --- .../org/example/myapp/config/LifeFour.java | 18 +++++++++++++ .../org/example/myapp/config/LifeOne.java | 17 +++++++++++++ .../org/example/myapp/config/LifeThree.java | 16 ++++++++++++ .../org/example/myapp/config/LifeTwo.java | 18 +++++++++++++ .../config/PostConstructParametersTest.java | 25 +++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 blackbox-test-inject/src/main/java/org/example/myapp/config/LifeFour.java create mode 100644 blackbox-test-inject/src/main/java/org/example/myapp/config/LifeOne.java create mode 100644 blackbox-test-inject/src/main/java/org/example/myapp/config/LifeThree.java create mode 100644 blackbox-test-inject/src/main/java/org/example/myapp/config/LifeTwo.java create mode 100644 blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeFour.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeFour.java new file mode 100644 index 00000000..e055fb0e --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeFour.java @@ -0,0 +1,18 @@ +package org.example.myapp.config; + +import io.avaje.inject.PostConstruct; +import jakarta.inject.Named; +import jakarta.inject.Singleton; + +@Singleton +public class LifeFour { + + public String _state; + + @PostConstruct + void post(@Named("foo") LifeOne one, LifeTwo two) { + _state = "post|" + + (one != null ? "one|" : "") + + (two != null ? "two" : ""); + } +} diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeOne.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeOne.java new file mode 100644 index 00000000..94ec1bbe --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeOne.java @@ -0,0 +1,17 @@ +package org.example.myapp.config; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.PostConstruct; +import jakarta.inject.Singleton; + +@Singleton +public class LifeOne { + + public String _state; + + @PostConstruct + void post(BeanScope scope) { + _state = "post|" + + (scope != null ? "scope" : ""); + } +} diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeThree.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeThree.java new file mode 100644 index 00000000..91d9b393 --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeThree.java @@ -0,0 +1,16 @@ +package org.example.myapp.config; + +import io.avaje.inject.PostConstruct; +import jakarta.inject.Singleton; + +@Singleton +public class LifeThree { + + public String _state; + + @PostConstruct + void post(LifeOne one) { + _state = "post|" + + (one != null ? "one" : ""); + } +} diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeTwo.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeTwo.java new file mode 100644 index 00000000..ebe8ec74 --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeTwo.java @@ -0,0 +1,18 @@ +package org.example.myapp.config; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.PostConstruct; +import jakarta.inject.Singleton; + +@Singleton +public class LifeTwo { + + public String _state; + + @PostConstruct + void post(LifeOne one, BeanScope scope) { + _state = "post|" + + (one != null ? "one|" : "") + + (scope != null ? "scope" : ""); + } +} diff --git a/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java b/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java new file mode 100644 index 00000000..1fcdc365 --- /dev/null +++ b/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java @@ -0,0 +1,25 @@ +package org.example.myapp.config; + +import io.avaje.inject.BeanScope; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PostConstructParametersTest { + + @Test + void factorySecondaryAsProvider() { + try (BeanScope testScope = BeanScope.builder().build()) { + var one = testScope.get(LifeOne.class); + var two = testScope.get(LifeTwo.class); + var three = testScope.get(LifeThree.class); + var four = testScope.get(LifeFour.class); + + assertThat(one._state).isEqualTo("post|scope"); + assertThat(two._state).isEqualTo("post|one|scope"); + assertThat(three._state).isEqualTo("post|one"); + assertThat(four._state).isEqualTo("post|one|two"); + } + } + +} From 4011c7aea8d2d435fbab28d3dd40fc51c7a680e0 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 7 Jan 2025 12:42:15 +1300 Subject: [PATCH 4/5] Adjust writeLifeCycleGet() and format changes --- .../io/avaje/inject/generator/BeanReader.java | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index ae413d4a..41a72af9 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -10,7 +10,6 @@ import java.util.stream.Stream; import javax.lang.model.element.Element; -import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; @@ -244,12 +243,11 @@ Set allGenericTypes() { factoryMethod.addDependsOnGeneric(allUTypes); } - postConstructMethod.ifPresent( - m -> - m.params().stream() - .filter(MethodParam::isGenericParam) - .map(MethodParam::getFullUType) - .forEach(allUTypes::add)); + postConstructMethod.ifPresent(m -> + m.params().stream() + .filter(MethodParam::isGenericParam) + .map(MethodParam::getFullUType) + .forEach(allUTypes::add)); return allUTypes; } @@ -335,7 +333,6 @@ void buildRegister(Append writer) { } void addLifecycleCallbacks(Append writer, String indent) { - if (postConstructMethod.isPresent() && !registerProvider()) { writePostConstruct(writer, indent, postConstructMethod.get()); } @@ -350,51 +347,43 @@ void addLifecycleCallbacks(Append writer, String indent) { } private void writePostConstruct(Append writer, String indent, MethodReader postConstruct) { - writer.indent(indent).append(" builder.addPostConstruct("); - final var simplename = postConstruct.name(); - + final var methodName = postConstruct.name(); final var params = postConstruct.params(); if (params.isEmpty() || Constants.BEANSCOPE.equals(params.get(0).getFullUType().shortType())) { - writer.append("$bean::%s);", simplename).eol(); + writer.append("$bean::%s);", methodName).eol(); } else { - writer.append("b -> $bean.%s(", simplename); - + writer.append("b -> $bean.%s(", methodName); writeLifeCycleGet(writer, params, "b", "b"); writer.append(");").eol(); } } void prototypePostConstruct(Append writer, String indent) { - postConstructMethod.ifPresent( - m -> { - writer.indent(indent).append(" bean.%s(", m.name()); - if (m.params().isEmpty()) { - writer.append(");").eol(); - } else { - writeLifeCycleGet( - writer, m.params(), "builder", "builder.get(io.avaje.inject.BeanScope.class)"); - writer.append(";").eol(); - } - writer.eol(); - }); - } - - private void writeLifeCycleGet( - Append writer, final List params, String builderName, String beanScopeString) { + postConstructMethod.ifPresent(m -> { + writer.indent(indent).append(" bean.%s(", m.name()); + if (m.params().isEmpty()) { + writer.append(");").eol(); + } else { + writeLifeCycleGet(writer, m.params(), "builder", "builder.get(io.avaje.inject.BeanScope.class)"); + writer.append(";").eol(); + } + writer.eol(); + }); + } + + private void writeLifeCycleGet(Append writer, List params, String builderName, String beanScopeString) { final var size = params.size(); for (int i = 0; i < size; i++) { + if (i > 0) { + writer.append(", "); + } final var param = params.get(i); - if (Constants.BEANSCOPE.equals(param.getFullUType().fullWithoutAnnotations())) { writer.append(beanScopeString); } else { param.builderGetDependency(writer, builderName); } - - if (i + 1 != size) { - writer.append(", "); - } } writer.append(")"); } @@ -424,7 +413,7 @@ private Set importTypes() { } } checkImports(); - if (!suppressGeneratedImport){ + if (!suppressGeneratedImport) { importTypes.add(Constants.GENERATED); } if (!suppressBuilderImport && !isGenerateProxy()) { From 65d93215ad4d5c4e9eddcdc4483d1a85cc083b71 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 7 Jan 2025 12:53:09 +1300 Subject: [PATCH 5/5] Add @Prototype scope test and b -> beanScope in generated source --- .../org/example/myapp/config/LifeProtoTwo.java | 18 ++++++++++++++++++ .../config/PostConstructParametersTest.java | 2 ++ .../io/avaje/inject/generator/BeanReader.java | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 blackbox-test-inject/src/main/java/org/example/myapp/config/LifeProtoTwo.java diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeProtoTwo.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeProtoTwo.java new file mode 100644 index 00000000..d7df00b8 --- /dev/null +++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/LifeProtoTwo.java @@ -0,0 +1,18 @@ +package org.example.myapp.config; + +import io.avaje.inject.BeanScope; +import io.avaje.inject.PostConstruct; +import io.avaje.inject.Prototype; + +@Prototype +public class LifeProtoTwo { + + public String _state; + + @PostConstruct + void post(LifeOne one, BeanScope scope) { + _state = "post|" + + (one != null ? "one|" : "") + + (scope != null ? "scope" : ""); + } +} diff --git a/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java b/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java index 1fcdc365..816956fa 100644 --- a/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java +++ b/blackbox-test-inject/src/test/java/org/example/myapp/config/PostConstructParametersTest.java @@ -14,11 +14,13 @@ void factorySecondaryAsProvider() { var two = testScope.get(LifeTwo.class); var three = testScope.get(LifeThree.class); var four = testScope.get(LifeFour.class); + var protoTwo = testScope.get(LifeProtoTwo.class); assertThat(one._state).isEqualTo("post|scope"); assertThat(two._state).isEqualTo("post|one|scope"); assertThat(three._state).isEqualTo("post|one"); assertThat(four._state).isEqualTo("post|one|two"); + assertThat(protoTwo._state).isEqualTo("post|one|scope"); } } diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java index 41a72af9..32b66f2b 100644 --- a/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java +++ b/inject-generator/src/main/java/io/avaje/inject/generator/BeanReader.java @@ -353,8 +353,8 @@ private void writePostConstruct(Append writer, String indent, MethodReader postC if (params.isEmpty() || Constants.BEANSCOPE.equals(params.get(0).getFullUType().shortType())) { writer.append("$bean::%s);", methodName).eol(); } else { - writer.append("b -> $bean.%s(", methodName); - writeLifeCycleGet(writer, params, "b", "b"); + writer.append("beanScope -> $bean.%s(", methodName); + writeLifeCycleGet(writer, params, "beanScope", "beanScope"); writer.append(");").eol(); } }