Core Spring : Annotations

It is possible to configure the dependency injection using annotations. So instead of using XML to describe a bean wiring, you can move the bean configuration into the component class itself by using annotations on the relevant class, method, or field declaration. Annotation injection is performed before XML injection, thus the latter configuration will override the former for properties wired through both approaches. Lets see few of the common ones:

@Required – This annotation simply indicates that the affected bean property must be populated at configuration time: either through an explicit property value in a bean definition or through autowiring. The container will throw an exception if the affected bean property has not been populated; this allows for eager and explicit failure, avoiding NullPointerExceptions or the like later on. It applies to bean property setter methods (not property). Employee and Address classes:


package mynotes.corespring.annotations;

import org.springframework.beans.factory.annotation.Required;

public class Employee{

private int id;
private String name;

private Address homeAddress;

public Employee(int id, String name) {
this.id = id;
this.name = name;
}

@Override
public String toString() {
return ("Id=>" + this.id + " Name=>" + this.name+ "\nhomeAddress=>"
+ this.homeAddress );
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Address getHomeAddress() {
return homeAddress;
}
@Required
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}

}


package mynotes.corespring.annotations;

public class Address {

private String street;
private String city;
private int pincode;

@Override
public String toString() {
return ("street::" + this.street + " city::" + this.city + " pincode::"
+ this.pincode);
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getPincode() {
return pincode;
}
public void setPincode(int pincode) {
this.pincode = pincode;
}

}

spring.xml:


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

<bean id="employee" class="mynotes.corespring.annotations.Employee">
<constructor-arg value="John" type="String"></constructor-arg>
<constructor-arg value="20" type="int"></constructor-arg>
</bean>

<bean id="homeAddress" class="mynotes.corespring.annotations.Address">
<property name="street" value="Street 1"></property>
<property name="city" value="City1 "></property>
<property name="pincode" value="123456"></property>
</bean>
</beans>

Notice above that we intentionally removed the property tag in employee bean whcih initialized homeAddress.
App.java:


package mynotes.corespring.annotations;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {

public static void main(String[] args) {
AbstractApplicationContext context=new ClassPathXmlApplicationContext("/mynotes/corespring/annotations/spring.xml");
context.registerShutdownHook();
Employee employee=(Employee) context.getBean("employee");
System.out.println(employee);

}

}

Running this code will code actually raise no exception and the epployee object will just have a null address. Output:


Id=>20 Name=>John
homeAddress=>null

Annotation wiring is not turned on in the Spring container by default. Spring actually knows to check these annotations using a out of the box BeanPostProcessors that helps in this validation. So the bean post processor actually throws the exception. For this @Required annotations to work we need te following RequiredAnnotationBeanPostProcessor. Add the following to the spring.xml:


<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"></bean>

Now run App.java again. Output:


Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'homeAddress' is required for bean 'employee'

Hence the exception is thrown while initializing employee object itself. Add the property homeAddress to employee bean  fix it.


<property name="homeAddress" ref="homeAddress"></property>

@Autowired annotation can be used to autowire bean on the setter method just like @Required annotation. It can apply to bean property setter methods, non-setter methods, constructor and properties. To automatically refer to homeAddress bean all you have do in Employee.java:


@Autowired
private Address homeAddress;

It can also be in property setter method. A constructor @Autowired annotation indicates that the constructor should be autowired when creating the bean, even if no <constructor-arg> elements are used while configuring the bean in XML file. By default, the @Autowired annotation implies the dependency is required similar to @Required annotation, however, you can turn off the default behavior by using (required=false) option with @Autowired.

Just adding @Autowired annotation wont be enough, it require AutowiredAnnotationBeanPostProcessor to check. Adding the following to spring.xml:


<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"></bean>

Autowired first looks for the type, if it find that any one bean is of that type, it uses it. But if there are multiple beans of the same type (homeAddress and officeAddress), then it will check if any one of these multiple beans have the same name (id) as that of the memberVariable of the class.

Sometimes you maynot have the same bean name (id) as that of the member variable of the class. For example, suppose we have ‘officeAddress’ name in our class but ‘office’ as bean id in our xml. In Employee.java


@Autowired
private Address officeAddress;

In spring.xml:


<!-- other beans - employee and homeAddress -->

<bean id="office" class="mynotes.corespring.annotations.Address">
<property name="street" value="Street 123"></property>
<property name="city" value="City123 "></property>
<property name="pincode" value="9877654"></property>
</bean>

In such case you can use @Qualifier annotation along with @Autowired to remove the confusion by specifying which exact bean will be wired. Employee.java:


@Autowired
@Qualifier(value="officeQualifier")
private Address officeAddress;

‘office’ bean in spring.xml:


<bean id="office" class="mynotes.corespring.annotations.Address">
<qualifier value="officeQualifier"></qualifier>
<property name="street" value="Street 123"></property>
<property name="city" value="City123 "></property>
<property name="pincode" value="9877654"></property>
</bean>

Yes there is another BeanPostProcessor to be implemented for this annotation to work. Instead of adding post processor for each annotation, we use the following configuration file in case you want to use any annotation in your Spring application.


<context:annotation-config/>

<!-- other beans - employee homeAddress office-->

@Resource annotation is annother way to inject dependencies in member variable of a class. It is from javax.annotation package, therefore not spring specific. For example:


@Resource(name="permAdd")
private Address permanentAddress;

In spring.xml add:


<bean id="permAdd" class="mynotes.corespring.annotations.Address">
<property name="street" value="Street 976"></property>
<property name="city" value="City 645645 "></property>
<property name="pincode" value="321456"></property>
</bean>

If we donot specify the name it will look for the same name as the name of the member variable.

You can use @PostConstruct annotation as an alternate of initialization callback and @PreDestroyannotation as an alternate of destruction callback. They are same as the init and destroy methods we discussed before. Note that they are also from javax package.


@PostConstruct
public void myCustomInit() {
System.out.println("myCustomInit called after bean initialized");
}
@PreDestroy
public void myCustomDestroy() {
System.out.println("myCustomDestroy called before bean get destroyed");
}

Till now we have been creating bean in spring.xml. To do this with annotations we use @Component annotation from org.springframework.stereotype.Component.


@Component
public class Employee{

Default bean name will be the name of the class in lowercase. Now Address class has 3 beans defimed in the spring.xml and if you add @Component to the Address class you will have only one bean. A disadvantage of using this.

Now even if you use @Component , spring needs to know that you have a bean inside your code using this annotation. Adding following in spring.xml:


<context:component-scan base-package="mynotes.corespring.annotations"/>

Spring will scan classes under these packages for any sterotype annotation like @Component.

In Enterprise web app build on spring there are many other annotation like Controllers,Service, dataObject etc.
Annotate all your service classes with @Service. All your business logic should be in Service classes. Annotate all your DAO classes with @Repository. All your database access logic should be in DAO classes. Annotate your controller classes with @Controller and many otheres. This helps us to know what role the bean is playing in the app.

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: