A Deep Dive into OSGi Components and Services
The Final Post in Our OSGi Series!
In our previous post, we explored the IoC, and Dependency Injection (DI) - the foundation that makes OSGi modular, dynamic, and flexible. Understanding these concepts is essential before diving into Components and Services, as they rely on IoC and DI for runtime wiring and dynamic service injection.
In this post, we’ll cover:
What Components are and how they relate to bundles
How Services work in OSGi
The role of the Service Registry in dynamic service discovery and injection
How Declarative Services (DS) simplify component and service management
By the end of this post, you’ll understand how OSGi bundles expose, consume, and dynamically wire services, completing the picture of how OSGi achieves true modularity and runtime flexibility.
What Is a Component?
A component in OSGi is a logical unit of functionality inside a bundle. It is self-contained, declarative, and managed by the container. Components allow bundles to provide or consume services dynamically without tight coupling.
Key Characteristics:
Self-contained: Implements a specific function or service
Declarative: Dependencies, lifecycle, and configuration are defined via annotations or metadata
Container-managed: The OSGi container controls activation, deactivation, and dependency injection
Example Component:
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
@Component
public class OrderProcessor {
@Reference
private PaymentService paymentService;
public void process(Order order) {
paymentService.processPayment(order);
}
}
Explanation:
OrderProcessoris a component inside a bundleIt declares a dependency on
PaymentServiceThe OSGi container injects the
PaymentServiceautomatically at runtime (DI)Component lifecycle is managed by the container - no need for manual initialization
Component Lifecycle in OSGi
Every OSGi component has a lifecycle managed by the container via SCR (Service Component Runtime). This ensures components are only active when all dependencies are satisfied, and they react dynamically to runtime changes.
Lifecycle States:
Inactive / Unsatisfied – Dependencies missing, component not active
Active / Satisfied – Dependencies available,
@Activateis calledModified (optional) – Runtime configuration changes trigger
@ModifiedDeactivated – Component is stopped or a dependency disappears;
@Deactivateis called
Example
import org.osgi.service.component.annotations.*;
@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
@Activate
void activate() {
System.out.println("PaypalPaymentService Activated");
}
@Modified
void modified() {
System.out.println("Configuration changed dynamically");
}
@Deactivate
void deactivate() {
System.out.println("PaypalPaymentService Deactivated");
}
}
Key Points:
The SCR manages the lifecycle automatically
Components never manually look up services - container injects them
Lifecycle + DS (Declarative Services) + SCR (Service Component Runtime) = dynamic, hot-swappable components
What Is a Service?
A service in OSGi is a Java object registered under one or more interfaces, optionally with metadata (properties).
Key Characteristics:
Interface-based – Consumers depend on the interface, not implementation
Dynamic – Services can appear, disappear, or be replaced at runtime
Registry-managed – Service Registry tracks all services
Example
import org.osgi.service.component.annotations.Component;
@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
@Override
public void processPayment(Order order) {
System.out.println("Processing via PayPal: " + order.getId());
}
}
Declares itself as a service
Automatically registered in Service Registry
Consumers can bind dynamically without knowing the implementation
Components vs Services
It’s important to clarify the relationship between components and services in OSGi:
Every service is a component:
A service must exist inside a component to be registered in the Service Registry.
The component defines how the service is implemented, activated, and injected.
Not every component is a service:
A component may perform internal logic or act as a helper without exposing any service interface.
Such components are still managed by the container and can consume services, but they do not register themselves in the Service Registry.
Example
@Component
public class InternalHelper {
// Component with internal logic, not registered as a service
public void doSomethingInternal() {}
}
@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
// Component exposing a service
}
Key Points:
PaypalPaymentService→ Component + ServiceInternalHelper→ Component only (no service registration)The container manages both, but only services appear in the Service Registry
How Components and Services Work Together
OSGi enables dynamic wiring of components and services:
Components declare dependencies (IoC)
Services are registered in the Service Registry
Container injects services into components (DI)
Components consume services without knowing implementation
Hot updates: Services can be replaced or updated at runtime
Declarative Services (DS): The Backbone of Modern OSGi Applications
Declarative Services (DS) is the recommended programming model for building OSGi components and services. It allows developers to declare components, their dependencies, and their lifecycle using metadata and annotations, rather than writing complex framework code.
In simple terms, DS lets you focus on business logic while the OSGi container handles wiring, lifecycle, and dependency management for you.
Why Declarative Services Exist
Before DS, developers had to:
Manually register services in the Service Registry
Track service availability
Handle dynamic binding and unbinding
Manage lifecycle code in Bundle Activators
This approach was:
Verbose
Error-prone
Hard to maintain
Declarative Services solve this by moving all that complexity into the framework.
Component Lifecycle in Declarative Services
DS components have a well-defined lifecycle, fully managed by the container:
@Component
public class InventoryService {
@Activate
void activate() {
System.out.println("Component activated");
}
@Deactivate
void deactivate() {
System.out.println("Component deactivated");
}
}
Lifecycle states are controlled by:
Service availability
Configuration changes
Bundle lifecycle events
You do not control lifecycle manually - DS does.
What Is SCR (Service Component Runtime)?
Declarative Services do not work by magic.
Behind the scenes, DS is implemented by the Service Component Runtime (SCR). It is the Engine Behind Declarative Services.
SCR is the runtime engine for Declarative Services:
Reads component metadata (annotations/XML)
Registers services in the Service Registry
Creating component instances
Injects dependencies dynamically (DI)
Manages lifecycle methods:
activate(),deactivate(),modified()Handles hot updates and dynamic rebinding
Example
@Component(service = PaymentService.class)
public class PaypalPaymentService implements PaymentService {
@Activate
void activate() {}
@Deactivate
void deactivate() {}
@Modified
void modified() {}
}
SCR performs all runtime wiring automatically
Ensures dynamic, modular behavior without manual service lookups
DS is the programming model
SCR is the runtime engine that makes it work
Without SCR, there is no DS.
Why DS Is the Preferred Model in OSGi
Declarative Services are considered best practice because they:
Remove boilerplate code
Enforce loose coupling
Support true runtime dynamism
Integrate seamlessly with IoC and DI
Work naturally with the Service Registry
Scale cleanly in large modular systems
This is why platforms like Adobe AEM, Eclipse, and Apache Karaf rely heavily on DS.
Key Takeaways
Components: Building blocks inside bundles
Services: Interface-based objects registered in Service Registry
IoC + DI: Components declare dependencies; container injects services
Declarative Services: Simplifies wiring and lifecycle management
SCR: Runtime engine that orchestrates DS, lifecycle, and service injection
Component Lifecycle: Inactive → Active → Modified → Deactivated, fully managed by container
This post wraps up our OSGi series. You now have a complete understanding of:
OSGi Container – runtime engine
Bundles & Classloading – modular packaging and isolation
IoC and DI – dynamic wiring
Components and Services – building and consuming dynamic modular functionality
SCR and DS – runtime orchestration
With this knowledge, you’re ready to design and implement truly modular, dynamic, and flexible Java applications using OSGi.

