Spring Core | Beans & Wiring | Part-1

Just imagine your favorite dish, it takes how many ingredients to pull together a dish which waters your mouth. These ingredients compliment or just say work in collaboration with each other to develop that exquisite taste. Now imagine what your favorite dish would be like had one of the ingredients is missing or added inappropriately.

In this respect, a great piece of software isn’t much different. Any non-trivial Java application is made up of several objects that must work together to meet some business goals. These objects must be aware of one another and communicate with one another to get their jobs done. These object which we are talking about, act as a backbone of our application is called Bean or JavaBeans in Spring lingo. We will be referring these objects as beans in future posts interchangeably.

In our previous post, we learned that beans shouldn’t be responsible for finding or creating other dependent beans to do their jobs. Instead, the container should give them a reference to their dependency. We also learned that the act of creating this association is called wiring.

Spring Configuration

As mentioned earlier, the Spring container is responsible for creating and managing relationships between beans via DI. But it’s your responsibility to tell spring container which beans to create and how to wire them. There are different ways to do that:

  • Automatic bean discovery and wiring
  • Explicit configuration in XMLs
  • Explicit configuration in Java

I’d favor the type-safe and more powerful JavaConfig over XML but it’s more of a personal choice. You can also mix JavaConfig and XML based upon your use-case.

1. Automatic Bean Discovery and Wiring

Spring can configure automatic wiring by two angles:

  • Component Scanning: Spring automatically discovers beans to be created in the application context.
  • Autowiring: Spring automatically discovers beans and satisfies dependencies.

To demonstrate the above two concepts we will be creating a few beans and wire them together.

1.1 Creating Discoverable Beans

In our previous post, we saw how Class Student should be injected with Interface Subject in order to function.

package com.lifeinhurry.person;
public Interface Subject{
    public void attend();
}
package com.lifeinurry.person;
import org.springframework.stereotype.Component;
@Component
public class History implements Subject{
     private String content = "history chapter";
     @Override
     public void attend(){
         System.out.print("Reading :" + content);
     }
}

The important thing to note above is that we have defined Subject as an Interface so that it can keep coupling between the implementation of Subject and the Student itself minimum. What you should also take note that we have annotated class History with @Component. This annotation identifies this class as a component class and serves as a clue to Spring that a bean should be created for this class.

However, Component scanning isn’t turned on by default. You’ll write an explicit configuration to tell Spring to check if our classes are annotated with @Component and create beans for them. Let us see how to write such a configuration.

package com.lifeinhurry;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages="com.lifeinhurry")
public class StudentConfig{
}

Class StudentConfig defines a Spring wiring specification, expressed in Java. We’ll learn more about Java-based spring configuration in further posts. But for now, observe that StudentConfig doesn’t explicitly define any beans itself. Instead, it’s annotated with @ComponentScan to enable component scanning in Spring. This will enable Spring to scan all components in com.lifeinhurry package and it’s sub-packages (You can also configure multiple packages in ComponentScan). Spring will find class History with @Component annotation and will automatically create a bean in a Spring container.

Let us see if component scanning really works or not with the help of Junit test, that will create a Spring application context and test if Subject bean is actually created or not.

package com.lifeinhurry;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=StudentConfig.class)
public class StudentTest{
    @Autowired
    private Subject subject;
    @Test
    public void subjectShouldNotBeNull() {
        assertNotNull(subject);
    }
}

StudentTest takes advantage of spring’s SpringJUnit4ClassRunner to have a spring application context automatically created when the test starts. And the @ContextConfiguration annotation tells it, to load configuration from the class StudentConfig. Because that configuration class includes ComponentScan, the resulting application context should include the Subject bean.

We learned how we can create a standalone bean in the Spring application context. But we know, many objects lean on other objects to get the job done. We need a way to wire our component-scanned beans with any dependency they have. To do that, we will look at autowiring further.

1.2 Annotating Beans for Autowiring

Spring automatically let beans to find their dependency within the application context, it is called autowiring. For autowiring to be enabled we need to use @Autowired annotation. To illustrate this, we will look at class Student, how it can be wired with bean of type Subject.

package com.lifeinhurry.person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Student implements Person{
    private Subject subject;
    @Autowired
    public Student (Subject subject) {
         this.subject= subject;
    }
    public void attendSubject() {
         subject.attend();
    }
}

Spring has already created a Subject bean in its container using @ComponetScan feature which we saw earlier. In the above example, Spring passes the bean to class Student via the constructor. Since the constructor is annotated with @Autowired it let Spring know that it has a dependency on a bean of type Subject to perform its job. It becomes the responsibility of Spring to find and satisfies Student dependency.

The @Autowired annotation’s use isn’t limited to constructors. It can be used in multiple ways:

//Option1: Can be used with setters
@Autowired
public void setSubject(Subject subject) {
    this.subject= subject;
}
//Option2: Can be used with any method
@Autowired
public void assignSubject(Subject subject) {
    this.subject= subject;
}
//Option3: Can be used directly with variable
@Autowired
private Subject subject;

Now, we can be assured that the Spring will automatically inject Student with its dependency. To be more certain, let us verify it by modifying StudentTest to attend class through Student bean.

package com.lifeinhurry;
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=StudentConfig.class)
public class StudentTest{ 
    @Autowired
    private Subject subject;
    @Rule
    public final StandardOutputStreamLog log = new StandardOutputStreamLog();
    @Autowired
    private Student student;
    @Test
    public void subjectShouldNotBeNull()
    {
        assertNotNull(subject);
    }
    @Test
    public void testAttendSubject()
    {
        assertEquals(
            "Reading :history chapter",
            log.getlog());
    } 
}

Now we know the basics of component scanning and autowiring. We learned how Spring can be configured to automatically create, identify and manage beans and their dependency. Sometimes, the automatic bean wiring may not be sufficient and we may have to write some explicit configuration. We will learn how to do that in our next post.