Dagger 2 reference documentation on Subcomponent is compact. It's not easily digestible. So I made one version referring to its content, and add illustration and code in Kotlin. Hope that helps make it easier to consume.
If you want a basic simple illustration on Subcomponent, you could also refer
In Dagger 2, component is the main container-like object that binds all the dependencies (or it's factory).
Subcomponent are components that is like an extension to its parent component.

It can be used for
- Partition the dependencies into different compartments. Avoid the parent component to have too many dependencies bound to it.
- Subcomponent and its parent component have different scope (of lifespan). The subcomponent scope is smaller than its parent.
Subcomponent can access all its parent bound dependencies, but not vice versa. The relationship is illustrated further below.

Declaring a subcomponent
To create as subcomponent,
- Use
@Subcomponentto annotate on an interface (or abstract class). - Add your subcomponent modules by adding to the
Moduleparameter. - Create the Builder using
@Subcomponent.Builder. (Optional, but preferred in some cases. Refer to this blog for comparison.
@Subcomponent(modules = [RequestModule::class])
internal interface RequestComponent {
fun requestHandler(): RequestHandler
@Subcomponent.Builder
interface Builder {
// the requestModule is optional if the Module
// doesn't have any constructor argument
fun requestModule(module: RequestModule): Builder
// the bindData is optional
@BindsIntance
fun bindData(data: Data): Builder
fun build(): RequestComponent
}
}
Adding a subcomponent to a parent component
Unlike coding inheritance, where the child define who the parent is, in Dagger 2, the parent define who the child (subcomponent) is.
In another word, the child could have multiple parents, as long each of the parent has all the child needed dependency
There are two approach
1. Through the parent component's module
Add the subcomponent class to the subcomponents attribute of a @Module that the parent component installs.
@Module(subcomponents = [RequestComponent::class])
class ServerModule
@Singleton
@Component(modules = [ServerModule::class])
interface ServerComponent {
val requestRouter: RequestRouter
}Note: Even thought the Subcomponent is linked through the ServerModule, other module (if there is) within the ServerModule can also have access to the Subcomponent's builder.
2. Through the parent component abstract function.
Create an abstract function (or in Kotlin, we could have interface property) that generate the Subcomponent's Builder (or the subcomponent itself)
@Singleton
@Component(modules = [ServerModule::class])
interface ServerComponent {
val requestRouter: RequestRouter
val requestComponentBuilder: RequestComponent.Builder
}
As of which to use or not, refer to this blog for comparison.
With either of the approach above, the parent bounded dependency can be injected with the child component Builder
@Singleton
class RequestRouter @Inject constructor(
private val requestComponentProvider:
Provider<RequestComponent.Builder>) {
fun dataReceived(data: Data) {
val requestComponent = requestComponentProvider.get()
.requestModule(RequestModule()) // Optional
.data(data) // Required if BindsInstance is defined.
.build()
requestComponent.requestHandler.writeResponse(200, "Hello")
}
}
Subcomponents and scope
In Dagger 2, Scope is used to help determined the lifespan of an object. An un-scope object is always created a new when being called. But a Scope object is created once per scope defined lifespan. Refer the below blog to understand more
For Subcomponent, it enables one to have smaller scope than what the parent component has. The default scope is @Singleton. If the parent component @Singleton, then its subcomponent need to have a smaller scope, which can only be constructed using custom scope.
Example custom scopes is written as below
@Scope
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
annotation class ChildScopeIn term of Scope, the rules as below
- The parent component and child component (or anything between the ancestry line) cannot be of the same scope
- Un-scope parent component can refer to a scope child component.
- The sibling components could may be using the same scope annotation, but they scope lifespan is different (within the respective subcomponent build existence)
@RootScope @Component
interface RootComponent {
val rootScopeChildComponent: RootScopeComponent.Builder // ERROR!
val siblingComponentOne: SiblingComponentOne.Builder // OK
val siblingComponentTwo: SiblingComponentTwo.Builder // OK
}
@RootScope @Subcomponent
interface RootScopeComponent {...}
@ChildScope @Subcomponent
interface SiblingComponentOne {...}
@ChildScope @Subcomponent
interface SiblingComponentTwo {...}
@Component
interface UnscopeComponent {
val rootScopeChildComponent: RootScopeComponent.Builder // OK
}
Another example, we have a RootComponent that is @Singleton scope, that has a child component called SessionComponent with @SessionScope. In SessionComponent, it has two child components, that is scoped with @RequestScope, named FooRequestComponent and BarRequestComponent, which is considered sibling to each others. This compiles find, as FooRequestComponent and BarRequestComponent doesn't have direct ancestry line between them.
@Singleton @Component
interface RootComponent2 {
val sessionComponent: SessionComponent.Builder
}
@SessionScope @Subcomponent
interface SessionComponent {
val fooRequestComponent: FooRequestComponent.Builder // OK
val barRequestComponent: BarRequestComponent.Builder // OK
@Subcomponent.Builder
interface Builder {...}
}
@RequestScope @Subcomponent
interface FooRequestComponent {...}
@RequestScope @Subcomponent
interface BarRequestComponent {...}
Subcomponents for encapsulation
The other benefit of Subcomponent is, it could be used to encapsulate its objects (in the Subcomponent).
Non-Encapsulation
Imagine if Database is dependent on DatabaseScheme and DatabaseConnectionPool, and all of them are provided by the within the same component, then DatabaseConnectionPool and DatabaseScheme could be easily accessible. This is not ideal.
@Singleton
@Component(modules = [DatabaseModule::class])
internal interface ApplicationComponent {
val database: Database
val databaseScheme: DatabaseSchema
val databaseConnectionPool: DatabaseConnectionPool
}
@Module
class DatabaseModule {
@NumberOfCores
@Provides
fun getNumberOfCore() = 10
@Provides
fun provideDatabaseConnectionPool(
@NumberOfCores concurrencyLevel: Int)
: DatabaseConnectionPool {
return DatabaseConnectionPool(concurrencyLevel)
}
@Provides
fun provideDatabaseSchema(): DatabaseSchema {
return DatabaseSchema()
}
@Provides
@Singleton
fun provideDatabase(
databaseSchema: DatabaseSchema,
databaseConnectionPool: DatabaseConnectionPool)
: Database {
return Database(databaseSchema, databaseConnectionPool)
}
}
Encapsulation
However, we can encapsulate those dependence into a Subcomponent (i.e. DatabaseComponent) as per the code below. This will hide away DatabaseScheme and DatabaseConnectionPool from being accessible by others other than Database itself.
@Singleton
@Component(modules = [DatabaseModule::class])
internal interface ApplicationComponent {
val database: Database
val databaseScheme: DatabaseSchema // Error
val databaseConnectionPool: DatabaseConnectionPool // Error
}
@Module(subcomponents = [DatabaseComponent::class])
class DatabaseModule {
@NumberOfCores
@Provides
fun getNumberOfCore() = 10
@Provides
@Singleton
fun provideDatabase(
@NumberOfCores numberOfCores: Int,
databaseComponentBuilder: DatabaseComponent.Builder)
: Database {
return databaseComponentBuilder
.databaseImplModule(
DatabaseImplModule(numberOfCores / 2))
.build()
.database()
}
}
@Module
class DatabaseImplModule(private val concurrencyLevel: Int) {
@Provides
fun provideDatabaseConnectionPool(): DatabaseConnectionPool {
return DatabaseConnectionPool(concurrencyLevel)
}
@Provides
fun provideDatabaseSchema(): DatabaseSchema {
return DatabaseSchema()
}
@PrivateToDatabase
@Provides
fun provideDatabase(
databaseSchema: DatabaseSchema,
databaseConnectionPool: DatabaseConnectionPool): Database {
return Database(databaseSchema, databaseConnectionPool)
}
}
@Subcomponent(modules = [DatabaseImplModule::class])
interface DatabaseComponent {
@PrivateToDatabase
fun database(): Database
@Subcomponent.Builder
interface Builder {
fun databaseImplModule(
databaseImplModule: DatabaseImplModule): Builder
fun build(): DatabaseComponent
}
}
Defining subcomponents with abstract factory methods
As mentioned in the Adding Subcomponent to Parent section above, we could have abstract factory methods (or interface property in Kotlin) that return the subcomponent
@Singleton
@Component(modules = [ServerModule::class])
val requestComponent: RequestComponent
}Module with parameter
Assuming if the Subcomponent's module (i.e. RequestModule) need to have argument
@Module
class RequestModule(private val data: Data) {
@Provides
fun getRequestHandler() = RequestHandler(data)
}
@Subcomponent(modules = [RequestModule::class])
interface RequestComponent {
val requestHandler: RequestHandler
}Then we could add the parameter to the abstract function (can't use Interface Property of Kotlin anymore in this case)
@Singleton
@Component
interface ServerComponent {
fun requestComponent(requestModule: RequestModule)
: RequestComponent
}Sharing module with parent
Imagine if both the Parent and the Subcomponent having the same module. The Subcomponent will just take the parent's component.
@Module
class ShareModule(private val data: Data) {
@Provides
fun getRequestHandler() = RequestHandler(data)
}
@Subcomponent(modules = [ShareModule::class])
interface RequestComponent {
val requestHandler: RequestHandler
}
@Singleton
@Component(modules = [ShareModule::class])
interface ServerComponent {
val requestComponent: RequestComponent
}
fun main() {
val requestComponent = DaggerServerComponent
.builder()
.shareModule(ShareModule(Data("Parent Module")))
.build()
.requestComponent
}
Return the Subcomponent's builder instead
In the parent component, other than returning the subcomponent, we could return the builder instead as shown in example code below. This would make it possible to use the subcomponent's builder provided parameter (e.g. Module creation, @BindsInstance data etc) .
@Singleton
@Component(modules = [ServerModule::class])
interface ServerComponent {
val requestComponentBuilder: RequestComponent.Builder
}Comparison consideration
Should we return the Builder or not? Should we consider using Abstract Factory Method or just use the subcomponent parameter in the Parent's Module?
Below are two blogs for more reading any comparing the differences.
Details
Extending multibindings
Multibindings is a feature in Dagger2 that allow binding of instances into a Set or Map of Data that could be provided/injected into its target.
Subcomponent has enable Multibindings feature a different (or rather super) set/map of data in the subcomponent, than what the parent provides.
Illustrated in the diagram below, the parent provide only string1 and string2. For the same Set<String>, within the child's scope (i.e. Subcomponent Scope), it provides string1, string2, string3 and string4. This enables each subcomponent to provides different set of Set<String>

For more about Multibindings, refers to the blog below.
Repeated Module in Subcomponent (Not supported Error)
If a required module by the subcomponent, already provided by the Parent, the subcomponent shouldn't build its own subcomponent. It will either compile error or clash during run time depending on how the builder is called.
Below are 3 examples
@Module
class RepeatedModule
@Component(modules = [RepeatedModule::class])
interface ComponentRoot {
val componentOne: ComponentOne
// Compile Error
// As a repeated module clash with what the parent provided
fun componentTwo(repeatedModule: RepeatedModule): ComponentTwo
val componentThreeBuilder: ComponentThree.Builder
}
@Subcomponent(modules = [RepeatedModule::class])
interface ComponentOne
@Subcomponent(modules = [RepeatedModule::class])
interface ComponentTwo
@Subcomponent(modules = [RepeatedModule::class])
interface ComponentThree {
@Subcomponent.Builder
interface Builder {
fun repeatedModule(repeatedModule: RepeatedModule): Builder
fun build(): ComponentThree
}
}
fun main() {
val componentRoot = DaggerComponentRoot.create()
componentRoot.componentThreeBuilder
// UnsupportedOperationException!
// As a repeated module clash with what the parent provided
.repeatedModule(RepeatedModule())
.build()
componentRoot.componentOne
}
Hope this provided a clearer understanding of what Subcomponent is and how it function.
The working code (in Kotlin) is available below
Thanks for reading. You could check out my other topics here.
Follow me on medium, Twitter, Facebook or Reddit for little tips and learning on mobile development etc related topics. ~Elye~