Spring Core Tutorial

Introduction to Spring Core

Spring Core is the fundamental module of the Spring Framework, providing the foundation for building robust and flexible Java applications. It includes essential features such as Inversion of Control (IoC) and Dependency Injection (DI), which are the backbone of the Spring Framework. These features help manage object creation and dependencies in a more maintainable and scalable way.

Key Concepts

  • Inversion of Control (IoC): A design principle in which the control of object creation and management is transferred from the application code to the Spring container.
  • Dependency Injection (DI): A design pattern that implements IoC by injecting dependencies into objects rather than having the objects create them directly.

Dependency Injection (DI)

Dependency Injection is a design pattern used to implement IoC, allowing the creation of dependent objects outside of a class and providing those objects to a class through various methods.

Types of DI

  1. Constructor Injection: Dependencies are provided through a class constructor.
  2. Setter Injection: Dependencies are provided through setter methods.
  3. Field Injection: Dependencies are provided directly into fields. (Not recommended for various reasons, such as difficulties in testing.)

Constructor Injection Example

Constructor Injection is the most preferred way of injecting dependencies in Spring. This method ensures that all mandatory dependencies are provided at the time of object creation.

Step-by-Step Example

  1. Create the Dependency Class
package com.example.springcore;

import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Dependency {
    public void doSomething() {
        System.out.println("Dependency doing something");
    }
}

Explanation:

  • @Component: This annotation marks the Dependency class as a Spring component, meaning Spring will automatically detect and create an instance of this class.
  1. Create the Consumer Class
package com.example.springcore;

import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Consumer {
    private final Dependency dependency;

    // Constructor injection is performed here
    public Consumer(Dependency dependency) {
        this.dependency = dependency;
    }

    public void performTask() {
        dependency.doSomething();
    }
}

Explanation:

  • @Component: This annotation marks the Consumer class as a Spring component.
  • Constructor Injection: The Consumer class receives the Dependency object through its constructor. This ensures that the Dependency is provided when the Consumer is created.
  1. Create the Configuration Class
package com.example.springcore;

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

// This annotation marks this class as a configuration class
@Configuration
// This annotation tells Spring to scan the specified package for components
@ComponentScan(basePackages = "com.example.springcore")
public class AppConfig {
}

Explanation:

  • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
  • @ComponentScan: This annotation configures component scanning directives for use with @Configuration classes. It tells Spring where to look for components (in this case, in the com.example.springcore package).
  1. Create the Main Application Class
package com.example.springcore;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringCoreApplication {
    public static void main(String[] args) {
        // Create the application context using the AppConfig class
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // Retrieve the Consumer bean from the context
        Consumer consumer = context.getBean(Consumer.class);

        // Perform the task
        consumer.performTask();
    }
}

Explanation:

  • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
  • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

Setter Injection Example

Setter Injection allows dependencies to be injected through setter methods. This method is useful for optional dependencies that might not be required at object creation.

Step-by-Step Example

  1. Create the Dependency Class
package com.example.springcore;

import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Dependency {
    public void doSomething() {
        System.out.println("Dependency doing something");
    }
}

Explanation:

  • @Component: This annotation marks the Dependency class as a Spring component, meaning Spring will automatically detect and create an instance of this class.
  1. Create the Consumer Class
package com.example.springcore;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Consumer {
    private Dependency dependency;

    // Setter method for dependency injection
    @Autowired
    public void setDependency(Dependency dependency) {
        this.dependency = dependency;
    }

    public void performTask() {
        dependency.doSomething();
    }
}

Explanation:

  • @Component: This annotation marks the Consumer class as a Spring component.
  • @Autowired: This annotation tells Spring to use this setter method to inject the Dependency bean into the Consumer bean.
  1. Create the Configuration Class
package com.example.springcore;

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

// This annotation marks this class as a configuration class
@Configuration
// This annotation tells Spring to scan the specified package for components
@ComponentScan(basePackages = "com.example.springcore")
public class AppConfig {
}

Explanation:

  • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
  • @ComponentScan: This annotation configures component scanning directives for use with @Configuration classes. It tells Spring where to look for components (in this case, in the com.example.springcore package).
  1. Create the Main Application Class
package com.example.springcore;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringCoreApplication {
    public static void main(String[] args) {
        // Create the application context using the AppConfig class
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // Retrieve the Consumer bean from the context
        Consumer consumer = context.getBean(Consumer.class);

        // Perform the task
        consumer.performTask();
    }
}

Explanation:

  • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
  • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

Field Injection Example (Not Recommended)

Field Injection allows dependencies to be injected directly into fields. However, this approach is generally not recommended due to issues with immutability and difficulties in testing.

Step-by-Step Example

  1. Create the Dependency Class
package com.example.springcore;

import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Dependency {
    public void doSomething() {
        System.out.println("Dependency doing something");
    }
}

Explanation:

  • @Component: This annotation marks the Dependency class as a Spring component, meaning Spring will automatically detect and create an instance of this class.
  1. Create the Consumer Class
package com.example.springcore;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// This annotation marks this class as a Spring-managed component
@Component
class Consumer {
    // Field injection for the dependency
    @Autowired
    private Dependency dependency;

    public void performTask() {
        dependency.doSomething();
    }
}

Explanation:

  • @Component: This annotation marks the Consumer class as a Spring component.
  • @Autowired: This annotation tells Spring to inject the Dependency bean directly into the Consumer bean field.
  1. Create the Configuration Class
package com.example.springcore;

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

// This annotation marks this class as a configuration class
@Configuration
// This annotation tells Spring to scan the specified package for components
@ComponentScan(basePackages = "com.example.springcore")
public class AppConfig {
}

Explanation:

  • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
  • @ComponentScan: This annotation configures component scanning directives for use with @Configuration classes. It tells Spring where to look for components (in this case, in the com.example.springcore package).
  1. Create the Main Application Class
package com.example.springcore;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringCoreApplication {
    public static void main(String[] args) {
        // Create the application context using the AppConfig class
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // Retrieve the Consumer bean from the context
        Consumer consumer = context.getBean(Consumer.class);

        // Perform the task
        consumer.performTask();
    }
}

Explanation:

  • AnnotationConfigApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.

    • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

    Inversion of Control (IoC)

    IoC is a design principle in which the control of object creation and management is transferred to a container or framework. In Spring, the IoC container is responsible for managing the lifecycle and configuration of application objects.

    How IoC Works

    1. Configuration: Define beans and their dependencies in a configuration file or class.
    2. Container Initialization: The Spring container reads the configuration and initializes the beans.
    3. Dependency Injection: The container injects dependencies into the beans based on the configuration.
    4. Usage: Application code retrieves the beans from the container and uses them.

    IoC Example

    1. Create the Dependency Class
    package com.example.springcore;
    
    public class Dependency {
        public void doSomething() {
            System.out.println("Dependency doing something");
        }
    }
    

    Explanation: This class defines a simple Dependency class with a method doSomething.

    1. Create the Consumer Class
    package com.example.springcore;
    
    public class Consumer {
        private Dependency dependency;
    
        // Constructor injection is performed here
        public Consumer(Dependency dependency) {
            this.dependency = dependency;
        }
    
        public void performTask() {
            dependency.doSomething();
        }
    }
    

    Explanation:

    • This class defines a Consumer class with a Dependency object.
    • The Consumer class receives the Dependency object through its constructor, ensuring that the Dependency is provided when the Consumer is created.
    1. Create the Configuration Class
    package com.example.springcore;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    // This annotation marks this class as a configuration class
    @Configuration
    public class AppConfig {
    
        // Define the Dependency bean
        @Bean
        public Dependency dependency() {
            return new Dependency();
        }
    
        // Define the Consumer bean and inject the Dependency bean
        @Bean
        public Consumer consumer() {
            return new Consumer(dependency());
        }
    }
    

    Explanation:

    • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
    • @Bean: This annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context.
    1. Create the Main Application Class
    package com.example.springcore;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class SpringCoreApplication {
        public static void main(String[] args) {
            // Create the application context using the AppConfig class
            ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
            // Retrieve the Consumer bean from the context
            Consumer consumer = context.getBean(Consumer.class);
    
            // Perform the task
            consumer.performTask();
        }
    }
    

    Explanation:

    • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
    • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

    Spring Annotation-Based Configuration

    Spring allows configuration using annotations, making the code more readable and reducing the need for XML configuration files.

    Example

    1. Create the Dependency Class
    package com.example.springcore;
    
    import org.springframework.stereotype.Component;
    
    // This annotation marks this class as a Spring-managed component
    @Component
    class Dependency {
        public void doSomething() {
            System.out.println("Dependency doing something");
        }
    }
    

    Explanation:

    • @Component: This annotation marks the Dependency class as a Spring component, meaning Spring will automatically detect and create an instance of this class.
    1. Create the Consumer Class
    package com.example.springcore;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    // This annotation marks this class as a Spring-managed component
    @Component
    class Consumer {
        private final Dependency dependency;
    
        // Constructor injection is performed here
        @Autowired
        public Consumer(Dependency dependency) {
            this.dependency = dependency;
        }
    
        public void performTask() {
            dependency.doSomething();
        }
    }
    

    Explanation:

    • @Component: This annotation marks the Consumer class as a Spring component.
    • @Autowired (Optional): This annotation tells Spring to use the constructor to inject the Dependency bean into the Consumer bean. Since there’s only one constructor, Spring can infer the dependency injection without this annotation.
    1. Create the Configuration Class
    package com.example.springcore;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    // This annotation marks this class as a configuration class
    @Configuration
    // This annotation tells Spring to scan the specified package for components
    @ComponentScan(basePackages = "com.example.springcore")
    public class AppConfig {
    }
    

    Explanation:

    • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
    • @ComponentScan: This annotation configures component scanning directives for use with @Configuration classes. It tells Spring where to look for components (in this case, in the com.example.springcore package).
    1. Create the Main Application Class
    package com.example.springcore;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class SpringCoreApplication {
        public static void main(String[] args) {
            // Create the application context using the AppConfig class
            ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
            // Retrieve the Consumer bean from the context
            Consumer consumer = context.getBean(Consumer.class);
    
            // Perform the task
            consumer.performTask();
        }
    }
    

    Explanation:

    • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
    • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

    Spring Java-Based Configuration

    Spring Java-based configuration provides a type-safe way of configuring Spring beans using Java classes.

    Example

    1. Create the Dependency Class
    package com.example.springcore;
    
    public class Dependency {
        public void doSomething() {
            System.out.println("Dependency doing something");
        }
    }
    

    Explanation: This class defines a simple Dependency class with a method doSomething.

    1. Create the Consumer Class
    package com.example.springcore;
    
    public class Consumer {
        private Dependency dependency;
    
        // Constructor injection is performed here
        public Consumer(Dependency dependency) {
            this.dependency = dependency;
        }
    
        public void performTask() {
            dependency.doSomething();
        }
    }
    

    Explanation:

    • This class defines a Consumer class with a Dependency object.
    • The Consumer class receives the Dependency object through its constructor, ensuring that the Dependency is provided when the Consumer is created.
    1. Create the Configuration Class
    package com.example.springcore;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    // This annotation marks this class as a configuration class
    @Configuration
    public class AppConfig {
    
        // Define the Dependency bean
        @Bean
        public Dependency dependency() {
            return new Dependency();
        }
    
        // Define the Consumer bean and inject the Dependency bean
        @Bean
        public Consumer consumer() {
            return new Consumer(dependency());
        }
    }
    

    Explanation:

    • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
    • @Bean: This annotation tells Spring that a method annotated with @Bean will return an object that should be registered as a bean in the Spring application context.
    1. Create the Main Application Class
    package com.example.springcore;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class SpringCoreApplication {
        public static void main(String[] args) {
            // Create the application context using the AppConfig class
            ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
            // Retrieve the Consumer bean from the context
            Consumer consumer = context.getBean(Consumer.class);
    
            // Perform the task
            consumer.performTask();
        }
    }
    

    Explanation:

    • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
    • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

    Spring Core Annotations

    1. @Component: Indicates that a class is a Spring component.
    2. @Autowired: Marks a constructor, field, or setter method to be autowired by Spring’s dependency injection.
    3. @Configuration: Indicates that a class declares one or more @Bean methods.
    4. @Bean: Indicates that a method produces a bean to be managed by the Spring container.
    5. @ComponentScan: Configures

    component scanning directives for use with @Configuration classes.

    Example

    1. Create the Dependency Class
    package com.example.springcore;
    
    import org.springframework.stereotype.Component;
    
    // This annotation marks this class as a Spring-managed component
    @Component
    class Dependency {
        public void doSomething() {
            System.out.println("Dependency doing something");
        }
    }
    

    Explanation:

    • @Component: This annotation marks the Dependency class as a Spring component, meaning Spring will automatically detect and create an instance of this class.
    1. Create the Consumer Class
    package com.example.springcore;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    // This annotation marks this class as a Spring-managed component
    @Component
    class Consumer {
        private final Dependency dependency;
    
        // Constructor injection is performed here
        @Autowired
        public Consumer(Dependency dependency) {
            this.dependency = dependency;
        }
    
        public void performTask() {
            dependency.doSomething();
        }
    }
    

    Explanation:

    • @Component: This annotation marks the Consumer class as a Spring component.
    • @Autowired (Optional): This annotation tells Spring to use the constructor to inject the Dependency bean into the Consumer bean. Since there’s only one constructor, Spring can infer the dependency injection without this annotation.
    1. Create the Configuration Class
    package com.example.springcore;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    // This annotation marks this class as a configuration class
    @Configuration
    // This annotation tells Spring to scan the specified package for components
    @ComponentScan(basePackages = "com.example.springcore")
    public class AppConfig {
    }
    

    Explanation:

    • @Configuration: This annotation indicates that the class has @Bean definition methods. Spring container can process the class to generate Spring Beans to be used in the application.
    • @ComponentScan: This annotation configures component scanning directives for use with @Configuration classes. It tells Spring where to look for components (in this case, in the com.example.springcore package).
    1. Create the Main Application Class
    package com.example.springcore;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class SpringCoreApplication {
        public static void main(String[] args) {
            // Create the application context using the AppConfig class
            ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
            // Retrieve the Consumer bean from the context
            Consumer consumer = context.getBean(Consumer.class);
    
            // Perform the task
            consumer.performTask();
        }
    }
    

    Explanation:

    • ApplicationContext: This is the central interface to provide configuration for an application. It is used to read the configuration metadata and instantiate, configure, and assemble the objects.
    • AnnotationConfigApplicationContext: This is a standalone application context, accepting @Configuration-annotated classes as input.

    Conclusion

    Spring Core provides the foundation for building scalable and maintainable Java applications by leveraging IoC and DI principles. With features like annotation-based and Java-based configuration, Spring simplifies the development process while promoting best practices. This tutorial covered the basics of Spring Core, DI types, IoC, and essential annotations, making it beginner-friendly and providing a solid foundation for further exploration of the Spring Framework. By following these examples, you can effectively use Spring Core to manage dependencies and configure your application components.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top