Spring Core-Setter Injection - spring

I need help regarding mechanism of setter injection in Spring 3.0
Following is my code
package com.common;
class B{
public B(){}
}
class A{
B b;
public A(){}
public void setB(B b){
syso("I am in setter of B");
}
}
In XML,I have,
<bean id="A" class="com.common.A">
<property name="b" ref="B" />
</bean>
<bean id="B" class="com.common.B">
Now,my question is, How does spring container knows that it has to inject dependency through setB(B b) method? What if I change name of setter as newSetB(B b)? I hope I have made my doubt clear.

whenever you have something like this in your property file
<bean id="A" class="com.common.A">
<property name="b" ref="B" />
</bean>
spring container by default searches for setter of that property i.e setPropertyName() after invoking a no-argument constructor of that class whose bean is being prepared, and as it confines to JavaSpecification it searches for method named as setPropertyName()
You can verify this by chaning the name of method or by removing the method entirely
you will get the exception telling that, No Public setter found for that particular property
hope this helps!
Good luck!

The mapping here happens in this way.
Lets say we have following bean
<bean id="A" class="com.common.A">
<property name="testProperty" ref="B" />
</bean>
Spring container tries to get set method as setTestProperty(), if not found it will through an error.
Hope it helps - Cheers !

when ever SpringContainer reads the xml file and finds this
<bean id="A" class="com.common.A">
<property name="b" ref="B"/>
</bean>
<bean id="B" class="com.common.B"/>
firstly it creates object for Bean A, after that it initializes the member variables here "public B b", b is member variable for Bean class A, so it searches for setter method setB(B b) if it finds it sets else if NO setB(B b) SETTER Method is FOUND (OR) SETTER Method named DIFFERRENTLY ex: newSetB(B b) it throws exception as
Invalid property 'b' of bean class [com.common.A]: Bean property 'b' is not writable or has an invalid setter method.
setter methods convention for SpringContainer is setPropertyname()

Related

Spring id-reference not working as expected

I'm working my way through the Spring Framework reference documentation with some very basic application code. So far, I've created an ApplicationContext from an XML file and loaded some beans. I believe I understand this process pretty well. I've loaded some basic beans with attributes based on fundamental types and found that straight-forward.
I'm now working on a composite bean with other beans as its attributes. So far, I've been able to set these attributes using a direct reference to a bean and an inner bean. However, when I try to get the idref element to work (see http://docs.spring.io/spring/docs/3.2.4.RELEASE/spring-framework-reference/html/beans.html#beans-idref-element) I get the following exception:
java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.example.BasicBean] for property 'idRef': no matching editors or conversion strategy found
Code snippets below:
Application Context XML
<?xml version="1.0" encoding="UTF-8"?>
<beans ...>
<bean id="id-bean" class="com.example.BasicBean" scope="singleton">
<property name="value" value="31"/>
</bean>
<bean id="ref-bean" class="com.example.BasicBean" scope="singleton">
<property name="value" value="37"/>
</bean>
<bean id="cb" class="com.example.CompositeBean" scope="singleton">
<property name="id" ref="id-bean"/> <!-- works -->
<property name="inner"> <!-- works -->
<bean class="com.example.BasicBean">
<property name="value" value="43"/>
</bean>
</property>
<property name="idRef"> <!-- exception thrown -->
<idref bean="ref-bean"/>
</property>
</bean>
</beans>
Java App Code
public void main()
{
context = new ClassPathXmlApplicationContext("beans.xml");
CompositeBean cb = context.getBean("cb", CompositeBean.class);
}
Java Bean Code
public class CompositeBean
{
private BasicBean id;
private BasicBean idRef;
private BasicBean inner;
// Default constructor exists
// Setters, getters for each attribute exist
...
}
public class BasicBean
{
private int value;
// Default constructor exists
// Setters, getters for each attribute exist
...
}
Version info: I'm using Eclipse IDE (Kepler), Maven 3.1, Java 7 (1.7.45), Spring 3.2.4
Any thoughts on what I'm doing wrong?
Thanks #Farid for pointing out that I was mis-using the idref attribute in this instance. As the Spring doco points out (which I'd read several times and still missed this) it's designed for passing the id of a bean around a container and not the actual bean. So, stick to the first two means above (ref and inner bean) for passing a bean around.

Spring dependency injection issue

I have a bean I am trying to configure in Spring context using Constructor injection. When I pass subclass for one of the constructor arguments, the bean is instantiated by Spring container only if I do not specify the "type" attribute. Would anybody have any idea what's wrong? Below are more specifics.
class MyClass{
public MyClass(SomeAbstractBase absObject){
//do stuff
}
}
class ConcreteClass extends SomeAbstractBase{
//
}
Spring configs (First and second do not work but the third one using type attribute works)-
Config I-
<bean id="concreteclass"
class="ConcreteClass"/>
<bean id="myclass"
class="MyClass">
<constructor-arg type="ConcreteClass" ref="concreteclass"/>
</bean>
Config II-
<bean id="concreteclass"
class="ConcreteClass"/>
<bean id="myclass"
class="MyClass">
<constructor-arg type="SomeAbstractBase" ref="concreteclass"/>
</bean>
Config III-
<bean id="concreteclass"
class="ConcreteClass"/>
<bean id="myclass"
class="MyClass">
<constructor-arg ref="concreteclass"/>
</bean>
I get the following exception at initialization-
Exception in thread "main"
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'jedispool' defined in class path resource
[cache-spring-config.xml]: Could not resolve matching constructor
(hint: specify index/type/name arguments for simple parameters to
avoid type ambiguities)
Why would neither of the first or second config work?
Thank you
Type argument accept only the full qualified type (because if not spring cannot determine exactly the package and the type will not match your class):
So you need to use the canonical name of your class to be a type
ConcreteClass => com.your.app.ConcreteClass
A class is not just a name, is a package + name (too be short)
I believe the error you had appeared because you had static nested classes. Here's an SSCCE
package test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) throws Exception {
System.out.println(MyClass.class.getName());
System.out.println(ConcreteClass.class.getName());
System.out.println(SomeAbstractBase.class.getName());
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
}
static class MyClass {
public MyClass(SomeAbstractBase absObject) {
}
}
static class ConcreteClass extends SomeAbstractBase {
//
}
static abstract class SomeAbstractBase {
}
}
With spring.xml containing
<bean id="concreteclass" class="test.Test.ConcreteClass" />
<bean id="myclass" class="test.Test.MyClass">
<constructor-arg type="test.Test.SomeAbstractBase" ref="concreteclass" />
</bean>
The above fails with a UnsatisfiedDependencyException. In this specific example, the class attribute isn't used correctly. The fully qualified class name for ConcreteClass is test.Test$ConcreteClass, not test.Test.ConcreteClass as I've specified. The same applies to MyClass and SomeAbstractBase.
However, specifying test.Test.ConcreteClass in the class attribute for the concreteclass bean doesn't fail because at some point during processing of bean declarations, Spring tries to resolve the class String into a Class object using Class.forName(String). It will call ClassUtils.forName(String, ClassLoader) to do this. Initially it will fail because there is no such class test.Test.ConcreteClass. However, this is done in a try-catch which on ClassNotFoundException will transform the String class name from test.Test.ConcreteClass to test.Test$ConcreteClass and try again. It will work and correctly create a bean of type test.Test$ConcreteClass for your concreteclass bean.
When it tries to create your myclass bean, however, it does not apply such logic for resolving which constructor to use and therefore cannot understand that with the type attribute value of test.Test.SomeAbstractBase, you actually meant test.Test$SomeAbstractBase, so it fails saying the type is ambiguous.
Change your bean declarations to correct types
<bean id="concreteclass" class="test.Test$ConcreteClass" />
<bean id="myclass" class="test.Test$MyClass">
<constructor-arg type="test.Test$SomeAbstractBase" ref="concreteclass" />
</bean>
and it will work.
Take a look at kakawait's answer, you need to specify the fully qualified class name for the bean you are trying to instantiate.

getting new bean instances of referencing beans every time

I have two queries ...
1) I have a query that beans are intialized in the spring container itself when the spring container is intialiazed and before the call is made to the get bean() method , this is the default behavior , how can I configure the application in such way that bean should be initalized in container only when a call is made of getbean() is made, shall we declare the bean as prototype to achieve this.
2) second query is that first please go through the below example first...
<beans>
<bean id="triangle" class="Demo.Triangle" scope="singleton" >
<property name="pointA" ref="zeropoint"/>
<property name="pointB" ref="firstpoint"/>
<property name="pointC" ref="secondpoint"/>
</bean>
<bean id="zeropoint" class="Demo.Point" scope="prototype" >
<property name="x" value="10" />
<property name="y" value="20" />
</bean>
<bean id="firstpoint" class="Demo.Point" scope="prototype" >
<property name="x" value="10" />
<property name="y" value="20" />
</bean>
<bean id="secondpoint" class="Demo.Point" scope="prototype">
<property name="x" value="10" />
<property name="y" value="20" />
</bean>
As indicated above that triangle bean is singleton and it references bean are protoype now when I access singleton the other refernces beans zeropoint,firstpoint and secondpoint are also initaizes only once with respect to triangle but Here I want that every time new instance of these three beans to be get created when the triangle bean is fetched , Please advise how that is achieable. is it achievable through my pojo implementing ApplicationContextAware interface, Please advise
Instead of relying on Spring infrastructure (BeanFactoryAware) I recommend trying lookup method feature:
abstract class Triangle {
public abstract Point createZeroPoint();
public abstract Point createFirstPoint();
public abstract Point createSecondPoint();
public void foo() {
Point p0 = createZeroPoint();
Point p1 = createFirstPoint();
Point p2 = createSecondPoint();
}
}
Every time you call create*Point() abstract method, it creates new instance of Point. But how do you implement this method and how does it know which bean to return? Spring implements this for you!
<bean id="triangle" class="Demo.Triangle" scope="singleton">
<lookup-method name="createZeroPoint" bean="zeropoint"/>
<lookup-method name="createFirstPoint" bean="firstpoint"/>
<lookup-method name="createSecondPoint" bean="secondpoint"/>
</bean>
Check out the comprehensive documentation: 4.4.6.1 Lookup method injection.
Its called Lazy loading
<bean id="myBean" class="a.b.MyBean" lazy-init="true"/>
First note that your firstpoint and secondpoint beans don't have a valid scope definition (you missed the scope=)
Prototype scope means that every bean that requires a prototype bean gets its own instance. If would define multiple triangles which all have a dependency to zeropoint every triangle would a separate zeropoint instance.
If you need new point instances inside your triangle class (e.g. every time you call a method of triangle) the best way is to receive instances directly from the bean factory:
E.g.
class MyClass implements BeanFactoryAware {
private BeanFactory beanFactory;
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void doSomethingThatRequiresNewInstance() {
Triangle t = beanFactory.getBean("zeropoint", Triangle.class);
// because zeropoint is defined as prototype you get a new instance everytime you call getBean(..)
}
}

Spring's overriding bean

Can we have duplicate names for the same bean id that is mentioned in the XML?
If not, then how do we override the bean in Spring?
Any given Spring context can only have one bean for any given id or name. In the case of the XML id attribute, this is enforced by the schema validation. In the case of the name attribute, this is enforced by Spring's logic.
However, if a context is constructed from two different XML descriptor files, and an id is used by both files, then one will "override" the other. The exact behaviour depends on the ordering of the files when they get loaded by the context.
So while it's possible, it's not recommended. It's error-prone and fragile, and you'll get no help from Spring if you change the ID of one but not the other.
I will add that if your need is just to override a property used by your bean, the id approach works too like skaffman explained :
In your first called XML configuration file :
<bean id="myBeanId" class="com.blabla">
<property name="myList" ref="myList"/>
</bean>
<util:list id="myList">
<value>3</value>
<value>4</value>
</util:list>
In your second called XML configuration file :
<util:list id="myList">
<value>6</value>
</util:list>
Then your bean "myBeanId" will be instantiated with a "myList" property of one element which is 6.
Not sure if that's exactly what you need, but we are using profiles to define the environment we are running at and specific bean for each environment, so it's something like that:
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="originalValue" />
</bean>
<beans profile="DEV, default">
<!-- Specific DEV configurations, also default if no profile defined -->
<bean name="myBean" class="myClass">
<constructor-arg name="name" value="overrideValue" />
</bean>
</beans>
<beans profile="CI, UAT">
<!-- Specific CI / UAT configurations -->
</beans>
<beans profile="PROD">
<!-- Specific PROD configurations -->
</beans>
So in this case, if I don't define a profile or if I define it as "DEV" myBean will get "overrideValue" for it's name argument. But if I set the profile to "CI", "UAT" or "PROD" it will get "originalValue" as the value.
An example from official spring manual:
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
Is that what you was looking for?
Updated link
Since Spring 3.0 you can use #Primary annotation. As per documentation:
Indicates that a bean should be given preference when multiple
candidates are qualified to autowire a single-valued dependency. If
exactly one 'primary' bean exists among the candidates, it will be the
autowired value. This annotation is semantically equivalent to the
element's primary attribute in Spring XML.
You should use it on Bean definition like this:
#Bean
#Primary
public ExampleBean exampleBean(#Autowired EntityManager em) {
return new ExampleBeanImpl(em);
}
or like this:
#Primary
#Service
public class ExampleService implements BaseServive {
}
Another good approach not mentioned in other posts is to use PropertyOverrideConfigurer in case you just want to override properties of some beans.
For example if you want to override the datasource for testing (i.e. use an in-memory database) in another xml config, you just need to use <context:property-override ..."/> in new config and a .properties file containing key-values taking the format beanName.property=newvalue overriding the main props.
application-mainConfig.xml:
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
p:driverClassName="org.postgresql.Driver"
p:url="jdbc:postgresql://localhost:5432/MyAppDB"
p:username="myusername"
p:password="mypassword"
destroy-method="close" />
application-testConfig.xml:
<import resource="classpath:path/to/file/application-mainConfig.xml"/>
<!-- override bean props -->
<context:property-override location="classpath:path/to/file/beanOverride.properties"/>
beanOverride.properties:
dataSource.driverClassName=org.h2.Driver
dataSource.url=jdbc:h2:mem:MyTestDB
Whether can we declare the same bean id in other xml for other reference e.x.
Servlet-Initialize.xml
<bean id="inheritedTestBean" class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
Other xml (Document.xml)
<bean id="inheritedTestBean" class="org.springframework.beans.Document">
<property name="name" value="document"/>
<property name="age" value="1"/>
</bean>
Question was more about XML but as annotation are more popular nowadays and it works similarly I'll show by example.
Let's create class Foo:
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and two Configuration files (you can't create one):
#Configuration
public class Configuration1 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration1");
return foo;
}
}
and
#Configuration
public class Configuration2 {
#Bean
public Foo foo() {
Foo foo = new Foo();
foo.setName("configuration2");
return foo;
}
}
and let's see what happens when calling foo.getName():
#SpringBootApplication
public class OverridingBeanDefinitionsApplication {
public static void main(String[] args) {
SpringApplication.run(OverridingBeanDefinitionsApplication.class, args);
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(
Configuration1.class, Configuration2.class);
Foo foo = applicationContext.getBean(Foo.class);
System.out.println(foo.getName());
}
}
in this example result is: configuration2.
The Spring Container gets all configuration metadata sources and merges bean definitions in those sources. In this example there are two #Beans. Order in which they are fed into ApplicationContext decide. You can flip new AnnotationConfigApplicationContext(Configuration2.class, Configuration1.class); and result will be configuration1.

Getting an instance of a bean excplicitly in runtime

I have a case where I have a bean (let's call it A) which needs some other bean (B).
This B is retrieved from a static method of a class using MethodInvokingFactoryBean.
This static method depends on the state of the system and will work after the web application is loaded.
I need to access the B only in runtime (no interaction in the constructor).
How can I configure the A to autowire bean B and only initialize it the first time A requires it?
Is using getBean on the Application context the only way?
Thanks!
*Edit - Added some xmls :) *
This is the definition of bean B.
<bean id="api" class="com.foo.API"/>
<bean id="B" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" lazy-init="true">
<property name="targetObject" ref="api"/>
<property name="targetMethod" value="getFactory"/>
<qualifier value="myQualifer"/>
</bean>
This is the definition of bean A.
<bean id="resources.someRESTResourceA" class="com.foo.MyRestResource"/>
I can't use Autowire to wire B into A because it will initialize it (B) on A's construction.
B's targetMethod will only work after the web app has been initialized.
I can use ApplicationContext.getBean("B") inside A, but it's not elegant and will be a problem with unit testing unless I do the following (which is also not desired):
public BInterface getB() {
if (b == null) {
b = ApplicationContext.getBean("B");
}
return b;
}
you should lazily initialize bean A.
<bean id="A" class="demo.A" lazy-init="true">
<property name="b" ref="B"/>
</bean>
You still need to retrieve the bean A from the Spring container when you need it with the getBean() method. It's easily accesible with the ApplicationContextAware interface.
If you autowire bean A into an another bean and that bean is retrieved before bean B is constructed, the Spring container will create bean A at the time it's injected as a property to the another bean.

Resources