This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 951d29d0451 Add lazy option to @BindToRegistry (#15361)
951d29d0451 is described below

commit 951d29d0451f5a3c28ef862a6f6dd6f28caced95
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 29 10:32:44 2024 +0200

    Add lazy option to @BindToRegistry (#15361)
    
    * CAMEL-21137: camel-core - Add lazy option to @BindToRegistry
---
 .../main/java/org/apache/camel/BindToRegistry.java | 10 ++-
 .../CamelDependencyInjectionAnnotationFactory.java |  5 +-
 .../impl/engine/DefaultCamelBeanPostProcessor.java | 98 ++++++++++++++++++----
 ...efaultDependencyInjectionAnnotationFactory.java | 15 +++-
 .../impl/BindToRegistryBeanLazyMethodTest.java     | 73 ++++++++++++++++
 .../camel/impl/BindToRegistryBeanLazyTest.java     | 70 ++++++++++++++++
 .../camel/support/scan/PackageScanHelper.java      | 54 ++++++++++--
 .../dsl/java/joor/JavaRoutesBuilderLoader.java     |  4 +-
 .../injection/AnnotationDependencyInjection.java   | 69 +++++++++------
 9 files changed, 342 insertions(+), 56 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java 
b/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
index f60ccbe05d8..7cf2bd2d43f 100644
--- a/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
+++ b/core/camel-api/src/main/java/org/apache/camel/BindToRegistry.java
@@ -26,7 +26,7 @@ import java.lang.annotation.Target;
  * Used for binding a bean to the registry.
  *
  * This annotation is not supported with camel-spring or camel-spring-boot as 
they have their own set of annotations for
- * registering beans in spring bean registry. Instead this annotation is 
intended for Camel standalone such as
+ * registering beans in spring bean registry. Instead, this annotation is 
intended for Camel standalone such as
  * camel-main or camel-quarkus or similar runtimes.
  *
  * If no name is specified then the bean will have its name auto computed 
based on the class name, field name, or method
@@ -43,7 +43,13 @@ public @interface BindToRegistry {
     String value() default "";
 
     /**
-     * Whether to perform bean post processing (dependency injection) on the 
bean
+     * Whether to perform bean post-processing (dependency injection) on the 
bean
      */
     boolean beanPostProcess() default false;
+
+    /**
+     * Whether to create the bean instance lazy (on-demand) instead of 
creating eager. Using lazy can be useful when you
+     * only need to create beans if they are explicit in-use.
+     */
+    boolean lazy() default false;
 }
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
 
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
index 47d2e204248..1eb23532078 100644
--- 
a/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
+++ 
b/core/camel-api/src/main/java/org/apache/camel/spi/CamelDependencyInjectionAnnotationFactory.java
@@ -28,9 +28,12 @@ public interface CamelDependencyInjectionAnnotationFactory {
      * @param  id              the bean id
      * @param  bean            the bean instance
      * @param  beanName        the bean name
+     * @param  beanType        the bean type (optional)
      * @param  beanPostProcess whether bean post processor should be performed
      * @return                 the created task to use for binding the bean
      */
-    Runnable createBindToRegistryFactory(String id, Object bean, String 
beanName, boolean beanPostProcess);
+    Runnable createBindToRegistryFactory(
+            String id, Object bean, Class<?> beanType,
+            String beanName, boolean beanPostProcess);
 
 }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
index 6d05404ddbf..c9b146dc40b 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultCamelBeanPostProcessor.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.List;
 import java.util.function.Function;
+import java.util.function.Supplier;
 
 import org.apache.camel.BeanConfigInject;
 import org.apache.camel.BeanInject;
@@ -33,6 +34,7 @@ import org.apache.camel.DeferredContextBinding;
 import org.apache.camel.EndpointInject;
 import org.apache.camel.Produce;
 import org.apache.camel.PropertyInject;
+import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.CamelBeanPostProcessorInjector;
 import org.apache.camel.support.DefaultEndpoint;
@@ -298,7 +300,7 @@ public class DefaultCamelBeanPostProcessor implements 
CamelBeanPostProcessor, Ca
 
             BindToRegistry bind = field.getAnnotation(BindToRegistry.class);
             if (bind != null) {
-                bindToRegistry(field, bind.value(), bean, beanName, 
bind.beanPostProcess());
+                bindToRegistry(field, bind.value(), bean, beanName, 
bind.beanPostProcess(), false);
             }
         });
     }
@@ -403,7 +405,7 @@ public class DefaultCamelBeanPostProcessor implements 
CamelBeanPostProcessor, Ca
         // bind each method
         methods.forEach(method -> {
             BindToRegistry bind = method.getAnnotation(BindToRegistry.class);
-            bindToRegistry(method, bind.value(), bean, beanName, 
bind.beanPostProcess());
+            bindToRegistry(method, bind.value(), bean, beanName, 
bind.beanPostProcess(), bind.lazy());
         });
     }
 
@@ -411,7 +413,7 @@ public class DefaultCamelBeanPostProcessor implements 
CamelBeanPostProcessor, Ca
         Class<?> clazz = bean.getClass();
         BindToRegistry ann = clazz.getAnnotation(BindToRegistry.class);
         if (ann != null) {
-            bindToRegistry(clazz, ann.value(), bean, beanName, 
ann.beanPostProcess());
+            bindToRegistry(clazz, ann.value(), bean, beanName, 
ann.beanPostProcess(), ann.lazy());
         }
     }
 
@@ -420,7 +422,7 @@ public class DefaultCamelBeanPostProcessor implements 
CamelBeanPostProcessor, Ca
             BindToRegistry ann = clazz.getAnnotation(BindToRegistry.class);
             if (ann != null) {
                 // it is a nested class so we don't have a bean instance for it
-                bindToRegistry(clazz, ann.value(), null, null, 
ann.beanPostProcess());
+                bindToRegistry(clazz, ann.value(), null, null, 
ann.beanPostProcess(), ann.lazy());
             }
         });
     }
@@ -501,55 +503,115 @@ public class DefaultCamelBeanPostProcessor implements 
CamelBeanPostProcessor, Ca
         }
     }
 
-    private void bindToRegistry(Class<?> clazz, String name, Object bean, 
String beanName, boolean beanPostProcess) {
+    private void bindToRegistry(
+            Class<?> clazz, String name, Object bean, String beanName,
+            boolean beanPostProcess, boolean lazy) {
         if (isEmpty(name)) {
             name = clazz.getSimpleName();
         }
+        boolean postProcess = beanPostProcess;
         if (bean == null) {
-            // no bean so then create an instance from its type
-            bean = getOrLookupCamelContext().getInjector().newInstance(clazz);
+            if (lazy) {
+                postProcess = false; // we do post-processing lazy
+                bean = (Supplier<Object>) () -> {
+                    Object answer = 
getOrLookupCamelContext().getInjector().newInstance(clazz);
+                    if (answer != null && beanPostProcess) {
+                        try {
+                            final CamelBeanPostProcessor beanPostProcessor = 
PluginHelper.getBeanPostProcessor(camelContext);
+                            
beanPostProcessor.postProcessBeforeInitialization(answer, beanName);
+                            
beanPostProcessor.postProcessAfterInitialization(answer, beanName);
+                        } catch (Exception e) {
+                            throw 
RuntimeCamelException.wrapRuntimeException(e);
+                        }
+                    }
+                    return answer;
+                };
+            } else {
+                // no bean so then create an instance from its type
+                bean = 
getOrLookupCamelContext().getInjector().newInstance(clazz);
+            }
         }
-
         if (unbindEnabled) {
             getOrLookupCamelContext().getRegistry().unbind(name);
         }
         // use dependency injection factory to perform the task of binding the 
bean to registry
         Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                .createBindToRegistryFactory(name, bean, beanName, 
beanPostProcess);
+                .createBindToRegistryFactory(name, bean, clazz, beanName, 
postProcess);
         task.run();
     }
 
-    private void bindToRegistry(Field field, String name, Object bean, String 
beanName, boolean beanPostProcess) {
+    private void bindToRegistry(
+            Field field, String name, Object bean, String beanName,
+            boolean beanPostProcess, boolean lazy) {
         if (isEmpty(name)) {
             name = field.getName();
         }
-        Object value = ReflectionHelper.getField(field, bean);
-
+        boolean postProcess = beanPostProcess;
+        Object value;
+        if (lazy) {
+            postProcess = false; // we do post-processing lazy
+            value = (Supplier<Object>) () -> {
+                Object answer = ReflectionHelper.getField(field, bean);
+                if (answer != null && beanPostProcess) {
+                    try {
+                        final CamelBeanPostProcessor beanPostProcessor = 
PluginHelper.getBeanPostProcessor(camelContext);
+                        
beanPostProcessor.postProcessBeforeInitialization(answer, beanName);
+                        
beanPostProcessor.postProcessAfterInitialization(answer, beanName);
+                    } catch (Exception e) {
+                        throw RuntimeCamelException.wrapRuntimeException(e);
+                    }
+                }
+                return answer;
+            };
+        } else {
+            value = ReflectionHelper.getField(field, bean);
+        }
         if (value != null) {
             if (unbindEnabled) {
                 getOrLookupCamelContext().getRegistry().unbind(name);
             }
             // use dependency injection factory to perform the task of binding 
the bean to registry
             Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                    .createBindToRegistryFactory(name, value, beanName, 
beanPostProcess);
+                    .createBindToRegistryFactory(name, value, field.getType(), 
beanName, postProcess);
             task.run();
         }
     }
 
-    private void bindToRegistry(Method method, String name, Object bean, 
String beanName, boolean beanPostProcess) {
+    private void bindToRegistry(
+            Method method, String name, Object bean, String beanName,
+            boolean beanPostProcess, boolean lazy) {
         if (isEmpty(name)) {
             name = method.getName();
         }
-        Object value = getPostProcessorHelper()
-                .getInjectionBeanMethodValue(getOrLookupCamelContext(), 
method, bean, beanName);
-
+        boolean postProcess = beanPostProcess;
+        Object value;
+        if (lazy) {
+            postProcess = false; // we do post-processing lazy
+            value = (Supplier<Object>) () -> {
+                Object answer = getPostProcessorHelper()
+                        
.getInjectionBeanMethodValue(getOrLookupCamelContext(), method, bean, beanName);
+                if (answer != null && beanPostProcess) {
+                    try {
+                        final CamelBeanPostProcessor beanPostProcessor = 
PluginHelper.getBeanPostProcessor(camelContext);
+                        
beanPostProcessor.postProcessBeforeInitialization(answer, beanName);
+                        
beanPostProcessor.postProcessAfterInitialization(answer, beanName);
+                    } catch (Exception e) {
+                        throw RuntimeCamelException.wrapRuntimeException(e);
+                    }
+                }
+                return answer;
+            };
+        } else {
+            value = getPostProcessorHelper()
+                    .getInjectionBeanMethodValue(getOrLookupCamelContext(), 
method, bean, beanName);
+        }
         if (value != null) {
             if (unbindEnabled) {
                 getOrLookupCamelContext().getRegistry().unbind(name);
             }
             // use dependency injection factory to perform the task of binding 
the bean to registry
             Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(getOrLookupCamelContext())
-                    .createBindToRegistryFactory(name, value, beanName, 
beanPostProcess);
+                    .createBindToRegistryFactory(name, value, 
method.getReturnType(), beanName, postProcess);
             task.run();
         }
     }
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
index a2b6fced400..1e1dcf70d11 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultDependencyInjectionAnnotationFactory.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.impl.engine;
 
+import java.util.function.Supplier;
+
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.RuntimeCamelException;
@@ -46,12 +48,13 @@ public class DefaultDependencyInjectionAnnotationFactory
     }
 
     @Override
-    public Runnable createBindToRegistryFactory(String id, Object bean, String 
beanName, boolean beanPostProcess) {
+    @SuppressWarnings("unchecked")
+    public Runnable createBindToRegistryFactory(
+            String id, Object bean, Class<?> beanType, String beanName, 
boolean beanPostProcess) {
         return () -> {
             if (beanPostProcess) {
                 try {
                     final CamelBeanPostProcessor beanPostProcessor = 
PluginHelper.getBeanPostProcessor(camelContext);
-
                     beanPostProcessor.postProcessBeforeInitialization(bean, 
beanName);
                     beanPostProcessor.postProcessAfterInitialization(bean, 
beanName);
                 } catch (Exception e) {
@@ -59,7 +62,13 @@ public class DefaultDependencyInjectionAnnotationFactory
                 }
             }
             CamelContextAware.trySetCamelContext(bean, camelContext);
-            camelContext.getRegistry().bind(id, bean);
+            if (bean instanceof Supplier) {
+                // must be Supplier<Object> to ensure correct binding
+                Supplier<Object> sup = (Supplier<Object>) bean;
+                camelContext.getRegistry().bind(id, beanType, sup);
+            } else {
+                camelContext.getRegistry().bind(id, bean);
+            }
         };
     }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyMethodTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyMethodTest.java
new file mode 100644
index 00000000000..c38e21be9d0
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyMethodTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class BindToRegistryBeanLazyMethodTest extends ContextTestSupport {
+
+    private String hello = "Hello World";
+
+    @BindToRegistry(lazy = false)
+    public FooService myEager() {
+        return new FooService(hello);
+    }
+
+    @BindToRegistry(lazy = true)
+    public FooService myLazy() {
+        return new FooService(hello);
+    }
+
+    @Test
+    public void testLazy() throws Exception {
+        // bean post processing dont run on ContextTestSupport
+        CamelBeanPostProcessor cbpp = 
PluginHelper.getBeanPostProcessor(context);
+        cbpp.postProcessBeforeInitialization(this, "this");
+        cbpp.postProcessAfterInitialization(this, "this");
+
+        // change message which should only affect lazy
+        hello = "Bye World";
+
+        FooService eager = 
context.getRegistry().lookupByNameAndType("myEager", FooService.class);
+        assertNotNull(eager);
+        assertEquals("Hello World", eager.getMessage());
+
+        FooService lazy = context.getRegistry().lookupByNameAndType("myLazy", 
FooService.class);
+        assertNotNull(lazy);
+        assertEquals("Bye World", lazy.getMessage());
+    }
+
+    public static class FooService {
+
+        private final String message;
+
+        public FooService(String message) {
+            this.message = message;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyTest.java
new file mode 100644
index 00000000000..b36d643e16d
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/impl/BindToRegistryBeanLazyTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl;
+
+import org.apache.camel.BeanInject;
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.spi.CamelBeanPostProcessor;
+import org.apache.camel.support.PluginHelper;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+public class BindToRegistryBeanLazyTest extends ContextTestSupport {
+
+    // field
+    @BindToRegistry(lazy = true, beanPostProcess = true)
+    private final FooService foo = new FooService();
+
+    // method
+    @BindToRegistry(lazy = true, beanPostProcess = true)
+    public FooService myOtherFoo() {
+        return new FooService();
+    }
+
+    @Test
+    public void testPostProcessor() throws Exception {
+        // bean post processing dont run on ContextTestSupport
+        CamelBeanPostProcessor cbpp = 
PluginHelper.getBeanPostProcessor(context);
+        cbpp.postProcessBeforeInitialization(this, "this");
+        cbpp.postProcessAfterInitialization(this, "this");
+
+        assertNotNull(foo);
+        assertSame(context, foo.getCamelContext());
+
+        FooService other = (FooService) 
context.getRegistry().lookupByName("myOtherFoo");
+        assertNotNull(other);
+        assertSame(context, other.getCamelContext());
+    }
+
+    public static class FooService {
+
+        @BeanInject
+        private CamelContext camelContext;
+
+        public CamelContext getCamelContext() {
+            return camelContext;
+        }
+
+        public void setCamelContext(CamelContext camelContext) {
+            this.camelContext = camelContext;
+        }
+    }
+}
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
index ffc8c9fb394..4e927d221d8 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
@@ -17,13 +17,16 @@
 package org.apache.camel.support.scan;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.apache.camel.BindToRegistry;
 import org.apache.camel.CamelContext;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.CamelBeanPostProcessor;
 import org.apache.camel.spi.Injector;
 import org.apache.camel.spi.PackageScanClassResolver;
 import org.apache.camel.spi.Registry;
@@ -55,18 +58,55 @@ public class PackageScanHelper {
                 Injector injector = camelContext.getInjector();
                 if (scanner != null && injector != null) {
                     Map<Class<?>, Object> created = new HashMap<>();
+                    Set<Class<?>> lazy = new HashSet<>();
                     for (String pkg : packages) {
                         Set<Class<?>> classes = 
scanner.findAnnotated(BindToRegistry.class, pkg);
                         for (Class<?> c : classes) {
-                            // phase-1: create empty bean instance without any 
bean post-processing
-                            Object b = injector.newInstance(c, false);
-                            if (b != null) {
-                                created.put(c, b);
+                            BindToRegistry ann = 
c.getAnnotation(BindToRegistry.class);
+                            if (ann != null && ann.lazy()) {
+                                // phase-1: remember lazy creating beans
+                                lazy.add(c);
+                            } else {
+                                // phase-1: create empty bean instance without 
any bean post-processing
+                                Object b = injector.newInstance(c, false);
+                                if (b != null) {
+                                    created.put(c, b);
+                                }
+                            }
+                        }
+                        for (Class<?> c : lazy) {
+                            // phase-2: special for lazy beans that must be 
registered and created on-demand
+                            BindToRegistry ann = 
c.getAnnotation(BindToRegistry.class);
+                            if (ann != null) {
+                                String name = ann.value();
+                                if (isEmpty(name)) {
+                                    name = c.getSimpleName();
+                                }
+                                String beanName = c.getName();
+                                Object bean = (Supplier<Object>) () -> {
+                                    Object answer = injector.newInstance(c);
+                                    if (answer != null && 
ann.beanPostProcess()) {
+                                        try {
+                                            final CamelBeanPostProcessor 
beanPostProcessor
+                                                    = 
PluginHelper.getBeanPostProcessor(camelContext);
+                                            
beanPostProcessor.postProcessBeforeInitialization(answer, beanName);
+                                            
beanPostProcessor.postProcessAfterInitialization(answer, beanName);
+                                        } catch (Exception e) {
+                                            throw 
RuntimeCamelException.wrapRuntimeException(e);
+                                        }
+                                    }
+                                    return answer;
+                                };
+                                // - bind to registry if 
@org.apache.camel.BindToRegistry is present
+                                // use dependency injection factory to perform 
the task of binding the bean to registry
+                                Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(camelContext)
+                                        .createBindToRegistryFactory(name, 
bean, c, beanName, false);
+                                task.run();
                             }
                         }
                         for (Entry<Class<?>, Object> entry : 
created.entrySet()) {
                             Class<?> c = entry.getKey();
-                            // phase-2: discover any created beans has 
@BindToRegistry to register them eager
+                            // phase-3: discover any created beans has 
@BindToRegistry to register them eager
                             BindToRegistry ann = 
c.getAnnotation(BindToRegistry.class);
                             if (ann != null) {
                                 String name = ann.value();
@@ -78,13 +118,13 @@ public class PackageScanHelper {
                                 // - bind to registry if 
@org.apache.camel.BindToRegistry is present
                                 // use dependency injection factory to perform 
the task of binding the bean to registry
                                 Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(camelContext)
-                                        .createBindToRegistryFactory(name, 
bean, beanName, false);
+                                        .createBindToRegistryFactory(name, 
bean, c, beanName, false);
                                 task.run();
                             }
                         }
                         for (Entry<Class<?>, Object> entry : 
created.entrySet()) {
                             Class<?> c = entry.getKey();
-                            // phase-3: now we can do bean post-processing on 
the created beans
+                            // phase-4: now we can do bean post-processing on 
the created beans
                             Object bean = entry.getValue();
                             String beanName = c.getName();
                             try {
diff --git 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
 
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
index 317693f93bd..d805e559cd7 100644
--- 
a/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
+++ 
b/dsl/camel-java-joor-dsl/src/main/java/org/apache/camel/dsl/java/joor/JavaRoutesBuilderLoader.java
@@ -29,6 +29,7 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.camel.BindToRegistry;
 import org.apache.camel.CamelContext;
 import org.apache.camel.CamelContextAware;
 import org.apache.camel.RoutesBuilder;
@@ -138,8 +139,9 @@ public class JavaRoutesBuilderLoader extends 
ExtendedRouteBuilderLoaderSupport {
 
             Class<?> clazz = result.getClass(className);
             if (clazz != null) {
+                BindToRegistry bir = clazz.getAnnotation(BindToRegistry.class);
                 boolean skip = clazz.isInterface() || 
Modifier.isAbstract(clazz.getModifiers())
-                        || Modifier.isPrivate(clazz.getModifiers());
+                        || Modifier.isPrivate(clazz.getModifiers()) || (bir != 
null && bir.lazy());
                 // must have a default no-arg constructor to be able to create 
an instance
                 boolean ctr = ObjectHelper.hasDefaultNoArgConstructor(clazz);
                 if (ctr && !skip) {
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/injection/AnnotationDependencyInjection.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/injection/AnnotationDependencyInjection.java
index 1d9ef7ace70..e042e6ecaa3 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/injection/AnnotationDependencyInjection.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/injection/AnnotationDependencyInjection.java
@@ -20,6 +20,7 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Supplier;
 
 import jakarta.enterprise.context.ApplicationScoped;
 import jakarta.enterprise.inject.Produces;
@@ -136,31 +137,51 @@ public final class AnnotationDependencyInjection {
         @Override
         public void postCompile(CamelContext camelContext, String name, 
Class<?> clazz, byte[] byteCode, Object instance)
                 throws Exception {
-            if (instance == null) {
-                return;
-            }
 
-            BindToRegistry bir = 
instance.getClass().getAnnotation(BindToRegistry.class);
-            Configuration cfg = 
instance.getClass().getAnnotation(Configuration.class);
-            if (bir != null || cfg != null || instance instanceof 
CamelConfiguration) {
-                CamelBeanPostProcessor bpp = 
PluginHelper.getBeanPostProcessor(camelContext);
-                if (bir != null && ObjectHelper.isNotEmpty(bir.value())) {
-                    name = bir.value();
-                } else if (cfg != null && 
ObjectHelper.isNotEmpty(cfg.value())) {
-                    name = cfg.value();
-                }
-                // to support hot reloading of beans then we need to enable 
unbind mode in bean post processor
-                bpp.setUnbindEnabled(true);
-                try {
-                    // this class uses camels own annotations so the bind to 
registry happens
-                    // automatic by the bean post processor
-                    bpp.postProcessBeforeInitialization(instance, name);
-                    bpp.postProcessAfterInitialization(instance, name);
-                } finally {
-                    bpp.setUnbindEnabled(false);
-                }
-                if (instance instanceof CamelConfiguration) {
-                    ((CamelConfiguration) instance).configure(camelContext);
+            BindToRegistry bir = clazz.getAnnotation(BindToRegistry.class);
+            Configuration cfg = clazz.getAnnotation(Configuration.class);
+
+            // special for lazy beans which we must create on-demand
+            if (instance == null && bir != null && bir.lazy()) {
+                final String beanName = bir.value();
+                instance = (Supplier<Object>) () -> {
+                    Object answer = 
camelContext.getInjector().newInstance(clazz);
+                    CamelBeanPostProcessor bpp = 
PluginHelper.getBeanPostProcessor(camelContext);
+                    try {
+                        bpp.postProcessBeforeInitialization(answer, beanName);
+                        bpp.postProcessAfterInitialization(answer, beanName);
+                    } catch (Exception e) {
+                        throw RuntimeCamelException.wrapRuntimeException(e);
+                    }
+                    return answer;
+                };
+                // unbind old bean and register lazy bean
+                camelContext.getRegistry().unbind(beanName);
+                // use dependency injection factory to perform the task of 
binding the bean to registry
+                Runnable task = 
PluginHelper.getDependencyInjectionAnnotationFactory(camelContext)
+                        .createBindToRegistryFactory(name, instance, clazz, 
beanName, false);
+                task.run();
+            } else {
+                if (bir != null || cfg != null || instance instanceof 
CamelConfiguration) {
+                    CamelBeanPostProcessor bpp = 
PluginHelper.getBeanPostProcessor(camelContext);
+                    if (bir != null && ObjectHelper.isNotEmpty(bir.value())) {
+                        name = bir.value();
+                    } else if (cfg != null && 
ObjectHelper.isNotEmpty(cfg.value())) {
+                        name = cfg.value();
+                    }
+                    // to support hot reloading of beans then we need to 
enable unbind mode in bean post processor
+                    bpp.setUnbindEnabled(true);
+                    try {
+                        // this class uses camels own annotations so the bind 
to registry happens
+                        // automatic by the bean post processor
+                        bpp.postProcessBeforeInitialization(instance, name);
+                        bpp.postProcessAfterInitialization(instance, name);
+                    } finally {
+                        bpp.setUnbindEnabled(false);
+                    }
+                    if (instance instanceof CamelConfiguration) {
+                        ((CamelConfiguration) 
instance).configure(camelContext);
+                    }
                 }
             }
         }

Reply via email to