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

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

commit 0c918e71c86cddaa1e4cd3e0abe5a83d8ec5f72d
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 26 12:56:12 2023 +0200

    CAMEL-19912: camel-core-model - Add factory-bean parameter
---
 .../main/java/org/apache/camel/spi/Injector.java   | 12 +++++
 .../apache/camel/impl/engine/DefaultInjector.java  | 13 ++++--
 .../java/org/apache/camel/impl/DefaultModel.java   | 21 ++++++++-
 .../camel/model/app/RegistryBeanDefinition.java    | 10 ++++
 .../xml/AbstractCamelContextFactoryBeanTest.java   |  5 ++
 .../camel/support/PropertyBindingSupportTest.java  |  5 ++
 .../org/apache/camel/util/ReflectionInjector.java  | 26 ++++++++++-
 ...opertyBindingSupportClassFactoryMethodTest.java | 15 +++---
 ...indingSupportHelperClassFactoryMethodTest.java} | 54 ++++++++++++++++++----
 .../org/apache/camel/support/EndpointHelper.java   | 20 +++++++-
 .../camel/support/PropertyBindingSupport.java      | 42 ++++++++++++++---
 .../java/org/apache/camel/xml/in/ModelParser.java  |  1 +
 .../java/org/apache/camel/xml/out/ModelWriter.java |  1 +
 .../org/apache/camel/xml/LwModelToXMLDumper.java   | 12 +++--
 .../camel/xml/jaxb/JaxbModelToXMLDumper.java       | 11 +++--
 .../org/apache/camel/yaml/out/ModelWriter.java     |  1 +
 .../org/apache/camel/yaml/LwModelToYAMLDumper.java |  8 ++--
 .../modules/ROOT/pages/property-binding.adoc       |  3 ++
 .../camel/main/download/KameletMainInjector.java   |  9 ++++
 .../xml/blueprint/BlueprintXmlBeansHandler.java    | 12 +++--
 .../main/xml/spring/SpringXmlBeansHandler.java     |  5 +-
 .../src/main/docs/java-xml-io-dsl.adoc             | 34 ++++++++++++++
 .../camel/dsl/xml/io/XmlRoutesBuilderLoader.java   |  6 ++-
 .../apache/camel/dsl/xml/io/XmlLoadAppTest.java    | 25 +++++++++-
 .../apache/camel/dsl/xml/io/beans/MyFacBean.java   |  2 +-
 .../io/beans/{MyFacBean.java => MyFacHelper.java}  | 31 +------------
 .../org/apache/camel/dsl/xml/io/camel-app7.xml     | 41 ++++++++++++++++
 .../dsl/yaml/deserializers/ModelDeserializers.java |  6 +++
 .../dsl/yaml/deserializers/BeansDeserializer.java  | 20 ++++----
 .../generated/resources/schema/camelYamlDsl.json   |  3 ++
 .../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc     | 35 ++++++++++++++
 .../org/apache/camel/dsl/yaml/BeansTest.groovy     | 24 ++++++++++
 .../dsl/yaml/support/model/MyFacHelper.groovy}     | 35 ++------------
 33 files changed, 427 insertions(+), 121 deletions(-)

diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
index c59bc50f32d..69df0a6959b 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/Injector.java
@@ -42,6 +42,18 @@ public interface Injector {
      */
     <T> T newInstance(Class<T> type, String factoryMethod);
 
+    /**
+     * Instantiates a new instance of the given type by using the factory 
class (this will not perform bean post
+     * processing)
+     *
+     * @param  type          the type of object to create
+     * @param  factoryClass  to create the new instance via factory class
+     * @param  factoryMethod to create the new instance via factory method 
which must be public static and return the
+     *                       type
+     * @return               a newly created instance
+     */
+    <T> T newInstance(Class<T> type, Class<?> factoryClass, String 
factoryMethod);
+
     /**
      * Instantiates a new instance of the given type; possibly injecting 
values into the object in the process (bean
      * post processing if enabled)
diff --git 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
index 2b4b47cdf01..332dd05b64f 100644
--- 
a/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
+++ 
b/core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultInjector.java
@@ -49,18 +49,25 @@ public class DefaultInjector implements Injector {
 
     @Override
     public <T> T newInstance(Class<T> type, String factoryMethod) {
+        return newInstance(type, null, factoryMethod);
+    }
+
+    @Override
+    public <T> T newInstance(Class<T> type, Class<?> factoryClass, String 
factoryMethod) {
+        Class<?> target = factoryClass != null ? factoryClass : type;
         T answer = null;
         try {
             // lookup factory method
-            Method fm = type.getMethod(factoryMethod);
-            if (Modifier.isStatic(fm.getModifiers()) && 
Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+            Method fm = target.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && 
Modifier.isPublic(fm.getModifiers())
+                    && fm.getReturnType() != Void.class) {
                 Object obj = fm.invoke(null);
                 answer = type.cast(obj);
             }
             // inject camel context if needed
             CamelContextAware.trySetCamelContext(answer, camelContext);
         } catch (Exception e) {
-            throw new RuntimeCamelException("Error invoking factory method: " 
+ factoryMethod + " on class: " + type, e);
+            throw new RuntimeCamelException("Error invoking factory method: " 
+ factoryMethod + " on class: " + target, e);
         }
         return answer;
     }
diff --git 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
index 729bb3ce5ff..1f651f3fc24 100644
--- 
a/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
+++ 
b/core/camel-core-engine/src/main/java/org/apache/camel/impl/DefaultModel.java
@@ -631,6 +631,22 @@ public class DefaultModel implements Model {
             if (className != null && (factoryMethod != null || parameters != 
null)) {
                 final CamelContext camelContext = 
routeTemplateContext.getCamelContext();
                 final Class<?> clazz = 
camelContext.getClassResolver().resolveMandatoryClass(className);
+                Class<?> fc = null;
+                if (factoryMethod != null) {
+                    String typeOrRef = StringHelper.before(factoryMethod, ":");
+                    if (typeOrRef != null) {
+                        // use another class with factory method
+                        factoryMethod = StringHelper.after(factoryMethod, ":");
+                        // special to support factory method parameters
+                        Object existing = 
camelContext.getRegistry().lookupByName(typeOrRef);
+                        if (existing != null) {
+                            fc = existing.getClass();
+                        } else {
+                            fc = 
camelContext.getClassResolver().resolveMandatoryClass(typeOrRef);
+                        }
+                    }
+                }
+                final Class<?> factoryClass = fc;
                 final String fqn = className;
                 final String fm = factoryMethod;
                 final String fp = parameters;
@@ -642,9 +658,10 @@ public class DefaultModel implements Model {
                         if (fm != null) {
                             if (fp != null) {
                                 // special to support factory method parameters
-                                local = 
PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, fm, 
params);
+                                Class<?> target = factoryClass != null ? 
factoryClass : clazz;
+                                local = 
PropertyBindingSupport.newInstanceFactoryParameters(camelContext, target, fm, 
params);
                             } else {
-                                local = 
camelContext.getInjector().newInstance(clazz, fm);
+                                local = 
camelContext.getInjector().newInstance(clazz, factoryClass, fm);
                             }
                             if (local == null) {
                                 throw new IllegalStateException(
diff --git 
a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
 
b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
index 39b34c1ff95..70100e769cd 100644
--- 
a/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
+++ 
b/core/camel-core-model/src/main/java/org/apache/camel/model/app/RegistryBeanDefinition.java
@@ -48,6 +48,8 @@ public class RegistryBeanDefinition implements ResourceAware {
     private String type;
     @XmlAttribute
     private String factoryMethod;
+    @XmlAttribute
+    private String factoryBean;
     @XmlElement(name = "constructors")
     @XmlJavaTypeAdapter(BeanConstructorsAdapter.class)
     private Map<Integer, Object> constructors;
@@ -79,6 +81,14 @@ public class RegistryBeanDefinition implements ResourceAware 
{
         this.factoryMethod = factoryMethod;
     }
 
+    public String getFactoryBean() {
+        return factoryBean;
+    }
+
+    public void setFactoryBean(String factoryBean) {
+        this.factoryBean = factoryBean;
+    }
+
     public Map<Integer, Object> getConstructors() {
         return constructors;
     }
diff --git 
a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
 
b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
index 4c112f03c79..524efd90cc7 100644
--- 
a/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
+++ 
b/core/camel-core-xml/src/test/java/org/apache/camel/core/xml/AbstractCamelContextFactoryBeanTest.java
@@ -74,6 +74,11 @@ public class AbstractCamelContextFactoryBeanTest {
                     return null;
                 }
 
+                @Override
+                public <T> T newInstance(Class<T> type, Class<?> factoryClass, 
String factoryMethod) {
+                    return null;
+                }
+
                 @Override
                 public <T> T newInstance(Class<T> type, boolean 
postProcessBean) {
                     return ObjectHelper.newInstance(type);
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
index 5c21ba511f4..f308f407b09 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/support/PropertyBindingSupportTest.java
@@ -408,6 +408,11 @@ public class PropertyBindingSupportTest extends 
ContextTestSupport {
                 return null;
             }
 
+            @Override
+            public <T> T newInstance(Class<T> type, Class<?> factoryClass, 
String factoryMethod) {
+                return null;
+            }
+
             @Override
             public <T> T newInstance(Class<T> type, boolean postProcessBean) {
                 return null;
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java 
b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
index 70b6b608de3..b363babc892 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/util/ReflectionInjector.java
@@ -25,8 +25,11 @@ import org.apache.camel.support.ObjectHelper;
 
 /**
  * A simple implementation of {@link Injector} which just uses reflection to 
instantiate new objects using their zero
- * argument constructor. For more complex implementations try the Spring or 
CDI implementations.
+ * argument constructor.
+ *
+ * Use {@link org.apache.camel.impl.engine.DefaultInjector} instead.
  */
+@Deprecated
 public class ReflectionInjector implements Injector {
 
     @Override
@@ -40,7 +43,8 @@ public class ReflectionInjector implements Injector {
         try {
             // lookup factory method
             Method fm = type.getMethod(factoryMethod);
-            if (Modifier.isStatic(fm.getModifiers()) && 
Modifier.isPublic(fm.getModifiers()) && fm.getReturnType() == type) {
+            if (Modifier.isStatic(fm.getModifiers()) && 
Modifier.isPublic(fm.getModifiers())
+                    && fm.getReturnType() != Void.TYPE) {
                 answer = (T) fm.invoke(null);
             }
         } catch (Exception e) {
@@ -49,6 +53,24 @@ public class ReflectionInjector implements Injector {
         return answer;
     }
 
+    @Override
+    @SuppressWarnings("unchecked")
+    public <T> T newInstance(Class<T> type, Class<?> factoryClass, String 
factoryMethod) {
+        T answer = null;
+        try {
+            // lookup factory method
+            Method fm = factoryClass.getMethod(factoryMethod);
+            if (Modifier.isStatic(fm.getModifiers()) && 
Modifier.isPublic(fm.getModifiers())
+                    && fm.getReturnType() != Void.TYPE) {
+                answer = (T) fm.invoke(null);
+            }
+        } catch (Exception e) {
+            throw new RuntimeCamelException(
+                    "Error invoking factory method: " + factoryMethod + " on 
factory class: " + factoryClass, e);
+        }
+        return answer;
+    }
+
     @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         return ObjectHelper.newInstance(type);
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
index f48d9c1f23f..2038916ac97 100644
--- 
a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
@@ -42,7 +42,8 @@ public class PropertyBindingSupportClassFactoryMethodTest {
                 .withCamelContext(context)
                 .withTarget(target)
                 .withProperty("name", "Donald")
-                .withProperty("myDriver", "#class:" + MyDriver.class.getName() 
+ "('localhost:2121', 'scott', 'tiger')")
+                .withProperty("myDriver",
+                        "#class:" + MyDriver.class.getName() + 
"#createDriver('localhost:2121', 'scott', 'tiger')")
                 .withRemoveParameters(false).bind();
 
         assertEquals("Donald", target.getName());
@@ -72,7 +73,7 @@ public class PropertyBindingSupportClassFactoryMethodTest {
                 .withTarget(target)
                 .withProperty("name", "Donald")
                 .withProperty("myDriver",
-                        "#class:" + MyDriver.class.getName() + "('{{myUrl}}', 
'{{myUsername}}', '{{myPassword}}')")
+                        "#class:" + MyDriver.class.getName() + 
"#createDriver('{{myUrl}}', '{{myUsername}}', '{{myPassword}}')")
                 .withRemoveParameters(false).bind();
 
         assertEquals("Donald", target.getName());
@@ -111,10 +112,12 @@ public class PropertyBindingSupportClassFactoryMethodTest 
{
         private String username;
         private String password;
 
-        public MyDriver(String url, String username, String password) {
-            this.url = url;
-            this.username = username;
-            this.password = password;
+        public static MyDriver createDriver(String url, String username, 
String password) {
+            MyDriver driver = new MyDriver();
+            driver.url = url;
+            driver.username = username;
+            driver.password = password;
+            return driver;
         }
 
         public String getUrl() {
diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java
similarity index 67%
copy from 
core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
copy to 
core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java
index f48d9c1f23f..ad713f7872a 100644
--- 
a/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportClassFactoryMethodTest.java
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/PropertyBindingSupportHelperClassFactoryMethodTest.java
@@ -28,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 /**
  * Unit test for PropertyBindingSupport
  */
-public class PropertyBindingSupportClassFactoryMethodTest {
+public class PropertyBindingSupportHelperClassFactoryMethodTest {
 
     @Test
     public void testFactory() throws Exception {
@@ -42,7 +42,9 @@ public class PropertyBindingSupportClassFactoryMethodTest {
                 .withCamelContext(context)
                 .withTarget(target)
                 .withProperty("name", "Donald")
-                .withProperty("myDriver", "#class:" + MyDriver.class.getName() 
+ "('localhost:2121', 'scott', 'tiger')")
+                .withProperty("myDriver", "#class:" + MyDriver.class.getName()
+                                          + "#" + 
MyDriverHelper.class.getName()
+                                          + ":createDriver('localhost:2121', 
'scott', 'tiger')")
                 .withRemoveParameters(false).bind();
 
         assertEquals("Donald", target.getName());
@@ -72,7 +74,35 @@ public class PropertyBindingSupportClassFactoryMethodTest {
                 .withTarget(target)
                 .withProperty("name", "Donald")
                 .withProperty("myDriver",
-                        "#class:" + MyDriver.class.getName() + "('{{myUrl}}', 
'{{myUsername}}', '{{myPassword}}')")
+                        "#class:" + MyDriver.class.getName()
+                                          + "#" + 
MyDriverHelper.class.getName()
+                                          + ":createDriver('{{myUrl}}', 
'{{myUsername}}', '{{myPassword}}')")
+                .withRemoveParameters(false).bind();
+
+        assertEquals("Donald", target.getName());
+        assertEquals("localhost:2121", target.getMyDriver().getUrl());
+        assertEquals("scott", target.getMyDriver().getUsername());
+        assertEquals("tiger", target.getMyDriver().getPassword());
+
+        context.stop();
+    }
+
+    @Test
+    public void testFactoryRef() throws Exception {
+        CamelContext context = new DefaultCamelContext();
+
+        context.getRegistry().bind("myDriverHelper", new MyDriverHelper());
+
+        context.start();
+
+        MyApp target = new MyApp();
+
+        PropertyBindingSupport.build()
+                .withCamelContext(context)
+                .withTarget(target)
+                .withProperty("name", "Donald")
+                .withProperty("myDriver", "#class:" + MyDriver.class.getName()
+                                          + 
"#myDriverHelper:createDriver('localhost:2121', 'scott', 'tiger')")
                 .withRemoveParameters(false).bind();
 
         assertEquals("Donald", target.getName());
@@ -105,18 +135,24 @@ public class PropertyBindingSupportClassFactoryMethodTest 
{
         }
     }
 
+    public static class MyDriverHelper {
+
+        public static MyDriver createDriver(String url, String username, 
String password) {
+            MyDriver driver = new MyDriver();
+            driver.url = url;
+            driver.username = username;
+            driver.password = password;
+            return driver;
+        }
+
+    }
+
     public static class MyDriver {
 
         private String url;
         private String username;
         private String password;
 
-        public MyDriver(String url, String username, String password) {
-            this.url = url;
-            this.username = username;
-            this.password = password;
-        }
-
         public String getUrl() {
             return url;
         }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
index c37820caa3f..11fba313980 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/EndpointHelper.java
@@ -377,11 +377,27 @@ public final class EndpointHelper {
             className = StringHelper.before(className, "#");
         }
         Class<?> clazz = 
camelContext.getClassResolver().resolveMandatoryClass(className);
+        Class<?> factoryClass = null;
+        if (factoryMethod != null) {
+            String typeOrRef = StringHelper.before(factoryMethod, ":");
+            if (typeOrRef != null) {
+                // use another class with factory method
+                factoryMethod = StringHelper.after(factoryMethod, ":");
+                // special to support factory method parameters
+                Object existing = 
camelContext.getRegistry().lookupByName(typeOrRef);
+                if (existing != null) {
+                    factoryClass = existing.getClass();
+                } else {
+                    factoryClass = 
camelContext.getClassResolver().resolveMandatoryClass(typeOrRef);
+                }
+            }
+        }
 
         if (factoryMethod != null && parameters != null) {
-            answer = 
PropertyBindingSupport.newInstanceFactoryParameters(camelContext, clazz, 
factoryMethod, parameters);
+            Class<?> target = factoryClass != null ? factoryClass : clazz;
+            answer = 
PropertyBindingSupport.newInstanceFactoryParameters(camelContext, target, 
factoryMethod, parameters);
         } else if (factoryMethod != null) {
-            answer = camelContext.getInjector().newInstance(type, 
factoryMethod);
+            answer = camelContext.getInjector().newInstance(type, 
factoryClass, factoryMethod);
         } else if (parameters != null) {
             answer = 
PropertyBindingSupport.newInstanceConstructorParameters(camelContext, clazz, 
parameters);
         } else {
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
 
b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
index 986cd2180d9..c8153a261fe 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/PropertyBindingSupport.java
@@ -70,8 +70,10 @@ import static 
org.apache.camel.util.StringHelper.startsWithIgnoreCase;
  * the instance via a factory method then you specify the method as shown: 
#class:com.foo.MyClassType#myFactoryMethod.
  * And if the factory method requires parameters they can be specified as 
follows:
  * #class:com.foo.MyClassType#myFactoryMethod('Hello World', 5, true). Or if 
you need to create the instance via
- * constructor parameters then you can specify the parameters as shown: 
#class:com.foo.MyClass('Hello World', 5,
- * true)</li>.
+ * constructor parameters then you can specify the parameters as shown: 
#class:com.foo.MyClass('Hello World', 5, true).
+ * If the factory method is on another bean or class, then you must specify 
this as shown:
+ * #class:com.foo.MyClassType#com.foo.MyFactory:myFactoryMethod. Where 
com.foo.MyFactory either refers to an class name,
+ * or can refer to an existing bean by id, such as: 
#class:com.foo.MyClassType#myFactoryBean:myFactoryMethod.</li>.
  * <li>valueAs(type):value</li> - To declare that the value should be 
converted to the given type, such as
  * #valueAs(int):123 which indicates that the value 123 should be converted to 
an integer.
  * <li>ignore case - Whether to ignore case for property keys</li>
@@ -1372,11 +1374,26 @@ public final class PropertyBindingSupport {
             for (int i = 0; i < found.getParameterCount(); i++) {
                 Class<?> paramType = found.getParameterTypes()[i];
                 Object param = params[i];
-                Object val = 
camelContext.getTypeConverter().convertTo(paramType, param);
+                Object val = null;
+                // special as we may refer to other #bean or #type in the 
parameter
+                if (param instanceof String) {
+                    String str = param.toString();
+                    if (str.startsWith("#")) {
+                        Object bean = resolveBean(camelContext, param);
+                        if (bean != null) {
+                            val = bean;
+                        }
+                    }
+                }
                 // unquote text
                 if (val instanceof String) {
                     val = StringHelper.removeLeadingAndEndingQuotes((String) 
val);
                 }
+                if (val != null) {
+                    val = 
camelContext.getTypeConverter().tryConvertTo(paramType, val);
+                } else {
+                    val = camelContext.getTypeConverter().convertTo(paramType, 
param);
+                }
                 arr[i] = val;
             }
 
@@ -1558,11 +1575,24 @@ public final class PropertyBindingSupport {
             }
             Class<?> type = 
camelContext.getClassResolver().resolveMandatoryClass(className);
             if (factoryMethod != null) {
-                if (parameters != null) {
+                Class<?> factoryClass = null;
+                String typeOrRef = StringHelper.before(factoryMethod, ":");
+                if (typeOrRef != null) {
+                    // use another class with factory method
+                    factoryMethod = StringHelper.after(factoryMethod, ":");
                     // special to support factory method parameters
-                    answer = newInstanceFactoryParameters(camelContext, type, 
factoryMethod, parameters);
+                    Object existing = 
camelContext.getRegistry().lookupByName(typeOrRef);
+                    if (existing != null) {
+                        factoryClass = existing.getClass();
+                    } else {
+                        factoryClass = 
camelContext.getClassResolver().resolveMandatoryClass(typeOrRef);
+                    }
+                }
+                if (parameters != null) {
+                    Class<?> target = factoryClass != null ? factoryClass : 
type;
+                    answer = newInstanceFactoryParameters(camelContext, 
target, factoryMethod, parameters);
                 } else {
-                    answer = camelContext.getInjector().newInstance(type, 
factoryMethod);
+                    answer = camelContext.getInjector().newInstance(type, 
factoryClass, factoryMethod);
                 }
                 if (answer == null) {
                     throw new IllegalStateException(
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 7d49154c5ad..7dcff72a36b 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -1634,6 +1634,7 @@ public class ModelParser extends BaseParser {
     protected RegistryBeanDefinition doParseRegistryBeanDefinition() throws 
IOException, XmlPullParserException {
         return doParse(new RegistryBeanDefinition(), (def, key, val) -> {
             switch (key) {
+                case "factoryBean": def.setFactoryBean(val); break;
                 case "factoryMethod": def.setFactoryMethod(val); break;
                 case "name": def.setName(val); break;
                 case "type": def.setType(val); break;
diff --git 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index 2c2ef105ef5..4e228274c16 100644
--- 
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++ 
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2569,6 +2569,7 @@ public class ModelWriter extends BaseWriter {
         doWriteAttribute("factoryMethod", def.getFactoryMethod());
         doWriteAttribute("name", def.getName());
         doWriteAttribute("type", def.getType());
+        doWriteAttribute("factoryBean", def.getFactoryBean());
         doWriteElement("constructors", new 
BeanConstructorsAdapter().marshal(def.getConstructors()), 
this::doWriteBeanConstructorsDefinition);
         doWriteElement("properties", new 
BeanPropertiesAdapter().marshal(def.getProperties()), 
this::doWriteBeanPropertiesDefinition);
         endElement(name);
diff --git 
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java 
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
index 252c86dfce2..7cec1ce562b 100644
--- 
a/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
+++ 
b/core/camel-xml-io/src/main/java/org/apache/camel/xml/LwModelToXMLDumper.java
@@ -324,12 +324,14 @@ public class LwModelToXMLDumper implements 
ModelToXMLDumper {
             if (type.startsWith("#class:")) {
                 type = type.substring(7);
             }
-            String factoryMethod = b.getFactoryMethod();
-            if (factoryMethod != null) {
-                buffer.write(String.format("    <bean name=\"%s\" type=\"%s\" 
factoryMethod=\"%s\">%n", b.getName(), type, factoryMethod));
-            } else {
-                buffer.write(String.format("    <bean name=\"%s\" 
type=\"%s\">%n", b.getName(), type));
+            buffer.write(String.format("    <bean name=\"%s\" type=\"%s\"", 
b.getName(), type));
+            if (b.getFactoryBean() != null) {
+                buffer.write(String.format(" factory-bean=\"%s\"", 
b.getFactoryBean()));
             }
+            if (b.getFactoryMethod() != null) {
+                buffer.write(String.format(" factory-method=\"%s\"", 
b.getFactoryMethod()));
+            }
+            buffer.write(">\n");
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) 
{
                 buffer.write(String.format("        <constructors>%n"));
                 for (Map.Entry<Integer, Object> entry : 
b.getConstructors().entrySet()) {
diff --git 
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
 
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
index 8fa8b190bc9..02e3078c6b1 100644
--- 
a/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
+++ 
b/core/camel-xml-jaxb/src/main/java/org/apache/camel/xml/jaxb/JaxbModelToXMLDumper.java
@@ -337,12 +337,15 @@ public class JaxbModelToXMLDumper implements 
ModelToXMLDumper {
             if (type.startsWith("#class:")) {
                 type = type.substring(7);
             }
+            buffer.write(String.format("    <bean name=\"%s\" type=\"%s\"", 
b.getName(), type));
             String factoryMethod = b.getFactoryMethod();
-            if (factoryMethod != null) {
-                buffer.write(String.format("    <bean name=\"%s\" type=\"%s\" 
factoryMethod=\"%s\">%n", b.getName(), type, factoryMethod));
-            } else {
-                buffer.write(String.format("    <bean name=\"%s\" 
type=\"%s\">%n", b.getName(), type));
+            if (b.getFactoryBean() != null) {
+                buffer.write(String.format(" factory-bean=\"%s\"", 
b.getFactoryBean()));
             }
+            if (b.getFactoryMethod() != null) {
+                buffer.write(String.format(" factory-method=\"%s\"", 
b.getFactoryMethod()));
+            }
+            buffer.write(">\n");
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) 
{
                 buffer.write(String.format("        <constructors>%n"));
                 for (Map.Entry<Integer, Object> entry : 
b.getConstructors().entrySet()) {
diff --git 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
index 09249ae2272..08c098e8cf0 100644
--- 
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
+++ 
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/ModelWriter.java
@@ -2569,6 +2569,7 @@ public class ModelWriter extends BaseWriter {
         doWriteAttribute("factoryMethod", def.getFactoryMethod());
         doWriteAttribute("name", def.getName());
         doWriteAttribute("type", def.getType());
+        doWriteAttribute("factoryBean", def.getFactoryBean());
         doWriteElement("constructors", new 
BeanConstructorsAdapter().marshal(def.getConstructors()), 
this::doWriteBeanConstructorsDefinition);
         doWriteElement("properties", new 
BeanPropertiesAdapter().marshal(def.getProperties()), 
this::doWriteBeanPropertiesDefinition);
         endElement(name);
diff --git 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
index 7cca44385c9..2507043724a 100644
--- 
a/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
+++ 
b/core/camel-yaml-io/src/main/java/org/apache/camel/yaml/LwModelToYAMLDumper.java
@@ -312,11 +312,13 @@ public class LwModelToYAMLDumper implements 
ModelToYAMLDumper {
             if (type.startsWith("#class:")) {
                 type = type.substring(7);
             }
-            String factoryMethod = b.getFactoryMethod();
             buffer.write(String.format("    - name: %s%n", b.getName()));
             buffer.write(String.format("      type: \"%s\"%n", type));
-            if (factoryMethod != null) {
-                buffer.write(String.format("      factoryMethod: \"%s\"%n", 
factoryMethod));
+            if (b.getFactoryBean() != null) {
+                buffer.write(String.format("      factoryBean: \"%s\"%n", 
b.getFactoryBean()));
+            }
+            if (b.getFactoryMethod() != null) {
+                buffer.write(String.format("      factoryMethod: \"%s\"%n", 
b.getFactoryMethod()));
             }
             if (b.getConstructors() != null && !b.getConstructors().isEmpty()) 
{
                 buffer.write(String.format("      constructors:%n"));
diff --git a/docs/user-manual/modules/ROOT/pages/property-binding.adoc 
b/docs/user-manual/modules/ROOT/pages/property-binding.adoc
index 267e87a3fae..8687eee222c 100644
--- a/docs/user-manual/modules/ROOT/pages/property-binding.adoc
+++ b/docs/user-manual/modules/ROOT/pages/property-binding.adoc
@@ -28,6 +28,9 @@ The `PropertyBindingSupport` class supports binding String 
valued properties to
        `#class:com.foo.MyClassType#myFactoryMethod('Hello World', 5, true)`.
        Or if you need to create the instance via constructor parameters then 
you can specify the parameters as shown:
        `#class:com.foo.MyClass('Hello World', 5, true)`.
+       If the factory method is on another bean or class, then you must 
specify this as shown:
+       `#class:com.foo.MyClassType#com.foo.MyFactory:myFactoryMethod`. Where 
`com.foo.MyFactory` either refers to a FQN classname,
+       or can refer to an existing bean by id, such as: 
`#class:com.foo.MyClassType#myFactoryBean:myFactoryMethod`.
 - _valueAs(type):value_ - To declare that the value should be converted to the 
given type, such as `#valueAs(int):123`
        which indicates that the value 123 should be converted to an integer.
 - _ignore case_ - Whether to ignore case for property keys (will ignore by 
default)
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java
index c1a691db61e..8e8a7449bdd 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/KameletMainInjector.java
@@ -56,6 +56,15 @@ public class KameletMainInjector implements Injector {
         return delegate.newInstance(type, factoryMethod);
     }
 
+    @Override
+    public <T> T newInstance(Class<T> type, Class<?> factoryClass, String 
factoryMethod) {
+        boolean accept = acceptComponent(type);
+        if (!accept) {
+            return (T) delegate.newInstance(StubComponent.class);
+        }
+        return delegate.newInstance(type, factoryClass, factoryMethod);
+    }
+
     @Override
     public <T> T newInstance(Class<T> type, boolean postProcessBean) {
         boolean accept = acceptComponent(type);
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
index 5548661cae2..cba1329dd65 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/blueprint/BlueprintXmlBeansHandler.java
@@ -107,7 +107,11 @@ public class BlueprintXmlBeansHandler {
         rrd.setType(XmlHelper.getAttribute(node, "class"));
         rrd.setName(name);
 
-        // factory method
+        // factory bean/method
+        String fb = XmlHelper.getAttribute(node, "factory-ref");
+        if (fb != null) {
+            rrd.setFactoryBean(fb);
+        }
         String fm = XmlHelper.getAttribute(node, "factory-method");
         if (fm != null) {
             rrd.setFactoryMethod(fm);
@@ -210,8 +214,10 @@ public class BlueprintXmlBeansHandler {
                 type = "#class:" + type;
             }
             try {
-                // factory method
-                if (def.getFactoryMethod() != null) {
+                // factory bean/method
+                if (def.getFactoryBean() != null && def.getFactoryMethod() != 
null) {
+                    type = type + "#" + def.getFactoryBean() + ":" + 
def.getFactoryMethod();
+                } else if (def.getFactoryMethod() != null) {
                     type = type + "#" + def.getFactoryMethod();
                 }
                 // property binding support has constructor arguments as part 
of the type
diff --git 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
index 61ad9c44fa8..b371c5a2ac0 100644
--- 
a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
+++ 
b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/xml/spring/SpringXmlBeansHandler.java
@@ -236,7 +236,10 @@ public class SpringXmlBeansHandler {
             rrd.setName(name);
             model.addRegistryBean(rrd);
 
-            // factory method
+            // factory bean/method
+            if (def.getFactoryBeanName() != null) {
+                rrd.setFactoryBean(def.getFactoryBeanName());
+            }
             if (def.getFactoryMethodName() != null) {
                 rrd.setFactoryMethod(def.getFactoryMethodName());
             }
diff --git a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc 
b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
index 4b1e029eff7..f96371e8a0f 100644
--- a/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
+++ b/dsl/camel-xml-io-dsl/src/main/docs/java-xml-io-dsl.adoc
@@ -188,6 +188,40 @@ public class MyBean {
 
 NOTE: The factory method must be `public static` and from the same class as 
the created class itself.
 
+=== Creating beans from factory bean
+
+A bean can also be created from a factory bean as shown below:
+
+[source,xml]
+----
+       <bean name="myBean" type="com.acme.MyBean"
+          factoryBean="com.acme.MyHelper" factoryMethod="createMyBean">
+        <constructors>
+          <constructor index="0" value="true"/>
+          <constructor index="1" value="Hello World"/>
+        </constructors>
+       </bean>
+----
+
+TIP: `factoryBean` can also refer to an existing bean by bean id instead of 
FQN classname.
+
+When using `factoryBean` and `factoryMethod` then the arguments to this method 
is taken from `constructors`.
+So in the example above, this means that class `com.acme.MyHelper` should be 
as follows:
+
+[source,java]
+----
+public class MyHelper {
+
+    public static MyBean createMyBean(boolean important, String message) {
+        MyBean answer = ...
+        // create and configure the bean
+        return answer;
+    }
+}
+----
+
+NOTE: The factory method must be `public static`.
+
 
 == See Also
 
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 541eda03ae2..604378a65ba 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
@@ -327,8 +327,10 @@ public class XmlRoutesBuilderLoader extends 
RouteBuilderLoaderSupport {
         if (type != null && !type.startsWith("#")) {
             type = "#class:" + type;
             try {
-                // factory method
-                if (def.getFactoryMethod() != null) {
+                // factory bean/method
+                if (def.getFactoryBean() != null && def.getFactoryMethod() != 
null) {
+                    type = type + "#" + def.getFactoryBean() + ":" + 
def.getFactoryMethod();
+                } else if (def.getFactoryMethod() != null) {
                     type = type + "#" + def.getFactoryMethod();
                 }
                 // property binding support has constructor arguments as part 
of the type
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
index 59dddb4ef9d..3f031dccd43 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/XmlLoadAppTest.java
@@ -145,7 +145,7 @@ public class XmlLoadAppTest {
     }
 
     @Test
-    public void testLoadCamelAppWithBeanFactory() throws Exception {
+    public void testLoadCamelAppWithBeanFactoryMethod() throws Exception {
         try (DefaultCamelContext context = new DefaultCamelContext()) {
             context.start();
 
@@ -167,4 +167,27 @@ public class XmlLoadAppTest {
         }
     }
 
+    @Test
+    public void testLoadCamelAppWithBeanFactoryBeanAndMethod() throws 
Exception {
+        try (DefaultCamelContext context = new DefaultCamelContext()) {
+            context.start();
+
+            Resource resource = 
PluginHelper.getResourceLoader(context).resolveResource(
+                    "/org/apache/camel/dsl/xml/io/camel-app7.xml");
+
+            RoutesLoader routesLoader = PluginHelper.getRoutesLoader(context);
+            routesLoader.preParseRoute(resource, false);
+            routesLoader.loadRoutes(resource);
+
+            assertNotNull(context.getRoute("r7"), "Loaded r7 route should be 
there");
+            assertEquals(1, context.getRoutes().size());
+
+            // test that loaded route works
+            MockEndpoint y7 = context.getEndpoint("mock:y7", 
MockEndpoint.class);
+            y7.expectedBodiesReceived("Hello Pluto. I am Camel and 44 years 
old!");
+            context.createProducerTemplate().sendBody("direct:x7", "Pluto");
+            y7.assertIsSatisfied();
+        }
+    }
+
 }
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
index 3db2c39de10..f226415da8b 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
@@ -26,7 +26,7 @@ public class MyFacBean {
         return new MyFacBean(field1, field2);
     }
 
-    private MyFacBean(String field1, String field2) {
+    MyFacBean(String field1, String field2) {
         this.field1 = field1;
         this.field2 = field2;
     }
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java
similarity index 62%
copy from 
dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
copy to 
dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java
index 3db2c39de10..e27b943eafc 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
+++ 
b/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacHelper.java
@@ -16,39 +16,10 @@
  */
 package org.apache.camel.dsl.xml.io.beans;
 
-public class MyFacBean {
-
-    private String field1;
-    private String field2;
-    private int age;
+public class MyFacHelper {
 
     public static MyFacBean createBean(String field1, String field2) {
         return new MyFacBean(field1, field2);
     }
 
-    private MyFacBean(String field1, String field2) {
-        this.field1 = field1;
-        this.field2 = field2;
-    }
-
-    public String getField1() {
-        return field1;
-    }
-
-    public String getField2() {
-        return field2;
-    }
-
-    public int getAge() {
-        return age;
-    }
-
-    public void setAge(int age) {
-        this.age = age;
-    }
-
-    public String hi(String body) {
-        return field1 + " " + body + ". I am " + field2 + " and " + age + " 
years old!";
-    }
-
 }
diff --git 
a/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml
 
b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml
new file mode 100644
index 00000000000..396f9768418
--- /dev/null
+++ 
b/dsl/camel-xml-io-dsl/src/test/resources/org/apache/camel/dsl/xml/io/camel-app7.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<camel xmlns="http://camel.apache.org/schema/spring"; 
xmlns:s="http://www.springframework.org/schema/beans";>
+
+    <bean name="myHelper" 
type="org.apache.camel.dsl.xml.io.beans.MyFacHelper"/>
+
+    <bean name="xml-bean-from-registry" 
type="org.apache.camel.dsl.xml.io.beans.MyFacBean"
+          factoryBean="myHelper" factoryMethod="createBean">
+        <constructors>
+            <constructor value="Hello"/>
+            <constructor value="Camel"/>
+        </constructors>
+        <properties>
+            <property key="age" value="44"/>
+        </properties>
+    </bean>
+
+    <route id="r7">
+        <from uri="direct:x7"/>
+        <bean ref="xml-bean-from-registry" method="hi"/>
+        <to uri="mock:y7"/>
+    </route>
+
+</camel>
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index b8e5b914ec2..45c0068500c 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -12349,6 +12349,7 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
             order = 
org.apache.camel.dsl.yaml.common.YamlDeserializerResolver.ORDER_LOWEST - 1,
             properties = {
                     @YamlProperty(name = "constructors", type = "object"),
+                    @YamlProperty(name = "factory-bean", type = "string"),
                     @YamlProperty(name = "factory-method", type = "string"),
                     @YamlProperty(name = "name", type = "string", required = 
true),
                     @YamlProperty(name = "properties", type = "object"),
@@ -12374,6 +12375,11 @@ public final class ModelDeserializers extends 
YamlDeserializerSupport {
                     target.setConstructors(val);
                     break;
                 }
+                case "factory-bean": {
+                    String val = asText(node);
+                    target.setFactoryBean(val);
+                    break;
+                }
                 case "factory-method": {
                     String val = asText(node);
                     target.setFactoryMethod(val);
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
index 917ea258fd3..905d5a4ce21 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/main/java/org/apache/camel/dsl/yaml/deserializers/BeansDeserializer.java
@@ -91,19 +91,21 @@ public class BeansDeserializer extends 
YamlDeserializerSupport implements Constr
         beanCache.clear();
     }
 
-    public Object newInstance(RegistryBeanDefinition bean, CamelContext 
context) throws Exception {
+    public Object newInstance(RegistryBeanDefinition def, CamelContext 
context) throws Exception {
 
-        String type = bean.getType();
+        String type = def.getType();
 
-        // factory method
-        if (bean.getFactoryMethod() != null) {
-            type = type + "#" + bean.getFactoryMethod();
+        // factory bean/method
+        if (def.getFactoryBean() != null && def.getFactoryMethod() != null) {
+            type = type + "#" + def.getFactoryBean() + ":" + 
def.getFactoryMethod();
+        } else if (def.getFactoryMethod() != null) {
+            type = type + "#" + def.getFactoryMethod();
         }
         // property binding support has constructor arguments as part of the 
type
         StringJoiner ctr = new StringJoiner(", ");
-        if (bean.getConstructors() != null && 
!bean.getConstructors().isEmpty()) {
+        if (def.getConstructors() != null && !def.getConstructors().isEmpty()) 
{
             // need to sort constructor args based on index position
-            Map<Integer, Object> sorted = new 
TreeMap<>(bean.getConstructors());
+            Map<Integer, Object> sorted = new TreeMap<>(def.getConstructors());
             for (Object val : sorted.values()) {
                 String text = val.toString();
                 if (!StringHelper.isQuoted(text)) {
@@ -116,8 +118,8 @@ public class BeansDeserializer extends 
YamlDeserializerSupport implements Constr
 
         final Object target = PropertyBindingSupport.resolveBean(context, 
type);
 
-        if (bean.getProperties() != null && !bean.getProperties().isEmpty()) {
-            PropertyBindingSupport.setPropertiesOnTarget(context, target, 
bean.getProperties());
+        if (def.getProperties() != null && !def.getProperties().isEmpty()) {
+            PropertyBindingSupport.setPropertiesOnTarget(context, target, 
def.getProperties());
         }
 
         return target;
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 7a3c7e0c5fc..4c298bdbda4 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -8026,6 +8026,9 @@
           "constructors" : {
             "type" : "object"
           },
+          "factoryBean" : {
+            "type" : "string"
+          },
           "factoryMethod" : {
             "type" : "string"
           },
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
index 49c68aca270..71fd6f385e6 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
@@ -232,6 +232,41 @@ public class MyBean {
 
 NOTE: The factory method must be `public static` and from the same class as 
the created class itself.
 
+=== Creating beans from factory bean
+
+A bean can also be created from a factory bean as shown below:
+
+[source,yaml]
+----
+- beans:
+  - name: myBean
+    type: com.acme.MyBean
+    factoryBean: com.acme.MyHelper
+    factoryMethod: createMyBean
+    constructors:
+      0: true
+      1: "Hello World"
+----
+
+TIP: `factoryBean` can also refer to an existing bean by bean id instead of 
FQN classname.
+
+When using `factoryBean` and `factoryMethod` then the arguments to this method 
is taken from `constructors`.
+So in the example above, this means that class `com.acme.MyHelper` should be 
as follows:
+
+[source,java]
+----
+public class MyHelper {
+
+    public static MyBean createMyBean(boolean important, String message) {
+        MyBean answer = ...
+        // create and configure the bean
+        return answer;
+    }
+}
+----
+
+NOTE: The factory method must be `public static`.
+
 == Configuring options on languages
 
 Some xref:components:languages:index.adoc[Languages] have additional 
configurations
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
index eb24f59d604..1d3209c6627 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/BeansTest.groovy
@@ -20,6 +20,7 @@ import org.apache.camel.dsl.yaml.support.YamlTestSupport
 import org.apache.camel.dsl.yaml.support.model.MyBean
 import org.apache.camel.dsl.yaml.support.model.MyCtrBean
 import org.apache.camel.dsl.yaml.support.model.MyFacBean
+import org.apache.camel.dsl.yaml.support.model.MyFacHelper
 
 class BeansTest extends YamlTestSupport {
 
@@ -154,5 +155,28 @@ class BeansTest extends YamlTestSupport {
         }
     }
 
+    def "beans with factory helper"() {
+        when:
+        loadRoutes """
+                - beans:
+                  - name: myFac
+                    type: ${MyFacBean.class.name}
+                    factoryBean: ${MyFacHelper.class.name}
+                    factoryMethod: createBean
+                    constructors:
+                      0: 'fac1'
+                      1: 'fac2'
+                    properties:
+                      age: 43 
+            """
+
+        then:
+        with(context.registry.lookupByName('myFac'), MyFacBean) {
+            it.field1 == 'fac1'
+            it.field2 == 'fac2'
+            it.age == 43
+        }
+    }
+
 
 }
diff --git 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy
similarity index 54%
copy from 
dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
copy to 
dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy
index 3db2c39de10..93e19d044ed 100644
--- 
a/dsl/camel-xml-io-dsl/src/test/java/org/apache/camel/dsl/xml/io/beans/MyFacBean.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/groovy/org/apache/camel/dsl/yaml/support/model/MyFacHelper.groovy
@@ -14,41 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.camel.dsl.xml.io.beans;
+package org.apache.camel.dsl.yaml.support.model
 
-public class MyFacBean {
+class MyFacHelper {
 
-    private String field1;
-    private String field2;
-    private int age;
-
-    public static MyFacBean createBean(String field1, String field2) {
+    static MyFacBean createBean(String field1, String field2) {
         return new MyFacBean(field1, field2);
     }
 
-    private MyFacBean(String field1, String field2) {
-        this.field1 = field1;
-        this.field2 = field2;
-    }
-
-    public String getField1() {
-        return field1;
-    }
-
-    public String getField2() {
-        return field2;
-    }
-
-    public int getAge() {
-        return age;
-    }
-
-    public void setAge(int age) {
-        this.age = age;
-    }
-
-    public String hi(String body) {
-        return field1 + " " + body + ". I am " + field2 + " and " + age + " 
years old!";
-    }
-
 }

Reply via email to