How do I use lifecycle callbacks like @PostConstruct and @PreDestroy in Spring beans?

In Spring, the lifecycle of a bean can be managed, and you can hook into these lifecycle phases using annotations like @PostConstruct and @PreDestroy.

  1. @PostConstruct:
    • This annotation is part of Jakarta annotations (not proprietary to Spring).
    • It is used to execute initialization logic after the bean’s properties have been set (i.e., after dependency injection is complete).
  2. @PreDestroy:
    • This annotation is also part of Jakarta annotations.
    • It is used to define a clean-up method that Spring calls before the bean is destroyed, typically during application shutdown.

To use these annotations in a Spring-managed bean:

Example with a Spring Bean:

package org.kodejava.spring;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    public MyBean() {
        System.out.println("MyBean constructor called");
    }

    @PostConstruct
    public void init() {
        System.out.println("MyBean @PostConstruct called: Initialization logic here");
    }

    @PreDestroy
    public void destroy() {
        System.out.println("MyBean @PreDestroy called: Cleanup logic here");
    }
}

Explanation:

  1. Constructor: When the bean is instantiated by Spring, the constructor is called.
  2. @PostConstruct (Initialization logic): Once the bean is instantiated and its dependencies are injected, this annotated method is invoked automatically.
  3. @PreDestroy (Cleanup logic): Before the bean is destroyed (typically when the application context is being closed), this annotated method is invoked automatically.

Notes:

  • Newer versions of Spring Boot automatically include this dependency.
  • If you’re working on Spring Boot, your application supports these lifecycle callbacks out of the box.

Alternative for Lifecycle Management:

You can also achieve similar functionality using Spring’s InitializingBean and DisposableBean interfaces or by explicitly configuring init and destroy methods in the bean definitions.

package org.kodejava.spring;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

@Component
public class MyBean implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MyBean initializing using InitializingBean");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("MyBean destroying using DisposableBean");
    }
}

However, using @PostConstruct and @PreDestroy is generally preferred because they are more concise and not tightly coupled to Spring APIs.


Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.2.6</version>
    </dependency>
    <dependency>
        <groupId>jakarta.annotation</groupId>
        <artifactId>jakarta.annotation-api</artifactId>
        <version>3.0.0</version>
    </dependency>
</dependencies>    

Maven Central Maven Central

Understanding @SpringBootApplication: The Heart of a Spring Boot App

The @SpringBootApplication annotation is a cornerstone of any Spring Boot application. It simplifies the configuration process and integrates core functionalities of Spring Boot in a single, convenient annotation. Here’s a comprehensive explanation to help you understand its importance and functionality:


What is @SpringBootApplication?

@SpringBootApplication is a composite annotation that combines three common Spring annotations to reduce configuration complexity. These annotations are:

  1. @SpringBootConfiguration
    • Denotes that this class is a configuration class.
    • Equivalent to Spring’s @Configuration annotation.
    • Allows the application to define @Bean definitions and Spring container settings.
  2. @EnableAutoConfiguration
    • Enables Spring Boot’s auto-configuration mechanism.
    • Automatically configures the Spring application based on the dependencies declared in the project.
    • For instance, if spring-boot-starter-web is in your dependencies, it will configure web-related beans like DispatcherServlet, ViewResolvers, etc.
  3. @ComponentScan
    • Scans the package where the annotated class resides and its sub-packages for Spring components (e.g., @Component, @Service, @Repository, @Controller, etc.).
    • This ensures that all required Spring-managed components are registered in the application context.

By combining these three annotations, @SpringBootApplication provides a streamlined way to configure and bootstrap Spring Boot applications.


Advantages of @SpringBootApplication

  1. Reduced Boilerplate Code
    Developers don’t need to explicitly annotate the main application class with @Configuration, @EnableAutoConfiguration, and @ComponentScan. The single annotation @SpringBootApplication takes care of it all.

  2. Auto-Configuration
    The @EnableAutoConfiguration part ensures that Spring Boot configures most components for you based on the classpath dependencies.

  3. Ease of Use
    Simplifies managing configurations and makes the application easier to understand and develop.


Where Should You Place @SpringBootApplication?

The @SpringBootApplication annotation is typically placed on the main class of the application, which is also the entry point for the main method. By convention, this main class should be located in the base package of the application. This ensures that the @ComponentScan directive will pick up all components, services, and controllers in sub-packages.

For example:

package org.kodejava.springboot.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

If your application’s main class is not located in the base package, you’ll need to manually configure the package path using the @ComponentScan annotation.


Customization Options of @SpringBootApplication

In some scenarios, you might want to override the default behavior of @SpringBootApplication by customizing its inner behavior:

  • Excluding Certain Auto-Configurations
    If you want to disable specific parts of Spring Boot’s auto-configuration:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • Setting Scanning Packages
    You can explicitly define scanning behavior using the @ComponentScan annotation:
@SpringBootApplication
@ComponentScan(basePackages = {"org.kodejava.springboot.demo.service", "org.kodejava.springboot.demo.dao"})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Keynotes

  1. Auto-Configuration Priority:
    • Auto-configuration mechanisms can be fine-tuned or overridden through application.properties or custom configuration classes.
  2. SpringApplication.run:
    • This method in the main method initializes the Spring application context and launches it.
  3. Custom Beans:
    • @SpringBootApplication does not prevent you from defining custom Beans or configurations if needed. You can still use annotations like @Bean and @Configuration alongside it.

Conclusion

@SpringBootApplication eliminates a lot of boilerplate code, making Spring Boot applications easier to configure and start. It combines the functionality of several essential Spring annotations into one. For most Spring Boot applications, simply annotating the main class with @SpringBootApplication is sufficient to bootstrap the application with sensible defaults, making it the “heart” of any Spring Boot app!


Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>3.4.4</version>
    </dependency>
</dependencies>

Maven Central

How do I manage dependencies using Java-based @Configuration classes?

In Spring, managing dependencies and configurations is commonly done using Java-based @Configuration classes. These classes allow you to define the beans and their dependencies programmatically. Here’s a step-by-step guide on how to manage dependencies with @Configuration classes:

1. Use the @Configuration Annotation

Mark your class with the @Configuration annotation. This tells Spring that the class defines one or more beans to be managed by the Spring container.

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

@Configuration
public class AppConfig {
    // Define beans here
}

2. Define Beans with the @Bean Annotation

Within the @Configuration class, use the @Bean annotation to define individual beans. Methods annotated with @Bean will produce bean instances that will be managed by the container.

import org.springframework.context.annotation.Bean;

@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }
}

In this example, MyService and MyRepository will be registered as beans in the Spring context.

3. Inject Dependencies Between Beans

You can inject dependencies by passing other beans as method parameters. Spring resolves these dependencies automatically.

@Configuration
public class AppConfig {

    @Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }

    @Bean
    public MyService myService(MyRepository myRepository) {
        return new MyService(myRepository);
    }
}

Here, MyService depends on MyRepository. Spring automatically resolves myRepository when creating the MyService bean.

4. Use @Primary for Bean Prioritization

If there are multiple beans of the same type, you can use the @Primary annotation to set the default bean to be used during injection.

@Configuration
public class AppConfig {

    @Bean
    @Primary
    public MyRepository mainRepository() {
        return new MyRepository();
    }

    @Bean
    public MyRepository backupRepository() {
        return new MyRepository();
    }
}

5. Use @Qualifier to Avoid Ambiguities

For cases where multiple beans of the same type exist but you don’t want to use the primary one, use the @Qualifier annotation along with the bean name.

@Bean("backupRepository")
public MyRepository createBackupRepository() {
    return new MyRepository();
}

Inject it as follows:

@Autowired
@Qualifier("backupRepository")
private MyRepository myRepository;

6. Leveraging Externalized Properties

You can link beans to properties defined in an application.properties file by using the @Value annotation or @ConfigurationProperties.

Using @Value:

@Configuration
public class AppConfig {

    @Value("${app.service.name}")
    private String serviceName;

    @Bean
    public MyService myService() {
        return new MyService(serviceName);
    }
}

7. Advance to Component Scanning (@Component)

Instead of manually defining @Bean methods, use annotations like @Component, @Service, @Repository, and @Controller for automatic bean detection, combined with @ComponentScan in the configuration. For example:

@Component
public class MyService {
    // Automatically registered
}

@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
    // Automatically scans for annotated beans in the package
}

8. Conditional Bean Creation

Use annotations like @Conditional, @ConditionalOnProperty, or profiles (@Profile) to conditionally create beans based on environment or other properties.

By using @Configuration classes, you retain full control of your beans programmatically while keeping your project modular and easier to maintain.


Maven Dependencies

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

Maven Central

How to Create Your First Spring Boot Project Using Spring Initializr

Here’s a step-by-step guide for creating your first Spring Boot project using Spring Initializr:

Step 1: Go to Spring Initializr

  1. Open the Spring Initializr website: https://start.spring.io/.
  2. You will see a friendly interface that allows you to configure your new Spring Boot project.

Step 2: Configure Your Project

  1. Project Settings:
    • Project Type: Choose either Maven or Gradle (Maven is common for beginners).
    • Language: Select Java, Kotlin, or Groovy. Use Java for now to keep it simple.
    • Spring Boot Version: Select the latest stable version (e.g., 3.x.x).
  2. Project Metadata:
    • Group: Enter your domain or namespace (e.g., com.example).
    • Artifact: This is the name of your project (e.g., demo).
    • Name and Description: Optional, but you can customize these values.
    • Package Name: Auto-generated from Group and Artifact but can be edited.
    • Packaging: Choose Jar.
    • Java Version: Select the Java version installed on your machine (e.g., Java 23 for compatibility).
  3. Dependencies:
    • Click Add Dependencies and search for the necessary modules, such as:
      • Spring Web for building web applications (REST APIs, etc.).
      • Spring Data JPA if you want database integration.
      • H2 Database for testing with an in-memory database.
      • Spring Boot DevTools for automatic application reload during development.

Add any additional dependencies you need for your project.

Step 3: Download the Project

  1. After configuring your project, click on the Generate button.
  2. A .zip file containing your project will be downloaded.

Step 4: Import the Project into IntelliJ IDEA

  1. Open IntelliJ IDEA.
  2. Go to File > Open and select the folder where you extracted your project.
  3. If IntelliJ suggests importing the project as a Maven/Gradle project, confirm the selection.
  4. Upon importing the project, IntelliJ will automatically resolve and download the dependencies.

Step 5: Run the Spring Boot Application

  1. Locate the main class in the project. It is usually located in src/main/java/<package-name> and has an @SpringBootApplication annotation.
  2. Right-click on the main class and select Run .
  3. Once the application starts, you will see the Spring Boot banner in the console and a log message indicating that the application started successfully.

Example Project Structure

Here’s a high-level view of what your project structure will look like:

src
├── main
|   ├── java
|   │   └── com.example.demo
|   │       └── DemoApplication.java (Main class)
|   └── resources
|       ├── application.properties (Configuration file)
|       ├── static
|       └── templates
└── test

Next Steps

  • Open the application.properties file in the src/main/resources/ directory to specify configuration data, such as database properties.
  • Start building your business logic, controller classes, and database entities.

Congratulations! 🎉 You’ve successfully created your first Spring Boot project using Spring Initializr.

How do I use @Value to inject values from properties files in Spring?

In Spring, the @Value annotation allows you to inject values from properties files or environment variables into your application. Here’s how you can use it step by step:

1. Add a properties or file to your project yaml

Create a properties file, such as application.properties or application.yml, and add your key-value configurations.

Example (application.properties):

app.name=My Application
app.version=1.0.0
app.description=A demo application using @Value

Example (application.yml):

app:
  name: My Application
  version: 1.0.0
  description: A demo application using @Value

2. Enable property loading in your Spring application

Spring Boot automatically loads application.properties or application.yml when running your application. You don’t need any additional configuration for this if you’re using Spring Boot.

For a non-Boot Spring application, ensure you configure the @PropertySource annotation. For example:

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

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig {
}

3. Inject property values using @Value

Add the @Value annotation to a field, constructor parameter, or setter to inject property values.

Example 1: Field Injection

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

@Component
public class AppProperties {

    @Value("${app.name}")
    private String appName;

    @Value("${app.version}")
    private String appVersion;

    @Value("${app.description}")
    private String appDescription;

    public void printProperties() {
        System.out.println("App Name: " + appName);
        System.out.println("App Version: " + appVersion);
        System.out.println("App Description: " + appDescription);
    }
}

Example 2: Constructor Injection

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

@Component
public class AppProperties {

    private final String appName;
    private final String appVersion;

    public AppProperties(
        @Value("${app.name}") String appName,
        @Value("${app.version}") String appVersion
    ) {
        this.appName = appName;
        this.appVersion = appVersion;
    }

    public void printProperties() {
        System.out.println("App Name: " + appName);
        System.out.println("App Version: " + appVersion);
    }
}

Example 3: Default Values

You can provide default values in case a specified property is not present:

@Value("${app.unknown:Default Value}")
private String unknownProperty;

4. Running the application

If you run a Spring Boot application with the above configuration, the values defined in application.properties (or application.yml) will be injected into the variables annotated with @Value.

5. Notes and Best Practices

  1. Use @ConfigurationProperties for Groups of Properties: When working with many properties, consider using @ConfigurationProperties instead of @Value. It is more organized and allows you to map properties to a dedicated class.
    • Example:
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ConfigurationProperties(prefix = "app")
    public class AppConfigProps {
       private String name;
       private String version;
       private String description;
    
       // Getters and setters
    }
    
  2. Placeholders for Environment Variables: You can use ${ENVIRONMENT_VAR} placeholders to inject environment-specific variables.

  3. Property Validation (Optional): For strict validation of the presence of properties, consider combining @Value with property validation tools like Hibernate Validator or custom logic.

Maven Dependencies

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

Maven Central