I have a question on Spring Autowire Annotation.The scenario is like this : Iam using #Autowire on class A and using it in 2 places -Class B and Class C like below:
public class B
{
#Autowired
private A a;
......
Map<String, Map<String,String>> map1=a.getNameValues();
Map<String, Map<String, String>> map2 = a.get("key");
if (map2!=null)
map1.putAll(map2);
and also in other class C as shown below:
public class C
{
#Autowired
private A a;
......
Map<String, Map<String,String>> map1=a.getNameValues();
Map<String, Map<String, String>> map2 = a.get("key");
if (map2!=null)
map1.putAll(map2);
}
The program control flows from Class B to class C. So since the class A is autowired in both the places. so when the control comes first to class B ,map2 is retrieved and put in map1 . when the control comes to Class C , map1 already has map2 values. What are the possible ways to control this kind of scenarios? As i want both classes to work independently and use the Autowired class. Let me know your thoughts.
#Autowire will automagically inject a spring bean into the given property.
It sounds like your question is actually related to the scope of the bean being injected. So assuming your A class looks like this:
#Component
public class A {
....
}
Then what will happen is spring will create a single instance (aka a Singleton) of A (in the given application context) and inject this into both B and C
Question - Is this the problem you are trying to solve? When you say you want both classes to act independently you mean the fact that the A object in B and C are the exact same object?
To get spring to wire a new instance of A you can simply change the scope of A to be prototype.
#Component
#Scope(value = "prototype")
public class A {
....
}
or in xml
<bean id="a" class="A" scope="prototype"/>
Related
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;
}
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).
I'm looking on to how I can inject a Map<Integer, CustomEnumType> in Spring, not using app-context.xml but using #Bean(name = "customMap") annotations. When I try to inject it by doing
#Inject
Map<Integer, CustomEnumType> customMap;
it complains because apparently it cannot find any injectable dependency of type CustomEnumType. However CustomEnumType is just an enumeration, not something that is supposed to be injected. I just want to use it as the value type of my map.
One solution is to create an injectable wrapper object that will contain the Map as a field but I'd like to avoid unnecessary clutter. It is also more clean and readable to see the type of Map being injected.
Don't try to inject Enums, instead, try to inject int values.
Enums are indeed classes, however, you can't create an instance/object of them. their constructor access modifier can be private only, that's another proof why you can't have instances of them.
With that being said, you can't have Beans of Enum, because there is no way to construct them.
The solution is to give each member in your enum, an int value, and just inject that int value.
For example:
public enum Color {
white(0),
black(1);
private int innerValue;
private Color(int innerValue) {
this.innerValue = innerValue;
}
public int getInnerValue() {
return innerValue;
}
}
Now, let's say I want to inject the value 1, which is black in my Enum. To another Class via its constructor. Then my constructor will look like this:
public Canvas(String id, int color) {
this.id = id;
this.color = Color.getColorByInt(color);
}
Now, let's say that the xml configuration file containing this:
<bean id="myCanvas" class="com.my.package.Canvas">
<constructor-arg name="id" value="9876543" />
<constructor-arg name="color" value="1" />
<!-- This is the black value for myCanvas bean -->
</bean>
I found the solution. Apparently #Inject and #Autowired fail to correctly find the type of #Bean method they need to use. However, using #Resource(name = "customMap") everything worked perfectly. There was no problem with the map creation even if the values were enumerations. The method used was:
#Bean(name = "customMap")
public Map<Integer, CustomEnumType> getCustomMap() {
Map<Integer, CustomEnumType> map = new HashMap<>();
map.put(1, CustomEnumType.type1);
map.put(2, CustomEnumType.type2);
//...
return map;
}
and injected using
#Resource(name = "customMap")
Map<Integer, CustomEnumType> customMap;
Note that CustomEnumType has no constructors defined and no values are assigned to the enumeration. In my case this was also impossible to do from the beginning since the CustomEnumType class is a dependency we cannot edit.
I am trying to autowire a member in a class using the constructor.
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(ClassA objectA) {
myMember = objectA;
}
}
If I have multiple sources that create beans of ClassA, is it possible to have a duplicate constructor definition that instantiates based on the bean that was autowired into this class?
I want to do something like this:
#Component
public class MyClass {
private ClassA myMember;
#Autowire
public MyClass(#Qualifier ("qualifierA") ClassA objectA) {
myMember = objectA;
}
#Autowire
public MyClass(#Qualifier ("qualifierB") ClassA objectB) {
myMember = objectB;
}
}
I tried using #Qualifier this way, but it didn't work.
Is it possible to do what I'm trying to do, with Spring? How can I disambiguate based on the name (qualifierA) or (qualifierB), if the bean definition is like:
#Bean (name = "qualifierA")
public ClassA getQualifierA() {
...
}
#Bean (name = "qualifierB")
public ClassA getQualifierB() {
...
}
You can't have two constructors with the exact same signature in a single class in Java. Nor any other programming language I've ever encountered. You might use method-injection instead, with two methods (named differently, of course), mark them as #Autowired(required = false) and use the proper #Qualifier(...) to specify the instance you want to inject. You might want to handle the case when both instances are present in the spring context, so no unexpected things happen.
The short answer is: no, that is not possible. In Java you cannot have two constructors with exactly the same signature. And also, you can assign only one value to your "myMember".
However, what are you trying to accomplish here? It seems that in some occasions MyClass needs to use "objectA" and in other occasions, you need "objectB".
For these scenarios, you should not use autowiring (you can't), but simply use explicit wiring:
#Bean
MyClass myObject() {
return new MyClass(qualifierA());
}
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;
}
}