Spring init failing when bean constructor using parameters - java

SpringBoot app fails while trying to initialize this class:
#Component
public class Weather {
private Map<Integer,Double> maxRainyDays;
private Map<Integer, WeatherDays> totalDays;
public Weather(Map<Integer, WeatherDays> totalDays, Map<Integer,Double> maxRainyDays){
this.setMaxRainyDays(maxRainyDays);
this.setTotalDays(totalDays);
}
The error:
Parameter 0 of constructor in SolarSystem.Models.Weather required a
bean of type 'SolarSystem.Utilities.WeatherDays' that could not be
found.
The mentioned bean is already defined (in the same basepackage):
public enum WeatherDays {
RAINY,
MILD,
DRY,
MAX_RAIN}
Workaround:
When I changed to Weather() constructor I solved the issue.Of course I had to use setters to set the properties of the object.
But I need to understand the reasons why happened

Because the collections (Map here) you are injecting via constructor parameter aren't registered as Spring beans. This is why you would annotate class with #service, #repository, etc and have them autowired into other classes. To fix, you can set up a config class like such:
#Configuration
public class BeanConfig {
#Bean
public Map<Integer, WeatherDays> totalDays() {
Map<Integer, WeatherDays> map = new HashMap<>();
map.put(1, WeatherDays.DRY);
return map;
}
#Bean
public Map<Integer, Double> maxRainyDays() {
Map<Integer, Double> map = new HashMap<>();
map.put(1, 0.2);
return map;
}
}

Related

Can I use a HashMap to pick which interface (DAO) to autowire in SpringBoot?

I will try to be as detailed as possible. I have many DAO and my service needs to use one of them based on the key i get. For instance -
if(key.equals("abc") {
obj = abcDAO.getOne(id);
} else if(key.equals("xyz") {
obj = xyzDAO.getOne(id);
}
The object is of type parent class and abc, xyz.. are all child classes.
My idea is to create a Map<String, ParentCLass> to get the object just by passing the key instead of If-else so that it will be easy to add for further changes. In case it would've been a normal class, I wouldv'e initialized the map as
Map<String, ParentClass> map.
map.put("abc", new Abc());
But since DAO are interfaces and require to be #Autowired for using them, I don't know how to proceed. I am a beginner. Any help appreciated.
Spring is able to inject all beans with the same interface in a map if the map has String as key (will contain the bean names) and the interface as value.
public interface MyDao {
}
#Autowired
private Map<String, MyDao> daos;
EDIT: If you use Spring Data Repository, there is already a tagging Interface: Repository. You can use below code to inject all DAOs in one bean.
#Autowired
private Map<String, Repository> daos;
EDIT2: Example
public interface UserRepo extends JpaRepository<User, Long> { ... }
#Service
public class MyService {
#Autowired
private Map<String, Repository> daos;
public List<User> findAll() {
return daos.get("userRepo").findAll();
}
}
You can create different bean for each of your Dao with a specific name
#Bean(name = "daoImpl1")
public Dao daoImpl1(){
return new DaoImpl1();
#Bean(name = "daoImpl2")
public Dao daoImpl2(){
return new DaoImpl2();
And then #Autowire them using #Qualifier with that name
#Autowired
#Qualifier("daoImpl1")
private Dao daoImpl1;
#Autowired
#Qualifier("daoImpl2")
private Dao daoImpl2;
i made methods to make simple jpa things,
i stored all of repositories in the HashMap
you just have to pass the child class and you get the corresponding JpaRepository
you can see more at https://github.com/fajaralmu/base_web_app
example :
public List<Page> getAllPages() {
List<Page> allPages = entityRepository.findAll(Page.class);
return allPages;
}

Do I need to synchronize #Autowired method in Spring when I want to save multiple beans in collection?

Let's imagine I have the next classes in the project based on Spring framework:
interface I {
String getName()
}
#Component
class I1 implements I {
#Override
String getName() {return "I1"}
}
#Component
class I2 implements I {
#Override
String getName() {return "I1"}
}
And I want to gather them all in the map using the #Autowired method:
#Component
public class A {
private Map<I> map = new HashMap<>()
#Autowired
public registerI(I i) {
map.put(i.getName(), i)
}
}
Should I make this method registerI synchronized? I mean, can Spring call this method in several threads simultaneously? Or this method will be called sequentially?
Thanks
You don't have to use synchronized because Spring bean initialization is single-threaded and thread-safe. You can think of gotchas like thread-scoped or lazy beans but for regular singleton beans initialization happens in one thread.
You might want to use synchronized to make sure that after registerI() method is called your object is safely published, although auto-wired constructor with final field is more readable.
#Component
public class A {
private final Map<String, I> map;
public A(List<I> list) {
map = list.stream().collect(Collectors.toMap(I::getName, i -> i));
}
}
You will get an exception during app startup because Spring cannot determine the correct implementation of interface "I" what you want to inject. You should use #Qualifier.
If you want to accomplish that scenario, this should be enough.
#Component
public static class A {
private Map<String,I> map = new HashMap<>();
public A(List<I> list) {
//map = list.stream().collect(Collectors.toMap(I::getName, x -> x));
for (I i : list) {
map.put(i.getName(), i);
}
}
}
You will end with only one value in the map.
The commented line works if there are not duplicate map keys.
You can autowire context and get all the interested beans from it in a #PostConstruct method and create a hashmap with it.
Or
If you want that Map to be shared amongst multiple classes, make it a #Bean
#Configuration
class SomeConfig{
#Autowire Context context;
#Bean(name = "mapBean")
public Map<String, MyCustomClassName1> mapBean() {
Map<String, MyCustomClassName1> map = new HashMap<>();
//populate the map here - from Context
return map;
}
}
Spring fills List by your beans. After you can create map in postConstruct
#Component
public class A {
#Autowired
private List<I> list;
#Autowired
private Map<String, I> map;
#PostConstruct
private void init(){
map = list.stream()
.collect(Collectors.toMap(I::getName, element->element);
}
}

Injecting a Map<String, Double> using Spring Boot and application.yml

I know, there are many questions that ask similar thing in SO, but I am not able to get out of this situation using them.
I have a Spring Boot application.
#SpringBootApplication
#EnableConfigurationProperties
public class Application implements ApplicationRunner {
public static void main(String[] args) {
SpringApplication.run(Application .class, args);
}
}
Then I have the following class.
#Component
#ConfigurationProperties(prefix = "somePrefix")
public class AClass {
private final AnotherClass anotherClass;
private final Map<String, Double> aMap;
#Autowired
public AffinityChecks(AnotherClass anotherClass,
Map<String, Double> aMap) {
this.anotherClass = anotherClass;
this.aMap = aMap;
}
// Omissis
Finally I have the following application.yml configuration file.
somePrefix:
aMap:
key1: 0.6
key2: 0.2
key3: 0.2
All I want is Spring to inject the map into object of typeAClass during building process. The error I obtain is the following.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'java.util.Map<java.lang.String, java.lang.Double>' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
What the hell is going on?
Thanks in advance.
Spring Boot does not support constructor injection for elements that are bound to the environment. You can of course inject actual beans the usual way.
You need to define each property you want to bind as a regular Javabean property (i.e. with getter/setter). There is one exception to this rule: Maps and non-scalar value (i.e. nested content) only need a getter.
Concretely if AnotherClass is a bean and FooClass some pojo with nested properties.
#Component
#ConfigurationProperties(prefix = "somePrefix")
public class AClass {
private final AnotherClass anotherClass;
private final Map<String, Double> aMap = new HashMap<>();
private final FooClass foo = new FooClass();
public AClass(AnotherClass anotherClass) { ...}
public Map<String, Double> getaMap() { ... }
public FooClass getFoo() { ... }
}
(Note that the getter is the thing that infers the name of the property. In the example above you could map somePrefix.foo.xyz if you had a getXyz() on FooClass).
There is an example in the documentation with additional details.
(A good hint that your code is wrong is that the map is not a bean so #Autowiring isn't the proper semantic for what you were trying to achieve).

Spring Bean is not initializing on Application startup

In my application i want to initialize the bean before hitting application url, and store the common values for drop-down. here it is declaration for bean
<beans:bean id="listService" class="com.system.beans.DropDownList"
init-method="populateMasterList" scope="application"/>
Bean:
public class DropDownList implements InitializingBean
{
private static final Logger logger = LoggerFactory.getLogger(DropDownList.class);
public static Map<String, Map<Integer, String>> listMap = new HashMap<String, Map<Integer, String>>();
#Autowired
private static SystemService systemService;
#Autowired(required = true)
#Qualifier(value = "systemService")
public void setSystemService(SystemService systemService)
{
this.systemService = systemService;
}
#PostConstruct
public static Map<String, Map<Integer, String>> populateMasterList()
{
logger.debug("Calling Institute Info Masters");
List<InstituteInfoMaster> masterList = systemService.listInstituteInfoMasters();
Map<Integer, String> masterMap = new HashMap<Integer, String>();
masterMap.put(0, "---Select---");
masterList.forEach((master) ->
{
masterMap.put(master.getListId(), master.getValue());
});
logger.debug("Created Map for List Masters");
listMap.put("infoList", masterMap);
return listMap;
}
public Map<String, Map<Integer, String>> getListMap()
{
return listMap;
}
public static void setListMap()
{
listMap = populateMasterList();
}
#Override
public void afterPropertiesSet() throws Exception
{
populateMasterList();
}
}
I observed that it does not initializes on Application startup. when i try to update the master by calling DropDownList.setListMap(); it gives NullPointerException. but if i calls the jsp page where i am calling the Map as ${listService.listMap['infoList']} it displays the Drop-down on jsp after it if i tries to save master it executes successfully.
it means when i calls the jsp page where i am showing drop-down that time only it initializing the bean not on Application startup.
The actual problem is that you are not accessing the Spring bean, but the class, staticly. When you use the bean, i.e. the listService instance, Spring will initalize it for you on first access.
You are calling a static method, but when this happens, the dependant beans are not populated. Autowiring works for instances (i.e. in non-static context), so systemService is null in your application.
Update: I have just realized this line:
#Autowired
private static SystemService systemService;
This is fundamentally wrong. You cannot autowire static fields, it makes absolutely no sense in Spring (or in any similar framework). Spring beans are instances, and the framework set the autowired fields to references to other spring beans.

How to create a MAP that has an application scope? And Where to declare it?

I need to create a Map that has application scope. And so, if user1 add to this Map an object using method1 of class1, user2 would find the new objects using method2 of class2.
I know there is this annotation :
#ApplicationScoped
But, I don't know where my map should be declared or used, to make it have the same state at anytime and by anywhere in the application deployment time.
An example representing a class where this Map is declared and a method of another class using it, would be so helpful.
Declare a CDI bean that will provide this Map for its consumption:
#Named
#ApplicationScoped
public class ApplicationScopedBean {
private Map<KeyClass, ValueClass> map;
#PostConstruct
public void init() {
//initialize the map and its data here
map = new ConcurrentHashMap<>();
map.put(..., ...);
//...
}
//provide a getter for the map
public Map<KeyClass, ValueClass> getMap() {
return this.map;
}
}
Now, the bean can be injected in clients and can show the data in your view.
I'm not sure about your experience with Java language. But why don't you create a static variable in class?
So, for example:
class A {
public static Map<String, String> globalMap;
}
From class B you could access or set it anywhere(ofcourse you need to import the class A at the top):
class B {
public void doAnything(){
Map anyMap<String, String> anyMap = new HashMap<String, String>();
anyMap.put("anyString", "anyString");
A.globalMap = anyMap;
}
}

Categories

Resources