Spring: A Head Start 🔥 — Beans Configuration (Part 2)

Beans configuration using XML, Annotations, and Java code.

Spring: A Head Start 🔥 — Beans Configuration

Spring Beans

The objects that are managed by the Spring IoC container are called beans. These beans are created with the configuration metadata that you supply to the container.

Bean definition contains the configuration metadata, which is needed for the container to know the following: [1] How to create a bean , [2] Bean’s lifecycle, [3] Bean’s dependencies.

There are 3 different ways for writing configurations: XML, Annotation, and Java Code. We’ll explore each of them … 🏃

Our Project — Application Services

Below, the class diagram and code for the project we will be working on during this tutorial for demonstration on how to define the configurations.

The idea is simple, Imagine that you have an application, which relies on different services such as database, logger, mail, etc.

📣 All of these files are in “src” folder of your Java application.

Finally, create “beans.xml” file.


Beans Configuration — XML

[1] Define the beans

In the “beans.xml” file, we can provide definitions for beans.

There’s a set of properties we can use for bean definition. In this example, we’ve used:

  • id: It’s a unique identifier for the bean.
  • class: It’s the bean class qualified name to be used to create the bean.
  • lazy-init: It tells the IoC container to create a bean instance when it is first requested, rather than at the startup.

[2] Create Spring container

It’s used to load the beans configuration file, and create the beans.
The ApplicationContext is the most usually used container, while there are other containers.

— BeanFactory Container

This is the simplest container providing the basic support for DI and defined by the BeanFactory interface. The most commonly implementation is the XmlBeanFactory class (⚠️ deprecated!).

The BeanFactory is usually preferred where the resources are limited. Otherwise, use the ApplicationContext.

— ApplicationContext Container

The ApplicationContext is Spring’s advanced container. It includes, and extends the functionality of the BeanFactory. It is generally recommended over BeanFactory.

The most commonly used ApplicationContext implementations are:

  • FileSystemXmlApplicationContext: This container loads the definitions of the beans from an XML file.
  • ClassPathXmlApplicationContext: This container loads the definitions of the beans from an XML file (no need for the full path of the XML file).
  • WebXmlApplicationContext: This container loads the XML file with definitions of all beans from within a web application.

[3] Retrieve beans from Spring container

Our application will talk to the container asking to return the bean object defined in the beans configuration file using getBean() method.

[4] Close the Spring container

It is important to close the spring container after use. So that all the resources will be released and all beans in its bean factory will be destroyed.

// How to close a Spring ApplicationContext?
context.close();
// ((ClassPathXmlApplicationContext) context).close();
// ((ConfigurableApplicationContext) context).close();

Spring Bean Scopes and Life Cycle — XML

Scopes defines the scope of the bean object (singleton, prototype, etc). Spring supports five scopes, three of them are available only with the web apps.

The default scope is “singleton”, while the “prototype” scope will create a new object every time we instantiate an object.

<bean id="app" class="App" scope="prototype"></bean>
💡With prototype scope, beans already are lazy instantiated. So, lazy instantiation is only useful when the scope is singleton.

Lifecycle of the bean is as the following:

Bean Instantiated → Dependencies Injected → Internal Processing → Custom Init and destroy method (hooks) → Bean is ready for use.

There are activities that take place behind the scene between the time of bean instantiation and its destruction.

When a bean is instantiated, it may be required to perform some initialization to get it into a usable state. Similarly, when the bean is no longer required and is removed from the container, some cleanup may be required.

We’ll will discuss only two important bean life cycle callback methods, which are required at the time of bean initialization and its destruction.

They are useful to make any business logic or handle resources (db, sockets, files, etc) at the beginning, as soon as object is instantiated or right before destroyed.

[1] Create the init and destroy methods

public class App {
/* same as before */
public void init(){
System.out.println("The application has been initialized");
}

public void close(){
System.out.println("Bye bye ...");
}

}

[2] Define the methods in configuration

Specify the methods to be called upon instantiation and just before a bean is removed.

<bean id="app" class="App" init-method="init" destroy-method="close"></bean>

Dependency Injection — XML

When you work on application, chances are, your object will have dependencies. Spring DI helps in wiring a class with it’s dependencies, at the same time keeping them decoupled, so we can inject these dependencies during the run time.

The dependencies are defined in the bean configuration. The two most common ways to inject objects using XML: Constructor and Setter Injection.

[1] Constructor Injection

Inject dependencies to the class constructor. The IoC Container will create the object passing a dependency object to the constructor.

In the beans.xml, define the App class dependencies.

The constructor-arg is used to inject dependencies and values to object’s properties through the constructor. The ref attribute is assigned to the id of a bean; a reference to an object (dependency).

You can’t have two or more constructors in the bean definition to be used. So, In our example, we can either inject a single service, or array of services.
⚠ Make sure to define the dependencies as beans so they can be injected.

[2] Setter Injection

Inject dependencies by calling the setter methods. The IoC Container will call the setter method of object’s property passing a dependency object.

In the beans.xml, define the class dependencies. The property is used to inject dependencies and values to object’s properties through setter method.

📣 How IoC knows which setter method to call? It calls the setter method with the name: The “set” keyword followed by property name with upper case first letter.

Injecting Values — XML

We’ve seen how to use constructor-arg and property to inject dependencies. We can also use them to inject values.

The value attribute is assigned to the value of object’s property. In case of a reference to another bean, use ref instead.

💡 If you need to pass an empty string or null as a value, then do as the following:
<bean id="app" class="App">
<property name="name" value=""/>
</bean>
<bean id="app" class="App">
<property name="name"><null/></property>
</bean>

Autowiring 🔌

We’ve been using <constructor-arg> and <property> to inject dependencies. Instead, we can autowire the dependencies, which helps in reducing the amount of configurations need to be written.

There are different options for autowiring that guide the Spring container on how to inject the dependencies. The default is no autowiring at all:

  1. byName: Autowiring by property name. Spring container tries to match between object’s property name and the beans id in the configuration.
  2. byType: Autowiring by property datatype. Spring container tries to match between object’s property type with exactly one of the beans of the same type. If more than one bean exists, a fatal exception is thrown.
  3. constructor: It’s byType, but, applies to constructor arguments. You can’t have more than one constructor for initializing the dependencies.
<bean id="app" class="App" autowire="byType"></bean>
💡You need to write setter methods for dependencies to be injected when autowiring byName or byType.

📣 What if there are multiple implementing classes for the Service interface?

In case of services (array of services implements Service interface): It won’t work in case of byName. Otherwise, it will inject all the implementing objects.

In case of mainService (an object implements Service interface):

→ For byType/constructor, assign “autowire-candidate” attribute in the <bean> tag of all implementing classes to false, and one of them to true.

<bean id="app" class="App" autowire="byType"></bean>
<bean id="database" class="Database" autowire-candidate="false"></bean>
<bean id="mail" class="Mail" autowire-candidate="false"></bean>
<bean id="logger" class="Logger" autowire-candidate="true"></bean>

→ For byName, either rename “mainService” in App class to one of the implementing classes (i.e. “logger”), and setter methods accordingly, or, rename the bean id of that class in the XML configuration to “mainService”.

<bean id="app" class="App" autowire="byName"></bean>
<bean id="database" class="Database"></bean>
<bean id="mail" class="Mail"></bean>
<bean id="mainService" class="Logger"></bean>

Autowiring Limitations

  • Overriding possibility: You can still specify dependencies using <constructor-arg> and <property> settings which will always override autowiring.
  • Primitive data types: You cannot autowire so-called simple properties such as primitives, Strings, and Classes.
  • Confusing nature: Autowiring is less exact than explicit wiring, so if possible prefer using explicit wiring.

Beans Configuration — Annotation

Instead of using XML to describe a bean configuration, you can move the bean configuration into the class itself by using annotations.

Annotations are special labels or markers on the relevant class, method, or field declaration. You probably have seen @Override, which in fact is an annotation that tells the compiler that this is an overridden method.

🔦 Java Annotations Vs XML For Configuration

XML configurations can take a lot of work when there are a lot of beans, while annotations minimize the XML configurations.

Annotation injection is performed before XML injection. Thus, XML configurations will override that of the annotations.

[1] Enable class/bean/component scanning in beans configuration file

Annotation configuration is not turned on in the Spring container by default. So, we will need to enable it first in our Spring configuration file.

<context:component-scan base-package="com.demo.annotationsconfig"></context:component-scan>
I’m assuming you’ve created a package called “com.demo.annotationsconfig” where all of your Java classes live.
💡On the other hand, The<context:annotation-config>tag scans and activates annotations for already registered beans in spring configuration XML file.

[2] Add the @Component annotation to classes

Sprint scans the classes, whenever it finds @Component annotations, it automatically register the bean in the Spring container, with a bean id.

💡The default bean id is the name of the class name with lower case first letter, unless the class name starts with upper case letters (i.e. RESTFortuneService → RESTFortuneService)

[3] Retrieve beans from Spring container

Basically, It’s same as XML configurations.

Spring Bean Scopes and Life Cycle — Annotations

The scope of a bean can be defined using @Scope annotation.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class App {
/* same as before */
}

For the life cycle of a bean, we can use @PostConstruct annotation as an initialization callback and @PreDestroy annotation as an destruction callback.

Dependency Injection — Annotations

Spring will scan all the classes with @Component annotation, register it as a bean, then it will inject the dependencies that have @Autowired annotation.

There are 3 ways to inject dependencies using @Autowired annotation: Constructor Injection, Setter Injection, and Field Injection.

⚠ Again! Make sure to create beans for the dependencies, either using @Componentannotation, or in XML configuration. Otherwise, It can’t be injected.

[1] Constructor Injection

Inject dependencies to the class constructor using @Autowired annotation.

⚠ Again! You can’t have two or more constructors to be used for autowiring.

Now, some questions might arise …

📣 Question 1: Since Service is an interface, what implementing service object (logging, etc) that will be injected?

If there is only one implementing class, this will be injected by default.
If there are multiple implementing classes, you need to use @Qualifier(“bean_id”)annotation to specify explicitly which object of the implementing classes to be autowired. Otherwise, Spring will choose one of the implementing classes based on property name, If no match, you’ll get an error.
In case of services (array of services implements Service interface): It will inject all the implementing objects.

📣 Question 2: Based on what Spring will inject the dependency?

By default is byType. When using @Qualifier annotation, It’s byName.

[2] Setter Injection

Inject dependencies by calling the setter methods which has @Autowire annotation.

You can also use @Autowired annotation on any method instead of the setters.

@Autowired
@Qualifier("logger")
public void setSomeCrazyStuff(Service mainService) {
this.mainService = mainService;
}

[3] Field Injection

Inject dependencies by using @Autowired annotation on objects’ fields.

Now the question is …

📣 Question: Which Injection type should we use?

There is no one better than the other. Just choose one of them and stay consistent in the whole project.

Injecting Values — Annoation

We’ve seen how to use constructors, setter methods, and fields to inject dependencies. We can also use them to inject values.


Beans Configuration — Java Code

Java code configuration enables you to write your Spring configuration without XML but with the help of Java-based annotations.

[1] Create Java configuration class using @Configuration annotation

Using the @Configuration tells the Spring IoC container to use it as a source of bean definitions.

💡The @Import annotation It allows for loading bean definitions from another configuration Java/XML file.
💡 We can add component scan using @ComponentScan(pacakge_name) annotation (optional) in the configuration file to scan all the classes within this package and create beans for all classes with @Component annotation. It works exactly same as <context:component-scan base-package=”…”> attribute.
💡With using@ComponentScan we are going to rely on “Beans Configuration — Annotation” method, and the configuration file will be in Java class (instead of XML file).
So, make sure there is no conflict; no beans defined with “Beans Configuration — Annotation” method and also with “Java Code”.

[2] Define methods for creating the beans using @Bean annotation

The @Bean annotation tells Spring that this method will return an object that should be registered as a bean in the Spring application context.

The method name will be used as the bean id when Spring register this bean.

[3] Inject bean dependencies

We can inject the dependencies by having one bean method calling another.

[3] Read the configuration class

This can be done using AnnotationConfigApplicationContext instead.

[4] Retrieve the bean from the Spring container

App app = context.getBean("app", App.class);

Thank you for reading! If you enjoyed it, please clap 👏 for it.