Repository: avaje/avaje-inject
Pull request #754 (2025-01-06 03:57:20)

#749 Add support for chained methods in @Bean(destroyMethod)
For example: `@Bean(destroyMethod = "reaper().stop()")`


Patch:
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..58cb8f23 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
@@ -321,7 +321,6 @@ final class BeanReader {
   }
 
   void addLifecycleCallbacks(Append writer, String indent) {
-
     if (postConstructMethod != null && !registerProvider()) {
       writer.indent(indent).append(" builder.addPostConstruct($bean::%s);", postConstructMethod.getSimpleName()).eol();
     }
diff --git a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
index 2dc58963..822c333e 100644
--- a/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
+++ b/inject-generator/src/main/java/io/avaje/inject/generator/MethodReader.java
@@ -287,7 +287,7 @@ final class MethodReader {
       if (hasInitMethod) {
         var addPostConstruct =
           multiRegister
-            ? "    .peek(b -> builder.addPostConstruct(b::%s))"
+            ? "    .peek($bean -> builder.addPostConstruct($bean::%s))"
             : "builder.addPostConstruct($bean::%s);";
         writer.indent(indent).append(addPostConstruct, initMethod).eol();
       }
@@ -296,14 +296,14 @@ final class MethodReader {
       if (notEmpty(destroyMethod)) {
         var addPreDestroy =
           multiRegister
-            ? "    .forEach(b -> builder.addPreDestroy(b::%s%s));"
-            : "builder.addPreDestroy($bean::%s%s);";
-        writer.indent(indent).append(addPreDestroy, destroyMethod, priority).eol();
+            ? "    .forEach($bean -> builder.addPreDestroy(%s%s));"
+            : "builder.addPreDestroy(%s%s);";
+        writer.indent(indent).append(addPreDestroy, addPreDestroy(destroyMethod), priority).eol();
 
       } else if (typeReader != null && typeReader.isClosable()) {
         var addPreDestroy =
           multiRegister
-            ? "    .forEach(b -> builder.addPreDestroy(b::close%s));"
+            ? "    .forEach($bean -> builder.addPreDestroy($bean::close%s));"
             : "builder.addPreDestroy($bean::close%s);";
         writer.indent(indent).append(addPreDestroy, priority).eol();
 
@@ -324,6 +324,13 @@ final class MethodReader {
     }
   }
 
+  static String addPreDestroy(String destroyMethod) {
+    if (!destroyMethod.contains(".")) {
+      return "$bean::" + destroyMethod;
+    }
+    return "() -> $bean." + destroyMethod;
+  }
+
   private boolean hasLifecycleMethods() {
     return notEmpty(initMethod) || notEmpty(destroyMethod) || (typeReader != null && typeReader.isClosable() || beanCloseable);
   }


Test patch:
diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/MyNestedDestroy.java b/blackbox-test-inject/src/main/java/org/example/myapp/MyNestedDestroy.java
new file mode 100644
index 00000000..6a70323a
--- /dev/null
+++ b/blackbox-test-inject/src/main/java/org/example/myapp/MyNestedDestroy.java
@@ -0,0 +1,29 @@
+package org.example.myapp;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class MyNestedDestroy {
+
+  public static AtomicInteger started = new AtomicInteger();
+  public static AtomicInteger stopped = new AtomicInteger();
+
+  public static void reset() {
+    started.set(0);
+    stopped.set(0);
+  }
+
+  public void start() {
+    started.incrementAndGet();
+  }
+
+  public Reaper reaper() {
+    return new Reaper();
+  }
+
+  public static class Reaper {
+
+    public void stop() {
+      stopped.incrementAndGet();
+    }
+  }
+}
diff --git a/blackbox-test-inject/src/main/java/org/example/myapp/config/AFactory.java b/blackbox-test-inject/src/main/java/org/example/myapp/config/AFactory.java
index ccf5f826..61f2ce69 100644
--- a/blackbox-test-inject/src/main/java/org/example/myapp/config/AFactory.java
+++ b/blackbox-test-inject/src/main/java/org/example/myapp/config/AFactory.java
@@ -2,12 +2,18 @@ package org.example.myapp.config;
 
 import io.avaje.inject.Bean;
 import io.avaje.inject.Factory;
+import org.example.myapp.MyNestedDestroy;
 
 import java.io.IOException;
 
 @Factory
 class AFactory {
 
+  @Bean(initMethod = "start", destroyMethod = "reaper().stop()")
+  MyNestedDestroy lifecycle2() {
+    return new MyNestedDestroy();
+  }
+
   @Bean
   A0.Builder build0() {
     return new I0();
diff --git a/blackbox-test-inject/src/test/java/org/example/myapp/HelloServiceTest.java b/blackbox-test-inject/src/test/java/org/example/myapp/HelloServiceTest.java
index 359edb3f..061b9fd3 100644
--- a/blackbox-test-inject/src/test/java/org/example/myapp/HelloServiceTest.java
+++ b/blackbox-test-inject/src/test/java/org/example/myapp/HelloServiceTest.java
@@ -14,6 +14,17 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 
 class HelloServiceTest {
 
+  @Test
+  void lifecycles() {
+    MyNestedDestroy.reset();
+    try (BeanScope beanScope = BeanScope.builder().build()) {
+      assertThat(beanScope.get(MyNestedDestroy.class)).isNotNull();
+      assertThat(MyNestedDestroy.started.get()).isEqualTo(1);
+      assertThat(MyNestedDestroy.stopped.get()).isEqualTo(0);
+    }
+    assertThat(MyNestedDestroy.stopped.get()).isEqualTo(1);
+  }
+
   /**
    * No mocking, no use of <code>@TestScope</code> so just like main.
    */
diff --git a/blackbox-test-inject/src/test/java/org/example/myapp/testconfig/CountTestScopeStart.java b/blackbox-test-inject/src/test/java/org/example/myapp/testconfig/CountTestScopeStart.java
index ef912000..45e97a7d 100644
--- a/blackbox-test-inject/src/test/java/org/example/myapp/testconfig/CountTestScopeStart.java
+++ b/blackbox-test-inject/src/test/java/org/example/myapp/testconfig/CountTestScopeStart.java
@@ -13,6 +13,5 @@ public class CountTestScopeStart {
 
   public void onStop() {
     stopped.incrementAndGet();
-    System.out.println("STOPPED !! ");
   }
 }
diff --git a/inject-generator/src/test/java/io/avaje/inject/generator/MethodReaderTest.java b/inject-generator/src/test/java/io/avaje/inject/generator/MethodReaderTest.java
new file mode 100644
index 00000000..4e269e2b
--- /dev/null
+++ b/inject-generator/src/test/java/io/avaje/inject/generator/MethodReaderTest.java
@@ -0,0 +1,19 @@
+package io.avaje.inject.generator;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class MethodReaderTest {
+
+  @Test
+  void addPreDestroy() {
+    assertThat(MethodReader.addPreDestroy("close")).isEqualTo("$bean::close");
+    assertThat(MethodReader.addPreDestroy("foo")).isEqualTo("$bean::foo");
+  }
+
+  @Test
+  void addPreDestroyNested() {
+    assertThat(MethodReader.addPreDestroy("foo().bar()")).isEqualTo("() -> $bean.foo().bar()");
+  }
+}