You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2023/08/02 13:56:51 UTC

[camel] branch scan created (now d231dba115b)

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

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


      at d231dba115b CAMEL-19693: camel-main - Base package scan should detect @BindToRegistry beans

This branch includes the following new commits:

     new d231dba115b CAMEL-19693: camel-main - Base package scan should detect @BindToRegistry beans

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[camel] 01/01: CAMEL-19693: camel-main - Base package scan should detect @BindToRegistry beans

Posted by da...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d231dba115b9919e3291d64c16dc2d074e4f9532
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed Aug 2 15:56:38 2023 +0200

    CAMEL-19693: camel-main - Base package scan should detect @BindToRegistry beans
---
 .../org/apache/camel/ExtendedCamelContext.java     |  12 ++-
 .../camel-main-configuration-metadata.json         |   2 +-
 core/camel-main/src/main/docs/main.adoc            |   2 +-
 .../org/apache/camel/main/BaseMainSupport.java     |  13 ++-
 .../camel/main/MainConfigurationProperties.java    |  10 +-
 .../java/org/apache/camel/main/MainScan2Test.java  |  44 +++++++++
 .../org/apache/camel/main/scan2/MyProcessor.java   |  31 +++++++
 .../apache/camel/main/scan2/MyRouteBuilder.java    |  28 ++++++
 .../camel/support/scan/PackageScanHelper.java      | 103 +++++++++++++++++++++
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   |  64 +------------
 10 files changed, 234 insertions(+), 75 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
index e5205dbe25a..e14101b4f23 100644
--- a/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
+++ b/core/camel-api/src/main/java/org/apache/camel/ExtendedCamelContext.java
@@ -445,16 +445,18 @@ public interface ExtendedCamelContext {
     String resolvePropertyPlaceholders(String text, boolean keepUnresolvedOptional);
 
     /**
-     * Package name to use as base (offset) for classpath scanning of custom {@link CamelConfiguration},
-     * {@link Configuration}, and {@link TypeConverter}.
+     * Package name to use as base (offset) for classpath scanning of RouteBuilder,
+     * {@link org.apache.camel.TypeConverter}, {@link CamelConfiguration} classes, and also classes annotated with
+     * {@link org.apache.camel.Converter}, or {@link org.apache.camel.BindToRegistry}.
      *
-     * @return the base package name (can bre null if not configured)
+     * @return the base package name (can be null if not configured)
      */
     String getBasePackageScan();
 
     /**
-     * Package name to use as base (offset) for classpath scanning of custom {@link CamelConfiguration},
-     * {@link Configuration}, and {@link TypeConverter}.
+     * Package name to use as base (offset) for classpath scanning of RouteBuilder,
+     * {@link org.apache.camel.TypeConverter}, {@link CamelConfiguration} classes, and also classes annotated with
+     * {@link org.apache.camel.Converter}, or {@link org.apache.camel.BindToRegistry}.
      *
      * @param basePackageScan the base package name
      */
diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
index 1ff621ee9d8..7e4a2e04b05 100644
--- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
+++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json
@@ -24,7 +24,7 @@
     { "name": "camel.main.backlogTracing", "description": "Sets whether backlog tracing is enabled or not. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.main.backlogTracingStandby", "description": "Whether to set backlog tracing on standby. If on standby then the backlog tracer is installed and made available. Then the backlog tracer can be enabled later at runtime via JMX or via Java API. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.main.backlogTracingTemplates", "description": "Whether backlog tracing should trace inner details from route templates (or kamelets). Turning this on increases the verbosity of tracing by including events from internal routes in the templates or kamelets. Default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
-    { "name": "camel.main.basePackageScan", "description": "Package name to use as base (offset) for classpath scanning of RouteBuilder , and org.apache.camel.TypeConverter classes. If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate your route builder classes with Component. In other words only use this for Camel Main in standalone mode.", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "string", "jav [...]
+    { "name": "camel.main.basePackageScan", "description": "Package name to use as base (offset) for classpath scanning of RouteBuilder , org.apache.camel.TypeConverter , CamelConfiguration classes, and also classes annotated with org.apache.camel.Converter , or org.apache.camel.BindToRegistry . If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate your route builder classes with Component. In other words only use this for Camel M [...]
     { "name": "camel.main.basePackageScanEnabled", "description": "Whether base package scan is enabled.", "sourceType": "org.apache.camel.main.MainConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true },
     { "name": "camel.main.beanIntrospectionExtendedStatistics", "description": "Sets whether bean introspection uses extended statistics. The default is false.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" },
     { "name": "camel.main.beanIntrospectionLoggingLevel", "description": "Sets the logging level used by bean introspection, logging activity of its usage. The default is TRACE.", "sourceType": "org.apache.camel.main.DefaultConfigurationProperties", "type": "object", "javaType": "org.apache.camel.LoggingLevel", "enum": [ "ERROR", "WARN", "INFO", "DEBUG", "TRACE", "OFF" ] },
diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc
index 02157749c5f..fff84225c33 100644
--- a/core/camel-main/src/main/docs/main.adoc
+++ b/core/camel-main/src/main/docs/main.adoc
@@ -35,7 +35,7 @@ The camel.main supports 120 options, which are listed below.
 | *camel.main.backlogTracing* | Sets whether backlog tracing is enabled or not. Default is false. | false | boolean
 | *camel.main.backlogTracing{zwsp}Standby* | Whether to set backlog tracing on standby. If on standby then the backlog tracer is installed and made available. Then the backlog tracer can be enabled later at runtime via JMX or via Java API. Default is false. | false | boolean
 | *camel.main.backlogTracing{zwsp}Templates* | Whether backlog tracing should trace inner details from route templates (or kamelets). Turning this on increases the verbosity of tracing by including events from internal routes in the templates or kamelets. Default is false. | false | boolean
-| *camel.main.basePackageScan* | Package name to use as base (offset) for classpath scanning of RouteBuilder , and org.apache.camel.TypeConverter classes. If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate your route builder classes with Component. In other words only use this for Camel Main in standalone mode. |  | String
+| *camel.main.basePackageScan* | Package name to use as base (offset) for classpath scanning of RouteBuilder , org.apache.camel.TypeConverter , CamelConfiguration classes, and also classes annotated with org.apache.camel.Converter , or org.apache.camel.BindToRegistry . If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate your route builder classes with Component. In other words only use this for Camel Main in standalone mode. |   [...]
 | *camel.main.basePackageScan{zwsp}Enabled* | Whether base package scan is enabled. | true | boolean
 | *camel.main.beanIntrospection{zwsp}ExtendedStatistics* | Sets whether bean introspection uses extended statistics. The default is false. | false | boolean
 | *camel.main.beanIntrospection{zwsp}LoggingLevel* | Sets the logging level used by bean introspection, logging activity of its usage. The default is TRACE. |  | LoggingLevel
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
index 9fe3bb74c92..ee60e0c621c 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java
@@ -71,6 +71,7 @@ import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.PropertyBindingSupport;
 import org.apache.camel.support.ResourceHelper;
 import org.apache.camel.support.SimpleEventNotifierSupport;
+import org.apache.camel.support.scan.PackageScanHelper;
 import org.apache.camel.support.service.BaseService;
 import org.apache.camel.support.startup.LoggingStartupStepRecorder;
 import org.apache.camel.util.FileUtil;
@@ -263,6 +264,14 @@ public abstract class BaseMainSupport extends BaseService {
         listeners.remove(listener);
     }
 
+    protected void loadCustomBeans(CamelContext camelContext) throws Exception {
+        // auto-detect custom beans via base package scanning
+        String basePackage = camelContext.getCamelContextExtension().getBasePackageScan();
+        if (basePackage != null) {
+            PackageScanHelper.registerBeans(getCamelContext(), Set.of(basePackage));
+        }
+    }
+
     protected void loadConfigurations(CamelContext camelContext) throws Exception {
         // auto-detect camel configurations via base package scanning
         String basePackage = camelContext.getCamelContextExtension().getBasePackageScan();
@@ -429,9 +438,9 @@ public abstract class BaseMainSupport extends BaseService {
         // configure from main configuration properties
         doConfigureCamelContextFromMainConfiguration(camelContext, mainConfigurationProperties, autoConfiguredProperties);
 
+        // try to load custom beans/configuration classes via package scanning
         configurePackageScan(camelContext);
-
-        // try to load configuration classes
+        loadCustomBeans(camelContext);
         loadConfigurations(camelContext);
 
         if (mainConfigurationProperties.isAutoConfigurationEnabled()) {
diff --git a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
index 77e58d187b4..99236072390 100644
--- a/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
+++ b/core/camel-main/src/main/java/org/apache/camel/main/MainConfigurationProperties.java
@@ -333,8 +333,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
     }
 
     /**
-     * Package name to use as base (offset) for classpath scanning of {@link RouteBuilder}, and
-     * {@link org.apache.camel.TypeConverter} classes.
+     * Package name to use as base (offset) for classpath scanning of {@link RouteBuilder},
+     * {@link org.apache.camel.TypeConverter}, {@link CamelConfiguration} classes, and also classes annotated with
+     * {@link org.apache.camel.Converter}, or {@link org.apache.camel.BindToRegistry}.
      *
      * If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate
      * your route builder classes with `@Component`. In other words only use this for Camel Main in standalone mode.
@@ -588,8 +589,9 @@ public class MainConfigurationProperties extends DefaultConfigurationProperties<
     }
 
     /**
-     * Package name to use as base (offset) for classpath scanning of {@link RouteBuilder}, and
-     * {@link org.apache.camel.TypeConverter} classes.
+     * Package name to use as base (offset) for classpath scanning of {@link RouteBuilder},
+     * {@link org.apache.camel.TypeConverter}, {@link CamelConfiguration} classes, and also classes annotated with
+     * {@link org.apache.camel.Converter}, or {@link org.apache.camel.BindToRegistry}.
      *
      * If you are using Spring Boot then it is instead recommended to use Spring Boots component scanning and annotate
      * your route builder classes with `@Component`. In other words only use this for Camel Main in standalone mode.
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/MainScan2Test.java b/core/camel-main/src/test/java/org/apache/camel/main/MainScan2Test.java
new file mode 100644
index 00000000000..7c5f42dd51d
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/MainScan2Test.java
@@ -0,0 +1,44 @@
+/*
+ * 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.main;
+
+import org.apache.camel.CamelContext;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+public class MainScan2Test {
+
+    @Test
+    public void testScan2() throws Exception {
+        Main main = new Main();
+        main.configure().withBasePackageScan("org.apache.camel.main.scan2");
+        main.start();
+
+        CamelContext camelContext = main.getCamelContext();
+        assertNotNull(camelContext);
+        assertEquals(1, camelContext.getRoutes().size());
+
+        String out = camelContext.createProducerTemplate().requestBody("direct:start", "Camel", String.class);
+        Assertions.assertEquals("Hello Camel", out);
+
+        main.stop();
+    }
+
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyProcessor.java b/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyProcessor.java
new file mode 100644
index 00000000000..76189f5b0a7
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyProcessor.java
@@ -0,0 +1,31 @@
+/*
+ * 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.main.scan2;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.Exchange;
+import org.apache.camel.Processor;
+
+@BindToRegistry("hello")
+public class MyProcessor implements Processor {
+
+    @Override
+    public void process(Exchange exchange) throws Exception {
+        exchange.getMessage().setBody("Hello " + exchange.getMessage().getBody());
+    }
+
+}
diff --git a/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyRouteBuilder.java b/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyRouteBuilder.java
new file mode 100644
index 00000000000..8442b3a0d46
--- /dev/null
+++ b/core/camel-main/src/test/java/org/apache/camel/main/scan2/MyRouteBuilder.java
@@ -0,0 +1,28 @@
+/*
+ * 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.main.scan2;
+
+import org.apache.camel.builder.RouteBuilder;
+
+public class MyRouteBuilder extends RouteBuilder {
+
+    @Override
+    public void configure() throws Exception {
+        from("direct:start")
+                .process("hello");
+    }
+}
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
new file mode 100644
index 00000000000..adc59296341
--- /dev/null
+++ b/core/camel-support/src/main/java/org/apache/camel/support/scan/PackageScanHelper.java
@@ -0,0 +1,103 @@
+/*
+ * 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.support.scan;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelContext;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.spi.Injector;
+import org.apache.camel.spi.PackageScanClassResolver;
+import org.apache.camel.spi.Registry;
+import org.apache.camel.support.PluginHelper;
+
+import static org.apache.camel.util.ObjectHelper.isEmpty;
+
+/**
+ * Helper for Camel package scanning.
+ */
+public class PackageScanHelper {
+
+    private PackageScanHelper() {
+    }
+
+    /**
+     * Scans the given Java packages for custom beans annotated with {@link BindToRegistry} and create new instances of
+     * these class and performs Camel dependency injection via {@link org.apache.camel.spi.CamelBeanPostProcessor}.
+     *
+     * @param camelContext the camel context
+     * @param packages     the Java packages to scan
+     */
+    public static void registerBeans(CamelContext camelContext, Set<String> packages) {
+        if (packages != null && !packages.isEmpty()) {
+            Registry registry = camelContext.getRegistry();
+            if (registry != null) {
+                PackageScanClassResolver scanner
+                        = camelContext.getCamelContextExtension().getContextPlugin(PackageScanClassResolver.class);
+                Injector injector = camelContext.getInjector();
+                if (scanner != null && injector != null) {
+                    Map<Class<?>, Object> created = new HashMap<>();
+                    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);
+                            }
+                        }
+                        for (Class<?> c : created.keySet()) {
+                            // phase-2: discover any created beans has @BindToRegistry to register them eager
+                            BindToRegistry ann = c.getAnnotation(BindToRegistry.class);
+                            if (ann != null) {
+                                String name = ann.value();
+                                if (isEmpty(name)) {
+                                    name = c.getSimpleName();
+                                }
+                                Object bean = created.get(c);
+                                String beanName = c.getName();
+                                // - 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);
+                                task.run();
+                            }
+                        }
+                        for (Class<?> c : created.keySet()) {
+                            // phase-3: now we can do bean post-processing on the created beans
+                            Object bean = created.get(c);
+                            String beanName = c.getName();
+                            try {
+                                // - call org.apache.camel.spi.CamelBeanPostProcessor.postProcessBeforeInitialization
+                                // - call org.apache.camel.spi.CamelBeanPostProcessor.postProcessAfterInitialization
+                                PluginHelper.getBeanPostProcessor(camelContext).postProcessBeforeInitialization(bean,
+                                        beanName);
+                                PluginHelper.getBeanPostProcessor(camelContext).postProcessAfterInitialization(bean,
+                                        beanName);
+                            } catch (Exception e) {
+                                throw new RuntimeCamelException("Error post-processing bean: " + beanName, e);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
index f1b32b1d1d3..4a40f99ec03 100644
--- a/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
+++ b/dsl/camel-xml-io-dsl/src/main/java/org/apache/camel/dsl/xml/io/XmlRoutesBuilderLoader.java
@@ -18,7 +18,6 @@ package org.apache.camel.dsl.xml.io;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -28,9 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
 
 import org.w3c.dom.Document;
 
-import org.apache.camel.BindToRegistry;
 import org.apache.camel.CamelContextAware;
-import org.apache.camel.RuntimeCamelException;
 import org.apache.camel.api.management.ManagedResource;
 import org.apache.camel.builder.RouteBuilder;
 import org.apache.camel.builder.RouteConfigurationBuilder;
@@ -47,22 +44,17 @@ import org.apache.camel.model.app.BeansDefinition;
 import org.apache.camel.model.app.RegistryBeanDefinition;
 import org.apache.camel.model.rest.RestDefinition;
 import org.apache.camel.model.rest.RestsDefinition;
-import org.apache.camel.spi.Injector;
-import org.apache.camel.spi.PackageScanClassResolver;
-import org.apache.camel.spi.Registry;
 import org.apache.camel.spi.Resource;
 import org.apache.camel.spi.annotations.RoutesLoader;
 import org.apache.camel.support.CachedResource;
-import org.apache.camel.support.PluginHelper;
 import org.apache.camel.support.PropertyBindingSupport;
+import org.apache.camel.support.scan.PackageScanHelper;
 import org.apache.camel.xml.in.ModelParser;
 import org.apache.camel.xml.io.util.XmlStreamDetector;
 import org.apache.camel.xml.io.util.XmlStreamInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import static org.apache.camel.util.ObjectHelper.isEmpty;
-
 @ManagedResource(description = "Managed XML RoutesBuilderLoader")
 @RoutesLoader(XmlRoutesBuilderLoader.EXTENSION)
 public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
@@ -279,59 +271,7 @@ public class XmlRoutesBuilderLoader extends RouteBuilderLoaderSupport {
         app.getComponentScanning().forEach(cs -> {
             packagesToScan.add(cs.getBasePackage());
         });
-        if (!packagesToScan.isEmpty()) {
-            Registry registry = getCamelContext().getRegistry();
-            if (registry != null) {
-                PackageScanClassResolver scanner
-                        = getCamelContext().getCamelContextExtension().getContextPlugin(PackageScanClassResolver.class);
-                Injector injector = getCamelContext().getInjector();
-                if (scanner != null && injector != null) {
-                    Map<Class<?>, Object> created = new HashMap<>();
-                    for (String pkg : packagesToScan) {
-                        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);
-                            }
-                        }
-                        for (Class<?> c : created.keySet()) {
-                            // phase-2: discover any created beans has @BindToRegistry to register them eager
-                            BindToRegistry ann = c.getAnnotation(BindToRegistry.class);
-                            if (ann != null) {
-                                String name = ann.value();
-                                if (isEmpty(name)) {
-                                    name = c.getSimpleName();
-                                }
-                                Object bean = created.get(c);
-                                String beanName = c.getName();
-                                // - 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(getCamelContext())
-                                        .createBindToRegistryFactory(name, bean, beanName, false);
-                                task.run();
-                            }
-                        }
-                        for (Class<?> c : created.keySet()) {
-                            // phase-3: now we can do bean post-processing on the created beans
-                            Object bean = created.get(c);
-                            String beanName = c.getName();
-                            try {
-                                // - call org.apache.camel.spi.CamelBeanPostProcessor.postProcessBeforeInitialization
-                                // - call org.apache.camel.spi.CamelBeanPostProcessor.postProcessAfterInitialization
-                                PluginHelper.getBeanPostProcessor(getCamelContext()).postProcessBeforeInitialization(bean,
-                                        beanName);
-                                PluginHelper.getBeanPostProcessor(getCamelContext()).postProcessAfterInitialization(bean,
-                                        beanName);
-                            } catch (Exception e) {
-                                throw new RuntimeCamelException("Error post-processing bean: " + beanName, e);
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        PackageScanHelper.registerBeans(getCamelContext(), packagesToScan);
 
         // <bean>s - register Camel beans directly with Camel injection
         for (RegistryBeanDefinition bean : app.getBeans()) {