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/10/26 12:47:41 UTC

[camel] branch main updated: Primary spring (#11842)

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 f275b466ced Primary spring (#11842)
f275b466ced is described below

commit f275b466ced60c3863139988f84a2dbe2a6ec39b
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Thu Oct 26 14:47:34 2023 +0200

    Primary spring (#11842)
    
    * CAMEL-20042: camel-spring - findSingleByType should favour @Primary beans from Spring
    
    * Bump threshold for test
    
    * CAMEL-20042: camel-spring - Autowire @Primary beans supported
    
    * CAMEL-20042: camel-spring - Autowire @Primary beans supported
    
    * CAMEL-20042: camel-sql - Stored component should use autowired datasource like sql do.
    
    * CAMEL-20042: camel-spring - Autowire @Primary beans supported
    
    * CAMEL-20042: camel-spring - Autowire @Primary beans supported
---
 .../camel/catalog/components/sql-stored.json       |  2 +-
 .../TransactedStackSizeBreakOnExceptionTest.java   |  2 +-
 .../org/apache/camel/spring/primary/Customer.java  | 23 ++++++
 .../apache/camel/spring/primary/CustomerImpl.java  | 31 ++++++++
 .../camel/spring/primary/FindSingleByTypeTest.java | 90 ++++++++++++++++++++++
 .../org/apache/camel/spring/primary/MyService.java | 36 +++++++++
 .../apache/camel/spring/primary/findBySingle.xml   | 38 +++++++++
 .../spi/ApplicationContextBeanRepository.java      | 13 ++++
 .../sql/stored/SqlStoredComponentConfigurer.java   |  5 ++
 .../camel/component/sql/stored/sql-stored.json     |  2 +-
 .../component/sql/stored/SqlStoredComponent.java   | 45 ++++-------
 .../component/sql/stored/SqlStoredEndpoint.java    |  4 +
 .../java/org/apache/camel/spi/BeanRepository.java  |  9 +--
 .../org/apache/camel/support/DefaultRegistry.java  | 26 +++++++
 .../ROOT/pages/camel-4x-upgrade-guide-4_2.adoc     | 15 +++-
 15 files changed, 304 insertions(+), 37 deletions(-)

diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql-stored.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql-stored.json
index 109ff9de0e7..db619b0c73b 100644
--- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql-stored.json
+++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/sql-stored.json
@@ -22,7 +22,7 @@
     "lenientProperties": false
   },
   "componentProperties": {
-    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
+    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
     "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
     "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
   },
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactedStackSizeBreakOnExceptionTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactedStackSizeBreakOnExceptionTest.java
index b1a6de28a69..11149462fd3 100644
--- a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactedStackSizeBreakOnExceptionTest.java
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/interceptor/TransactedStackSizeBreakOnExceptionTest.java
@@ -27,7 +27,7 @@ public class TransactedStackSizeBreakOnExceptionTest extends TransactionClientDa
 
     private static final boolean PRINT_STACK_TRACE = false;
     private int total = 100;
-    private int failAt = 75;
+    private int failAt = 80;
 
     @Test
     public void testStackSize() throws Exception {
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java
new file mode 100644
index 00000000000..283f848bbbd
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/Customer.java
@@ -0,0 +1,23 @@
+/*
+ * 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.spring.primary;
+
+public interface Customer {
+
+    String name();
+
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.java
new file mode 100644
index 00000000000..bbe788beb8c
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/CustomerImpl.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.spring.primary;
+
+public class CustomerImpl implements Customer {
+
+    private final String name;
+
+    public CustomerImpl(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java
new file mode 100644
index 00000000000..e570c05bd7a
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/FindSingleByTypeTest.java
@@ -0,0 +1,90 @@
+/*
+ * 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.spring.primary;
+
+import java.util.Set;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.NoSuchBeanTypeException;
+import org.apache.camel.model.ModelCamelContext;
+import org.apache.camel.spi.UuidGenerator;
+import org.apache.camel.util.IOHelper;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.context.support.AbstractApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class FindSingleByTypeTest extends ContextTestSupport {
+
+    private AbstractApplicationContext applicationContext;
+
+    @Override
+    @BeforeEach
+    public void setUp() throws Exception {
+        super.setUp();
+        if (context != null) {
+            context.stop();
+        }
+        applicationContext = new ClassPathXmlApplicationContext("org/apache/camel/spring/primary/findBySingle.xml");
+        context = applicationContext.getBean("myCamel", ModelCamelContext.class);
+    }
+
+    @Override
+    @AfterEach
+    public void tearDown() throws Exception {
+        // we're done so let's properly close the application context
+        IOHelper.close(applicationContext);
+
+        super.tearDown();
+    }
+
+    @Test
+    public void testFindSingle() {
+        // should find primary
+        Customer c = context.getRegistry().findSingleByType(Customer.class);
+
+        Assertions.assertNotNull(c);
+        Assertions.assertEquals("Donald", c.name());
+
+        // should not find anything
+        Object o = context.getRegistry().findSingleByType(UuidGenerator.class);
+        Assertions.assertNull(o);
+    }
+
+    @Test
+    public void testFindByType() {
+        // should find primary
+        Set<Customer> set = context.getRegistry().findByType(Customer.class);
+
+        // should find both beans
+        Assertions.assertEquals(2, set.size());
+    }
+
+    @Test
+    public void testFindSingleMandatory() {
+        // should find primary
+        Customer c = context.getRegistry().mandatoryFindSingleByType(Customer.class);
+        Assertions.assertEquals("Donald", c.name());
+
+        // should not find anything
+        Assertions.assertThrows(NoSuchBeanTypeException.class,
+                () -> context.getRegistry().mandatoryFindSingleByType(UuidGenerator.class));
+    }
+
+}
diff --git a/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java
new file mode 100644
index 00000000000..a458b2cca4b
--- /dev/null
+++ b/components/camel-spring-xml/src/test/java/org/apache/camel/spring/primary/MyService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.spring.primary;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+
+@Component
+public class MyService {
+
+    @Bean
+    @Primary
+    public Customer goodCustomer() {
+        return new CustomerImpl("Donald");
+    }
+
+    @Bean
+    public Customer badCustomer() {
+        return new CustomerImpl("Jack");
+    }
+}
diff --git a/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml
new file mode 100644
index 00000000000..199b59ae39f
--- /dev/null
+++ b/components/camel-spring-xml/src/test/resources/org/apache/camel/spring/primary/findBySingle.xml
@@ -0,0 +1,38 @@
+<?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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://camel.apache.org/schema/spring
+       http://camel.apache.org/schema/spring/camel-spring.xsd
+	http://www.springframework.org/schema/beans
+           http://www.springframework.org/schema/beans/spring-beans.xsd
+           http://www.springframework.org/schema/context
+           http://www.springframework.org/schema/context/spring-context.xsd">
+
+  <context:component-scan base-package="org.apache.camel.spring.primary"/>
+
+  <!-- empty -->
+  <camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">
+    <jmxAgent id="jmx" disabled="true"/>
+  </camelContext>
+
+
+</beans>
diff --git a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
index 95be7e1fcd9..fb1e2a2ee25 100644
--- a/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
+++ b/components/camel-spring/src/main/java/org/apache/camel/spring/spi/ApplicationContextBeanRepository.java
@@ -25,6 +25,7 @@ import org.apache.camel.spi.BeanRepository;
 import org.springframework.beans.factory.BeanFactoryUtils;
 import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.beans.factory.config.NamedBeanHolder;
 import org.springframework.context.ApplicationContext;
 
 /**
@@ -83,4 +84,16 @@ public class ApplicationContextBeanRepository implements BeanRepository {
         return BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, type);
     }
 
+    @Override
+    public <T> T findSingleByType(Class<T> type) {
+        try {
+            // this API allows to support @Primary beans that should take precedence in
+            // case there are 2+ beans of the same type.
+            NamedBeanHolder<T> holder = applicationContext.getAutowireCapableBeanFactory().resolveNamedBean(type);
+            return holder.getBeanInstance();
+        } catch (NoSuchBeanDefinitionException e) {
+            return null;
+        }
+    }
+
 }
diff --git a/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/SqlStoredComponentConfigurer.java b/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/SqlStoredComponentConfigurer.java
index e5eda080e46..87140ea58b5 100644
--- a/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/SqlStoredComponentConfigurer.java
+++ b/components/camel-sql/src/generated/java/org/apache/camel/component/sql/stored/SqlStoredComponentConfigurer.java
@@ -31,6 +31,11 @@ public class SqlStoredComponentConfigurer extends PropertyConfigurerSupport impl
         }
     }
 
+    @Override
+    public String[] getAutowiredNames() {
+        return new String[]{"dataSource"};
+    }
+
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase() : name) {
diff --git a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
index 109ff9de0e7..db619b0c73b 100644
--- a/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
+++ b/components/camel-sql/src/generated/resources/org/apache/camel/component/sql/stored/sql-stored.json
@@ -22,7 +22,7 @@
     "lenientProperties": false
   },
   "componentProperties": {
-    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": false, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
+    "dataSource": { "index": 0, "kind": "property", "displayName": "Data Source", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "javax.sql.DataSource", "deprecated": false, "autowired": true, "secret": false, "description": "Sets the DataSource to use to communicate with the database." },
     "lazyStartProducer": { "index": 1, "kind": "property", "displayName": "Lazy Start Producer", "group": "producer", "label": "producer", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": false, "description": "Whether the producer should be started lazy (on the first message). By starting lazy you can use this to allow CamelContext and routes to startup in situations where a producer may otherwise fail [...]
     "autowiredEnabled": { "index": 2, "kind": "property", "displayName": "Autowired Enabled", "group": "advanced", "label": "advanced", "required": false, "type": "boolean", "javaType": "boolean", "deprecated": false, "autowired": false, "secret": false, "defaultValue": true, "description": "Whether autowiring is enabled. This is used for automatic autowiring options (the option must be marked as autowired) by looking up in the registry to find if there is a single instance of matching t [...]
   },
diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredComponent.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredComponent.java
index 20754ab017b..1a4f329c8d8 100644
--- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredComponent.java
+++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredComponent.java
@@ -17,7 +17,6 @@
 package org.apache.camel.component.sql.stored;
 
 import java.util.Map;
-import java.util.Set;
 
 import javax.sql.DataSource;
 
@@ -36,45 +35,35 @@ public class SqlStoredComponent extends DefaultComponent {
 
     private static final Logger LOG = LoggerFactory.getLogger(SqlStoredComponent.class);
 
-    @Metadata
+    @Metadata(autowired = true)
     private DataSource dataSource;
 
     @Override
     protected Endpoint createEndpoint(String uri, String template, Map<String, Object> parameters) throws Exception {
-        DataSource target = null;
 
-        // endpoint options overrule component configured datasource
-        DataSource ds = resolveAndRemoveReferenceParameter(parameters, "dataSource", DataSource.class);
-        if (ds != null) {
-            target = ds;
-        }
-        if (target == null) {
-            // fallback and use component
-            target = dataSource;
-        }
-        if (target == null) {
-            // check if the registry contains a single instance of DataSource
-            Set<DataSource> dataSources = getCamelContext().getRegistry().findByType(DataSource.class);
-            if (dataSources.size() > 1) {
-                throw new IllegalArgumentException(
-                        "Multiple DataSources found in the registry and no explicit configuration provided");
-            } else if (dataSources.size() == 1) {
-                target = dataSources.iterator().next();
-            }
+        SqlStoredEndpoint endpoint = new SqlStoredEndpoint(uri, this);
+        endpoint.setTemplate(template);
+        setProperties(endpoint, parameters);
+
+        // endpoint configured data source takes precedence
+        DataSource ds = dataSource;
+        if (endpoint.getDataSource() != null) {
+            ds = endpoint.getDataSource();
         }
-        if (target == null) {
+        if (ds == null) {
             throw new IllegalArgumentException("DataSource must be configured");
         }
-        LOG.trace("Using DataSource: {}", target);
-
-        JdbcTemplate jdbcTemplate = new JdbcTemplate(target);
 
+        // create template
+        JdbcTemplate jdbcTemplate = new JdbcTemplate(ds);
         Map<String, Object> templateOptions = PropertiesHelper.extractProperties(parameters, "template.");
         PropertyBindingSupport.bindProperties(getCamelContext(), jdbcTemplate, templateOptions);
 
-        SqlStoredEndpoint endpoint = new SqlStoredEndpoint(uri, this, jdbcTemplate);
-        endpoint.setTemplate(template);
-        setProperties(endpoint, parameters);
+        // set template on endpoint
+        endpoint.setJdbcTemplate(jdbcTemplate);
+        endpoint.setDataSource(ds);
+        endpoint.setTemplateOptions(templateOptions);
+
         return endpoint;
     }
 
diff --git a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredEndpoint.java b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredEndpoint.java
index 1f4c345042d..0f9b3622eb9 100644
--- a/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredEndpoint.java
+++ b/components/camel-sql/src/main/java/org/apache/camel/component/sql/stored/SqlStoredEndpoint.java
@@ -64,6 +64,10 @@ public class SqlStoredEndpoint extends DefaultEndpoint {
               description = "Configures the Spring JdbcTemplate with the key/values from the Map")
     private Map<String, Object> templateOptions;
 
+    public SqlStoredEndpoint(String uri, SqlStoredComponent component) {
+        super(uri, component);
+    }
+
     public SqlStoredEndpoint(String uri, SqlStoredComponent component, JdbcTemplate jdbcTemplate) {
         super(uri, component);
         setJdbcTemplate(jdbcTemplate);
diff --git a/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java b/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
index fb875d2812f..ecb9ab93675 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/BeanRepository.java
@@ -88,12 +88,11 @@ public interface BeanRepository {
      *              found.
      */
     default <T> T mandatoryFindSingleByType(Class<T> type) {
-        Set<T> set = findByType(type);
-        if (set.size() == 1) {
-            return set.iterator().next();
-        } else {
-            throw new NoSuchBeanTypeException(type, set.size());
+        T answer = findSingleByType(type);
+        if (answer == null) {
+            throw new NoSuchBeanTypeException(type);
         }
+        return answer;
     }
 
     /**
diff --git a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
index 0cdca505fae..dd52d5a6b07 100644
--- a/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
+++ b/core/camel-support/src/main/java/org/apache/camel/support/DefaultRegistry.java
@@ -342,6 +342,32 @@ public class DefaultRegistry extends ServiceSupport implements Registry, LocalBe
         return answer;
     }
 
+    @Override
+    public <T> T findSingleByType(Class<T> type) {
+        T found = null;
+
+        // local repository takes precedence
+        BeanRepository local = localRepositoryEnabled ? localRepository.get() : null;
+        if (local != null) {
+            found = local.findSingleByType(type);
+        }
+
+        if (found == null && repositories != null) {
+            for (BeanRepository r : repositories) {
+                found = r.findSingleByType(type);
+            }
+        }
+
+        if (found == null) {
+            found = supplierRegistry.findSingleByType(type);
+        }
+        if (found == null) {
+            found = fallbackRegistry.findSingleByType(type);
+        }
+
+        return found;
+    }
+
     @Override
     protected void doStop() throws Exception {
         super.doStop();
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
index dca28e4a9a3..40457010611 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_2.adoc
@@ -39,8 +39,21 @@ expected to function correctly if using Java 21.
 
 A new release of Apache Ignite is expected that will start to support Java 21.
 
-=== camel-spring-boot
+=== camel-spring & camel-spring-boot
+
+==== Autowiring Primary beans
+
+Camel will now take into account `@Primary` beans from Spring when autowiring by type.
+For example a JDBC `DataSource` in the SQL component will now use the `@Primary` data source
+when multiple data sources are defined.
+
+Previously Camel would not autowire if there are 2 or more beans for a given type.
+
+NOTE: This is a change in behaviour, that can affect your applications when upgrading.
+
+==== Properties
 
 The `initialProperties` and `overrideProperties` on Camel `PropertiesComponent` will now
 take precedence over Spring Boot properties. This can be used for testing purpose,
 to allow overriding properties when using `CamelTestSupport` for unit testing.
+