Get bean name by the object - java

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?

Related

How to use RabbitTemplate in all Java classes

I edited my code into this configuration:
#SpringBootApplication
public class EndPoint {
String QUEUE_PROCESSING_TRANSACTION = "processing-process-queue";
String QUEUE_DATABASE_TRANSACTION = "database-transa-queue";
......
#Bean
public Queue queueProcessingTransaction() {
return new Queue(QUEUE_PROCESSING_TRANSACTION, true);
}
#Bean
public Queue queueDatabaseEventLogs() {
return new Queue(QUEUE_DATABASE_EVENT_LOGS, true);
}
#Bean
public Binding bindingQueueProcessingTransaction() {
return BindingBuilder.bind(new Queu........
}
#Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(HOST);
return connectionFactory;
}
#Bean
public AmqpAdmin amqpAdmin(CachingConnectionFactory connectionFactory) {
RabbitAdmin admin = new RabbitAdmin(connectionFactory);
.........
admin.declareQueue(new Queue(QUEUE_PROCESSING_TRANSACTION, true));
return admin;
}
#Bean
public RabbitTemplate processingTemplate(CachingConnectionFactory connectionFactory) {
RabbitTemplate processingTemplate = new RabbitTemplate(connectionFactory);
processingTemplate.setExchange(EXCHANGE_PROCESSING);
.......
return processingTemplate;
}
Previously I used this configuration into Java class which I extend in second Java class in order to access RabbitTemplate.
How I can use RabbitTemplate in Java classes? Probably there is already implemented facility designed in Spring?
You can just add another bean that creates a template starting from the connection factory:
#Bean
public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
return rabbitTemplate;
}
You can autowire it in container managed classes:
#Autowired private RabbitTemplate rabbitTemplate;
You can inject the RabbitTemplate bean in another Spring Bean and use it, so for example you can create a new Spring Bean (component) like the following:
#Component
public class MyComponent {
#Autowired
private RabbitTemplate template;
public void testRabbitTemplate() {
System.out.println(template);
}
}
Remember that the injection works only if you retrieve MyComponent from the Spring Context (i.e. you must not instantiate it using the new keyword).
You can also inject the same RabbitTemplate in your EndPoint class simply adding the following line into the class body:
#Autowired private RabbitTemplate template;

#Bean declaration with #Qualifier doesn't work

Lets say I have a config class (JmsQueueConfig, see below). In this class, I want to configure multiple queues for my entire application. For one queue, there is no problem. However when I add a second queue and try to use one of these queues from a service (MemberService), then Spring-boot tells me
Parameter 1 of constructor in
com.example.notification.application.jms.JmsEventPublisher
required a single bean, but 2 were found:
- queueAccountToNotification: defined by method 'queueAccountToNotification' in class path resource
[com/example/notification/application/jms/JmsQueueConfig.class]
- queueNotificationToAccount: defined by method 'queueNotificationToAccount' in class path resource
[com/example/notification/application/jms/JmsQueueConfig.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
Here is my Config-Class:
#Configuration
#EnableJms
#ImportAutoConfiguration(classes = {
JmsAutoConfiguration.class,
ActiveMQAutoConfiguration.class
})
public class JmsQueueConfig {
#Value("${APP_QUEUE_ACCOUNT_TO_NOTIFICATION}")
private String queueAccountToNotificationName;
#Value("${APP_QUEUE_NOTIFICATION_TO_ACCOUNT}")
private String queueNotificationNameToAccount;
#Bean
#Qualifier("q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
#Bean
#Qualifier("q2")
public Queue queueNotificationToAccount() {
return new ActiveMQQueue(queueNotificationNameToAccount);
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
#Qualifier("p1")
public EventPublisher eventPublisher(JmsTemplate jmsTemplate) {
return new JmsEventPublisher(jmsTemplate, new ActiveMQQueue(queueAccountToNotificationName));
}
#Bean
public MessageConverter messageConverter() {
return new JmsMessageConverter();
}
}
My Service:
#Service
#FieldDefaults(level = AccessLevel.PRIVATE)
#AllArgsConstructor
#Slf4j
public class MemberService {
#Autowired
#Qualifier("q1")
Queue q;
#Qualifier("p1")
EventPublisher eventPublisher;
public void createMemberSubscription(final Member member) {
final MembershipSubscriptionEvent event = new MembershipSubscriptionEvent(UUID.randomUUID().toString(), member.getEmail());
//eventPublisher.publish(event);
log.info("createMemberSubscription");
}
public void removeMemberSubscription(final Member member) {
final MembershipRemovalEvent event = new MembershipRemovalEvent(UUID.randomUUID().toString());
//eventPublisher.publish(event);
log.info("removeMemberSubscription");
}
}
I am new to Spring ecosystem and I might not have understand well the #Autowired and bindings. Any good documentation or example would be much appreciated.
On Spring and SoF, I haven't found any such documentation.
Updated:
JmsEventPublisher class
#Component
#FieldDefaults(level = AccessLevel.PRIVATE)
#Slf4j
#AllArgsConstructor
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
final Destination destination;
#Override
public void publish(DomainEvent event) {
jmsTemplate.convertAndSend(destination, event);
log.trace("Sent event. [destination={}, event={}]", destination, event);
}
}
To correct the accepted answer,your understanding for the use of #Qualifier is correct. It can be used in two contexts. It can be used with a #Bean configuration method to provide a qualifier for the bean. If not provided this defaults to the bean name.
It can also be used at an injection target i.e method or field with #Autowired or #Inject annotation. At this context it assist Spring autowiring infrastructure to filter the bean candidates matching the injection target based on the qualifier(provided with #Bean method) if multiple beans are found
The cause of the error was due to
#AllArgsConstructor
public class JmsEventPublisher implements EventPublisher
The #AllArgsConstructor will generate the following constructor
public JmsEventPublisher(JmsTemplate jmsTemplate, Destination destination){
//body
}
Spring will attempt to autowire JmsEventPublisher by constructor since it has one constructor which is not a no argument constructor. However the parameter of type Destination matches two beans of type Queue.
The solution is to either use an explicit constructor. ie remove #AllArgsConstructor and define constrctor as follows
public JmsEventPublisher(JmsTemplate jmsTemplate, #Qualifier("q1")Destination destination){
//body
}
or use field or setter injection instead i.e remove #AllArgsConstructor and inject fields or setter methods
public class JmsEventPublisher implements EventPublisher {
private JmsTemplate jmsTemplate;
#Qualifier("q1")
private Destination destination;
}
I think you have misunderstood #Qualifier
From the documentation, "This annotation may be used on a field or parameter as a qualifier for candidate beans when autowiring."
In your case #Qualifier is of no meaning.
#Bean
#Qualifier("q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
Instead you should be doing like this
#Bean(name = "q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
#Bean(name = "q2")
public Queue queueNotificationToAccount() {
return new ActiveMQQueue(queueNotificationNameToAccount);
}
Similarly remove #Qualifier on eventPublisher(...)
That doesn't solve all the problem. :)
As the exception indicates, spring is not able to autowire Destination field in JmsEventPublisher. Because it has two beans of type Destination(q1 and q2).
To solve this what you can do is .
Put #Primary on one of the bean declaration and then use #Qualifier.
#Primary
#Bean(name = "q1")
public Queue queueAccountToNotification() {
return new ActiveMQQueue(queueAccountToNotificationName);
}
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
#Qualifier("q1")
final Destination destination;
..........
}
Bottom line is For #Qualifier to work in case of multiple beans of same type, you need to put #Primary
Another option is instead of using #Primary, you can name the variables exactly as Bean names, then spring will automagically inject correct beans for you. i.e.
public class JmsEventPublisher implements EventPublisher {
final JmsTemplate jmsTemplate;
final Destination q1; // q1 or q2
.....
}
similiarly in MemberService
public class MemberService {
#Autowired
Queue q1; // q1 or q2
.....
}
Change final Destination destination to final Destination q1 and it should work. I had the same problem and it worked for me.

SPRING - Create MessageSources programmatically and use them as beans

I have to create different messageSources programmatically and put them in a Bean in order to use the correct one when needed.
The application must have a messageSource for each of our Customers, so i created a Configuration class
#Configuration
public class MessageSourceConfig implements BeanFactoryAware {
private BeanFactory beanFactory;
#Autowired
private ICompanyService service;
private Map<Company, MessageSource> messageSourceMap = new HashMap<Company, MessageSource>();
// default messageSource
#Bean
#Primary
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setCacheSeconds(5);
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
#PostConstruct
public void onPostConstruct() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
Iterable<Company> companies = service.findAll();
for(Company c : companies) {
String beanName= c.getSlug()+"_messageSource";
MessageSource bean = getCompanyMessageSource(c);
configurableBeanFactory.registerSingleton(beanName, bean);
messageSourceMap.put(c, bean);
}
}
private MessageSource getCompanyMessageSource(Company company) {
ReloadableResourceBundleMessageSource ms = new ReloadableResourceBundleMessageSource();
ms.setBasename("classpath:" + company.getSlug() + "/messages");
ms.setUseCodeAsDefaultMessage(true);
ms.setCacheSeconds(5);
ms.setDefaultEncoding("UTF-8");
return ms;
}
public MessageSource companyMessageSource(Company company) {
return messageSourceMap.get(company);
}
In this way we have a default messageSource and one specific messageSource for each Company.
The idea was to put this specific messageSources into a Map and then accessing the correct one from the map when we need it.
The problem is that companyMessageSource should be a bean, but i cannot pass a parameter to the bean, how can i access dynamically the correct source?
I am not entirely sure I understand how you want to use the created beans, but one way to get the registered singletons of MessageSource is to get them programmatically something like this:
#Service
public class CompanyService {
#Autowired
private ApplicationContext applicationContext;
public void useCompanySpecificMessageSource(Company c) {
MessageSource ms = applicationContext.getBean(c.getSlug() + "_messageSource");
log.debug(ms.getMessage("code", null, new Locale("en", "GB"));
}
}
Hope this helps.

Error while creating Bean even though the Dependency Bean is there

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

Referring applicationContext.xml bean in Spring #Configuration

I have something like this:
#Configuration
public class SpringConfigUtil {
private static final Logger logger = Logger.getLogger(SpringConfigUtil.class);
#Value("#{ systemProperties['activemq.username'] }")
private String userName;
#Value("#{ systemProperties['activemq.password'] }")
private String password;
#Value("#{ systemProperties['activemq.URL'] }")
private String brokerURL;
#Value("#{ systemProperties['activemq.queueName'] }")
private String queueName;
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean(name="producer")
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDefaultDestination(new ActiveMQQueue(queueName));
jmsTemplate.setConnectionFactory(connectionFactory());
return jmsTemplate;
}
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL(brokerURL);
activeMQConnectionFactory.setUserName(userName);
activeMQConnectionFactory.setPassword(password);
return activeMQConnectionFactory;
}
}
It works fine.
Let's say I also have an applicationContext.xml file which has loaded some beans.
How do I refer to those beans here in #Configuration?
I don't want to create beans programmatically again as they are already created by loading applicationContext.xml.
Let's have I have 50+ properties. Is there a better way to refer to them than defining something like the following for every property?
#Value("#{ systemProperties['activemq.URL'] }")
private String brokerURL;
Thanks for your time!
How do I refer to those beans here in #Configuration?
According to http://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch06.html, please try:
...
#Configuration
#AnnotationDrivenConfig // enable the #Autowired annotation (??)
#ImportXml("classpath:<*path*/to/your/*>applicationContext.xml")
public class SpringConfigUtil { ...
Let's have I have 50+ properties. Is there a better way to refer to them than defining something like the following for every property?
None, that I can imagine :-( ... how could you "better access" 50+ properties, than "by their name" - "by index", array-like!? Though some properties can be coupled, at the end of the day" each has (should have) its special meaning and purpose - and for that touched/wired/configured individually. (the best practice tip is just to do it as rare as possible)

Categories