package lifecycle import ( "context" "fmt" "reflect" "sync" ) // Container provides dependency injection functionality type Container struct { services map[reflect.Type]*ServiceDescriptor instances map[reflect.Type]interface{} namedServices map[string]*ServiceDescriptor namedInstances map[string]interface{} singletons map[reflect.Type]interface{} factories map[reflect.Type]FactoryFunc interceptors []Interceptor config ContainerConfig mu sync.RWMutex parent *Container scoped map[string]*Container } // ServiceDescriptor describes how a service should be instantiated type ServiceDescriptor struct { ServiceType reflect.Type Implementation reflect.Type Lifetime ServiceLifetime Factory FactoryFunc Instance interface{} Name string Dependencies []reflect.Type Tags []string Metadata map[string]interface{} Interceptors []Interceptor } // ServiceLifetime defines the lifetime of a service type ServiceLifetime string const ( Transient ServiceLifetime = "transient" // New instance every time Singleton ServiceLifetime = "singleton" // Single instance for container lifetime Scoped ServiceLifetime = "scoped" // Single instance per scope ) // FactoryFunc creates service instances type FactoryFunc func(container *Container) (interface{}, error) // Interceptor can intercept service creation and method calls type Interceptor interface { Intercept(ctx context.Context, target interface{}, method string, args []interface{}) (interface{}, error) } // ContainerConfig configures the container behavior type ContainerConfig struct { EnableReflection bool `json:"enable_reflection"` EnableCircularDetection bool `json:"enable_circular_detection"` EnableInterception bool `json:"enable_interception"` EnableValidation bool `json:"enable_validation"` MaxDepth int `json:"max_depth"` CacheInstances bool `json:"cache_instances"` } // ServiceBuilder provides a fluent interface for service registration type ServiceBuilder struct { container *Container serviceType reflect.Type implType reflect.Type lifetime ServiceLifetime factory FactoryFunc instance interface{} name string tags []string metadata map[string]interface{} interceptors []Interceptor } // NewContainer creates a new dependency injection container func NewContainer(config ContainerConfig) *Container { container := &Container{ services: make(map[reflect.Type]*ServiceDescriptor), instances: make(map[reflect.Type]interface{}), namedServices: make(map[string]*ServiceDescriptor), namedInstances: make(map[string]interface{}), singletons: make(map[reflect.Type]interface{}), factories: make(map[reflect.Type]FactoryFunc), interceptors: make([]Interceptor, 0), config: config, scoped: make(map[string]*Container), } // Set default configuration if container.config.MaxDepth == 0 { container.config.MaxDepth = 10 } return container } // Register registers a service type with its implementation func (c *Container) Register(serviceType, implementationType interface{}) *ServiceBuilder { c.mu.Lock() defer c.mu.Unlock() sType := reflect.TypeOf(serviceType) if sType.Kind() == reflect.Ptr { sType = sType.Elem() } if sType.Kind() == reflect.Interface { sType = reflect.TypeOf(serviceType).Elem() } implType := reflect.TypeOf(implementationType) if implType.Kind() == reflect.Ptr { implType = implType.Elem() } return &ServiceBuilder{ container: c, serviceType: sType, implType: implType, lifetime: Transient, tags: make([]string, 0), metadata: make(map[string]interface{}), interceptors: make([]Interceptor, 0), } } // RegisterInstance registers a specific instance func (c *Container) RegisterInstance(serviceType interface{}, instance interface{}) *ServiceBuilder { c.mu.Lock() defer c.mu.Unlock() sType := reflect.TypeOf(serviceType) if sType.Kind() == reflect.Ptr { sType = sType.Elem() } if sType.Kind() == reflect.Interface { sType = reflect.TypeOf(serviceType).Elem() } return &ServiceBuilder{ container: c, serviceType: sType, instance: instance, lifetime: Singleton, tags: make([]string, 0), metadata: make(map[string]interface{}), interceptors: make([]Interceptor, 0), } } // RegisterFactory registers a factory function for creating instances func (c *Container) RegisterFactory(serviceType interface{}, factory FactoryFunc) *ServiceBuilder { c.mu.Lock() defer c.mu.Unlock() sType := reflect.TypeOf(serviceType) if sType.Kind() == reflect.Ptr { sType = sType.Elem() } if sType.Kind() == reflect.Interface { sType = reflect.TypeOf(serviceType).Elem() } return &ServiceBuilder{ container: c, serviceType: sType, factory: factory, lifetime: Transient, tags: make([]string, 0), metadata: make(map[string]interface{}), interceptors: make([]Interceptor, 0), } } // Resolve resolves a service instance by type func (c *Container) Resolve(serviceType interface{}) (interface{}, error) { sType := reflect.TypeOf(serviceType) if sType.Kind() == reflect.Ptr { sType = sType.Elem() } if sType.Kind() == reflect.Interface { sType = reflect.TypeOf(serviceType).Elem() } return c.resolveType(sType, make(map[reflect.Type]bool), 0) } // ResolveNamed resolves a named service instance func (c *Container) ResolveNamed(name string) (interface{}, error) { c.mu.RLock() defer c.mu.RUnlock() // Check if instance already exists if instance, exists := c.namedInstances[name]; exists { return instance, nil } // Get service descriptor descriptor, exists := c.namedServices[name] if !exists { return nil, fmt.Errorf("named service not found: %s", name) } return c.createInstance(descriptor, make(map[reflect.Type]bool), 0) } // ResolveAll resolves all services with a specific tag func (c *Container) ResolveAll(tag string) ([]interface{}, error) { c.mu.RLock() defer c.mu.RUnlock() var instances []interface{} for _, descriptor := range c.services { for _, serviceTag := range descriptor.Tags { if serviceTag == tag { instance, err := c.createInstance(descriptor, make(map[reflect.Type]bool), 0) if err != nil { return nil, fmt.Errorf("failed to resolve service with tag %s: %w", tag, err) } instances = append(instances, instance) break } } } return instances, nil } // TryResolve attempts to resolve a service, returning nil if not found func (c *Container) TryResolve(serviceType interface{}) interface{} { instance, err := c.Resolve(serviceType) if err != nil { return nil } return instance } // IsRegistered checks if a service type is registered func (c *Container) IsRegistered(serviceType interface{}) bool { sType := reflect.TypeOf(serviceType) if sType.Kind() == reflect.Ptr { sType = sType.Elem() } if sType.Kind() == reflect.Interface { sType = reflect.TypeOf(serviceType).Elem() } c.mu.RLock() defer c.mu.RUnlock() _, exists := c.services[sType] return exists } // CreateScope creates a new scoped container func (c *Container) CreateScope(name string) *Container { c.mu.Lock() defer c.mu.Unlock() scope := &Container{ services: make(map[reflect.Type]*ServiceDescriptor), instances: make(map[reflect.Type]interface{}), namedServices: make(map[string]*ServiceDescriptor), namedInstances: make(map[string]interface{}), singletons: make(map[reflect.Type]interface{}), factories: make(map[reflect.Type]FactoryFunc), interceptors: make([]Interceptor, 0), config: c.config, parent: c, scoped: make(map[string]*Container), } c.scoped[name] = scope return scope } // GetScope retrieves a named scope func (c *Container) GetScope(name string) (*Container, bool) { c.mu.RLock() defer c.mu.RUnlock() scope, exists := c.scoped[name] return scope, exists } // AddInterceptor adds a global interceptor func (c *Container) AddInterceptor(interceptor Interceptor) { c.mu.Lock() defer c.mu.Unlock() c.interceptors = append(c.interceptors, interceptor) } // GetRegistrations returns all service registrations func (c *Container) GetRegistrations() map[reflect.Type]*ServiceDescriptor { c.mu.RLock() defer c.mu.RUnlock() registrations := make(map[reflect.Type]*ServiceDescriptor) for t, desc := range c.services { registrations[t] = desc } return registrations } // Validate validates all service registrations func (c *Container) Validate() error { c.mu.RLock() defer c.mu.RUnlock() if !c.config.EnableValidation { return nil } for serviceType, descriptor := range c.services { if err := c.validateDescriptor(serviceType, descriptor); err != nil { return fmt.Errorf("validation failed for service %s: %w", serviceType.String(), err) } } return nil } // Dispose cleans up the container and all instances func (c *Container) Dispose() error { c.mu.Lock() defer c.mu.Unlock() // Dispose all scoped containers for _, scope := range c.scoped { scope.Dispose() } // Clear all maps c.services = make(map[reflect.Type]*ServiceDescriptor) c.instances = make(map[reflect.Type]interface{}) c.namedServices = make(map[string]*ServiceDescriptor) c.namedInstances = make(map[string]interface{}) c.singletons = make(map[reflect.Type]interface{}) c.factories = make(map[reflect.Type]FactoryFunc) c.scoped = make(map[string]*Container) return nil } // Private methods func (c *Container) resolveType(serviceType reflect.Type, resolving map[reflect.Type]bool, depth int) (interface{}, error) { if depth > c.config.MaxDepth { return nil, fmt.Errorf("maximum resolution depth exceeded for type %s", serviceType.String()) } // Check for circular dependencies if c.config.EnableCircularDetection && resolving[serviceType] { return nil, fmt.Errorf("circular dependency detected for type %s", serviceType.String()) } c.mu.RLock() defer c.mu.RUnlock() // Check if singleton instance exists if instance, exists := c.singletons[serviceType]; exists { return instance, nil } // Check if cached instance exists if c.config.CacheInstances { if instance, exists := c.instances[serviceType]; exists { return instance, nil } } // Get service descriptor descriptor, exists := c.services[serviceType] if !exists { // Try parent container if c.parent != nil { return c.parent.resolveType(serviceType, resolving, depth+1) } return nil, fmt.Errorf("service not registered: %s", serviceType.String()) } resolving[serviceType] = true defer delete(resolving, serviceType) return c.createInstance(descriptor, resolving, depth+1) } func (c *Container) createInstance(descriptor *ServiceDescriptor, resolving map[reflect.Type]bool, depth int) (interface{}, error) { // Use existing instance if available if descriptor.Instance != nil { return descriptor.Instance, nil } // Use factory if available if descriptor.Factory != nil { instance, err := descriptor.Factory(c) if err != nil { return nil, fmt.Errorf("factory failed for %s: %w", descriptor.ServiceType.String(), err) } if descriptor.Lifetime == Singleton { c.singletons[descriptor.ServiceType] = instance } return c.applyInterceptors(instance, descriptor) } // Create instance using reflection if descriptor.Implementation == nil { return nil, fmt.Errorf("no implementation or factory provided for %s", descriptor.ServiceType.String()) } instance, err := c.createInstanceByReflection(descriptor, resolving, depth) if err != nil { return nil, err } // Store singleton if descriptor.Lifetime == Singleton { c.singletons[descriptor.ServiceType] = instance } return c.applyInterceptors(instance, descriptor) } func (c *Container) createInstanceByReflection(descriptor *ServiceDescriptor, resolving map[reflect.Type]bool, depth int) (interface{}, error) { if !c.config.EnableReflection { return nil, fmt.Errorf("reflection is disabled") } implType := descriptor.Implementation if implType.Kind() == reflect.Ptr { implType = implType.Elem() } // Find constructor (assumes first constructor or struct creation) var constructorFunc reflect.Value // Look for constructor function _ = "New" + implType.Name() // constructorName not used yet if implType.PkgPath() != "" { // Try to find package-level constructor // This is simplified - in a real implementation you'd use build tags or reflection // to find the actual constructor functions } // Create instance if constructorFunc.IsValid() { // Use constructor function return c.callConstructor(constructorFunc, resolving, depth) } else { // Create struct directly return c.createStruct(implType, resolving, depth) } } func (c *Container) createStruct(structType reflect.Type, resolving map[reflect.Type]bool, depth int) (interface{}, error) { // Create new instance instance := reflect.New(structType) elem := instance.Elem() // Inject dependencies into fields for i := 0; i < elem.NumField(); i++ { field := elem.Field(i) fieldType := elem.Type().Field(i) // Check for dependency injection tags if tag := fieldType.Tag.Get("inject"); tag != "" { if !field.CanSet() { continue } var dependency interface{} var err error if tag == "true" || tag == "" { // Inject by type dependency, err = c.resolveType(field.Type(), resolving, depth) } else { // Inject by name dependency, err = c.ResolveNamed(tag) } if err != nil { // Check if injection is optional if optionalTag := fieldType.Tag.Get("optional"); optionalTag == "true" { continue } return nil, fmt.Errorf("failed to inject dependency for field %s: %w", fieldType.Name, err) } field.Set(reflect.ValueOf(dependency)) } } return instance.Interface(), nil } func (c *Container) callConstructor(constructor reflect.Value, resolving map[reflect.Type]bool, depth int) (interface{}, error) { constructorType := constructor.Type() args := make([]reflect.Value, constructorType.NumIn()) // Resolve constructor arguments for i := 0; i < constructorType.NumIn(); i++ { argType := constructorType.In(i) arg, err := c.resolveType(argType, resolving, depth) if err != nil { return nil, fmt.Errorf("failed to resolve constructor argument %d (%s): %w", i, argType.String(), err) } args[i] = reflect.ValueOf(arg) } // Call constructor results := constructor.Call(args) if len(results) == 0 { return nil, fmt.Errorf("constructor returned no values") } instance := results[0].Interface() // Check for error result if len(results) > 1 && !results[1].IsNil() { if err, ok := results[1].Interface().(error); ok { return nil, fmt.Errorf("constructor error: %w", err) } } return instance, nil } func (c *Container) applyInterceptors(instance interface{}, descriptor *ServiceDescriptor) (interface{}, error) { if !c.config.EnableInterception { return instance, nil } // Apply service-specific interceptors for _, interceptor := range descriptor.Interceptors { // Apply interceptor (simplified - real implementation would create proxies) _ = interceptor } // Apply global interceptors for _, interceptor := range c.interceptors { // Apply interceptor (simplified - real implementation would create proxies) _ = interceptor } return instance, nil } func (c *Container) validateDescriptor(serviceType reflect.Type, descriptor *ServiceDescriptor) error { // Validate that implementation implements the service interface if descriptor.Implementation != nil { if serviceType.Kind() == reflect.Interface { if !descriptor.Implementation.Implements(serviceType) { return fmt.Errorf("implementation %s does not implement interface %s", descriptor.Implementation.String(), serviceType.String()) } } } // Validate dependencies for _, depType := range descriptor.Dependencies { if !c.IsRegistered(depType) && (c.parent == nil || !c.parent.IsRegistered(depType)) { return fmt.Errorf("dependency %s is not registered", depType.String()) } } return nil } // ServiceBuilder methods // AsSingleton sets the service lifetime to singleton func (sb *ServiceBuilder) AsSingleton() *ServiceBuilder { sb.lifetime = Singleton return sb } // AsTransient sets the service lifetime to transient func (sb *ServiceBuilder) AsTransient() *ServiceBuilder { sb.lifetime = Transient return sb } // AsScoped sets the service lifetime to scoped func (sb *ServiceBuilder) AsScoped() *ServiceBuilder { sb.lifetime = Scoped return sb } // WithName sets a name for the service func (sb *ServiceBuilder) WithName(name string) *ServiceBuilder { sb.name = name return sb } // WithTag adds a tag to the service func (sb *ServiceBuilder) WithTag(tag string) *ServiceBuilder { sb.tags = append(sb.tags, tag) return sb } // WithMetadata adds metadata to the service func (sb *ServiceBuilder) WithMetadata(key string, value interface{}) *ServiceBuilder { sb.metadata[key] = value return sb } // WithInterceptor adds an interceptor to the service func (sb *ServiceBuilder) WithInterceptor(interceptor Interceptor) *ServiceBuilder { sb.interceptors = append(sb.interceptors, interceptor) return sb } // Build finalizes the service registration func (sb *ServiceBuilder) Build() error { sb.container.mu.Lock() defer sb.container.mu.Unlock() descriptor := &ServiceDescriptor{ ServiceType: sb.serviceType, Implementation: sb.implType, Lifetime: sb.lifetime, Factory: sb.factory, Instance: sb.instance, Name: sb.name, Tags: sb.tags, Metadata: sb.metadata, Interceptors: sb.interceptors, } // Store by type sb.container.services[sb.serviceType] = descriptor // Store by name if provided if sb.name != "" { sb.container.namedServices[sb.name] = descriptor } return nil } // DefaultInterceptor provides basic interception functionality type DefaultInterceptor struct { name string } func NewDefaultInterceptor(name string) *DefaultInterceptor { return &DefaultInterceptor{name: name} } func (di *DefaultInterceptor) Intercept(ctx context.Context, target interface{}, method string, args []interface{}) (interface{}, error) { // Basic interception - could add logging, metrics, etc. return nil, nil }