I have my Configuration Class with some Dependent Beans
public class WebConfig{
#Bean
#Qualifier("geojedis")
public StringRedisTemplate geoJedisTemplate(
#Qualifier("geographyJedisConnectionFactory") final JedisConnectionFactory connectionFactory) {
// Create a RedisTemplate implementation which is basically of string
// data structure.
StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory);
return redisTemplate;
}
#Bean
#Qualifier("capacityStringRedisTemplate")
public StringRedisTemplate capacityStringRedisTemplate(
#Qualifier("capacityJedisConnectionFactory") final JedisConnectionFactory connectionFactory) {
// Create a RedisTemplate implementation which is basically of string
// data structure.
StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory);
return redisTemplate;
}
#Bean
public JedisConnectionFactory geographyJedisConnectionFactory() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
return connectionFactory;
}
#Bean
public JedisConnectionFactory capacityJedisConnectionFactory() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
return connectionFactory;
}
}
But I am getting the below error. When i checked the configurations all are fine and I have also defined the Qualifier for mapping the correct dependencies. Any help is much appreciated.
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'redisTemplate' defined in class path
resource
[org/springframework/boot/autoconfigure/redis/RedisAutoConfiguration$RedisConfiguration.class]:
Unsatisfied dependency expressed through constructor argument with
index 0 of type
[org.springframework.data.redis.connection.RedisConnectionFactory]: :
No qualifying bean of type
[org.springframework.data.redis.connection.RedisConnectionFactory] is
defined: expected single matching bean but found 2:
geographyJedisConnectionFactory,capacityJedisConnectionFactory; nested
exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type
[org.springframework.data.redis.connection.RedisConnectionFactory] is
defined: expected single matching bean but found 2:
geographyJedisConnectionFactory,capacityJedisConnectionFactory
There is a bean inside RedisAutoConfiguration that is created if there is no default "redisTemplate" in Spring Context.
#Bean
#ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
This one needs a single RedisConnectionFactory, but finds two.
As a work-around you can create a dummy RedisTemplate with the name "redisTemplate" and not use it.
Since it checks by bean name, the following could be enough as long as nothing tries to #Autowire it:
#Bean
public Object redisTemplate() {
return new Object();
}
You can simply call the connection factory bean creation method instead of injection:
#Bean
public StringRedisTemplate capacityStringRedisTemplate() {
// Create a RedisTemplate implementation which is basically of string
// data structure.
StringRedisTemplate redisTemplate =
new StringRedisTemplate(capacityJedisConnectionFactory());
return redisTemplate;
}
This will point directly to the one your looking for
use #EnableAutoConfiguration(exclude = RedisAutoConfiguration.class) above your config class and provide the custom connection properties
Related
I have two bean configurations:
#Bean("name1")
public RabbitTemplate template1(RabbitProperties properties, ConnectionFactory factory) {
return rabbitCreateOneTemplate(factory, properties.getTemplate());
}
#Bean("name2")
public RabbitTemplate template2(RabbitProperties properties, ConnectionFactory factory) {
return rabbitCreateTwoTemplate(factory, properties.getTemplate());
}
And inject
private final RabbitTemplate template;
public Const( #Qualifier("name1") RabbitTemplate template){
this.template = template
}
I want by object template to get it name, how to get it?
I know the is getBeanDefinitionNames() but it will return the names of all beans, but I need to get the bean name of only a specific object. I want to give template and get "name1", is it possible to do this?
I am trying to implement remember function for my Spring Security Application. And I used Redis also. I configured, SpringSessionBackedSessionRegistry beans. But when I try to #Autowired FindByIndexNameSessionRepository I got the following error,
Caused by: java.lang.IllegalArgumentException: sessionRepository
cannot be null
The code is as follows,
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private FindByIndexNameSessionRepository<? extends Session> sessionRepository;
#Bean
public SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry(this.sessionRepository);
}
}
Handler.java
#Component
public class Handler {
private #Autowired SessionRegistry sessionRegistry;
private #Autowired FindByIndexNameSessionRepository<? extends Session> sessionRepository;
}
And Redis Session Configuration,
#Configuration
#EnableSpringHttpSession
public class SessionConfig {
private final RedisConnectionFactory redisConnectionFactory;
public SessionConfig(ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
this.redisConnectionFactory = redisConnectionFactory.getIfAvailable();
}
#Bean
public RedisOperations<String, Object> sessionRedisOperations() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
#Bean
public RedisSessionRepository redisSessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
return new RedisSessionRepository(sessionRedisOperations);
}
}
I get errors like,
Caused by: java.lang.IllegalArgumentException: sessionRepository
cannot be null Caused by:
org.springframework.beans.BeanInstantiationException: Failed to
instantiate
[org.springframework.session.security.SpringSessionBackedSessionRegistry]:
Circular reference involving containing bean 'securityConfig' -
consider declaring the factory method as static for independence from
its containing instance. Factory method 'sessionRegistry' threw
exception; nested exception is java.lang.IllegalArgumentException:
sessionRepository cannot be null Caused by:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'sessionRegistry' defined in class path
resource [SecurityConfig.class]: Bean instantiation via factory method
failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate
[org.springframework.session.security.SpringSessionBackedSessionRegistry]:
Circular reference involving containing bean 'securityConfig' -
consider declaring the factory method as static for independence from
its containing instance. Factory method 'sessionRegistry' threw
exception; nested exception is java.lang.IllegalArgumentException:
sessionRepository cannot be null
Also it says,
No beans of 'FindByIndexNameSessionRepository<? extends Session>' type
found.
So what I am doing wrong here?
Maybe, I am not sure, because this circular dependency error...
In any case, the problem probably is happening because you do not actually have a FindByIndexNameSessionRepository injected in your Spring context, at least not the one you are trying to configure, but a SessionRepository, because you are defining a bean of type RedisSessionRepository and this interface implements SessionRepository, not FindByIndexNameSessionRepository. Please, try to use a RedisIndexedSessionRepository instead, something like:
#Configuration
#EnableSpringHttpSession
public class SessionConfig {
private final RedisConnectionFactory redisConnectionFactory;
public SessionConfig(ObjectProvider<RedisConnectionFactory> redisConnectionFactory) {
this.redisConnectionFactory = redisConnectionFactory.getIfAvailable();
}
#Bean
public RedisOperations<String, Object> sessionRedisOperations() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(this.redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
#Bean
public FindByIndexNameSessionRepository redisSessionRepository(RedisOperations<String, Object> sessionRedisOperations) {
return new RedisIndexedSessionRepository(sessionRedisOperations);
}
}
I am trying to configure multiple JPA entity/transaction managers within the same application context using Spring's #Configuration class.
When the context loads, Spring is having difficulties auto-wiring the beans because they implement the same interfaces.
Unfortunately, I'm using legacy code so I can't auto-wire the beans directly and use the #Qualifier annotations, which is why I'm trying to do it using the configuration class.
Within a #Bean declaration, is there any way to qualify which bean should be injected? I thought that using a direct method call would be enough, but it typically results in errors such as
NoUniqueBeanDefinitionException: No qualifying bean of type
[javax.sql.DataSource] is defined: expected single matching bean but
found 4
Here's an example of what I'm trying to do below:
#Configuration
public class ApplicationConfig {
#Bean(name = "transactionManager1")
public PlatformTransactionManager transactionManager1() {
return new JpaTransactionManager(entityManagerFactory1());
}
#Bean(name = "entityManagerFactory1")
public EntityManagerFactory entityManagerFactory1() {
...
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource1());
...
}
#Bean(destroyMethod = "")
#ConfigurationProperties(prefix = "datasource.test1")
public JndiObjectFactoryBean jndiObjectFactoryBean1() {
return new JndiObjectFactoryBean();
}
#Bean(name = "dataSource1")
public DataSource dataSource1() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiObjectFactoryBean1().getJndiName());
}
#Bean(name = "transactionManager2")
public PlatformTransactionManager transactionManager2() {
return new JpaTransactionManager(entityManagerFactory2());
}
#Bean(name = "entityManagerFactory2")
public EntityManagerFactory entityManagerFactory2() {
...
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(dataSource2());
...
}
#Bean(destroyMethod = "")
#ConfigurationProperties(prefix = "datasource.test2")
public JndiObjectFactoryBean jndiObjectFactoryBean2() {
return new JndiObjectFactoryBean();
}
#Bean(name = "dataSource2")
public DataSource dataSource2() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource(jndiObjectFactoryBean2().getJndiName());
}
I suppose I could try to inject the beans directly via the Spring context's getBean() method, but is there a cleaner way of doing this?
I'm not too familiar with the #Primary annotation, but based on what I've read I don't know how spring would autowire the secondary data source in this case since it looks like it would always pick the beans with #Primary first.
If you cannot change the injection sites to add qualifiers, then you're going to have to create a delegating DataSource based on some logic (which you haven't detailed in the question).
Something like this.
#Primary #Bean
public DelegatingDataSource delegatingDataSource(List<DataSource> sources) {
return new DelegatingDataSource() {
#Override
public DataSource getTargetDataSource() {
// decide which dataSource to delegate to
return sources.get(0);
}
}
}
I've used DelegatingDataSource, but that may not be able to provide what you need. You may need to get more advanced with some kind of interceptor/aspect to get details of the caller on which to base the DataSource selection.
However it's implemented, you need to specify a #Primary bean and use it as a proxy.
I'm using Spring boot and I defined the spring.datasource.* properties to enable my datasource. If I only use this it works fine. However, I'm now trying to add JMS to my application as well, using the following config:
#Configuration
#EnableJms
public class TriggerQueueConfig {
private Logger logger = LoggerFactory.getLogger(getClass());
#Value("${jms.host:localhost}")
private String host;
#Value("${jms.port:1414}")
private int port;
#Value("${jms.concurrency.min:3}-${jms.concurrency.max:10}")
private String concurrency;
#Value("${jms.manager}")
private String queueManager;
#Value("${jms.cache:100}")
private int cacheSize;
#Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory() throws JMSException {
logger.debug("Setting queue concurrency to {} (min-max)", concurrency);
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(cachedConnectionFactory());
factory.setMessageConverter(messageConverter());
factory.setTransactionManager(transactionManager());
factory.setSessionTransacted(true);
factory.setConcurrency(concurrency);
return factory;
}
#Bean(name = "jmsTransactionManager")
public JmsTransactionManager transactionManager() throws JMSException {
JmsTransactionManager transactionManager = new JmsTransactionManager();
transactionManager.setConnectionFactory(cachedConnectionFactory());
return transactionManager;
}
#Bean
#Primary
public ConnectionFactory cachedConnectionFactory() throws JMSException {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(ibmConnectionFactory());
connectionFactory.setSessionCacheSize(cacheSize);
connectionFactory.setCacheConsumers(true);
return connectionFactory;
}
#Bean
public ConnectionFactory ibmConnectionFactory() throws JMSException {
logger.debug("Connecting to queue on {}:{}", host, port);
MQQueueConnectionFactory connectionFactory = new MQQueueConnectionFactory();
connectionFactory.setHostName(host);
connectionFactory.setPort(port);
connectionFactory.setQueueManager(queueManager);
connectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
return connectionFactory;
}
#Bean
public MessageConverter messageConverter() {
MarshallingMessageConverter converter = new MarshallingMessageConverter();
converter.setMarshaller(marshaller());
converter.setUnmarshaller(marshaller());
return converter;
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setPackagesToScan("com.example");
return marshaller;
}
}
The JMS listener I created is working fine. However, when I'm trying to persist data using my repository (Spring Data JPA) in a #Transactional method, I'm getting the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single matching bean but found 2: transactionManager,jmsTransactionManager
This makes sense, because both transactionmanagers are PlatformTransactionManager's. Usually you would put #Primary on top of the bean that should be the default one. However, in this case I'm using Spring boot's autoconfiguration so I can't add the #Primary on it.
An alternative solution would be to provide the name of the transaction manager with each #Transactional annotation (for example #Transactional("transactionManager"), but this would be a lot of work, and it would make more sense to have a default transactionmanager because the JMS transactionmanager is an exceptional case.
Is there an easy way to define the automatically configured transactionmanager to be used by default?
The Spring boot 'magic' is really only this:
#Bean
#ConditionalOnMissingBean(PlatformTransactionManager.class)
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
in org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration class.
Notice the #ConditionalOnMissingBean annotation - this will get configured only if a bean of type PlatformTransactionManager doesn't exist. So you can override this by creating your own bean with #Primary annotation:
#Bean
#Primary
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager();
}
I've just updated from Spring 3.1.1 to 3.2.6
With 3.1 the following code worked well:
#Bean(name = DEMO_DS)
public JndiObjectFactoryBean demoDataSource()
{
JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName(JDBC_DEMO_DS);
factory.setProxyInterface(DataSource.class);
return factory;
}
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory(#Qualifier(DEMO_DS) DataSource dataSource)
{
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}
However with the uprgraded version I get the following exception:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [javax.sql.DataSource] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier(value=DemoDataSource)}
I have multiple DataSources hence the #Qualifier is a need.
Thanks.
Edit:
It seems that this solves the problem:
public DataSource dataSourceFactory() {
try
{
return (DataSource) demoDataSource().getObject();
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
...
sessionFactory.setDataSource(dataSourceFactory());
However I don't think it's a nice solution.
Depending on your need rewrite your configuration a little. If you don't really need the datasource injected you can do something like this.
#Bean(name = DEMO_DS)
public JndiObjectFactoryBean demoDataSource() {
JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName(JDBC_DEMO_DS);
factory.setProxyInterface(DataSource.class);
return factory;
}
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory() {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(demoDataSource().getObject());
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}
If you need to have a datasource injected you might want to switch to using a JndiLocatorDelegate to do the lookup instead of a JndiObjectFactoryBean.
#Bean(name = DEMO_DS)
public DataSource demoDataSource() throws NamingException {
return JndiLocatorDelegate.createDefaultResourceRefLocator().lookup(JDBC_DEMO_DS, DataSource.class);
}
This gives you a DataSource directly instead of a FactoryBean<Object> (which is what the JndiObjctFactoryBean is) what probably is the source of the problem.
Or (in theory) you should also be able to use a #Value annotation on a property in your config class. Instead of a #Value a normal #Resource should also do the trick (that can also delegate a call to JNDI for a lookup).
public class MyConfig {
#Value("${" + JDBC_DEMO_DS + "}")
private DataSource demoDs;
}
With #Resource
public class MyConfig {
#Resource(mappedName=JDBC_DEMO_DS)
private DataSource demoDs;
}
And you can then simply reference it in your configuration method.
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory() {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(demoDs);
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}