How do I write a suspend function in Kotlin?

In Kotlin, a suspend function is declared with the suspend modifier.

suspend fun fetchUser(): User {
    // Can call other suspend functions here
    return api.getUser()
}

A suspend function can pause without blocking the thread and later resume. It is commonly used with coroutines for asynchronous work.

Example:

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

suspend fun greetAfterDelay() {
    delay(1000)
    println("Hello after 1 second")
}

fun main() = runBlocking {
    greetAfterDelay()
}

Key points:

  • Use suspend fun to define one.
  • A suspend function can call other suspend functions.
  • It must be called from:
    • another suspend function, or
    • a coroutine scope such as runBlocking, launch, or async.

Example with return value:

suspend fun loadMessage(): String {
    delay(500)
    return "Loaded message"
}

fun main() = runBlocking {
    val message = loadMessage()
    println(message)
}

So the basic syntax is:

suspend fun functionName() {
    // suspending work
}

How do I use delay instead of Thread.sleep in Kotlin coroutines?

Use kotlinx.coroutines.delay(...) inside a coroutine instead of Thread.sleep(...).

Thread.sleep blocks the current thread. delay suspends the coroutine without blocking the thread, so other coroutines can keep running.

import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    println("Before delay")

    delay(1_000) // suspends for 1 second, does not block the thread

    println("After delay")
}

If you currently have:

Thread.sleep(1000)

replace it with:

delay(1000)

But note: delay is a suspend function, so it can only be called from another suspend function or from inside a coroutine builder such as launch, async, or runBlocking.

Example with launch:

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        delay(1000)
        println("Coroutine finished after 1 second")
    }

    println("This prints immediately")
}

Example in a suspend function:

import kotlinx.coroutines.delay

suspend fun doWork() {
    delay(500)
    println("Work done")
}

Avoid doing this in coroutines:

Thread.sleep(1000) // blocks the underlying thread

Prefer:

delay(1000) // suspends only the coroutine

How do I launch a coroutine in Kotlin using launch and runBlocking?

In Kotlin, you can use runBlocking to start a coroutine scope that blocks the current thread until its coroutines finish, and launch to start a new coroutine inside that scope.

import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        println("Coroutine is running")
    }

    println("Main coroutine continues")
}

Output may look like:

Main coroutine continues
Coroutine is running

runBlocking creates a coroutine and blocks the current thread until all child coroutines complete.

launch starts a new coroutine that runs concurrently with the rest of the code inside the runBlocking scope.

A slightly clearer example:

import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch {
        delay(1000)
        println("World!")
    }

    println("Hello")
}

Output:

Hello
World!

Here:

  • runBlocking { ... } starts a blocking coroutine scope.
  • launch { ... } starts a child coroutine.
  • delay(1000) suspends the coroutine for 1 second without blocking the thread.
  • runBlocking waits until the launched coroutine finishes before main exits.

How do I use external configuration with Spring?

External configuration means keeping settings such as application names, URLs, ports, feature flags, credentials, or environment-specific values outside your Java code.

Spring supports this mainly through:

  • application.properties
  • application.yml
  • environment variables
  • command-line arguments
  • external property files
  • @Value
  • @ConfigurationProperties

1. Using application.properties

In a Spring or Spring Boot application, you can place configuration in:

src/main/resources/application.properties

Example:

app.name=My Spring App
app.version=1.0.0
app.description=Example application using external configuration

Then inject values with @Value:

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);
    }
}

2. Using application.yml

You can also use YAML:

app:
  name: My Spring App
  version: 1.0.0
  description: Example application using external configuration

The same @Value expressions still work:

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

3. Using @PropertySource in non-Boot Spring

If you are using plain Spring with Java configuration, register a property file using @PropertySource:

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

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

Then Spring can resolve values like:

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

If you are using Spring Boot, you usually do not need @PropertySource for application.properties or application.yml. Spring Boot loads them automatically.


4. Recommended Spring Boot Approach: @ConfigurationProperties

For multiple related properties, prefer @ConfigurationProperties over many @Value fields.

Example application.properties:

app.name=My Spring App
app.version=1.0.0
app.description=Example application using external configuration

Create a properties class:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {

    private String name;
    private String version;
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

Then inject it into another bean:

import org.springframework.stereotype.Service;

@Service
public class GreetingService {

    private final AppProperties appProperties;

    public GreetingService(AppProperties appProperties) {
        this.appProperties = appProperties;
    }

    public void printGreeting() {
        System.out.println("Welcome to " + appProperties.getName());
        System.out.println("Version: " + appProperties.getVersion());
        System.out.println(appProperties.getDescription());
    }
}

5. External Files Outside the JAR

You can override configuration from outside the application.

For Spring Boot:

java -jar myapp.jar --spring.config.location=file:/opt/myapp/application.properties

Or include an additional config file:

java -jar myapp.jar --spring.config.additional-location=file:/opt/myapp/

Example external file:

app.name=Production App
app.version=2.0.0
app.description=Running with production configuration

6. Environment Variables

Spring Boot can read environment variables automatically.

For example, this property:

app.name=My Spring App

Can be overridden with:

APP_NAME=Production App

Spring Boot maps environment variable names to property names using relaxed binding:

APP_NAME -> app.name
SERVER_PORT -> server.port
SPRING_DATASOURCE_URL -> spring.datasource.url

7. Command-Line Arguments

You can override properties when starting the application:

java -jar myapp.jar --app.name="Command Line App" --server.port=9090

Command-line arguments usually have high priority and override values from property files.


8. Profiles for Environment-Specific Config

Profiles let you separate configuration by environment.

Common files:

application.properties
application-dev.properties
application-test.properties
application-prod.properties

Example:

# application-prod.properties
app.name=Production App
server.port=8080

Run with a profile:

java -jar myapp.jar --spring.profiles.active=prod

Or set an environment variable:

SPRING_PROFILES_ACTIVE=prod

9. Default Values with @Value

You can provide fallback values:

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

If app.name is missing, Spring uses "Default App".


10. Common Property Examples

server.port=8081

spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=myuser
spring.datasource.password=secret

logging.level.org.springframework=INFO

app.name=My Spring App
app.version=1.0.0

Summary

Use external configuration like this:

Use case Recommended approach
Simple single value @Value("${property.name}")
Group of related settings @ConfigurationProperties
Spring Boot default config application.properties or application.yml
Plain Spring Java config @PropertySource
Environment-specific config Spring profiles
Production overrides external file, environment variables, or command-line args

For most Spring Boot applications, the usual setup is:

src/main/resources/application.properties

plus a configuration class using:

@ConfigurationProperties(prefix = "app")

This keeps configuration clean, type-safe, and easy to override per environment.

How do I manage bean scope in Spring?

In Spring, bean scope controls how many instances of a bean Spring creates and how long those instances live.

By default, Spring beans are singleton scoped, meaning Spring creates one shared instance per ApplicationContext.


Common Spring Bean Scopes

Scope Meaning
singleton One shared instance per Spring container
prototype A new instance every time the bean is requested
request One instance per HTTP request
session One instance per HTTP session
application One instance per ServletContext
websocket One instance per WebSocket session

The most commonly used scopes are:

  • singleton
  • prototype
  • request
  • session

1. Singleton Scope

singleton is the default scope.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope("singleton")
public class UserService {
}

This is equivalent to:

import org.springframework.stereotype.Service;

@Service
public class UserService {
}

Spring creates only one UserService object, and every injection point receives the same instance.


Example

@Service
public class CounterService {

    private int count = 0;

    public int increment() {
        return ++count;
    }
}

Because this bean is singleton scoped, the count field is shared across the application.

That means singleton beans should usually be stateless, especially in web applications.

Prefer this:

@Service
public class PriceCalculator {

    public BigDecimal calculatePrice(BigDecimal amount) {
        return amount.multiply(new BigDecimal("1.10"));
    }
}

Avoid storing request-specific or user-specific data in singleton beans.


2. Prototype Scope

A prototype bean creates a new instance every time Spring is asked for the bean.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope("prototype")
public class ReportBuilder {

    private String title;

    public void setTitle(String title) {
        this.title = title;
    }

    public String build() {
        return "Report: " + title;
    }
}

Each direct request to Spring for ReportBuilder creates a new object.


Prototype with @Bean

You can also define prototype scope on a @Bean method:

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

@Configuration
public class AppConfig {

    @Bean
    @Scope("prototype")
    public ReportBuilder reportBuilder() {
        return new ReportBuilder();
    }
}

3. Important: Prototype Inside Singleton

A common surprise is this:

@Service
public class ReportService {

    private final ReportBuilder reportBuilder;

    public ReportService(ReportBuilder reportBuilder) {
        this.reportBuilder = reportBuilder;
    }
}

If ReportService is singleton and ReportBuilder is prototype, Spring injects one prototype instance when ReportService is created.

It does not automatically create a new ReportBuilder every time you use it.


Correct Way: Use ObjectProvider

If a singleton needs a fresh prototype instance on demand, use ObjectProvider.

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
public class ReportService {

    private final ObjectProvider<ReportBuilder> reportBuilderProvider;

    public ReportService(ObjectProvider<ReportBuilder> reportBuilderProvider) {
        this.reportBuilderProvider = reportBuilderProvider;
    }

    public String createReport(String title) {
        ReportBuilder builder = reportBuilderProvider.getObject();
        builder.setTitle(title);
        return builder.build();
    }
}

Now each call to getObject() returns a new prototype instance.


4. Request Scope

request scope creates one bean instance per HTTP request.

This is useful in Spring MVC applications for request-specific data.

import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.RequestScope;

@Component
@RequestScope
public class RequestContext {

    private String correlationId;

    public String getCorrelationId() {
        return correlationId;
    }

    public void setCorrelationId(String correlationId) {
        this.correlationId = correlationId;
    }
}

Each HTTP request gets its own RequestContext.


5. Session Scope

session scope creates one bean instance per HTTP session.

import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.SessionScope;

@Component
@SessionScope
public class ShoppingCart {

    private final List<String> items = new ArrayList<>();

    public void addItem(String item) {
        items.add(item);
    }

    public List<String> getItems() {
        return items;
    }
}

Each user session gets its own ShoppingCart.

This is useful for things like:

  • shopping carts
  • user preferences
  • wizard-style form state

6. Using Scope Constants

Instead of writing scope names as strings, you can use Spring constants.

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class ReportBuilder {
}

For singleton:

import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

@Service
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class UserService {
}

7. XML Configuration Example

If you use XML configuration, define the scope with the scope attribute.

Singleton

<bean id="service" class="com.example.DummyService" scope="singleton"/>

Since singleton is the default, this is also valid:

<bean id="service" class="com.example.DummyService"/>

Prototype

<bean id="service" class="com.example.DummyService" scope="prototype"/>

With singleton scope, repeated calls to getBean("service") return the same object.

With prototype scope, repeated calls to getBean("service") return different objects.


8. Lifecycle Difference

Singleton beans are fully managed by Spring, including destruction callbacks.

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

@Service
public class CacheService {

    @PostConstruct
    public void init() {
        System.out.println("Cache initialized");
    }

    @PreDestroy
    public void shutdown() {
        System.out.println("Cache shutting down");
    }
}

For singleton beans:

  • Spring creates the bean
  • Spring initializes it
  • Spring calls destruction callbacks when the context closes

For prototype beans:

  • Spring creates and initializes the bean
  • Spring does not manage the full destruction lifecycle after handing it out

So if a prototype bean holds resources, your application is responsible for cleanup.


9. Choosing the Right Scope

Use this as a practical guide:

Use case Recommended scope
Stateless service singleton
Repository/data access object singleton
Controller usually singleton
Stateful object created per use prototype
Request-specific data request
User-session data session

Best Practices

  1. Keep singleton beans stateless when possible.
  2. Do not store user-specific data in singleton services.
  3. Use prototype for objects that need independent state per use.
  4. Use ObjectProvider<T> when a singleton needs fresh prototype instances.
  5. Use @RequestScope for per-request web data.
  6. Use @SessionScope carefully, because session state consumes memory.
  7. Prefer annotation-based configuration in modern Spring applications.
  8. Use XML scope configuration only when working with XML-based Spring setup.

Bottom Line

You manage bean scope in Spring by declaring the scope on the bean:

@Component
@Scope("prototype")
public class MyBean {
}

or:

@Bean
@Scope("prototype")
public MyBean myBean() {
    return new MyBean();
}

If you do not specify a scope, Spring uses:

singleton

So in most applications, services, repositories, and controllers are singleton beans, while stateful per-use objects should be prototype, request, or session scoped depending on how long their state should live.