Bringing Spring Framework to make app loosely coupled

Mayank SinghMayank Singh
5 min read

Spring Configuration

@Configuration Annotation

Spring @Configuration annotation is part of the spring core framework. Spring Configuration annotation indicates that the class has @Bean definition methods. So Spring container can process the class and generate Spring Beans to be used in the application. Spring @Configuration annotation allows us to use annotations for dependency injection.

Pom.xml

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.0.6.RELEASE</version>
</dependency>

@Bean Annotation

The @Bean annotation is used to indicate that a method instantiates, configures, and initializes a new object to be managed by the Spring IoC container. Spring Bean is any java Object managed by spring. Spring uses IOC containers (BeanFactory or ApplicationContext) to manage these objects.

helloWorldConfiguration.java

package org.example;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

record Person(String name, int age){};
record Address(String city, String country){};

@Configuration
public class helloWorldConfiguration {

    @Bean
    public String name(){
        return "Mayank";
    }

    @Bean
    public int age(){
        return 15;
    }

    @Bean
    public Person person(){
        return new Person("Ayush", 20);
    }

    @Bean
    public Address address(){
        return new Address("Kutch", "India");
    }
}

app02HelloWorldSpring.java

package org.example;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class app02HelloWorldSpring {
    public static void main(String[] args) {
        // Launch Spring Context
        var context= new AnnotationConfigApplicationContext(helloWorldConfiguration.class);

        System.out.println(context.getBean("name") );
        System.out.println(context.getBean("age") );
        System.out.println(context.getBean("person") );
        System.out.println(context.getBean("address") );

    }
}

Let's Utilize our Knowledge in A practical example:

CodeBase:

package org.example.game;

public class gameRunner {
    private gamingConsole game;

    public gameRunner(gamingConsole game) {
        this.game = game;
    }

    public void run() {
        System.out.println("Welcome to the game: "+ game);
        game.up();
        game.down();
        game.left();
        game.right();
    }
}
package org.example.game;

public interface gamingConsole {
    void up();
    void down();
    void left();
    void right();
}
package org.example.game;

public class mario implements gamingConsole{
    public void up(){
        System.out.println("Move forward");
    }
    public void down(){
        System.out.println("Go into Hole");
    }
    public void left(){
        System.out.println("Go Back");
    }
    public void right(){
        System.out.println("Accelerate");
    }
}
package org.example.game;

public class pacman implements gamingConsole{
    public void up(){
        System.out.println("up");
    }
    public void down(){
        System.out.println("dowm");
    }
    public void left(){
        System.out.println("left");
    }
    public void right(){
        System.out.println("right");
    }
}
package org.example;

import org.example.game.gameRunner;
import org.example.game.mario;
import org.example.game.pacman;
import org.example.game.superContra;

public class app01BasicsJava {
    public static void main(String[] args) {
//        var game= new mario();
        var game= new pacman();
        var runner= new gameRunner(game);
        runner.run();

    }
}

By using gamingConsole interface we achieved Loose Coupling. The above code works fine But till now in the above code, we haven't used our knowledge of spring ie Bean, and Configuration.

Below is Code using @Configuration and Bean.

@Configuration
class basicGameConfiguration {
    @Bean
    public gamingConsole game(){
        var game= new mario();
        return game;
    }

    @Bean
    public gameRunner gameRunner(gamingConsole game){
        var runner= new gameRunner(game);
        return runner;
    }
}
public class app03GamingJava {
    public static void main(String[] args) {
        var context= new AnnotationConfigApplicationContext(basicGameConfiguration.class);
        context.getBean(gameRunner.class).run();
    }
}

But till now we writing code to create objects for us and Spring is only managing it using ApplicationContext, then can we use Spring to create objects, configure, and Manage them too?

The answer is YES, by making use of @Component and @ComponentScan.

@Component and @ComponentScan

How Lets see...

Annotate pacman.java and gameRunner.java with @Component annotation.

@Component is an annotation that allows Spring to detect our custom beans automatically. In other words, without having to write any explicit code, Spring will: Scan our application for classes annotated with @Component. Instantiate them and inject any specified dependencies into them.

Now how will out main class app03BasicGameJava know where to search for Beans. For that, we have @ComponentScan annotation. It will tell our class where to search for Components. In arguments of ComponentScan we pass pakages which we want to be scanned

With Spring, we use the @ComponentScan annotation along with the @Configuration annotation to specify the packages that we want to be scanned. @ComponentScan without arguments tells Spring to scan the current package and all of its sub-packages.

@Configuration
@ComponentScan("org.example.game")
public class app03GamingJava {
    public static void main(String[] args) {
        var context= new AnnotationConfigApplicationContext(app03GamingJava.class);
        context.getBean(gameRunner.class).run();
    }
}

See now by use of above code it will create objects and manage by itself.

Till now I have added @Component to only pacman game. What if i also annotate mario and superContra with @Component annotation. It will lead to ambiguity.

@Primary and @Qualifier

To resolve that ambiguity we make use of @Primary and @Qualifier annotation.

@Primary: A Bean should be given preference when multiple options/ candidates are qualified.

@Qualifier: A specific bean should be autowired(name of the bean should be used as qualifier).

So @Primary is simple. The one class out of mario, pacman, superContra having @Primary annotation will be selected.

@Qualifier has priority in case both @primary and @qualifier is used.

@Component vs @Bean

Now since we learnt about both @Component and @Bean. Let's compare both of them.

Where?

@Component can be used on any Java class whereas @bean is used on methods in the Spring Configuration class (classes annotated with @Configuration ).

Which is easy to use?

@Component is very easy. Just add an annotation whereas in the case of @Bean, you have to write all the code.

In the case of @Component Spring framework creates the beans whereas in the case of @Bean, we write the code for bean creation.

Which to use when?

Mostly in all our applications, we make use of @Component annotation but @Bean is used when we need to write some custom business logic for example we are making use of Spring security, then we cannot change the code of Spring security and add @Component annotation so we make use of @Bean annotation.

Eager and Lazy Initialization

By default, each bean is loaded at the time of initialization i.e. Eager Initialization. But we can annotate any class that is annotated with already @Component or @Configuration (in this case all beans in the configuration class will be lazily initialized ) or on any method that is annotated with @Bean with @Lazy such that those beans are only loaded when made use of them.

But an Eager initialization is recommended. Because error in configuration are discovered immediately at application startup, in the case of lazy initialization, all the errors will result in a runtime exception and not at the time of application startup.

Then when to use @Lazy initialization?

If we have a lot of complex logic and we don't want to delay the initialization startup in that case we can make use of @Lazy.

0
Subscribe to my newsletter

Read articles from Mayank Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mayank Singh
Mayank Singh