Dependency Injection¶
After this page, you'll understand how PySpring automatically resolves and injects dependencies — and why it matters.
What is dependency injection?¶
Dependency injection (DI) is a pattern where an object receives its dependencies from an external source, rather than creating them itself.
Without DI, your code looks like this:
The problem: UserService is hardwired to UserRepository. You can't swap it for testing, you can't reuse it with a different repository, and changing UserRepository's constructor breaks UserService.
With PySpring's DI:
from py_spring_core import Component
class UserRepository(Component):
def find_all(self):
return []
class UserService(Component):
user_repository: UserRepository # Injected by PySpring
def get_all_users(self):
return self.user_repository.find_all()
PySpring sees the type hint user_repository: UserRepository, finds the matching component, and injects it. You never call a constructor manually.
How it works¶
During startup, PySpring:
- Scans your project for all
Component,Properties,RestController, andBeanCollectionclasses - Resolves the dependency graph — figuring out which components depend on which
- Instantiates them in the right order
- Injects each dependency by matching type hints to registered instances
- Calls
post_construct()on each component after all dependencies are injected
Note
PySpring resolves dependencies by type, not by name. If you have a field my_service: UserService, PySpring looks for a registered component of type UserService.
Declaring dependencies¶
You declare dependencies as class-level type annotations:
from py_spring_core import Component
class EmailService(Component):
def send(self, to: str, message: str):
print(f"Sending to {to}: {message}")
class NotificationService(Component):
email_service: EmailService # Injected!
def notify(self, user_email: str):
self.email_service.send(user_email, "You have a notification!")
You can inject:
- Components — any class extending
Component - Properties — configuration classes extending
Properties - Beans — objects created by
BeanCollectionmethods
Accessing injected dependencies¶
Use post_construct() to work with injected dependencies — that's when they're guaranteed to be available:
from py_spring_core import Component
class AppStartupService(Component):
email_service: EmailService
notification_service: NotificationService
def post_construct(self):
print("All dependencies are ready!")
print(f"Email service: {self.email_service}")
print(f"Notification service: {self.notification_service}")
Warning
Don't access injected dependencies in __init__(). At that point, injection hasn't happened yet. Always use post_construct().
Recap¶
PySpring's dependency injection system:
- Resolves dependencies automatically from type hints
- Supports Components, Properties, and Beans
- Handles the full dependency graph — including transitive dependencies
- Requires no decorators or manual registration
- Makes your code loosely coupled and testable
Next, let's learn about REST Controllers — how to build APIs with PySpring.