Annotation Based Configuration in Spring Framework

Till now, we have seen XML Based configuration. Now in this article, we will learn how to use the annotation-based configuration in Spring Framework.

Spring 2.5 came in 2007, introduced the Annotation-based configuration. It helps in injecting the dependencies. It has method-level annotations i.e the annotation is applied on the bean property of the setter method.

The following is the list of annotations that we will be using in the coming articles and also you should be aware of which annotation does what.

Annotation
Description
Applied on
@Required It is applied to bean property setter methods. Fails the configuration if dependency is not injected. Constructor, Field & Method
@Autowired It autowired the appropriate bean into class members. Constructor, Field & Method
@Qualifier It filters what beans should be used to @Autowire a field. Constructor, Field & Method
@Bean It says that the method produces beans that are managed by a container. Method
@Configuration It makes a class a source of beans definition. Class
@ComponentScan It tells spring to scan the package for configuration. Class
@Component It makes a class into the bean and will be auto-detected. Class
@Service It is a specialized version of @Component. Class
@Controller It is a specialized version of @Component to annotate Controllers while developing web-based apps. Class
@Repository It is used to annotate Data Access Object Class. Class
@Lazy It makes @Bean and @Component be initialized on demand rather than eagerly. Class & Method
@Value It indicates a default value for any field or parameter. Constructor, Field & Method

To enable annotation wiring in Spring Container, First, we have to enable the annotation-based configuration inside the configuration file like the following.

<?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:context="http://www.springframework.org/schema/context"
  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">
<!-- To activate annotation in spring -->
    <context:annotation-config />
</beans>

There is also one more way, in which we can activate the annotation-based configuration by specifying the bean definition of RequiredAnnotationBeanPostProcessor class in the config file.

<?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:context="http://www.springframework.org/schema/context"
  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">
 <!-- To activate the annotation in spring -->
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"></bean>
</beans>

Now, we will take an example of how to implement this annotation in the spring Framework.

Example for @Required Annotation

@Required:  It is applied to bean property setter methods. It fails the configuration if dependency is not injected.

Create a Spring Project Go to File> New > Other > Search maven > Select Maven Project > Next > Search Filter org.apache.maven.archetypes/webapp > Next > Enter Group Id & Archetype id > Finish.

In this example, we will be creating an Interface called Number, and two classes that will implement the Number Interface are Roman Number and Real Number. Following are the files that we will create

  • pom.xml – (because it’s a maven project)
  • Number.java
  • RealNumbers.java & RomanNumber.java
  • App.java
  • beans.xml – configuration file.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>New</groupId>
  <artifactId>DependencyInjection</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>DependencyInjection Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.3.4</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.4</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>DependencyInjection</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven 
        defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Number.java

  • In this interface, we have one method typeOfNumbers().
package com.codedec.annotation;

public interface Numbers {

  void typeOfNumber();
}

RomanNumber.java

The @Required annotation is applied on the setter method means it will inject the dependencies.

package com.codedec.annotation;

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

public class RomanNumber implements Numbers {

  private String numberType;
  
  @Required
  public void setNumberType(String numberType) {
    this.numberType = numberType;
  }


  @Override
  public void typeOfNumber() {
    System.out.println("Number in Roman Form :"+numberType);
  }

}

RealNumber.java

The @Required annotation is applied on the setter method means it will inject the dependencies.

package com.codedec.annotation;

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

public class RealNumber implements Numbers {

  private String numberType;
  
  @Required
  public void setNumberType(String numberType) {
    this.numberType = numberType;
  }


  @Override
  public void typeOfNumber() {
    System.out.println("Number in Real Form: "+numberType);
    
  }

}

beans.xml

  • In this XML file, we have used the RequiredAnnotationBeanPostProcessor class to enable annotation-based configuration.
<?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:context="http://www.springframework.org/schema/context"
  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="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"></bean>
  <bean id="realNum" class="com.codedec.annotation.RealNumber">
    <property name="numberType" value="1"></property>
  </bean>
  <bean id="romanNum" class="com.codedec.annotation.RomanNumber">
    <property name="numberType" value="I"></property>
  </bean>
</beans>

App.java

  • In this class, we get the bean definition from the context and will call the method typeOfNumber().
package com.codedec.annotation;

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

public class App {

  public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    Numbers numbers = context.getBean("realNum", Numbers.class);
    numbers.typeOfNumber();
    Numbers numbers1 = context.getBean("romanNum", Numbers.class);
    numbers1.typeOfNumber();
  }
}

Output

Number in Real Form: 1
Number in Roman Form :I

Thus, we get the above output by using the annotation-based configuration.

Note: If you don’t supply dependencies from the beans.xml file then it will throw an exception BeanInitializationException

Modify the above beans.xml file. Here, On-Line no:13, we are not supplying dependencies. Run the App.java once again.

<?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:context="http://www.springframework.org/schema/context"
  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="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"></bean>
  <bean id="realNum" class="com.codedec.annotation.RealNumber">
    
  </bean>
  <bean id="romanNum" class="com.codedec.annotation.RomanNumber">
    <property name="numberType" value="I"></property>
  </bean>
</beans>

Output

Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'numberType' is required for bean 'realNum'
  at org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.postProcessPropertyValues(RequiredAnnotationBeanPostProcessor.java:158)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1418)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)

Thus, If we apply @Required annotation and if you don’t provide the dependencies it would throw the above exception.

Thus, this was all about @Required annotation with a simple and clear example. In the next article, we will understand @Autowire annotation using a simple example.