DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • How Stalactite ORM Implements Its Fluent DSL
  • Spring Beans With Auto-Generated Implementations: How-To
  • How Spring and Hibernate Simplify Web and Database Management
  • Build a REST API With Just 2 Classes in Java and Quarkus

Trending

  • Ingesting Fixed-Width Mainframe Files Into Delta Lake: The Details Nobody Writes Down
  • From AI Chaos to Control: Building Enterprise-Grade LLM Gateways With MuleSoft Anypoint
  • One Query, Four GPUs: Tracing a Distributed Training Stall Across Nodes
  • Beyond Partitioning and Z-Order: A Deep Dive into Liquid Clustering for Unity Catalog Managed Tables
  1. DZone
  2. Coding
  3. Frameworks
  4. Graceful Shutdown: Spring Framework vs Golang Web Services

Graceful Shutdown: Spring Framework vs Golang Web Services

Discover how Spring and Golang handle graceful shutdowns, resource cleanup, and system signals. Learn the differences, pitfalls, and best practices.

By 
Ilia Ivankin user avatar
Ilia Ivankin
·
Mar. 13, 25 · Analysis
Likes (2)
Comment
Save
Tweet
Share
7.0K Views

Join the DZone community and get the full member experience.

Join For Free

Graceful application shutdown is a critical aspect of ensuring service reliability, particularly in high-load systems. 

In this article, we will explore how graceful shutdown is implemented in two popular technologies: Spring Framework and Golang, using examples that interact with a PostgreSQL database.

Why Did I Choose These Two Stacks for Comparison?

It's quite simple: when transitioning from Java and other JVM-based technologies to languages without a robust framework, developers are faced with the reality of managing many tasks manually. In Spring, many mechanisms for resource management come out of the box, while in Go, these processes need to be implemented by hand.

For example, many developers don't realize what Spring does under the hood. On the other hand, in Gohttps://dzone.com/articles/golang-tutorial-learn-golang-by-examples, we are responsible for ensuring proper resource cleanup to prevent issues like connection leaks in the database.

Let’s take a detailed look at how both technologies handle system signals, resource closure, connections, and background tasks, followed by a thorough comparison of their approaches.

The Concept of Graceful Shutdown

Before diving into code, let’s understand what a graceful shutdown is.

Graceful shutdown is a mechanism that allows an application to terminate cleanly after receiving a shutdown signal (e.g., SIGTERM or SIGINT).

The key objectives of a graceful shutdown are:

  1. Complete ongoing operations without accepting new ones.
  2. Release resources (e.g., database connections, files, communication channels).
  3. Shut down servers and background tasks.
  4. Prevent resource leaks and errors during restarts.

Graceful Shutdown Mechanism in Spring Framework

Spring Framework implements graceful shutdown through the application’s lifecycle (ApplicationContext) and automatic resource management.

When a Spring Boot application receives a shutdown signal (SIGTERM or SIGINT), it triggers a shutdown hook registered in the JVM:

Java
 
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    applicationContext.close();
}));


Steps Involved in Graceful Shutdown in Spring

1. Triggering the ContextClosedEvent

All event listeners can perform resource cleanup at this point.

Java
 
    @Component
    public class ShutdownListener implements ApplicationListener<ContextClosedEvent> {
        
      	@Override
        public void onApplicationEvent(ContextClosedEvent event) {
            System.out.println("Context is closing... Cleaning up resources.");
        }
    }


2. Calling @PreDestroy Methods

All beans annotated with @PreDestroy execute their shutdown logic.

Java
 
    @Component
    public class ExampleService {
      
        @PreDestroy
        public void onDestroy() {
            System.out.println("Bean is being destroyed...");
        }
    }


3. Closing Connection Pools

If the application uses a database with Spring Data and a connection pool (e.g., HikariCP), Spring automatically invokes the shutdown() method on the pool.

4. Terminating Background Tasks

Tasks created with @Scheduled or @Async are automatically stopped.

Basically, that's it. 

Silly photo of a drunk baby

Let's see an example.

Example of Graceful Shutdown in Spring

Java
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.PreDestroy;
import java.util.logging.Logger;

@SpringBootApplication
public class Application implements CommandLineRunner {

    private static final Logger logger = Logger.getLogger(Application.class.getName());

    @PreDestroy
    public void onShutdown() {
        logger.info("Application is shutting down gracefully...");
    }

    @Autowired
    private UserRepository userRepository;

    @Override
    public void run(String... args) {
        logger.info("Application started.");
        userRepository.findByName("Alice").ifPresent(user -> logger.info("User found: " + user.getName()));
    }

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


In this case, developers have minimal responsibilities regarding resource management, as Spring handles most of the shutdown logic. However, for custom cleanup, methods like @PreDestroy or implementing DisposableBean are essential.

Graceful Shutdown Mechanism in Golang

In Go, graceful shutdown requires explicit implementation. The key steps are:

  1. Handling system signals. 
    • The os/signal package is used to capture termination signals.
  2. Context and cancel functions.  
    • context.Context is used to control task termination.
  3. Manual resource cleanup.
    • All open resources and connections must be closed manually using methods like Close() or cancel().

Example of Graceful Shutdown in Go

Go
 
package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
  
    "github.com/jackc/pgx/v4"
)

// Background task with context
func backgroundTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Background task stopped")
            return
        default:
            fmt.Println("Background task running...")
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    fmt.Println("Starting application...")

    // Context for task management
    ctx, cancel := context.WithCancel(context.Background())

    // Database connection
    conn, err := pgx.Connect(ctx, "postgres://user:password@localhost:5432/mydb")
    if err != nil {
        fmt.Printf("Unable to connect to database: %v\n", err)
        return
    }
    defer conn.Close(ctx)

    // Start background task
    go backgroundTask(ctx)

    // Wait for termination signal
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
    <-stop

    fmt.Println("Shutting down gracefully...")
    cancel() // Terminate tasks
    fmt.Println("Application stopped.")
}


That's not all — it's a good idea to create something like a Closer function that gathers all Close methods in the main function and then executes them.

In other words, all open connections and resources need to be closed manually by calling Close() methods or cancel() functions. To make this process more convenient and centralized, you can use the Closer function.

Let’s take a look at a universal Closer function for shutdown in Golang.

The Closer function allows you to pass a list of termination functions (cancel(), Close()) and automatically invoke them in the correct order. This helps prevent errors and simplifies the code.

Go
 
package main

import (
    "fmt"
    "sync"
)

func Closer(closeFuncs ...func()) {
    var wg sync.WaitGroup
  
    for _, closeFunc := range closeFuncs {
        wg.Add(1)
      
        go func(f func()) {
            defer wg.Done()
            f()
        }(closeFunc)
    }
  
    wg.Wait()
}


With this function, you can clean up multiple resources in parallel:

Go
 
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    conn, _ := pgx.Connect(ctx, "postgres://user:password@localhost:5432/mydb")

    // Start background task
    go backgroundTask(ctx)

    // Wait for termination signal
    stop := make(chan os.Signal, 1)
    signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
    <-stop

    Closer(
        func() { conn.Close(ctx) },
        cancel,
    )

    fmt.Println("Application stopped.")
}


Here:

1. Context and Background Tasks

The ctx context is used to manage tasks. When cancel() is called, background tasks automatically stop through the ctx.Done() channel.

2. Using the Closer Function

The Closer function takes termination functions (cancel(), Close(), etc.) and executes them in parallel. In this example, the database connection is closed using conn.Close().

  • The cancel() function is called to terminate all related tasks.

3. Completion Wait

  • The sync.WaitGroup ensures that all termination functions complete before the application shuts down.

Advantages of the Closer Function

  • Convenience. All resource cleanup functions can be grouped and passed in one place.
  • Parallel execution. Resource cleanup operations are performed in parallel, reducing overall shutdown time.
  • Safety. Using sync.WaitGroup prevents premature application termination by ensuring that all resources are fully released.

Important: The next section presents a simple comparison of the two implementations. If you're a professional, you can skip directly to the conclusion. This visualization is intended for colleagues who are either beginners or transitioning from other programming languages.

Comparison

feature spring framework golang
Signal handling Automatic via SpringApplication Manual via os/signal
Resource managemen @PreDestroy auto management  Manual with Close() and cancel()
Flexibility High-level abstraction Full control over processes and resources
Performance overhead Potentially higher due to abstraction layers Minimal overhead
Thread management Automatic (@Async, @Scheduled) or manual green threads Manual through goroutines


In instance, with database simple connections:

Feature Spring Framework (spring data) Golang (pgx)
Database connection Automatic via application.yml configuration Manual with pgx.Connect()
Transaction management Managed by @Transactional via AOP Transactions handled through context
Connection pool handling Automatic with pools like HikariCP Manual with explicit Close() calls (but there is pgxpool)
Error handling Exceptions with try-catch or Error Handler by Spring, or ControllerAdvice Errors returned from functions


Conclusion

What should you choose? There is no single correct answer — it depends on you!

  • Spring Framework offers high-level abstractions, making development faster and easier to maintain. However, this can reduce flexibility and introduce performance overhead.
  • Golang, on the other hand, requires more manual work but gives you full control over processes and resources. This approach is especially beneficial for microservices and high-load systems.

In any case, exploring different programming languages and frameworks is always valuable. It helps you understand how things work elsewhere and refine your own practices.

Take care!

Golang Spring Framework Framework Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • How Stalactite ORM Implements Its Fluent DSL
  • Spring Beans With Auto-Generated Implementations: How-To
  • How Spring and Hibernate Simplify Web and Database Management
  • Build a REST API With Just 2 Classes in Java and Quarkus

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook