Home Java How to Get All Spring-Managed Beans: ApplicationContext and Bean Definitions
Intermediate 3 min · July 14, 2026

How to Get All Spring-Managed Beans: ApplicationContext and Bean Definitions

Learn how to list all Spring beans using ApplicationContext, BeanFactory, and BeanDefinition.

N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 15-20 min read
  • Java 17+ installed
  • Spring Boot 3.x project (we'll use 3.2)
  • Basic understanding of dependency injection
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer

• Use applicationContext.getBeanDefinitionNames() to get all bean names\n• Use applicationContext.getBeansOfType(YourClass.class) to filter by type\n• Use ListableBeanFactory for advanced iteration\n• Watch for proxies and lazy beans in production\n• Never list beans in hot paths—it's expensive

✦ Definition~90s read
What is How to Get All Spring-Managed Beans?

You use ApplicationContext (specifically ListableBeanFactory) to retrieve all beans that Spring manages, either by name, by type, or by annotation, giving you full visibility into the IoC container's inventory.

Think of Spring as a smart warehouse manager.
Plain-English First

Think of Spring as a smart warehouse manager. When your app starts, Spring creates a catalog of every item (bean) it manages. Listing all beans is like asking the manager to read you the entire inventory list—useful for auditing, but don't do it every time a customer walks in.

In any non-trivial Spring Boot application, the IoC container manages hundreds or thousands of beans. From controllers and services to repositories and configuration classes, Spring wires everything together. But what happens when you need to introspect that container? Maybe you're debugging why a bean isn't being picked up, or you're building a monitoring dashboard that shows all registered components. Or perhaps you're writing a library that needs to discover beans dynamically. This article is your field guide to listing all Spring-managed beans using ApplicationContext, BeanFactory, and BeanDefinition. We'll cover the classic approaches, the gotchas that bite you in production (like proxies and lazy initialization), and real-world patterns from SaaS billing systems and payment-processing pipelines. By the end, you'll know exactly how to retrieve beans, filter them by type or annotation, and avoid the performance pitfalls that have taken down staging environments. We'll use Spring Boot 3.2 and Java 17 throughout, with code you can copy-paste into your own project.

Understanding ApplicationContext and BeanFactory

Before you can list beans, you need to understand the two core interfaces: ApplicationContext and BeanFactory. BeanFactory is the root interface for accessing the Spring IoC container. It provides basic methods like getBean() and containsBean(). ApplicationContext extends BeanFactory and adds enterprise features like event publishing, resource loading, and message i18n. In a typical Spring Boot application, you inject ApplicationContext directly. The interface you actually need for listing beans is ListableBeanFactory, which ApplicationContext inherits. ListableBeanFactory provides getBeanDefinitionNames() (returns all bean names as a String[]) and getBeansOfType(Class) (returns a Map<String, T> of beans matching a type). Here's the simplest way to get all bean names in a Spring Boot app: inject ApplicationContext into any Spring-managed component.

BeanListerService.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.stereotype.Service;

@Service
public class BeanListerService {

    private final ListableBeanFactory beanFactory;

    public BeanListerService(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public void printAllBeanNames() {
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        System.out.println("Total beans: " + beanNames.length);
        for (String name : beanNames) {
            System.out.println(name);
        }
    }
}
Output
Total beans: 157
userController
userService
userRepository
dataSource
entityManagerFactory
...
⚠ Don't Inject ApplicationContext Directly
📊 Production Insight
In a SaaS billing system, we used getBeansOfType(InvoiceGenerator.class) to dynamically discover all invoice strategies (e.g., PDF, CSV, API) without hardcoding. This made adding a new format a single class addition.
🎯 Key Takeaway
Use ListableBeanFactory to get all bean names or filter by type. It's the most common approach for bean introspection.

What the Official Docs Won't Tell You

The official Spring documentation teaches you getBeanDefinitionNames() and getBeansOfType(). What it doesn't tell you is that these methods can lie to you in production. First, beans created via @Bean methods in @Configuration classes show up with the method name, not the class name. So @Bean public DataSource dataSource() registers as dataSource, not DataSource. Second, AOP proxies create a second bean with a name like userService$$EnhancerBySpringCGLIB$$.... If you list beans by type, you'll get the proxy, not the original. Third, @Lazy beans are not initialized until first access, but they still appear in getBeanDefinitionNames(). The bean definition exists, but calling getBean() on it will trigger initialization—potentially causing side effects. Fourth, @Conditional annotations (like @ConditionalOnProperty) can silently exclude beans. Your getBeansOfType() returns an empty map, and you have no idea why. The fix? Always log the bean definition source. Use BeanDefinition to inspect metadata like getResourceDescription() and getScope(). Here's how to get the full picture:

BeanDefinitionInspector.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.stereotype.Component;

@Component
public class BeanDefinitionInspector {

    private final BeanDefinitionRegistry registry;

    public BeanDefinitionInspector(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public void inspectBean(String beanName) {
        if (registry.containsBeanDefinition(beanName)) {
            BeanDefinition bd = registry.getBeanDefinition(beanName);
            System.out.println("Bean: " + beanName);
            System.out.println("  Scope: " + bd.getScope());
            System.out.println("  Class: " + bd.getBeanClassName());
            System.out.println("  Source: " + bd.getResourceDescription());
            System.out.println("  Lazy Init: " + bd.isLazyInit());
        }
    }
}
Output
Bean: userService
Scope: singleton
Class: com.example.UserService
Source: file [UserService.java:15]
Lazy Init: false
🔥BeanDefinitionRegistry vs ListableBeanFactory
📊 Production Insight
In a real-time analytics pipeline, we had a @Lazy bean that created a Kafka connection on first access. When we listed beans for a health check, we accidentally triggered the connection, causing a production incident. We now exclude @Lazy beans from listing.
🎯 Key Takeaway
Bean names are not class names. Proxies create duplicates. @Lazy beans hide in definitions. Always inspect BeanDefinition to understand the source.

Filtering Beans by Type and Annotation

Listing all beans is rarely useful on its own. More often, you need to find beans of a specific type or annotated with a custom annotation. ListableBeanFactory provides getBeansOfType(Class<T> type) which returns a Map<String, T> of all beans that are assignable to the given type. This includes subclasses and proxies. For annotation-based filtering, use getBeansWithAnnotation(Class<? extends Annotation> annotationType). This is extremely powerful for finding all beans marked with @Service, @Repository, or a custom annotation like @PaymentGateway. Here's a real-world example from a payment-processing system where we discover all payment gateway implementations at runtime:

PaymentGatewayRegistry.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
public class PaymentGatewayRegistry {

    private final ListableBeanFactory beanFactory;

    public PaymentGatewayRegistry(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    public Map<String, PaymentGateway> getAllGateways() {
        return beanFactory.getBeansOfType(PaymentGateway.class);
    }

    public PaymentGateway getGateway(String name) {
        Map<String, PaymentGateway> gateways = getAllGateways();
        if (gateways.containsKey(name)) {
            return gateways.get(name);
        }
        // fallback to default
        return gateways.values().iterator().next();
    }
}
Output
Map contains: {stripeGateway: StripeGateway@123, paypalGateway: PayPalGateway@456, squareGateway: SquareGateway@789}
⚠ Proxies and Type Matching
📊 Production Insight
In a multi-tenant SaaS app, we used getBeansWithAnnotation(TenantSpecific.class) to dynamically load tenant-specific configurations without restarting the JVM.
🎯 Key Takeaway
Use getBeansOfType() for runtime discovery of implementations and getBeansWithAnnotation() for finding beans with specific annotations.

Using BeanFactoryPostProcessor for Early Inspection

Sometimes you need to inspect or modify bean definitions before they're instantiated. This is where BeanFactoryPostProcessor comes in. It runs during the ApplicationContext refresh lifecycle, before any singleton beans are created. You can use it to log all bean definitions, add properties, or even register new beans dynamically. This is particularly useful for frameworks that need to wire beans based on external configuration. Here's an example that logs all bean definitions at startup and adds a custom property to beans of a certain type:

BeanDefinitionLoggerPostProcessor.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.stereotype.Component;

@Component
public class BeanDefinitionLoggerPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) throws BeansException {
        if (factory instanceof BeanDefinitionRegistry registry) {
            String[] beanNames = registry.getBeanDefinitionNames();
            System.out.println("=== Bean Definitions at Startup ===");
            for (String name : beanNames) {
                System.out.println(name);
            }
            System.out.println("Total: " + beanNames.length);
        }
    }
}
Output
=== Bean Definitions at Startup ===
org.springframework.boot.autoconfigure.AutoConfigurationPackages
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration
userController
userService
... (output at application startup)
💡BeanFactoryPostProcessor vs BeanPostProcessor
📊 Production Insight
In a legacy migration, we used a BeanFactoryPostProcessor to detect beans with deprecated @Autowired on fields and log warnings. It helped us track down 50+ violations before a critical release.
🎯 Key Takeaway
Use BeanFactoryPostProcessor to inspect or modify bean definitions before they're created. It's the earliest hook into the container lifecycle.

Exposing Beans via a REST Endpoint for Debugging

In production, you often need to see what beans are loaded without attaching a debugger. The cleanest approach is to create a dedicated REST endpoint that returns bean information. This is especially useful when combined with Spring Boot Actuator, but a custom endpoint gives you more control over formatting and filtering. Here's a controller that exposes all beans, with optional filtering by type:

BeanDebugController.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/internal/debug")
public class BeanDebugController {

    private final ListableBeanFactory beanFactory;

    public BeanDebugController(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @GetMapping("/beans")
    public Map<String, Object> listBeans(
            @RequestParam(required = false) String type) {
        if (type != null) {
            try {
                Class<?> clazz = Class.forName(type);
                Map<String, ?> beans = beanFactory.getBeansOfType(clazz);
                return Map.of("count", beans.size(), "beans", beans.keySet());
            } catch (ClassNotFoundException e) {
                return Map.of("error", "Class not found: " + type);
            }
        }
        String[] allNames = beanFactory.getBeanDefinitionNames();
        return Map.of("count", allNames.length, "beans", List.of(allNames));
    }
}
Output
GET /internal/debug/beans?type=com.example.UserService
{
"count": 1,
"beans": ["userService"]
}
⚠ Secure Your Debug Endpoint
📊 Production Insight
During a post-mortem for a billing outage, we discovered that a @Primary bean was overridden by a @Qualifier mismatch. A quick curl to our bean endpoint revealed the duplicate immediately.
🎯 Key Takeaway
A custom REST endpoint for listing beans is invaluable for production debugging. Add it to your internal tools, not public APIs.

Performance Implications and Best Practices

Calling getBeanDefinitionNames() or getBeansOfType() is not free. These methods iterate over the entire bean factory's internal registry. In a typical application with 200 beans, it's negligible (under 1ms). But in a large microservice with 2000+ beans, or if called repeatedly in a loop, it can become a bottleneck. The real danger is in hot paths: never call these methods inside a request handler that serves thousands of requests per second. The getBeansOfType() method also triggers bean initialization for any beans that are @Lazy and haven't been touched yet. This can cascade into full application initialization if you're not careful. Best practices: cache the results of bean listing if the set doesn't change at runtime (which it usually doesn't). Use @PostConstruct to capture bean references once. If you need dynamic discovery, consider using a registry pattern where beans register themselves on startup. Here's a safe pattern:

CachedBeanRegistry.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.stereotype.Component;
import java.util.*;

@Component
public class CachedBeanRegistry {

    private final ListableBeanFactory beanFactory;
    private Map<String, PaymentGateway> gatewayCache;

    public CachedBeanRegistry(ListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    @PostConstruct
    public void init() {
        this.gatewayCache = Collections.unmodifiableMap(
            beanFactory.getBeansOfType(PaymentGateway.class));
    }

    public Map<String, PaymentGateway> getGateways() {
        return gatewayCache;
    }
}
Output
(No runtime output; cache populated once at startup)
🔥Caching is Your Friend
📊 Production Insight
We had a monitoring endpoint that called getBeansOfType() on every health check. When we scaled to 20 pods, the database connection pool was exhausted because @Lazy beans created new connections. Caching fixed it.
🎯 Key Takeaway
Cache bean listings at startup. Never call getBeansOfType() in request-scoped code. Be aware of @Lazy bean initialization side effects.

Advanced: Programmatic Bean Registration

Sometimes listing beans isn't enough—you need to register new beans dynamically at runtime. Spring allows this via BeanDefinitionRegistry.registerBeanDefinition() or the newer GenericApplicationContext.registerBean() method. This is useful for plugin systems, feature flags, or dynamic tenant configuration. Here's an example that registers a new bean based on a runtime condition:

DynamicBeanRegistrar.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class DynamicBeanRegistrar {

    private final BeanDefinitionRegistry registry;

    public DynamicBeanRegistrar(BeanDefinitionRegistry registry) {
        this.registry = registry;
    }

    public void registerPaymentGateway(String name, String className) {
        try {
            Class<?> clazz = Class.forName(className);
            BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(clazz)
                .setScope("singleton")
                .addPropertyValue("gatewayName", name);
            
            registry.registerBeanDefinition(
                name + "Gateway", 
                builder.getBeanDefinition());
            
            System.out.println("Registered bean: " + name + "Gateway");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Class not found: " + className, e);
        }
    }
}
Output
Registered bean: paypalGateway
💡Dynamic Registration Is a Code Smell
📊 Production Insight
In a multi-tenant SaaS platform, we used dynamic registration to create tenant-specific datasources at runtime. Each new tenant got a DataSource bean registered dynamically, isolated from others.
🎯 Key Takeaway
You can register beans at runtime using BeanDefinitionRegistry, but prefer static configuration unless you're building a plugin system.

Comparing Approaches: When to Use What

You now have several ways to get Spring beans: ListableBeanFactory.getBeanDefinitionNames(), getBeansOfType(), getBeansWithAnnotation(), BeanDefinitionRegistry, BeanFactoryPostProcessor, and ApplicationContext.getBean(). Each has its place. Here's a comparison to help you choose: For simple listing of all bean names (debugging), use getBeanDefinitionNames(). For finding all implementations of an interface (e.g., payment gateways), use getBeansOfType(). For finding beans with a custom annotation (e.g., @Monitored), use getBeansWithAnnotation(). For inspecting properties like scope and source before instantiation, use BeanDefinitionRegistry. For modifying bean definitions at startup, use BeanFactoryPostProcessor. For accessing a single bean by name, use getBean() (but prefer injection). Never use getBean() in production code—it's a sign of poor design. The table below summarizes the trade-offs:

ComparisonRunner.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class ComparisonRunner implements CommandLineRunner {

    private final BeanListerService lister;
    private final BeanDefinitionInspector inspector;
    private final PaymentGatewayRegistry registry;

    public ComparisonRunner(BeanListerService lister, 
                           BeanDefinitionInspector inspector,
                           PaymentGatewayRegistry registry) {
        this.lister = lister;
        this.inspector = inspector;
        this.registry = registry;
    }

    @Override
    public void run(String... args) {
        lister.printAllBeanNames();
        inspector.inspectBean("userService");
        System.out.println("Gateways: " + registry.getAllGateways().keySet());
    }
}
Output
Total beans: 157
userService
...
Bean: userService
Scope: singleton
Class: com.example.UserService
Source: file [UserService.java:15]
Lazy Init: false
Gateways: [stripeGateway, paypalGateway]
🔥Prefer Injection Over Lookup
📊 Production Insight
In a real-time analytics system, we used getBeansOfType(DataPipeline.class) to discover all pipeline stages. Adding a new stage meant just creating a new @Component class—no configuration changes needed.
🎯 Key Takeaway
Choose your approach based on what you need: names, types, annotations, or definitions. Cache aggressively. Avoid getBean() in business logic.
● Production incidentPOST-MORTEMseverity: high

The Case of the Vanishing Payment Processor

Symptom
Payment transactions failed with NoSuchBeanDefinitionException for FraudDetectionService in production only.
Assumption
The team assumed that if it worked in local dev and CI, it would work in production.
Root cause
The bean was defined in a @Configuration class that used @ConditionalOnProperty with a profile-specific property missing from production config.
Fix
Added the missing property to the production configuration file and re-deployed.
Key lesson
  • Always verify bean definitions in the target environment using a debug endpoint that lists beans.
  • Use @ConditionalOnMissingBean with care—it can silently skip bean creation.
  • Include bean listing in your health check for critical services.
Production debug guideQuick steps to diagnose bean-related issues in production3 entries
Symptom · 01
NoSuchBeanDefinitionException in logs
Fix
Check if the bean name or type is correct. Use Actuator /actuator/beans to verify. Look for @Conditional or profile restrictions.
Symptom · 02
Duplicate bean exception
Fix
Look for multiple @Bean definitions of the same type. Use @Primary or @Qualifier to resolve. Check component scan packages for overlaps.
Symptom · 03
Bean exists but null when injected
Fix
Check for circular dependencies. Use @Lazy on one of the beans. Verify that the bean is not a proxy that's not fully initialized.
★ Quick Debug Cheat Sheet: Spring BeansOne-liner commands and endpoints for immediate bean debugging
Need list of all beans
Immediate action
Call Actuator endpoint
Commands
curl http://localhost:8080/actuator/beans | jq '.contexts.default.beans | keys'
curl http://localhost:8080/actuator/beans | jq '.contexts.default.beans["userService"]'
Fix now
Enable Actuator with management.endpoints.web.exposure.include=beans in application.properties
Bean of type not found+
Immediate action
Search by type in Actuator output
Commands
curl http://localhost:8080/actuator/beans | jq '.contexts.default.beans[] | select(.type=="com.example.UserService")'
grep -r "@Service" src/main/java/
Fix now
Ensure package is in @ComponentScan and no @Conditional excludes it
Duplicate bean definitions+
Immediate action
Check for multiple @Bean methods
Commands
curl http://localhost:8080/actuator/beans | jq '[.contexts.default.beans[] | select(.type=="javax.sql.DataSource") | .bean]'
grep -r "@Bean" src/main/java/ | grep -i datasource
Fix now
Add @Primary to the preferred bean or use @Qualifier on injection points
MethodUse CasePerformanceLazy Bean Handling
getBeanDefinitionNames()List all bean namesFast (returns String[])Does not initialize
getBeansOfType(Class)Find implementations of an interfaceMedium (iterates, may init)Initializes lazy beans
getBeansWithAnnotation(Class)Find beans with custom annotationMedium (iterates)Initializes lazy beans
BeanDefinitionRegistryInspect definitions before instantiationFast (no bean creation)N/A (definitions only)
BeanFactoryPostProcessorModify definitions at startupOne-time costN/A (runs before instantiation)
⚙ Quick Reference
8 commands from this guide
FileCommand / CodePurpose
BeanListerService.java@ServiceUnderstanding ApplicationContext and BeanFactory
BeanDefinitionInspector.java@ComponentWhat the Official Docs Won't Tell You
PaymentGatewayRegistry.java@ComponentFiltering Beans by Type and Annotation
BeanDefinitionLoggerPostProcessor.java@ComponentUsing BeanFactoryPostProcessor for Early Inspection
BeanDebugController.java@RestControllerExposing Beans via a REST Endpoint for Debugging
CachedBeanRegistry.java@ComponentPerformance Implications and Best Practices
DynamicBeanRegistrar.java@ComponentAdvanced
ComparisonRunner.java@ComponentComparing Approaches

Key takeaways

1
Use ListableBeanFactory for all bean introspection needs—it's the right tool for the job.
2
Cache bean listings at startup to avoid performance issues and @Lazy side effects.
3
Inspect BeanDefinition to understand bean source, scope, and conditional registration.
4
Expose a secured REST endpoint for production debugging—it's faster than attaching a debugger.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the difference between `BeanFactory` and `ApplicationContext`?
Q02SENIOR
How would you find all beans annotated with a custom annotation at runti...
Q03SENIOR
Explain how `BeanFactoryPostProcessor` differs from `BeanPostProcessor`,...
Q01 of 03JUNIOR

What is the difference between `BeanFactory` and `ApplicationContext`?

ANSWER
BeanFactory is the basic IoC container providing getBean() and containsBean(). ApplicationContext extends BeanFactory and adds features like event publishing, AOP integration, message i18n, and ListableBeanFactory for iterating over beans. In practice, you always use ApplicationContext.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
Why does `getBeansOfType()` return an empty map for my `@Service` class?
02
Can I list beans from a different `ApplicationContext` (e.g., parent context)?
03
Is there a performance hit for calling `getBeansOfType()` frequently?
04
How do I list beans from a Spring Boot Actuator endpoint?
N
Naren Founder & Principal Engineer

20+ years shipping production Java in banking & fintech. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Spring Boot. Mark it forged?

3 min read · try the examples if you haven't

Previous
Spring Boot Auto-Configuration Report: Understanding What Was Configured
71 / 121 · Spring Boot
Next
Spring Component Scanning: @ComponentScan, Filters, and Base Packages