Intermediate Reading time: ~8 min

ApplicationContext

BeanFactory vs ApplicationContext, XML config, Java config, annotation-based config

ApplicationContext

1. Definition

ApplicationContext is the central interface of the Spring Framework that provides full IoC container functionality. It extends BeanFactory but goes far beyond it by offering integrated AOP support, internationalization (i18n), event handling, and environment abstraction. Most Spring applications use ApplicationContext instead of BeanFactory because all services needed for production operation are available here.

In Spring Boot applications, ApplicationContext is created automatically by SpringApplication.run() — typically as AnnotationConfigServletWebServerApplicationContext for web applications.


2. Core Concepts

BeanFactory vs ApplicationContext

Feature BeanFactory ApplicationContext
Bean creation Lazy (on first lookup) Eager (singletons at startup)
Event handling ApplicationEventPublisher
i18n (MessageSource)
AOP integration Manual Automatic
BeanPostProcessor registration Manual Automatic
Environment / Profile
ResourceLoader Basic Advanced (classpath, file, URL)

BeanFactory is a lightweight container that only handles bean instantiation and DI. ApplicationContext contains everything BeanFactory does, plus enterprise-level services.

Main ApplicationContext Implementations

  • AnnotationConfigApplicationContext — Java config (@Configuration classes) in standalone applications
  • ClassPathXmlApplicationContext — XML configuration from classpath
  • GenericWebApplicationContext — Web environment, servlet container integration
  • AnnotationConfigServletWebServerApplicationContext — Spring Boot web application (default)

Configuration Styles

XML-based — The classic approach, <beans> root element, <bean> definitions:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="...">
    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
        <property name="jdbcUrl" value="jdbc:h2:mem:test"/>
    </bean>
</beans>

Java config@Configuration + @Bean:

@Configuration
public class AppConfig {
    @Bean
    public DataSource dataSource() {
        HikariDataSource ds = new HikariDataSource();
        ds.setJdbcUrl("jdbc:h2:mem:test");
        return ds;
    }
}

Annotation-based@ComponentScan + stereotype annotations (@Component, @Service, @Repository):

@Configuration
@ComponentScan("com.example")
public class AppConfig {}

In practice most Spring Boot projects use a combination of Java config and annotation-based approaches. XML config appears in legacy code.


3. Practical Usage

Starting Context in a Standalone Application

// Java config
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
UserService userService = ctx.getBean(UserService.class);

// XML config (legacy)
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

Spring Boot Application

@SpringBootApplication
public class MyApp {
    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MyApp.class, args);
        // ctx is automatically available, rarely need to manually get beans
    }
}

Environment and Profiles

@Configuration
@Profile("production")
public class ProdConfig {
    @Bean
    public DataSource dataSource() {
        // production DataSource
    }
}

Activation: spring.profiles.active=production (application.properties or JVM arg).

Event Handling

// Publishing an event
@Service
public class OrderService {
    private final ApplicationEventPublisher publisher;

    public OrderService(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    public void placeOrder(Order order) {
        // business logic
        publisher.publishEvent(new OrderPlacedEvent(order));
    }
}

// Listening for events
@Component
public class NotificationListener {
    @EventListener
    public void onOrderPlaced(OrderPlacedEvent event) {
        // send notification
    }
}

i18n (MessageSource)

@Autowired
private MessageSource messageSource;

String msg = messageSource.getMessage("greeting", null, Locale.forLanguageTag("hu"));

4. Code Examples

Full Java Config Application

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

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

    @Bean
    public AppInfo appInfo() {
        return new AppInfo(appName);
    }
}

Combining Multiple Configs

@Configuration
@Import({DataConfig.class, SecurityConfig.class})
public class RootConfig {}

Context Hierarchy (Parent-Child)

AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(SharedConfig.class);
AnnotationConfigApplicationContext child = new AnnotationConfigApplicationContext();
child.setParent(parent);
child.register(WebConfig.class);
child.refresh();
// child context can access parent beans, but not vice versa

ResourceLoader Usage

@Service
public class TemplateService {
    private final ResourceLoader resourceLoader;

    public TemplateService(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public String loadTemplate(String path) throws IOException {
        Resource resource = resourceLoader.getResource("classpath:" + path);
        return new String(resource.getInputStream().readAllBytes());
    }
}

5. Trade-offs

Aspect Advantage Disadvantage
Eager initialization (singleton) Errors surface at startup (fail-fast) Slower startup with many beans
Java config vs XML Type-safe, IDE refactoring More complex @Conditional logic
XML config Declarative, can change without code Verbose, no compiler checks
Annotation-based config Minimal boilerplate Scattered configuration, hard to review
@ComponentScan Automatic, less code Not obvious what enters the context
Explicit @Bean Full control, documented More code, maintenance

When to Use Which Approach?

  • New project: Java config + annotations (Spring Boot default)
  • External library bean: @Bean method in @Configuration class
  • Legacy integration: @ImportResource("legacy-context.xml")
  • Conditional bean: @Conditional* annotations in Java config

6. Common Mistakes

❌ Not Closing Context (Standalone App)

// BAD — resource leak
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
// usage...
// @PreDestroy callbacks never execute

// GOOD — try-with-resources
try (var ctx = new AnnotationConfigApplicationContext(AppConfig.class)) {
    ctx.getBean(MyService.class).doWork();
} // @PreDestroy called automatically

❌ Using BeanFactory Instead of ApplicationContext

// BAD — no AOP, no Events, no auto-registered BeanPostProcessors
BeanFactory factory = new DefaultListableBeanFactory();

// GOOD — use ApplicationContext
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);

❌ Calling Context refresh() Multiple Times

// BAD — duplicate beans, memory leak
ctx.refresh();
ctx.refresh();

❌ Wrong Profile Activation Order

// BAD — profile must be set before context refresh
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.refresh();
ctx.getEnvironment().setActiveProfiles("prod"); // No effect!

// GOOD
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("prod");
ctx.register(AppConfig.class);
ctx.refresh();

7. Deep Dive

Context Lifecycle

  1. Bean definition loading — parsing @Configuration classes, XML files
  2. BeanFactoryPostProcessor execution — modifying bean definitions (e.g., PropertySourcesPlaceholderConfigurer)
  3. Bean instantiation — eager creation of singleton beans
  4. BeanPostProcessor execution — AOP proxy, @Autowired resolution
  5. Context readyContextRefreshedEvent published
  6. Operation — beans in use
  7. Context closeContextClosedEvent, @PreDestroy callbacks

BeanFactoryPostProcessor vs BeanPostProcessor

Aspect BeanFactoryPostProcessor BeanPostProcessor
When does it run? After bean definitions loaded, before instantiation After each bean instantiation
What does it modify? Bean definitions (metadata) Bean instances
Typical usage Property placeholder resolution, bean definition modification AOP proxy, @Autowired, @PostConstruct

Context Hierarchy (Parent-Child)

The classic Spring MVC model: root context (service, repository beans) and servlet context (controllers, view resolvers). The child context can see parent beans, but not vice versa. In Spring Boot this has been simplified — typically a flat context is used.

@Configuration Class CGLIB Proxy

Spring loads @Configuration classes via a CGLIB proxy. This ensures that inter-bean references (one @Bean method calling another) maintain singleton semantics. Setting @Configuration(proxyBeanMethods = false) (lite mode) disables this behavior.

@Configuration
public class AppConfig {
    @Bean
    public ServiceA serviceA() {
        return new ServiceA(commonDep()); // CGLIB: returns singleton
    }

    @Bean
    public ServiceB serviceB() {
        return new ServiceB(commonDep()); // CGLIB: returns SAME instance
    }

    @Bean
    public CommonDep commonDep() {
        return new CommonDep();
    }
}

8. Interview Questions

  1. What is the difference between BeanFactory and ApplicationContext? BeanFactory is the basic DI container (lazy init). ApplicationContext contains everything plus events, i18n, AOP, auto-registered BeanPostProcessors, Environment.

  2. What ApplicationContext implementations do you know? AnnotationConfigApplicationContext, ClassPathXmlApplicationContext, GenericWebApplicationContext, Spring Boot: AnnotationConfigServletWebServerApplicationContext.

  3. What is the difference between Java config and annotation-based config? Java config: explicit @Bean methods in @Configuration classes. Annotation-based: @ComponentScan + stereotype annotations (@Component, @Service). In practice, they are combined.

  4. How does the @Configuration CGLIB proxy work? Spring creates a CGLIB subclass of the @Configuration class. This way inter-bean references (one @Bean method calling another) always return the instance from the singleton cache. proxyBeanMethods = false disables this.

  5. What is the context lifecycle order? Bean definition load → BeanFactoryPostProcessor → Singleton instantiation → BeanPostProcessor → ContextRefreshedEvent → Usage → ContextClosedEvent → @PreDestroy.

  6. When would you use XML config in 2024? Legacy integration, third-party framework constraints, or when configuration must be modifiable outside of code. For new projects, Java config is recommended.

  7. What is context hierarchy and what is it for? Parent-child context: child can see parent beans, but not vice versa. In classic Spring MVC — root (service) and servlet (web) contexts. In Spring Boot, a flat context is typical.


9. Glossary

Term Meaning
BeanFactory Basic IoC container interface, lazy bean init
ApplicationContext Full-featured container (BeanFactory + enterprise services)
@Configuration Java-based configuration class, with CGLIB proxy
@ComponentScan Automatic bean registration by scanning packages
@PropertySource Loads external property file into Environment
@Import Connects multiple configuration classes together
@Profile Conditional activation of beans/config based on profile
MessageSource i18n interface, resolving localized messages
ApplicationEventPublisher Interface for publishing events
BeanFactoryPostProcessor Modifies bean definitions before instantiation
ContextRefreshedEvent Event fired after full context initialization
proxyBeanMethods @Configuration attribute to toggle CGLIB proxy

10. Cheatsheet

BeanFactory           →  Lightweight, lazy init, DI only
ApplicationContext    →  BeanFactory + AOP + Event + i18n + Environment

Configuration styles:
  XML:        ClassPathXmlApplicationContext("beans.xml")
  Java:       AnnotationConfigApplicationContext(AppConfig.class)
  Boot:       SpringApplication.run(MyApp.class, args)

Context lifecycle:
  Def load → BFPP → Instantiate → BPP → @PostConstruct → Ready → Close → @PreDestroy

@Configuration:
  CGLIB proxy → inter-bean ref = singleton
  proxyBeanMethods=false → lite mode, no proxy

Common implementations:
  AnnotationConfigApplicationContext        standalone + Java config
  ClassPathXmlApplicationContext            standalone + XML
  AnnotationConfigServletWebServerAC        Spring Boot web

Context hierarchy:
  child.setParent(parent)
  child → parent beans visible
  parent → child beans NOT visible

Common mistakes:
  ✗ Using BeanFactory instead of ApplicationContext
  ✗ Forgetting to close context (standalone)
  ✗ Setting profile after refresh()
  ✗ Calling refresh() multiple times

🎮 Games

10 questions