Dynamic injection of collections into Spring managed beans - java

How can I inject a collection into a spring bean dynamically.
As I know
Example of my Author class
import java.util.List;
public class Author {
private String name;
private List<String> listOfBooks;
public Author(String name, List<String> listOfBooks) {
this.name = name;
this.listOfBooks = listOfBooks;
}
public Author() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getListOfBooks() {
return listOfBooks;
}
public void setListOfBooks(List<String> listOfBooks) {
this.listOfBooks = listOfBooks;
}
}
And my bean id is:
<bean id="authorID" class="com.test.Author">
</bean>
and some where I am using as #AutoWired bean for the Authors class.
And if I wants to add the books to the variable "listOfBooks".
How can I add it. is it by using reference to the Authors object and by accessing the getListOfBooks() method then adding the values. or is there any best way for this approach.
Thanks.

Some more detail explanation:
<property name="bookList">
<list>
<value>Java</value>
<value>C++</value>
</list>
</property>
This way we are Passing bean reference for java.util.List
Remember: To use above bean definition, you need to define your setter methods in such a way that they should be able to handle references as well.
ApplicationContext context =
new ClassPathXmlApplicationContext("YourBeans.xml");
Author a=(Author)context.getBean("authorID");
a.getListOfBooks();
Your Getter and Setter will be:
public void setListOfBooks(List<String> bookList) {
this.listOfBooks = bookList;
}
Also change getter accordingly.
Thank you

<property name="addressList">
<list>
<value>Book1</value>
<value>Book2</value>
<value>Book3</value>
<value>Book4</value>
</list>
</property>
In you context file.
For more information please see this post

Spring Injecting Collection here you can get an idea of injecting all types of collection [list,set,map] and array, and it's values as a primitive/reference

Related

How to define a bean in ApplicationContext.xml that has no constructor?

I have a class
public class DataStore {
public String name;
public String username;
public String password;
public String token;
public String connectionString;
public String type;
public String scheme;
public boolean usesBasicAuth;
public boolean usesBearerAuth;
}
I need to create an bean for it in another project. But i need to fill the fields somehow. The problem is I can not use <constructor-arg ... /> because there is no constructor.
The code below results in BeanCreationException: "Could not resolve matching constructor"
<bean id="dataStore"
class="com.fressnapf.sdk.dataaccess.services.DataStore">
<constructor-arg index="0" value="${spring.datastore.name}"/>
...
</bean>
Assuming, You have public (getters and setters for your) properties, and only the "default (no args) constructor", then You can change your configuration to:
<bean id="dataStore" class="com.fressnapf.sdk.dataaccess.services.DataStore">
<property name="connectionString" value="..."/>
<!-- ... -->
</bean>
Using property instead of constructor-arg.
Docs (Spring 4.2): https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/xsd-configuration.html
Yes, there is a constructor.
If you don't explicitly put one, Java will automatically add a default (non argument) constructor.
Use that one.
It will set all the instance variables to the default value of their type.
In your case: null for every String variable, false for every boolean.

Spring Setter Injection on shadowed fields

I want to shadow a variable and change its type from boolean to string in its child class. Yet, the setter injection fails. I know it is a bad practice to use shadowing, however I would like to know if it is possible and how.
Example what I would like to do:
public class A {
public boolean field;
#SuppressWarnings("javadoc")
public boolean isField() {
return field;
}
#SuppressWarnings("javadoc")
public void setField(boolean field) {
this.field = field;
}
}
I want to shadow the attribute 'field' to be a String in the subclass.
public class B extends A {
private String field;
#SuppressWarnings("javadoc")
public String getField() {
return field;
}
#SuppressWarnings("javadoc")
public void setField(String field) {
this.field = field;
}
}
I get an exception that the setter Injection does not work:
org.springframework.beans.TypeMismatchException: Failed to convert property value of type [java.lang.String] to required type [boolean] for property 'field'
<bean id="exampleBean" class="B" >
<property name="field" value="value"/>
</bean>
Though I haven't tested it. But you can try the following way.
<bean id="beanA" class="A">
<property name="field" value="false"></property>
</bean>
<bean id="beanB" parent="beanA" class="B">
<property name="field" value="anyValue"></property>
</bean>
Let me know If it works for you.
Edit-
Please go through the Bean definition inheritance doc.
I have tested the example for shadowed field that you are using. You must have to configure a bean for parent class as well, because if you only configure bean for child class, you would get exception below -
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'helloWorld' is defined
I don't know what you are trying to accomplish using so-called shadowed fields. But here I have created a working example which might help you. Check it out.
MainApp.java
package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String args[]){
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
HelloWorld hw = (HelloWorld) context.getBean("helloWorld");
System.out.println("Is message:" + hw.isMsg());
HelloCountry hc = (HelloCountry) context.getBean("helloCountry");
System.out.println("Message:" + hc.getMessage());
}
}
class HelloWorld {
private boolean message;
public void setMessage(boolean message){
this.message = message;
}
public boolean isMsg(){
return message;
}
}
class HelloCountry extends HelloWorld {
private String message;
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
Beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- A parent class bean must be defined to create its instance -->
<bean id = "helloWorld" class = "com.example.HelloWorld">
<property name = "message" value = "false"/>
</bean>
<bean id = "helloCountry" class = "com.example.HelloCountry">
<property name = "message" value = "Hello Country!!!"/>
</bean>
</beans>
Output
Note-
You would get a below exception only when you pass any value other than boolean in your parent bean. In your case, your data type of child class field is matching with the injected bean value, but you must have to have a configured bean for parent class as well. In short this is how It works Dependency Injection in case of Inheritance.-
Caused by: org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'java.lang.String' to required type 'boolean' for property 'message';
Let me know If it was helpful.

AutoWiring with constructor, how IOC will select constructor among multiple constructor [duplicate]

This question already has answers here:
Dependency injection with autowire="constructor" when multiple constructors are present?
(2 answers)
Closed 6 years ago.
I have three beans: Movie, Director, Hero
Movie bean:
public class Movie {
private Director director;
private String name;
private Hero hero;
public Movie(Director director, Hero hero) {
System.out.println("m1");
this.director = director;
this.name = name;
this.hero = hero;
}
public Movie(Director director, String name) {
System.out.println("m2");
this.director = director;
this.name = name;
}
public Movie(Director director) {
System.out.println("m3");
this.director = director;
}
public Movie() {
System.out.println("m4");
}
}
Director and Hero beans:
public class Director {
}
public class Hero {
}
spring-core.xml
<bean id="movieBean" class="Movie" autowire="constructor" />
<bean id="directorBean" class="Director"></bean>
<bean id="heroBean" class="Hero"></bean>
Please note in the above XML I have declared all the three beans
MainClass
ApplicationContext context = new ClassPathXmlApplicationContext("spring-core.xml");
Movie movie = (Movie) context.getBean("movieBean");
System.out.println(movie);
The output is "m1" i.e. the constructor called is Movie(Director director, Hero hero)
My question is: why only this constructor is called and how IOC container will choose among multiple constructor while injecting dependencies if we are using autowiring using constructor
Only one constructor can be called to instantiate the bean. This is similar to how when creating a new Object you only invoke one constructor (If you want to be really precise, yes you could chain constructors calling this)
In this case the constructor with most arguments that can be satisfied by other beans available in the context will be used.

Spring autowire by constructor

I have two classes Test and SampleClassWithMultipleProperty for which I have bean definitions as follows:
<bean name="test" class="Test" autowire="constructor">
</bean>
<bean id="instance1" class="SampleClassWithMultipleProperty">
<constructor-arg type="java.lang.String" value="constructor-arg-1"/>
<constructor-arg type="java.lang.String" value="constructor-arg-2"/>
<constructor-arg type="java.lang.String" value="constructor-arg-3"/>
<constructor-arg type="int" value="4"/>
<constructor-arg type="java.lang.Integer" value="5"/>
</bean>
When I specify any name id like for example "instance1" ,"instance2","instance3" it works.
But when I have another bean of same type SampleClassWithMultipleProperty as follows :
<bean id="instance2" class="SampleClassWithMultipleProperty">
<constructor-arg type="java.lang.String" value="constructor-arg-1-ind"/>
<constructor-arg type="java.lang.String" value="constructor-arg-2-ind"/>
<constructor-arg type="java.lang.String" value="constructor-arg-3-ind"/>
<constructor-arg type="int" value="4"/>
<constructor-arg type="java.lang.Integer" value="5"/>
</bean>
The container is not able to initialize the bean instance of SampleClassWithMultipleProperty (with name sampleClassWithMultipleProperty) inside the Test class (Has a relation).
It successfully does that when I change any of the ids of the bean from "instance1" to "sampleClassWithMultipleProperty" OR "instance2" to "sampleClassWithMultipleProperty" .
But why is it behaving like this. I read somewhere that autowiring by constructor is similar to autowiring byType. So ideally it should match the type of the bean i.e. class name.
Please find my classes below:
public class Test {
SampleClassWithMultipleProperty sampleClassWithMultipleProperty;
public Test() {
super();
// TODO Auto-generated constructor stub
}
public Test(SampleClassWithMultipleProperty sampleClassWithMultipleProperty) {
super();
System.out.println("in Test constructor");
this.sampleClassWithMultipleProperty = sampleClassWithMultipleProperty;
}
public SampleClassWithMultipleProperty getSampleClassWithMultipleProperty() {
return sampleClassWithMultipleProperty;
}
public void setSampleClassWithMultipleProperty(
SampleClassWithMultipleProperty sampleClassWithMultipleProperty) {
this.sampleClassWithMultipleProperty = sampleClassWithMultipleProperty;
}
}
public class SampleClassWithMultipleProperty {
private String property1;
private String property2;
private String property3;
private int property4;
private Integer property5;
public SampleClassWithMultipleProperty() {
super();
// TODO Auto-generated constructor stub
}
public SampleClassWithMultipleProperty(String property1, String property2, String property3,
int property4, Integer property5) {
super();
this.property1 = property1;
this.property2 = property2;
this.property3 = property3;
this.property4 = property4;
this.property5 = property5;
}
public String getProperty1() {
return property1;
}
public void setProperty1(String property1) {
this.property1 = property1;
}
public String getProperty2() {
return property2;
}
public void setProperty2(String property2) {
this.property2 = property2;
}
public String getProperty3() {
return property3;
}
public void setProperty3(String property3) {
this.property3 = property3;
}
public int getProperty4() {
return property4;
}
public void setProperty4(int property4) {
this.property4 = property4;
}
public Integer getProperty5() {
return property5;
}
public void setProperty5(Integer property5) {
this.property5 = property5;
}
}
Hi I am not a spring expert but I know some bits and did some searching around and I would like to answer on basis of that.
I assume you have default-autowire by id. You have two bean definitions of class SampleClassWithMultipleProperty. This both are eligible for creation and to be used as constructor arg for Test.
By default, autowiring scan and matches all bean definitions in scope. If you want to exclude some bean definitions so that they can not be injected through autowiring mode, you can do this using autowire-candidate set to false.
So what you can do is:
<bean id="instance2" class="SampleClassWithMultipleProperty" autowire-candidate="false">
Also you have option to use default-autowire-candidates property which should help you out here. You can just pass a pattern which will be used to scan the probable candidates for bean creation.
Reference Link 1
Reference Link 2
Reference Link 3
Your Test bean has 2 constructors : a no arg constructor and a constructor that needs an instance of a SampleClassWithMultipleProperty.
When you only have one SampleClassWithMultipleProperty instance in your classpath spring will just choose the constructor that takes a SampleClassWithMultipleProperty.
When you have two SampleClassWithMultipleProperty spring won't be able to choose between them so the no arg constructor is going to be chosen.
When you are changing the id of one of your SampleClassWithMultipleProperty bean that match the name of the parameter of the constructor spring will be able to choose between them and will be able to create your bean with the constructor that expect an instance of SampleClassWithMultipleProperty.
If in the second case (2 SampleClassWithMultipleProperty beans and none matching the parameter's name) you would have had a NoUniqueBeanDefinitionException because spring would not have been able to use a proper constructor to build your bean.
Spring will automatically choose the constructor satisfying the most parameters possible.

injecting enum(Spring)

I have an enum and I need to inject it with spring bean.
my enum is:
public enum Status {
IN_PROCESS(1,"In process"),
DONE(0,"Successful"),
CANNOT_DONE(2,"Unsuccessful");
private final int code;
private final String description;
private Status(int code, String description){
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}
what my bean.xml should look like for this enum..
thanks.
You can not create an Enum via its constructor from outside of this Enum (not in java and not in Spring) because Enum values are constants!
An Enum constructor can only be invoked from the Enum declarion itselve.
Of course you can use an instance of this Enum, even in Spring, but you can not create it:
public Class Entity {
public Entity(Status status) {...}
}
<bean name="entity" class="package.Entity">
<property name="status" value="IN_PROCESS" />
</bean>
Technically you may try to register enum as a bean like this:
#Configuration
class EnumProducer {
#Bean
Status inProgress() {
return Status.IN_PROGRESS;
}
}
and then inject it like:
#Autowired("inProgress") Status status.
But there is no any sense for doing it.
Check <util:constant/> here:
http://static.springsource.org/spring/docs/3.0.x/reference/xsd-config.html

Categories

Resources