Question 5 · Section 5

How to Create a Bean in Spring?

4. @Primary -> if multiple beans of the same type 5. Functional registration -> GraalVM Native 6. @Configuration is proxied -> method calls = cache

Language versions: English Russian Ukrainian

Junior Level

Ways to create a bean:

1. Annotations (most common):

@Service
public class UserService { }

@Component
public class EmailSender { }

2. @Bean in configuration:

@Configuration
public class Config {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Rule:

  • Your own code -> @Service, @Component
  • Third-party classes -> @Bean

Middle Level

Stereotype Annotations

@Component        // General component
@Service          // Business logic
@Repository       // Database access (automatic exception translation)
@Controller       // Web controller
@RestController   // REST API (@Controller + @ResponseBody)

How it works:

@ComponentScan -> scans packages
  -> Found @Component
  -> Created BeanDefinition
  -> Registered in context

Java Config (@Bean)

@Configuration
public class InfrastructureConfig {

    @Bean
    @Primary  // Primary bean if there are multiple
    public ObjectMapper mapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.findAndRegisterModules();
        return mapper;
    }

    @Bean(initMethod = "init", destroyMethod = "close")
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

FactoryBean

// For complex creation logic
public class MyFactoryBean implements FactoryBean<MyService> {
    public MyService getObject() { return new MyService(); }
    public Class<?> getObjectType() { return MyService.class; }
}

// Spring will call getObject() and register the result

Senior Level

CGLIB Proxying of @Configuration

@Configuration
public class Config {
    @Bean
    public A a() { return new A(); }

    @Bean
    public B b() {
        return new B(a());  // Calling method a()!
    }
}

// Spring proxies Config via CGLIB:
// -> First call to a() -> creates bean, caches it
// -> Second call to a() -> returns from cache!
// -> Always one instance (Singleton)

Programmatic Registration (Spring 5+)

// Without reflection and annotation scanning -> faster startup.
// Spring Boot uses this for GraalVM Native Image, where reflection is limited.
context.registerBean(MyService.class, () -> new MyService());

// For GraalVM Native Image -> reflection is limited
// Programmatic registration is preferred

ImportSelector / ImportBeanDefinitionRegistrar

// ImportSelector - an interface that allows dynamically importing bean classes by condition. This is how all @Enable... annotations work (e.g., @EnableCaching, @EnableScheduling).

// ASM - library for reading class bytecode without loading them into the JVM. Spring uses it during @ComponentScan to find annotations.

// This is how all @Enable... annotations work
public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata metadata) {
        return new String[] { "com.example.Service1", "com.example.Service2" };
    }
}

// How it is used:
@Import(MyImportSelector.class)
public @interface EnableMyFeature { }
// Now @EnableMyFeature imports Service1 and Service2

@EnableMyFeature  // -> Imports Service1 and Service2

Scanning: ASM Library

ClassPathBeanDefinitionScanner:
  -> Reads bytecode via ASM
  -> Does NOT load classes into JVM!
  -> Searches for annotations
  -> Creates BeanDefinition

-> On large projects -> slow startup
-> Limit @ComponentScan to specific packages

When NOT to Use @Bean

  1. Simple data objects - DTOs, Entities - created via new or ORM
  2. Too many beans - each bean = startup overhead. For 1000+ beans consider lazy initialization
  3. FactoryBean - needed only when creation logic is complex and depends on external conditions (e.g., EntityManagerFactory)

Production Experience

Real scenario: slow startup

  • 10,000 classes in classpath
  • Scanning: 15 seconds
  • Solution: limited @ComponentScan(“com.myapp”)
  • Result: startup 2 seconds

Best Practices

  1. @Service/@Component -> your own code
  2. @Bean -> third-party libraries
  3. Limit scanning -> specific packages
  4. @Primary -> if multiple beans of the same type
  5. Functional registration -> GraalVM Native
  6. @Configuration is proxied -> method calls = cache

Summary for Senior

  • @Component -> scanning via ASM
  • @Bean -> Java Config, CGLIB proxy
  • FactoryBean -> complex creation logic
  • registerBean -> programmatically, GraalVM
  • ImportSelector -> @Enable… annotations
  • Limit scanning -> faster startup

Interview Cheat Sheet

Must know:

  • Your own code -> @Service, @Component, @Repository, @Controller (automatic scanning)
  • Third-party libraries -> @Bean in @Configuration (manual configuration)
  • @ComponentScan scans packages via ASM (reading bytecode without loading classes)
  • @Configuration is proxied via CGLIB: repeated method calls return cached Singleton
  • @Primary - primary bean if there are multiple of the same type
  • FactoryBean - for complex creation logic (Spring calls getObject())
  • Programmatic registration registerBean() - GraalVM Native Image, no reflection
  • ImportSelector - dynamic bean import, this is how all @Enable... annotations work

Common follow-up questions:

  • Why does calling a() inside @Configuration return the same bean? CGLIB proxy intercepts the call and returns the Singleton from cache.
  • Why limit @ComponentScan? On large projects, scanning thousands of classes slows down startup (15 sec -> 2 sec).
  • When to use FactoryBean? When creation depends on external conditions (e.g., EntityManagerFactory).
  • What is ImportSelector? An interface for dynamic import of bean classes - the basis of all @Enable... annotations.

Red flags (DO NOT say):

  • “You can declare @Bean for DTO/Entity” (these are regular data objects, not beans)
  • “The bigger @ComponentScan, the better - Spring will find everything” (slow startup on large projects)
  • @Configuration methods are called directly” (CGLIB proxy intercepts and caches)
  • “FactoryBean and @Bean are the same” (FactoryBean is an interface with complex creation logic, @Bean is a simple method)

Related topics:

  • [[04. What is a Bean in Spring]]
  • [[06. What is Bean Lifecycle]]
  • [[07. What are the Bean Lifecycle stages]]
  • [[01. What is Dependency Injection]]
  • [[08. What is BeanPostProcessor]]