How to get a list of beans? - java

I have something like :
#Configuration
#EnableTransactionManagement
public class JdbcTemplateConfig{
#Bean("JdbcTemplateOne")
public NamedParameterJdbcTemplate (#Qualifier(firstDataSource final DataSource ds)){
return new NamedParameterJdbcTemplate(ds);
}
#Bean("JdbcTemplateTwo")
public NamedParameterJdbcTemplate (#Qualifier(secondDataSource final DataSource ds)){
return new NamedParameterJdbcTemplate(ds);
}
#Bean("JdbcTemplateThree")
public NamedParameterJdbcTemplate (#Qualifier(thirdDataSource final DataSource ds)){
return new NamedParameterJdbcTemplate(ds);
}
}
and now I need a list of above templates. To get a one by one (for example in an IT-Test) I can make something like:
#SpringBootTest
public class SomeITCase{
#Autowired
#Qualifier("JdbcTemplateOne")
private NamedParameterJdbcTemplate jdbcTemplate1;
#Autowired
#Qualifier("JdbcTemplateTwo")
private NamedParameterJdbcTemplate jdbcTemplate2;
#Autowired
#Qualifier("JdbcTemplateThree")
private NamedParameterJdbcTemplate jdbcTemplate3;
???
List<NamedParameterJdbcTemplate> myList = ???
}
Question: How to get all templates in a list without to have declaring them one by one?

What you a re looking for is a straight forward #Autowired annotation.
Let's suppose we have several beans defined like in your above example.
Now, we create a test like below:
#SpringBootTest
#Slf4j
public class SomeITCase {
#Autowired
#Qualifier("JdbcTemplateTwo")
private NamedParameterJdbcTemplate jdbcTemplate2;
#Autowired
List<NamedParameterJdbcTemplate> allTemplates;
#Test
public void testMyList() {
assertThat(allTemplates)
.hasSize(3)
.contains(jdbcTemplate2);
allTemplates.forEach(template -> {
log.info(template.toString());
});
}
}
Executing this test should make it really obvious: Spring injects all beans of matching type in your list. The test ensures that the size is three and at least the one known bean reference is contained.
For further information on getting started with #Autowired, maybe take a look at Injecting collections - Injecting Bean references.

To get list of all the beans from the Application context, you can do:
public void beanNames(ApplicationContext ctx) {
String[] beanNames = ctx.getBeanDefinitionNames();
for (String beanName : beanNames) {
System.out.println(beanName);
}
}
You can also look into Spring boot acutator. It provides endpoints like /bean to get all the beans registered with Spring.

If you just want a list of all NamedParameterJdbcTemplate beans in your application, you could just ask spring to autowire it for you. It allows you to autowire a list of all beans of a specific type:
{
...
#Autowired
private List<NamedParameterJdbcTemplate> allJdbcTemplates;
...
}

Related

#Autowire an Interface as Map<String, InterfaceName> is possible?

Let's say I have a "SomeInterface" and I have two springComponentImpl that implements that "SomeInterface".
I know I can autowire both implementations, together at once, with:
#Autowire
private List<SomeInterface> springComponentsImplList;
Could I inject them in a way like this?:
#Autowire
private Map<String,SomeInterface> springComponentsImplList;
So that way I could get the implementation by a "key"? It would be perfect if that key is the class name or something and generated automatically.
The following should work out-of-the-box, where the map contains the bean names as keys and the corresponding bean instances as values:
#Autowired
private Map<String, Foo> allBeansOfType;
But you may also try to get all bean instances of a particular type along with their names using ListableBeanFactory:
private Map<String, Foo> allBeansOfType;
#Autowired
public MyClass(ListableBeanFactory beanFactory) {
this.allBeansOfType = beanFactory.getBeansOfType(Foo.class);
}
You can inject the spring beans into two separate List
First approach : If you have only each of them, then name them differently and use #Qualifier
Service Interface
public interface ServiceInterf {
}
ImplementOne
#Service("implementOne")
public class ImplementOne implements ServiceInterf {
}
ImplementTwo
#Service("implementTwo")
public class ImplementTwo implements ServiceInterf {
}
And you can use #Qualifier
#Autowired
#Qualifier("implementOne")
private List<ServiceInterf> implementOne;
#Autowired
#Qualifier("implementTwo")
private List<ServiceInterf> implementTwo;
Second approach : If you have multiple of them, then you can declare them in config class using #Qualifier 5.2. Using #Qualifier to Select Beans
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf1() {
return new ImplementOne();
}
#Bean
#Qualifier("implementOne")
public ServiceInterf getServiceInterf2() {
return new ImplementOne();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf3() {
return new ImplementTwo();
}
#Bean
#Qualifier("implementTwo")
public ServiceInterf getServiceInterf4() {
return new ImplementTwo();
}
And also if you want to do them into Map use constructor injection
private Map<String, List<ServiceInterf>> mapOfLists;
#Autowired
public TestMap(#Qualifier("implementOne") List<ServiceInterf> implementOne,
#Qualifier("implementTwo") List<ServiceInterf> implementTwo) {
mapOfLists = Map.of("implementOne",implementOne,"implementTwo",implementTwo);
// Map.of is from java 9
}

Returning values from #Bean methods [duplicate]

Why can't I use #Autowired in this case?
#SpringBootApplication
public class Application {
#Autowired
BookingService bookingService;
public static void main(String[] args) {
bookingService.book("Alice", "Bob", "Carol");
}
}
but can use #Bean
#SpringBootApplication
public class Application {
#Bean
BookingService bookingService() {
return new BookingService();
}
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
BookingService bookingService = ctx.getBean(BookingService.class);
bookingService.book("Alice", "Bob", "Carol");
}
}
Aren't the two ways to generate BookingService the same thing?
#Bean and #Autowired do two very different things. The other answers here explain in a little more detail, but at a simpler level:
#Bean tells Spring 'here is an instance of this class, please keep hold of it and give it back to me when I ask'.
#Autowired says 'please give me an instance of this class, for example, one that I created with an #Bean annotation earlier'.
Does that make sense? In your first example, you're asking Spring to give you an instance of BookingService, but you're never creating one, so Spring has nothing to give you. In your second example, you're creating a new instance of BookingService, telling Spring about it, and then, in the main() method, asking for it back.
If you wanted, you could remove the two additional lines from the second main() method, and combine your two examples as below:
#SpringBootApplication
public class Application {
#Autowired
BookingService bookingService;
#Bean
BookingService bookingService() {
return new BookingService();
}
public static void main(String[] args) {
bookingService.book("Alice", "Bob", "Carol");
}
}
In this case, the #Bean annotation gives Spring the BookingService, and the #Autowired makes use of it.
This would be a slightly pointless example, as you're using it all in the same class, but it becomes useful if you have the #Bean defined in one class, and the #Autowired in a different one.
#Bean
BookingService bookingService() {
return new BookingService();
}
Annotating #Bean only registers the service as a bean(kind of an Object) in the spring application context. In simple words, it is just registration and nothing else.
#Autowired
BookingService bookingService;
Annotating a variable with #Autowired injects a BookingService bean(i.e Object) from Spring Application Context.
(i.e) The registered object with #Bean annotation will be injected to the variable annotated with #Autowired.
Hope this clears your doubt!
great answer by #DaveyDaveDave
In the example instead of
#Bean
BookingService bookingService() {
return new BookingService();
}
You can use #Service annotation on BookingService class
Contrary to what the highest voted answer here claims, they are NOT two very different things. #Bean and #Autowired and interchangeable in most cases.
Suppose you have a #Bean method that returns an instance of a Car. You can literally get rid of that bean method and add #Component on the Car class and then autowire it.
And vice versa. Whatever class you have instantiated using #Autowired, you can instantiate it inside a class with #Configuration annotation using #Bean on the method.
Places where you will use #Bean instead of #Autowired
1>You do not have access to change the class to add #Component annotation, hence you cannot autowire it.
2>You want to customize the instantiation of the class.
For example if you are instantiating a Resilience4J Circuit breaker class, if you do it inside a method with #Bean, you have the option of setting all the config using code like this
#Bean
public CircuitBreaker fooCircuitBreaker() {
CircuitBreakerConfig.Builder builder = CircuitBreakerConfig.custom().
slidingWindowSize(xxx).
failureRateThreshold(xxx).
waitDurationInOpenState(xxx)).
ignoreException(e -> {
if (e instanceof HttpStatusCodeException) {
HttpStatusCodeException httpStatusCodeException = (HttpStatusCodeException) e;
if (httpStatusCodeException.getStatusCode().is4xxClientError()) {
return true;
}
}
return false;
});
circuitBreakerRegistry.addConfiguration(xxx, builder.build());
return circuitBreakerRegistry.circuitBreaker(xxx, xxx);
}
Here's good article about #Autowired annotation: http://www.baeldung.com/spring-autowire
The #Autowired annotation can instantiate your injectables by defining #ComponentScan("namespace.with.your.components.for.inject") on config class
#Configuration
#ComponentScan("com.baeldung.autowire.sample")
public class AppConfig {}
All components must be marked by #Component annotation. It replaces the #Bean annotation.
#Bean is just for the metadata definition to create the bean(equivalent to tag). #Autowired is to inject the dependancy into a bean(equivalent to ref XML tag/attribute).

Autowired object is getting null

I am using Spring Boot for my application. I am defining JNDI name in the application.properties file.
When I am trying to get JdbcTemplate in below class, its null:
#Configuration
public class DemoClass
{
#Autowired
private JdbcTemplate template;
#Bean
private DataSource getDS(){
return template.getDataSource(); //NPE
}
}
Another Class
#Component
public class SecondClass {
#Autowired
private JdbcTemplate template;
public void show(){
template.getDataSource(): // Working Fine
}
}
I am not sure this configured by default.. In case it is not, then maybe you can try configuring it yourself:
#Autowired
DataSoure dataSource;
#Bean
public JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(dataSource);
}
in any case if you need only the DataSource, I think it is auto-configured by Spring Boot so you can autowire it directly when you need it.
#Repository
public class DataRepository {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public int updateCandidate() {
return this.jdbcTemplate.update("update .... from table where ....");
}
}
application.properties
database connection details
spring.datasource.url=jdbc:oracle:thin:***
spring.datasource.username=Scott
spring.datasource.password=Tiger
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.tomcat.initial-size=1
spring.datasource.tomcat.max-active=1
spring.datasource.tomcat.min-idle=1
spring.datasource.tomcat.max-idle=1
If you're getting a NPE at getDS. This means JdbcTemplate hasn't been injected yet, maybe it couldn't be injected.
Give spring a hint at bean dependencies by
#Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
return new JdbcTemplate(dataSource)
}
Or
#Bean
#DependsOn("template")
public DataSouce getDS(){
return template.getDataSource();
}
By default #Autowired sets required=true so the DemoClass should not be constructed by Spring.
Most likely you are creating new DemoClass() or disabled the annotation config altogether and the DemoClass class is registered manually e.g. using XML.
Instead ensure that the DemoClass class is discovered using Spring's component scan e.g. using #SpringBootApplication or #ComponentScan e.g. as per this example.

CacheManager bean definition in Config.class leads to NoSuchBeanDefinitionException

I have a Spring service which is checking database entries. To minimize my repository calls both find methods are "#Cacheable". But when I try to init my service bean while my configuration class has a CacheManager bean definition I get following NoSuchBeanDefinitionException:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'foo.mediacode.directory.MediaCodeDirectoryService' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:353)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:340)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1093)
at foo.mediacode.directory.MediaCodeDirectoryService.implementation(MediaCodeDirectoryService.java:63)
at foo.campaigntree.directory.CampaignTreeDirectoryService.<init>(CampaignTreeDirectoryService.java:18)
... 15 more
If I take out the CacheManager bean definition, I can init my service bean and it runs without any problems and caching!
Here is my code:
Configuration
...
#Configuration
#EnableCaching
#EnableJpaRepositories(...)
#PropertySource({...})
public class MediaCodeDirectoryServiceConfig {
private static Logger configLogger = Logger.getLogger(MediaCodeDirectoryServiceConfig.class.getName());
#Value("${jpa.loggingLevel:FINE}")
private String loggingLevel;
#Value("${mysql.databaseDriver}")
private String dataBaseDriver;
#Value("${mysql.username}")
private String username;
#Value("${mysql.password}")
private String password;
#Value("${mysql.databaseUrl}")
private String databaseUrl;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
...
}
#Bean
public MediaCodeDirectoryService mediaCodeDirectoryService() {
return new MediaCodeDirectoryService();
}
#Bean
public CacheManager mediaCodeCacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("mediaCodeMappingRegexCache"),
new ConcurrentMapCache("mediaCodeMappingsCache")));
return cacheManager;
}
#Bean
public JpaTransactionManager transactionManager() {
...
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
...
}
public DataSource getDataSource() {
...
}
public JpaDialect getJpaDialect() {
...
}
public Properties getEclipseLinkProperty() {
...
}
public JpaVendorAdapter getJpaVendorAdapter() {
...
}
}
Service
....
public class MediaCodeDirectoryService implements MediaCodeDirectoryServiceApi {
...
#Autowired
private MediaCodeDirectoryRepository repo;
#SuppressWarnings("resource")
public static MediaCodeDirectoryServiceApi implementation() {
if (INSTANCE == null) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(MediaCodeDirectoryServiceConfig.class);
INSTANCE = ctx.getBean(MediaCodeDirectoryService.class);
}
return INSTANCE;
}
...
Repository
...
#Repository
public interface MediaCodeDirectoryRepository extends CrudRepository<MediaCodeDao, Integer> {
#Cacheable("mediaCodeMappingRegexes")
#Query("SELECT m FROM #{#entityName} m WHERE (m.fooId = :fooId) AND (m.isRegex = :isRegex) ORDER BY (m.orderId DESC, m.id ASC)")
List<MediaCodeDao> findByfooIdAndIsRegexOrderByOrderIdDescAndIdAsc(#Param("fooId") int fooId, #Param("isRegex") boolean isRegex);
#Cacheable("mediaCodeMappings")
List<MediaCodeDao> findByMediaCode(String MediaCode, Pageable pageable);
}
When I debug into DefaultListableBeanFactory I can find within beanDefinitionMap my mediaCodeDirectoryService and also within beanDefinitionNames mediaCodeDirectoryService appears. But DefaultListableBeanFactory.getBean(...) cannot resolve name and namedBean in line 364 is null.
When I try to get the context via String like:
INSTANCE = (MediaCodeDirectoryService) ctx.getBean("mediaCodeDirecotryService")
I avoid the NoSuchBeanDefinitionException but I run into an other one.
Anybody here has an idea on what might be the cause of this? Did I missed something in my configuration? Thx!
Caching is applied through AOP. For AOP Spring uses a proxy based approach and the default is to create interface based proxies.
public class MediaCodeDirectoryService implements MediaCodeDirectoryServiceApi {... }
With this class definition at runtime you will get a dynamically created class (Proxy$51 or something along those lines) which implements all interfaces but it isn't a MediaCodeDirectoryService. It is however a MediaCodeDirectoryServiceApi.
You have 2 ways of fixing this, either program to interfaces (which you should have been doing anyway because you have defined interfaces) instead of concrete classes or use class based proxies.
The first option involves you changing your code in the places the directly #Autowire or get an instance of MediaCodeDirectoryService to use MediaCodeDirectoryServiceApi instead (which imho you should already do, why else define an interface). Now you will get the proxy injected and everything will work.
The second option involves you setting proxyTargetClass=true on your #EnableCaching annotation. Then instead of an interface based proxy you will get a class based proxy.
#EnableCaching(proxyTargetClass=true)

Single API, multiple Elasticsearch instances

We have a Spring Boot Restful API that needs to get data from 2 different Elasticsearch instances (on different servers), 1 for "shared" data (with about 5 different indexes on it) and 1 for "private" data (with about 3 different indexes). Currently running against just the "private" data instance, everything is good. But we now need to get at the "shared" data now.
In our Spring Boot application, we have enabled Elasticsearch repositories like this
#SpringBootApplication
#EnableElasticsearchRepositories(basePackages = {
"com.company.core.repositories", //<- private repos here...
"com.company.api.repositories" //<-- shared repos here...
})
public class Application { //... }
Then we access the "private" data with an ElasticsearchRepository like:
package com.company.core.repositories
public interface DocRepository extends ElasticsearchRepository<Doc, Integer> { ... }
In our endpoint, we have...
#RestController
#CrossOrigin
#RequestMapping("/v2/statuses/")
public class StatusEndpoint {
#Resource
private ElasticsearchTemplate template;
#Autowired
private DocRepository docRepository;
#Autowired
private Validator validator;
//...
}
Now we want to add another repository like:
package com.company.api.repositories
public interface LookupRepository extends ElasticsearchRepository<Lookup, Integer> { ... }
Then in our API layer we would add an auto-wired instance...
#Autowired
private LookupRepository lookupRepo;
We were thinking that we could define multiple Beans with different names, but how do we associate each of the "elasticsearchTemplate" beans with the different ElasticsearchRepository instances that need them? Also, how do we associate the "private" bean/configuration with injected instances of
#Resource
private ElasticsearchTemplate template;
Where we need to use that natively?
You can solve this with 2 unique Elasticsearch configuration beans and an #Resource(name="XXX") annotation for the template injection in your StatusEndpoint controller.
If you segregate your repositories into different packages depending on which Elasticsearch cluster they should use, you can associate them with different configurations using the #EnableElasticsearchRepositories annotation.
For example:
If you have these packages and classes:
com.company.data.repositories.private.YourPrivateRepository
com.company.data.repositories.shared.YourSharedRepository
And then these Configurations:
#Configuration
#EnableElasticsearchRepositories(
basePackages = {"com.company.data.repositories.private"},
elasticsearchTemplateRef = "privateElasticsearchTemplate")
public class PrivateElasticsearchConfiguration {
#Bean(name="privateElasticsearchTemplate")
public ElasticsearchTemplate privateTemplate() {
//code to create connection to private ES cluster
}
}
#Configuration
#EnableElasticsearchRepositories(
basePackages = {"com.company.data.repositories.shared"},
elasticsearchTemplateRef = "sharedElasticsearchTemplate")
public class SharedElasticsearchConfiguration {
#Bean(name="sharedElasticsearchTemplate")
public ElasticsearchTemplate sharedTemplate() {
//code to create connection to shared ES cluster
}
}
Because of the elasticsearchTemplateRef parameter in the #EnableElasticsearchRepositories annotation, the JPA code that implements the repositories will use the specified template for repositories in the basePackages list.
For the StatusEndpoint portion, you would just provide your #Resource annotation with the correct template bean name. Your StatusEndpoint would look like this:
#RestController
#CrossOrigin
#RequestMapping("/v2/statuses/")
public class StatusEndpoint {
#Resource(name="privateElasticsearchTemplate")
private ElasticsearchTemplate template;
#Autowired
private DocRepository docRepository;
#Autowired
private Validator validator;
//...
}
There might be multiple ways to do this. Here is one that takes advantage of the #Bean name and the #Resource name.
#Configuration
public class MyElasticConfig{
#Bean //this is your private template
public ElasticsearchTemplate template(){
//construct your template
return template;
}
#Bean //this is your public template
public ElasticsearchTemplate publicTemplate(){
//construct your template
return template;
}
}
then you can get them like this...
#Resource
private ElasticsearchTemplate template;
#Resource
private ElasticsearchTemplate publicTemplate;
or
#Resource(name="template")
private ElasticsearchTemplate anyName;
#Resource(name="publicTemplate")
private ElasticsearchTemplate anyOtherName;
also you can name your #Bean's directly instead of relying on the #Bean's method name.
#Bean (name="template")
public ElasticsearchTemplate myPrivateTemplate(){
//construct your template
return template;
}
#Bean (name="publicTemplate")
public ElasticsearchTemplate myPubTemplate(){
//construct your template
return template;
}
Check out these wonderful resources on the topic.
SPRING INJECTION WITH #RESOURCE, #AUTOWIRED AND #INJECT
Bean Annotation Type
Autowired vs Resource

Categories