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.
Related
Hi i got a little problem with my app.
I'm trying to get bean data from xml.
beans.xml
<bean id="s3Bean" class="com.example.demo.controller.S3Bean">
<property name="s3Host" value="${s3.host}" />
<property name="s3AccessKey" value="${s3.accessKey}"/>
<property name="s3SecretKey" value="${s3.secretKey}"/>
<property name="s3Bucket" value="${s3.bucket}"/>
</bean>
S3Bean.java
#ImportResource("classpath:beans.xml")
public class S3Bean {
private String s3Host;
private String s3AccessKey;
private String s3SecretKey;
private String s3Bucket;
public void setS3AccessKey(String s3AccessKey) {
this.s3AccessKey = s3AccessKey;
}
public void setS3SecretKey(String s3SecretKey) {
this.s3SecretKey = s3SecretKey;
}
public void setS3Bucket(String s3Bucket) {
this.s3Bucket = s3Bucket;
}
public void sets3Host(String s3Host) {
this.s3Host = s3Host;
}
}
Class also have getters.
In my controller im trying to get bean like that.
public S3Bean getS3Bean() {return S3Bean;}
public void setS3Bean(S3Bean s3Bean) {S3Bean = s3Bean;}
private S3Bean S3Bean;
...
S3Bean.getS3Host();
This get returns null pointer.
When im geting bean from application context it prints fine.
ApplicationContext ctx = new ClassPathXmlApplicationContext("/beans.xml");
S3Bean bean = ctx.getBean(S3Bean.class);
System.out.println(bean.toString());
Could you explain why the first way of getting property dosent working?
StackTrace
java.lang.NullPointerException: null
at com.example.demo.controller.hello.sayHello(hello.java:27) ~[classes/:na]
27 line is S3Bean.getS3Host();
I found out what was the problem. I didnt declared S3Bean in beans.xml.
<bean id="springBean.Impl" class="com.example.demo.controller.Impl">
<property name="S3Bean" ref="springBean.S3Bean"/>
</bean>
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.
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.
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
I have this Singleton:
public enum Elvis {
INSTANCE;
private int age;
public int getAge() {
return age;
}
}
I know how to create the enum bean in spring:
<bean id="elvis" class="com.xyz.Elvis" factory-method="valueOf">
<constructor-arg>
<value>INSTANCE</value>
</constructor-arg>
</bean>
How do I pass the int returned by INSTANCE.getAge() into another beans constructor?
You can use Spring Expression Language:
<constructor-arg value = "#{elvis.age}" />
or without elvis bean:
<constructor-arg value = "#{T(com.xyz.Elvis).INSTANCE.age}" />