^ Click Here

Thursday, March 15, 2012

Spring Autowiring types with examples

 [ All the examples in this post are basically an extension from the previous post of Spring Basic ]

Autowiring is a feature in Spring through which we can pass the reference of any dependent bean automatically.......This is a step where we are telling the Spring framework to take care of the dependencies.
In that case when Application Context is created the Spring Framework will search for the beans on which it is dependent on according to set guidelines and if and when it finds the bean it will inject that automatically hence we no further need to take care of passing the reference.
Controls whether bean properties are "autowired". This is an automagical process in which bean references don't need to be coded explicitly in the XML bean definition file, but rather the Spring container works out dependencies. There are 4 modes: 1. "no" The traditional Spring default. No automagical wiring. Bean references must be defined in the XML file via the element (or "ref" attribute). We recommend this in most cases as it makes documentation more explicit. Note that this default mode also allows for annotation- driven autowiring, if activated. "no" refers to externally driven autowiring only, not affecting any autowiring demands that the bean class itself expresses. 2. "byName" Autowiring by property name. If a bean of class Cat exposes a "dog" property, Spring will try to set this to the value of the bean "dog" in the current container. If there is no matching bean by name, nothing special happens. 3. "byType" Autowiring if there is exactly one bean of the property type in the container. If there is more than one, a fatal error is raised, and you cannot use byType autowiring for that bean. If there is none, nothing special happens. 4. "constructor" Analogous to "byType" for constructor arguments. If there is not exactly one bean of the constructor argument type in the bean factory, a fatal error is raised. Note that explicit dependencies, i.e. "property" and "constructor-arg" elements, always override autowiring. Note: This attribute will not be inherited by child bean definitions. Hence, it needs to be specified per concrete bean definition

What did I mean by "set guidelines"?? for Spring Framework to find the correct bean we must set some rules.............well nothing speaks better than example....Hence going with our previous example

Our config.xml file will be

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context         
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">


<bean class="pkg.Person" id="person" p:name="Rahul" autowire="byType">   
</bean>
<bean class="pkg.Dog" id="Dog" p:name="Doug" autowire-candidate="false"></bean>
<bean class="pkg.Cat" id="Cat" p:name="Jejebel"> 
 </bean></beans>
Note that the autowiring is "byType" , it simply means in the Person Class the type of "animal" is IAnimal......Hence the Spring framework in the Application Context will search for all beans with type IAnimal and will inject(connect) it to the animal and hence it's autowired. We don't need to explicitly pass the reference of any bean for animal property as in Spring Basic Example.

Now the question arises what if it finds more than one bean which have type IAnimal as in our case "Dog" and "Cat" both are of type IAnimal.........It certainly would throw an error due to indecision for which bean to use something like - "org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'person' defined in class path resource [config.xml]: Unsatisfied dependency expressed through bean property 'animal': : No unique bean of type [pkg.IAnimal] is defined: expected single matching bean but found 2: [Dog, Cat]; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [pkg.IAnimal] is defined: expected single matching bean but found 2: [Dog, Cat]".

If we would have used byName this problem won't have been there since two beans can't have same name(id) more on it later.........but we are not using that.
the solution is using an autowire-candidate property on beans as the bean "Dog" have autowire-candidate as false which simply means that this bean is not available for autowiring that simply means that only bean "Cat" is available for autowiring.
Indicates whether or not this bean should be considered when looking for matching candidates to satisfy another bean's autowiring requirements. Note that this does not affect explicit references by name, which will get resolved even if the specified bean is not marked as an autowire candidate.

Now when we run our test class it will give an output "Rahul has a  Cat with name Jejebel" 

It could be tedious to provide the kind of autowire for many beans specially when you are following a definite norm.......like you want all the beans to be autowired with "byName". Spring has provided the facility, all you need to do is in the configuration xml file set the default autowire as "byName". point to keep in mind that it will only work for one configuration file (you can have multiple) and not for the full application context. So you can organize all your same type autowired bean in one configuration file and set the default for that very xml file

test will again give you the same result .

But what If you need to have a default-autowire as "byType" in your configuration file but there are a few exceptions which needs to be autowired as "byName". Well in that case you can override the default autowiring just by specifically declaring it for the respective bean as "byName".

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context         
         http://www.springframework.org/schema/context/spring-context-3.0.xsd"
          default-autowire="byType">
   
<bean class="pkg.Person" id="person" p:name="Rahul">  
</bean>
<bean class="pkg.Dog" id="Dog" p:name="Doug" autowire-candidate="false"></bean>
<bean class="pkg.Cat" id="Cat" p:name="Jejebel">
</bean></beans>

Not just that you still can use the (older style) property with ref attribute for passing the dependency even if you are using an autowire tag on it. Hence its flexible.

Let's talk about the kind of autowiring

What we saw in the earlier example was "byType". have a look at the whole bunch :-

 > no - no autowiring should be done on the particular bean and the dependency
must be passed explicitly through "ref" attribute.

 > byNameAttempts to match all properties of the autowired bean with beans
that have the same name (or ID) as the properties. Properties for which there’s
no matching bean will remain unwired.

 > byTypeAttempts to match all properties of the autowired bean with beans
whose types are assignable to the properties. Properties for which there’s no
matching bean will remain unwired.

 > constructorTries to match up a constructor of the autowired bean with
beans whose types are assignable to the constructor arguments.

 > autodetectAttempts to apply constructor autowiring first. If that fails,
byType will be tried.


We have already seen the example of autowire as "byType"

now let's see an example with autowire equal to "byName"

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context         
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">


<bean class="pkg.Person" id="person" p:name="Rahul" autowire="byName">  
</bean>
<bean class="pkg.Dog" id="animal" p:name="Doug"></bean>
<bean class="pkg.Cat" id="Cat" p:name="Jejebel">
</bean></beans>

run the test again and it will output "Rahul has a  Dog with name Doug".

Thing to notice is that the name of bean "Dog" has been changed to "animal" on purpose, this way the the IAnimal type "animal" variable of class Person is mapped with the bean named "animal" and hence the dependency is autowired by Spring Framework according to the name.

Hence when the application context is created the framework search for the bean named "animal" and inject it into the bean "person" since the autowiring is "byName"

Now let's see the autowiring by constructor......to display this I would need to alter the class as I need to add a constructor to the Person class

the Person Class

public class Person {
 private String name;
 private IAnimal animal;
 
 public Person(IAnimal animal) {
  this.animal = animal;
 }
 public void setName(String name) {
  this.name = name;
 }
 public String getName() {
  return name;
 }
 public IAnimal getAnimal() {
  return animal;
 }
 public String toString() {
  // TODO Auto-generated method stub
  return name+" has a "+animal.toString();
 }

changes are that there are no setter for IAnimal type animal.......instead it is set with the constructor.....in the constructor the reference is passed as an argument.

config.xml

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
         http://www.springframework.org/schema/context         
         http://www.springframework.org/schema/context/spring-context-3.0.xsd">


<bean class="pkg.Person" id="person" p:name="Rahul" autowire="constructor">  
</bean>
<bean class="pkg.Dog" id="Dog" p:name="Doug" autowire-candidate="false"></bean>

<bean class="pkg.Cat" id="Cat" p:name="Jejebel">
</bean></beans>

Run the test and it will print "Rahul has a  Cat with name Jejebel".

Hence it is clear that when autowire is constructor.......Spring framework will search for the constructor with argument type as IAnimal and then will search for the bean type "IAnimal" and then passes that bean to set animal.

autowire-candidate="false" is used for the same reason as for it was used in autowire byType.

I am not providing any example of autowire as "autodetect" since this is nothing but a combination of autowire by constructor and byType respectively i.e. it looks for constructor with argument type and if there is none it looks for the type and searches the bean accordingly.


Well this is not all.....we still have "autowire with annotation" and autodiscover features....................but that in other post......

No comments:

Post a Comment