Confusion about "No qualifying bean of type...RedisConnectionFactory" - spring-boot

Since the different configs, I have written two LettuceConnectionFactory:
ItemFactoryConfig
ActivityFactoryConfig
the code as follows:
#Configuration
#PropertySource(value = "classpath:config/redis.properties", encoding = "UTF-8")
public class ItemFactoryConfig {
#Value("${item.host}")
private String host;
#Value("${item.port}")
private int port;
#Bean
public LettuceConnectionFactory itemFactory(){
RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
redisConfiguration.setHostName(host);
redisConfiguration.setPort(port);
return new LettuceConnectionFactory(redisConfiguration);
}
}
#Configuration
#PropertySource(value = "classpath:config/redis.properties", encoding = "UTF-8")
public class ActivityFactoryConfig {
#Value("${activity.host}")
private String host;
#Value("${activity.port}")
private int port;
#Bean
public LettuceConnectionFactory activityFactory(){
RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration();
redisConfiguration.setHostName(host);
redisConfiguration.setPort(port);
return new LettuceConnectionFactory(redisConfiguration);
}
}
And I added #Qualifier when these LettuceConnectionFactories were autowired.
#Configuration
public class ItemTemplateConfig {
#Autowired
#Qualifier("itemFactory")
private LettuceConnectionFactory itemFactory;
#Bean
public RedisTemplate<String, String> itemTemplate(){...}
}
#Configuration
public class ActivityTemplateConfig {
#Autowired
#Qualifier("activityFactory")
private LettuceConnectionFactory activityFactory;
#Bean
public RedisTemplate<String, String> activityTemplate(){...}
}
However, spring boot still report the NoUniqueBeanDefinitionException.
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'org.springframework.data.redis.connection.RedisConnectionFactory' available: expected single matching bean but found 2: activityFactory,itemFactory
Spring boot Description:
Parameter 0 of method redisTemplate in org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration required a single bean, but 2 were found:
- activityFactory: defined by method 'activityFactory' in class path resource [com/lyf/redisdemo/config/Activity/ActivityFactoryConfig.class]
- itemFactory: defined by method 'itemFactory' in class path resource [com/lyf/redisdemo/config/Item/ItemFactoryConfig.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
spring boot: 2.1.0.RELEASE

Related

Autowireing of Qualified Datasource not working

I have a SpringBoot 2.0.4 app, and I am using JdbcTemplates. I had it all working, when I got a requirement to do a data transfer between 2 DBs.
So I set up 2 Data Sources like this:
#Configuration
public class OracleConfiguration {
#Bean(name = "oracleDataSource")
#ConfigurationProperties(prefix = "oracle.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
and my DAO is like this:
#Repository
#Component
public class personDao extends JdbcDaoSupport {
static final Logger logger = LoggerFactory.getLogger(CymNetworkDao.class);
#Autowired
public void setDs(#Qualifier("oracleDataSource") DataSource dataSource) {
setDataSource(dataSource);
}
public List<PersonBean> findAll() {
List<PersonBean> result = getJdbcTemplate().query("SELECT * FROM PERSON", new PersonRowMapper());
return result;
}
}
I am getting this error:
Unsatisfied dependency expressed through field 'jdbcTemplate';
nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'org.springframework.jdbc.core.JdbcTemplate' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=oracleJdbcTemplate)}
My reading of tutorials was telling me to just autowire the datasource, and the jdbcTemplate would create itself. Am I qualifying wrong or something else?
you should create jdbcTemplate by injecting DataSource like following example
#Repository
public class personDao {
private JdbcTemplate oracleJdbcTemplate;
#Autowired
public void setDataSource(#Qualifier("oracleDataSource") DataSource dataSource) {
this.oracleJdbcTemplate = new JdbcTemplate(dataSource);
}
And in error message it clearly shows it was missing JdbcTemplate bean
#org.springframework.beans.factory.annotation.Qualifier(value=oracleJdbcTemplate)}

Unit Test Error creating bean with name "amqAdmin" while testing spring integration TCP component

I'm writing a sidecar micro service that talks TCP to a legacy app and uses rabbitMQ on the other side. I'm just getting started and writing tests as I go along to better understand how everything works as I'm new to Spring. My app builds, deploys, and runs fine.
However, writing clean tests has been a bit more complicated. I have been patterning my code off the spring-integration tcp basic examples. I started with having a mock TCPServer to test my code defined outside my test class. But the mock TCPServer kept being built for every test class. So I moved it into the TCPGatewayTest class similar to the example I found.
This led to some missing bean issues which led me to add a #ContextConfiguration annotation which has gotten me further. But now I have other missing beans. I'm sure I'm messing up my ApplicationContext with #ContextConfiguration. Is there a better way of doing this with a different annotation or doing it slightly different? I'm not going the xml route and would like to steer clear of it if possible.
The familiar no qualifying bean error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.amqp.rabbit.connection.ConnectionFactory' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
My Test Class is the following
package org.inc.imo;
#ComponentScan("org.inc")
#ContextConfiguration(classes = {TCPGatewayTest.TCPServerMock.class,
org.inc.imo.Configuration.TCPConfig.class,
org.inc.imo.Configuration.RabbitConfig.class })
#TestPropertySource(locations= "classpath:test.properties")
#RunWith(SpringRunner.class)
#SpringBootTest
public class TCPGatewayTest {
#Autowired
private TCPGateway gateway;
#Autowired
AbstractServerConnectionFactory crLfServer;
#Before
public void setup() {
TestingUtilities.waitListening(this.crLfServer, 10000L);
}
#Test
public void testConnectionToMockServer() {
String result = gateway.send("Hello World");
assertEquals("HELLO WORLD", result);
}
#Configuration
#MessageEndpoint
public static class TCPServerMock {
#Value("${imo.port}")
private int port;
#Bean()
public AbstractServerConnectionFactory serverCF() {
return new TcpNetServerConnectionFactory(this.port);
}
#Transformer(inputChannel="fromTcp", outputChannel="toEcho")
public String convert(byte[] bytes) {
return new String(bytes);
}
#ServiceActivator(inputChannel="toEcho")
public String upCase(String in) {
return in.toUpperCase();
}
#Bean
public TcpInboundGateway tcpInGate(AbstractServerConnectionFactory connectionFactory) {
TcpInboundGateway inGate = new TcpInboundGateway();
inGate.setConnectionFactory(connectionFactory);
inGate.setRequestChannel(fromTcp());
return inGate;
}
#Bean
public MessageChannel fromTcp() {
return new DirectChannel();
}
}
}
Rabbit Config Class
package org.inc.imo.configuration;
#EnableIntegration
#IntegrationComponentScan
#ComponentScan
#Configuration
public class RabbitConfig {
public final static String IMO_REQUEST_JEA = "imo.request.jea";
public final static String IMO_REQUEST_INFO = "imo.request.info";
#Bean
public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) {
RabbitAdmin admin = new RabbitAdmin(connectionFactory);
admin.declareQueue(jeaRequestQueue());
admin.declareQueue(infoRequestQueue());
return admin;
}
#Bean
public Queue jeaRequestQueue() {
return new Queue(IMO_REQUEST_JEA);
}
#Bean
public Queue infoRequestQueue() {
return new Queue(IMO_REQUEST_INFO);
}
#Bean
public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
#Bean
public ObjectWriter objectWriter() {
return new ObjectMapper().writer();
}
}
TCPConfig class
package org.inc.imo.configuration;
#EnableIntegration
#IntegrationComponentScan
#ComponentScan
#Configuration
public class TCPConfig {
#Value("${imo.hostname}")
private String host;
#Value("${imo.port}")
private int port;
private static MessageChannel sendChannel;
private static MessageChannel replyChannel;
#Bean
public MessageChannel replyChannel() {
replyChannel = new DirectChannel();
return replyChannel;
}
#Bean(name="sendChannel")
public MessageChannel sendChannel() {
MessageChannel directChannel = new DirectChannel();
sendChannel = directChannel;
return directChannel;
}
#Bean
public TcpNetClientConnectionFactory connectionFactory() {
TcpNetClientConnectionFactory connectionFactory = new TcpNetClientConnectionFactory(host, port);
connectionFactory.setSingleUse(false);
return connectionFactory;
}
#Bean
#ServiceActivator(inputChannel = "sendChannel")
public TcpOutboundGateway tcpOutboundGateway() {
TcpOutboundGateway tcpOutboundGateway = new TcpOutboundGateway();
tcpOutboundGateway.setConnectionFactory(connectionFactory());
tcpOutboundGateway.setReplyChannel(this.replyChannel());
tcpOutboundGateway.setRequiresReply(true);
return tcpOutboundGateway;
}
}
TCPGateway Interface
package org.inc.imo.Domain;
#MessagingGateway(defaultRequestChannel = "sendChannel")
public interface TCPGateway {
String send(String message);
}
The exception is like:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.amqp.rabbit.connection.ConnectionFactory' available: expected at least 1 bean which qualifies as autowire candidate.
And the suffered bean is like:
#Bean
public AmqpAdmin amqpAdmin(final ConnectionFactory connectionFactory) {
RabbitAdmin admin = new RabbitAdmin(connectionFactory);
admin.declareQueue(jeaRequestQueue());
admin.declareQueue(infoRequestQueue());
return admin;
}
So, you request here org.springframework.amqp.rabbit.connection.ConnectionFactory bean injection, but there is no one.
If you simply test against the local RabbitMQ, there is just enough to add one more bean:
#Bean
ConnectionFactory connectionFactory() {
return new CachingConnectionFactory();
}
and your AmqpAdmin will be able to connect there.

#Bean does not get injected by Name

I am using Spring Boot 1.3, and I have the configuration class below:
#Configuration
public class MainConfig {
#Bean(name="dateAndTimeFormater")
public SimpleDateFormat dateAndTimeFormater(){
return new SimpleDateFormat("yyyy-MM-dd"+Constants.STRING_SEPARATOR+"hh:mm");
}
#Bean(name="dateFormater")
public SimpleDateFormat dateFormaterBean(){
return new SimpleDateFormat("yyyy-MM-dd"+Constants.STRING_SEPARATOR+"hh:mm");
}
}
When I try to inject one of the below beans by name, it throws :
No qualifying bean of type [java.text.SimpleDateFormat] is defined: expected single matching bean but found 2: dateAndTimeFormater,dateFormater.
here is where I am injecting the bean:
private static SimpleDateFormat sdf;
#Autowired
#Qualifier("dateAndTimeFormater")
public static void setSdf(SimpleDateFormat sdf) {
myClass.sdf = sdf;
}
I tried with #Ressource, #Inject. it didn't work.
Any Advise will be much appreciated?
It is because you are trying to wire that static method, spring container will not wire dependencies looking static references or methods, why can't you do that
#Autowired
#Qualifier("dateAndTimeFormater")
public void setSdf(SimpleDateFormat sdf) {
myClass.sdf = sdf;
}

Could not find default ScheduledExecutorService bean with Redis and Spring

I have just develop a configuration with Spring Session and Redis, everything work fine, but in my console logs, I got
2015-06-29 15:45:44,088 [main] DEBUG org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor - Could not find default ScheduledExecutorService bean
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.scheduling.TaskScheduler] is defined
How can I configure ScheduledExecutorService bean?
Update :
#Configuration
#EnableRedisHttpSession
#Conditional(RedisDeclarationCondition.class)
public class LocalRedisConfig extends WebMVCConfig{
#Value("${redis.host}")
private String host;
#Value("${redis.port}")
private String port;
#Bean
public JedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisConnectionFactory jedisConnectionFactory(){
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(5);
poolConfig.setMaxTotal(10);
poolConfig.setMinIdle(1);
poolConfig.setTestOnBorrow(true);
poolConfig.setTestOnReturn(true);
poolConfig.setTestWhileIdle(true);
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(poolConfig);
// RedisOperationsSessionRepository cleanup = new RedisOperationsSessionRepository(jedisConnectionFactory);
//optional
//jedisConnectionFactory.setHostName(host);
//jedisConnectionFactory.setPort(Integer.valueOf(port));
return jedisConnectionFactory;
}
#Bean
public StringRedisTemplate redisTemplate(){
StringRedisTemplate redisTemplate = new StringRedisTemplate(jedisConnectionFactory());
return redisTemplate;
}
it is a DEBUG message, could be ignored by set your logging.level.org.springframework=INFO
Using #Scheduled and #EnableScheduling but gives NoSuchBeanDefinitionException

Spring Java Configuration: Create Bean From Dynamic Class Name

My situation is that the class name of a bean can be configured by an external properties-file. It's fine to do this with XML
<bean id="myService" class="${app.serviceClass}" />
and properties-file
app.serviceClass=com.example.GreatestClassThereIs
I tried to convert it to Java using a BeanFactoryPostProcessor:
#Configuration
public class MyConfiguration implements BeanFactoryPostProcessor {
#Value("${app.serviceClass}")
private String serviceClassName;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
logger.warn("let's register bean of class " + serviceClassName + "...");
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(serviceClassName);
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
registry.registerBeanDefinition("myService", beanDefinition);
logger.warn("done " + beanDefinition);
}
}
The problem is that the lifecycle of Spring hasn't yet set handled the #Value and serviceClassName is null. How do I get the property in there?
Why not simply define a new Bean in your ApplicationContext by using Class.forName() from the #Value injected into the #Configuration?
So something like this:
#Configuration
public class MyConfiguration
{
#Value("${app.serviceClass}")
private String serviceClassName;
#Bean
public Object myService()
{
return Class.forName(serviceClassName).newInstance();
}
}
EDIT by sjngm (for better readability than in the comment):
#Bean
public MyInterface myService()
{
Class<?> serviceClass = Class.forName(serviceClassName);
MyInterface service = MyInterface.class.cast(serviceClass.newInstance());
return service;
}

Resources