Skip to content

Commit 009d7b6

Browse files
committed
add documentation for lifecycle-aware components with initialization and disposal examples
1 parent 0dc7e68 commit 009d7b6

File tree

1 file changed

+63
-0
lines changed

1 file changed

+63
-0
lines changed

docs/Components.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,69 @@ object MyApp extends Components {
164164
}
165165
```
166166

167+
## Lifecycle-aware components
168+
169+
Many application services have explicit initialization and shutdown phases. Components support this out of the box.
170+
171+
There are four lifecycle traits you can implement on the values produced by your components:
172+
173+
- InitializingComponent — synchronous init hook def init(): Unit
174+
- AsyncInitializingComponent — asynchronous init hook def init()(implicit ec: ExecutionContext): Future[Unit]
175+
- DisposableComponent — synchronous shutdown hook def destroy(): Unit
176+
- AsyncDisposableComponent — asynchronous shutdown hook def destroy()(implicit ec: ExecutionContext): Future[Unit]
177+
178+
How it works:
179+
180+
- When you call component.init, after all dependencies have been initialized, the framework automatically detects
181+
if the created instance implements InitializingComponent or AsyncInitializingComponent and invokes its init hook.
182+
For cached components created with singleton/asyncSingleton the init hook is executed only once and subsequent
183+
init calls return the same instance without re-running the hook.
184+
- When you call component.destroy, components are destroyed in reverse dependency order. If a component’s type
185+
implements DisposableComponent or AsyncDisposableComponent, its destroy hook is called automatically. Independent
186+
components are destroyed in parallel where possible. For cached components, destroy clears the cached instance so
187+
the next init will create a new instance.
188+
- Destroy is a no-op for components that were never initialized in the current process (nothing is created, so
189+
there’s nothing to destroy).
190+
191+
Custom cleanup:
192+
193+
- You can register additional cleanup with destroyWith/asyncDestroyWith to complement lifecycle traits:
194+
195+
```scala
196+
val httpServer: Component[HttpServer] =
197+
component(new HttpServer(db.ref))
198+
.destroyWith(_.close()) // runs in addition to lifecycle destroy hooks if present
199+
```
200+
201+
Example with lifecycle hooks:
202+
203+
```scala
204+
import com.avsystem.commons.di._
205+
import scala.concurrent.{ExecutionContext, Future}
206+
207+
final class Cache extends InitializingComponent with DisposableComponent {
208+
def init(): Unit = println("warming up cache...")
209+
def destroy(): Unit = println("clearing cache...")
210+
}
211+
212+
object MyApp extends Components {
213+
def cache: Component[Cache] = singleton(new Cache)
214+
}
215+
216+
// Somewhere in your main:
217+
import ExecutionContext.Implicits.global
218+
for {
219+
_ <- MyApp.cache.init // prints "warming up cache..." only once
220+
_ <- MyApp.cache.destroy // prints "clearing cache..."
221+
} yield ()
222+
```
223+
224+
Notes:
225+
226+
- Lifecycle init hooks are invoked by Component during init. Lifecycle destroy hooks are wired into the destroy
227+
function by the component macros based on the component’s static type, so you don’t need to call destroyWith
228+
explicitly when using DisposableComponent/AsyncDisposableComponent.
229+
167230
## Complete example
168231

169232
See [ComponentsExample.scala](https://github.com/AVSystem/scala-commons/blob/master/core/jvm/src/test/scala/com/avsystem/commons/di/ComponentsExample.scala)

0 commit comments

Comments
 (0)