@Required annotation in spring
We are going to look at how to configure the Spring beans and dependency routing using annotations. Spring provides support for annotation based container configuration. There are some annotations that are used to configure beans and dependency injection. One of them is @Required annotation
We have,
BankApp.java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package com.j2eereference.spring; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class BankApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); Bank bank = (Bank)context.getBean("hdfc"); bank.getBranchInfo(); } } |
Here, we are getting “hdfc” bean from spring.xml and calling getBranchInfo().
spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="hdfc" class="com.j2eereference.spring.HDFC" > <property name="branch" ref="branchA" /> </bean> <bean id="branchA" class="com.j2eereference.spring.Branch" > <property name="code" value="1234"></property> <property name="address" value="BranchA Address"></property> </bean> <bean id="branchB" class="com.j2eereference.spring.Branch" > <property name="code" value="1234"></property> <property name="address" value="BranchB Address"></property> </bean> </bean> </beans> |
We have defined “hdfc” to be a bean of type HDFC.java
HDFC.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package com.j2eereference.spring; public class HDFC implements Bank{ private Branch branch; public Branch getBranch() { return branch; } public void setBranch(Branch branch) { this.branch = branch; } @Override public void getBranchInfo() { System.out.println("HDFC Branch code : "+branch.getCode()+"and Address : "+branch.getAddress() ); } } |
HDFC has just a ‘branch’ and getBranchInfo() method that prints the branch information.
Branch.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.j2eereference.spring; public class Branch { private int code; private String address; public Branch(int code, String address) { this.code = code; this.address = address; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } |
Branch has code and address attributes of type int and String respectively.
Bank.java
1 2 3 4 5 6 7 |
package com.j2eereference.spring; public interface Bank { public void getBranchInfo(); } |
Bank interface is implemented by HDFC.java.
Look at the spring.xml that has “hdfc” bean. It has a property branch that references to another bean ‘branchA’. When we run this, we get the output as :
HDFC Branch code : 1234 and Address : BranchA Address
What happens if we remove the ‘branch’ property from ‘hdfc’ bean?? We have the bean configured which does not have any dependencies specified in the spring.xml. The attribute ‘branch’ in HDFC class will not be assigned any value. When the getBranchInfo() is called, it will give the obvious NullPointerException in the getBranchInfo() method. In real-time applications with thousands of lines code, we never know when we might encounter a NullPointerException like this. We might have the application deployed and running and days down the line we might encounter a NullPointerException because one of the dependencies was not met. So, we have to validate, before the application starts, that all the required dependencies are met. This is where we use the @Required annotation.
In our example we have to let Spring know that ‘branch’ attribute is required. We need to make sure that there is some kind of bean wiring so that ‘branch’ member variable is not null when the application runs. When Spring initializes all the beans, we want Spring to validate that there is some bean that gets assigned to ‘branch’ member variable of ‘hdfc’ bean. In order to do that, we have to configure the HDFC class and tell that ‘branch’ member variable is required. We can do it on the setter method of the ‘branch’ member variable. This setter has to be run thereby setting some value to the ‘branch’ member variable when the ‘hdfc’ bean is being initialized. If the ‘branch’ is not assigned any value, we want the exception to be thrown, not when the application is running, but while initializing the beans itsels so that we can handle all these errors upfront. To tell spring that ‘branch’ is requires, we use @Required on setter method.
HDFC.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.j2eereference.spring; import org.springframework.beans.factory.annotation.Required; public class HDFC implements Bank{ private Branch branch; public Branch getBranch() { return branch; } @Required public void setBranch(Branch branch) { this.branch = branch; } @Override public void getBranchInfo() { System.out.println("HDFC Branch code : "+branch.getCode()+"and Address : "+branch.getAddress() ); } } |
We can do this on any number of classes. We can mark all the required dependencies with @Required annotation. It tells Spring that ‘branch’ is a required member variable, if it is not set, Spring will not proceed with execution of the application. It will throw an exception.
In addition to marking with @Required annotation, we have to declare a BeanPostProcessor in the spring.xml. When all the beans are being instantiated, it checks for the @Required annotations. If it finds a @Required annotation that is not met, it is the BeanPostProcessor that actually throws the exception. To use the BeanPostProcessor, define the bean in the spring.xml.
spring.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="hdfc" class="com.j2eereference.spring.HDFC" > </bean> <bean id="branchA" class="com.j2eereference.spring.Branch" > <property name="code" value="1234"></property> <property name="address" value="BranchA Address"></property> </bean> <bean id="branchB" class="com.j2eereference.spring.Branch" > <property name="code" value="1234"></property> <property name="address" value="Bangalore"></property> </bean> <bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor" /> </beans> |
RequiredAnnotationBeanPostProcessor has a method that gets executed on initialization of each of these beans and checks for the @Required annotation. If it finds any required member variable that is not set, it throws the exception. Note that, in spring.xml, we are not setting ‘branch’ property. We have just added a BeanPostProcessor. When we run this, we get the exception, not from getBranchInfo() but in the bean initialization phase itself, much before the execution of the application. This is how @Required annotation is helpful to validate that all our required dependencies are met.