源码解读——Resolver

原文链接

image

Resolver 是一个采用 Swift 编写的轻量级依赖注入/服务定位框架。关于依赖注入和服务定位的概念,可以阅读 《控制反转、依赖注入、服务定位》 一文。本文源码解读的 Resolver 版本是 1.5.0。

基本原理

Resolver 是一个依赖注入/服务定位框架,但是它的核心逻辑主要还是服务定位的典型设计实现。服务定位的设计通常包括两个主要步骤:

  • 服务注册(Service Register)
  • 服务解析(Service Resolve)

服务注册通常是注册一个工厂方法,工厂方法能够根据相关参数可以实例化一个服务对象。由于允许注册多个服务,因此会有一个容器(Container)来存储这些工厂方法。通常情况下,会使用一个哈希表存储这些工厂方法,其中以服务的名称为键,以对应的工厂方法为值。这里我们暂且称这个哈希表为 注册项哈希表。

服务解析是指根据服务的名称在容器中查找对应的工厂方法,执行并返回一个服务对象的过程。通常情况下,会根据不同的需求采用不同的解析策略。比如:在某些场景下,每次都调用工厂方法初始化一个服务对象;在某些场景下,对于同一种服务只初始化一次,后续重复使用该服务对象。对于后一种情况,一般会使用另一个哈希表存储这些服务对象,其中以服务的名称为键,以对应的服务对象为值。这里我们暂且称这个哈希表为 缓存哈希表。

整体架构

在了解了服务定位的设计原理之后,我们再来看看 Resolver 的设计实现。下图所示为 Resolver 的类图,其中设计的核心在于红色虚线框中的部分,这里主要涉及到 3 个类:Resolver、ResolverRegistration、ResolverScope。这 3 个类基本覆盖了 Resolver 的主体逻辑,下面我们依次进行介绍。

image

ResolverRegistration

ResolverRegistration 从命名上看就知道是用来表示一个 注册项。如下所示,是 ResolverRegistration 的定义。

public class ResolverRegistration<Service>: ResolverOptions<Service> {
    public var key: Int
    public var cacheKey: String

    public init(resolver: Resolver, key: Int, name: Resolver.Name?) {
        self.key = key
        if let namedService = name {
            self.cacheKey = String(key) + ":" + namedService.rawValue
        } else {
            self.cacheKey = String(key)
        }
        super.init(resolver: resolver)
    }

    public func resolve(resolver: Resolver, args: Any?) -> Service? {
        fatalError("abstract function")
    }
}

按照基本原理一节中的介绍,ResolverRegistration 按道理应该会定义工厂方法,但是这里我们只看到了关于 key 和 cacacheKey 的定义,这是什么情况?事实上,我们从 resolve(resolver: Resolver, args: Any?) -> Service? 方法的定义可以确定 ResolverRegistration 是一个抽象类。因此,我们需要再来看看它的子类。

ResolverRegistration 定义了三个子类,分别是:

  • ResolverRegistrationOnly
  • ResolverRegistrationResolver
  • ResolverRegistrationArgumentsN

这三个子类的定义分别如下所示,可以发现,子类中定义了工厂方法。

/// ResolverRegistration stores a service definition and its factory closure.
public final class ResolverRegistrationOnly<Service>: ResolverRegistration<Service> {

    public var factory: ResolverFactory<Service>

    public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactory<Service>) {
        self.factory = factory
        super.init(resolver: resolver, key: key, name: name)
    }

    public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
        guard let service = factory() else {
            return nil
        }
        mutate(service, resolver: resolver, args: args)
        return service
    }
}

/// ResolverRegistrationResolver stores a service definition and its factory closure.
public final class ResolverRegistrationResolver<Service>: ResolverRegistration<Service> {

    public var factory: ResolverFactoryResolver<Service>

    public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactoryResolver<Service>) {
        self.factory = factory
        super.init(resolver: resolver, key: key, name: name)
    }

    public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
        guard let service = factory(resolver) else {
            return nil
        }
        mutate(service, resolver: resolver, args: args)
        return service
    }
}

/// ResolverRegistrationArguments stores a service definition and its factory closure.
public final class ResolverRegistrationArgumentsN<Service>: ResolverRegistration<Service> {

    public var factory: ResolverFactoryArgumentsN<Service>

    public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactoryArgumentsN<Service>) {
        self.factory = factory
        super.init(resolver: resolver, key: key, name: name)
    }

    public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
        guard let service = factory(resolver, Resolver.Args(args)) else {
            return nil
        }
        mutate(service, resolver: resolver, args: args)
        return service
    }
}

此外,我们还发现,这三个类的内部逻辑几乎一模一样,唯一的区别在于工厂方法的类型不一样,分别是:

  • ResolverFactory<Service>
  • ResolverFactoryResolver<Service>
  • ResolverFactoryArgumentsN<Service>

这三种工厂的定义分别如下,可以看出它们都是用于初始化服务的工厂方法,区别在于服务的初始化入参不同,有些需要参数,有些不需要,有些甚至连 Resolver 都不需要。

public typealias ResolverFactory<Service> = () -> Service?
public typealias ResolverFactoryResolver<Service> = (_ resolver: Resolver) -> Service?
public typealias ResolverFactoryArgumentsN<Service> = (_ resolver: Resolver, _ args: Resolver.Args) -> Service?

从这三个类对于 resolve 的实现来看可以发现,它们才是服务初始化的真正执行者。

看完 ResolverRegistration 的子类后,我们再来看它的父类 ResolverOptions,具体如下所示:

public class ResolverOptions<Service> {

    // MARK: - Parameters

    public var scope: ResolverScope

    fileprivate var mutator: ResolverFactoryMutator<Service>?
    fileprivate var mutatorWithArgumentsN: ResolverFactoryMutatorArgumentsN<Service>?
    fileprivate weak var resolver: Resolver?

    // MARK: - Lifecycle

    public init(resolver: Resolver) {
        self.resolver = resolver
        self.scope = Resolver.defaultScope
    }
    
    @discardableResult
    public final func implements<Protocol>(_ type: Protocol.Type, name: Resolver.Name? = nil) -> ResolverOptions<Service> {
        resolver?.register(type.self, name: name) { r, _ in r.resolve(Service.self) as? Protocol }
        return self
    }
    
    @discardableResult
    public final func resolveProperties(_ block: @escaping ResolverFactoryMutator<Service>) -> ResolverOptions<Service> {
        mutator = block
        return self
    }
    
    @discardableResult
    public final func resolveProperties(_ block: @escaping ResolverFactoryMutatorArgumentsN<Service>) -> ResolverOptions<Service> {
        mutatorWithArgumentsN = block
        return self
    }
    
    @discardableResult
    public final func scope(_ scope: ResolverScope) -> ResolverOptions<Service> {
        self.scope = scope
        return self
    }
    
    fileprivate func mutate(_ service: Service, resolver: Resolver, args: Any?) {
        self.mutator?(resolver, service)
        if let mutatorWithArgumentsN = mutatorWithArgumentsN {
            mutatorWithArgumentsN(resolver, service, Resolver.Args(args))
        }
    }
}

从 ResolverOptions 的属性上看,在结合 ResolverRegistration 及其子类的定义,我们可以知道一个注册项的定义大致包含如下这些内容:

  • factory: 工厂方法,用于初始化服务对象,可以有三种工厂方法。
  • key:用于服务注册的注册项哈希表中的键。
  • cacheKey:用于服务解析的缓存哈希表中的键。
  • resolver:Resolver 类型,它是服务注册的核心实现,内部维护一个注册项哈希表,同时也是与用户打交道的类,可以认为是 Service Locator。
  • scope:ResolverScope 类型,它是服务解析的核心实现,内部维护一个缓存哈希表。
  • mutator: ResolverFactoryMutator<Service>? 类型,允许用户对初始化后的服务对象进行额外的修改。
  • mutator:ResolverFactoryMutatorArgumentsN<Service>? 类型,允许用户使用参数对初始化后的服务对象进行额外的修改。
image

ResolverScope

在上一节中,我们提到了 ResolverScope 主要负责服务解析,此外内部维护了一个缓存哈希表。下面,我们来深入分析一下,如下所示为 ResolverScope 的定义。

public class ResolverScope: ResolverScopeType {

    // Moved definitions to ResolverScope to allow for dot notation access

    /// All application scoped services exist for lifetime of the app. (e.g Singletons)
    public static let application = ResolverScopeCache()
    /// Cached services exist for lifetime of the app or until their cache is reset.
    public static let cached = ResolverScopeCache()
    /// Graph services are initialized once and only once during a given resolution cycle. This is the default scope.
    public static let graph = ResolverScopeGraph()
    /// Shared services persist while strong references to them exist. They're then deallocated until the next resolve.
    public static let shared = ResolverScopeShare()
    /// Unique services are created and initialized each and every time they're resolved.
    public static let unique = ResolverScopeUnique()

    // abstract base for class never called
    public func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
        fatalError("abstract")
    }
}

从 resolve 的实现来看,我们也能够确认 ResolverScope 是一个抽象类。除此之外,它还定义了几个静态变量。这几个静态变量的类型都是 ResolverScope 的子类,而且类型有所不同。

下面,我们来看看它的子类。

ResolverScopeCache

ResolverScopeCache 的定义如下,其内部维护了一个缓存哈希表 cacahedServices。另外,它定义了一套自己的服务解析策略,从 resolve 方法可以看出大致的策略如下:

  1. 根据注册项的 cacheKey 查找服务对象,如果查到,则直接返回对应的服务对象;否则,进入步骤 2
  2. 调用服务初始化的真正执行者 ResolverRegistration 的 resolve 方法进行初始化服务对象。
  3. 如果初始化完成,则加入缓存哈希表进行缓存。
  4. 最后,返回服务对象。

此外,ResolverScopeCache 还提供一个 reset 方法,可以清空缓存哈希表。

/// Cached services exist for lifetime of the app or until their cache is reset.
public class ResolverScopeCache: ResolverScope {

    public override init() {}

    public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
        // 根据注册项的 cachedKey 读取缓存
        if let service = cachedServices[registration.cacheKey] as? Service {
            return service
        }
        // 如果没有缓存,则解析并实例化 sevice,并存入缓存
        let service = registration.resolve(resolver: resolver, args: args)
        if let service = service {
            cachedServices[registration.cacheKey] = service
        }
        return service
    }

    public final func reset() {
        cachedServices.removeAll()
    }

    fileprivate var cachedServices = [String : Any](minimumCapacity: 32)
}

ResolverScopeGraph

ResolverScopeGraph 的定义如下所示,它也持有一个缓存哈希表,其服务解析策略与 ResolverScopeCache 大同小异,区别在于:它在一次解析周期中对于相同的服务只解析一次,当一次解析周期完毕后,自动清空缓存哈希表。

/// Graph services are initialized once and only once during a given resolution cycle. This is the default scope.
public final class ResolverScopeGraph: ResolverScope {

    public override init() {}

    public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
        // 根据注册项的 cachedKey 读取缓存
        if let service = graph[registration.cacheKey] as? Service {
            return service
        }
        // 如果没有缓存,则解析并实例化 sevice,并存入缓存
        resolutionDepth = resolutionDepth + 1
        let service = registration.resolve(resolver: resolver, args: args)
        resolutionDepth = resolutionDepth - 1
        if resolutionDepth == 0 {
            graph.removeAll()
        } else if let service = service, type(of: service as Any) is AnyClass {
            graph[registration.cacheKey] = service
        }
        return service
    }

    private var graph = [String : Any?](minimumCapacity: 32)
    private var resolutionDepth: Int = 0
}

ResolverScopeShare

ResolverScopeShare 的定义如下所示,其内部同样持有一个缓存哈希表,它的解析策略也是优先查找缓存哈希表,如果找不到,则初始化服务对象并存入缓存。不过它通过一个封装类型弱引用了服务对象,从而允许服务对象被共享。

/// Shared services persist while strong references to them exist. They're then deallocated until the next resolve.
public final class ResolverScopeShare: ResolverScope {

    public override init() {}

    public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
        if let service = cachedServices[registration.cacheKey]?.service as? Service {
            return service
        }
        let service = registration.resolve(resolver: resolver, args: args)
        if let service = service, type(of: service as Any) is AnyClass {
            cachedServices[registration.cacheKey] = BoxWeak(service: service as AnyObject)
        }
        return service
    }

    public final func reset() {
        cachedServices.removeAll()
    }

    private struct BoxWeak {
        weak var service: AnyObject?
    }

    private var cachedServices = [String : BoxWeak](minimumCapacity: 32)
}

ResolverScopeUnique

ResolverScopeUnique 的定义如下所示,它内部并没有缓存哈希表,因此每一次进行服务解析时,它都会重新初始化一个服务对象。

/// Unique services are created and initialized each and every time they're resolved.
public final class ResolverScopeUnique: ResolverScope {

    public override init() {}
    public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
        return registration.resolve(resolver: resolver, args: args)
    }

}

ResolverScopeContainer

ResolverScopeContainer 的定义如下所示,它内部没有缓存哈希表,它的解析策略是让 Resolver 的 ResolverScopeCache 来代理执行服务解析。

/// Proxy to container's scope. Cache type depends on type supplied to container (default .cache)
public final class ResolverScopeContainer: ResolverScope {

    public override init() {}

    public override final func resolve<Service>(registration: ResolverRegistration<Service>, resolver: Resolver, args: Any?) -> Service? {
        return resolver.cache.resolve(registration: registration, resolver: resolver, args: args)
    }
}

Resolver

Resolver 是服务注册的核心,也是与用户直接打交道的类,用户通过调用它提供的方法进行注册和解析。

下面,我们来看一下 Resolver 的实现,由于它的代码较多,这里进行了部分删减,具体如下所示。


public final class Resolver {

    // MARK: - Defaults

    /// Default registry used by the static Registration functions.
    public static var main: Resolver = Resolver()
    /// Default registry used by the static Resolution functions and by the Resolving protocol.
    public static var root: Resolver = main
    /// Default scope applied when registering new objects.
    public static var defaultScope: ResolverScope = .graph
    
    // MARK: - Lifecycle

    public init(parent: Resolver? = nil) {
        self.parent = parent
    }
    
    ...
    
    @discardableResult
    public final func register<Service>(_ type: Service.Type = Service.self, name: Resolver.Name? = nil,
                                        factory: @escaping ResolverFactory<Service>) -> ResolverOptions<Service> {
        lock.lock()
        defer { lock.unlock() }
        let key = ObjectIdentifier(Service.self).hashValue
        let registration = ResolverRegistrationOnly(resolver: self, key: key, name: name, factory: factory)
        add(registration: registration, with: key, name: name)
        return registration
    }
    
    ...
    
    public final func resolve<Service>(_ type: Service.Type = Service.self, name: Resolver.Name? = nil, args: Any? = nil) -> Service {
        lock.lock()
        defer { lock.unlock() }
        registrationCheck()
        if let registration = lookup(type, name: name),
            let service = registration.scope.resolve(resolver: self, registration: registration, args: args) {
            return service
        }
        fatalError("RESOLVER: '\(Service.self):\(name?.rawValue ?? "NONAME")' not resolved. To disambiguate optionals use resolver.optional().")
    }
    
    ...
    
    // MARK: - Internal

    /// Internal function searches the current and parent registries for a ResolverRegistration<Service> that matches
    /// the supplied type and name.
    private final func lookup<Service>(_ type: Service.Type, name: Resolver.Name?) -> ResolverRegistration<Service>? {
        let key = ObjectIdentifier(Service.self).hashValue
        let containerName = name?.rawValue ?? NONAME
        if let container = registrations[key], let registration = container[containerName] {
            return registration as? ResolverRegistration<Service>
        }
        if let parent = parent, let registration = parent.lookup(type, name: name) {
            return registration
        }
        return nil
    }

    /// Internal function adds a new registration to the proper container.
    private final func add<Service>(registration: ResolverRegistration<Service>, with key: Int, name: Resolver.Name?) {
        if var container = registrations[key] {
            container[name?.rawValue ?? NONAME] = registration
            registrations[key] = container
        } else {
            registrations[key] = [name?.rawValue ?? NONAME : registration]
        }
    }

    private let NONAME = "*"
    private let parent: Resolver?
    private let lock = Resolver.lock
    private var registrations = [Int : [String : Any]]()
}

从源码中可以看出,Resolver 内部维护了一个注册项哈希表 registrations。

当用户调用 register 方法进行服务注册时,首先会在内部初始化一个注册项( ResolverRegistration 的子类)。然后,调用 add 方法将注册项存入注册项哈希表中。

当用户调用 resolve 方法进行服务解析时,首先会调用 loopup 方法查找注册项哈希表中对应的注册项,然后调用注册项对应的 scope 进行服务解析,本质上服务的初始化最终还是由注册项完成。

在 Resolver 中,默认使用的 ResolverScope 是 ResolverScopeGraph 类型,当然我们也可以在注册时,通过调用注册项的 scope 方法进行切换。

我们仔细阅读 loopup 方法和 init 方法,可以发现,Resolver 可以其实是可以构成一棵 Resolver 树,在查找注册项时,会递归地查找父级 Resolver 中的注册项哈希表。

Resolving 协议

Resolver 为服务注册和服务解析同时实现了两套方法——静态方法和实例方法。

当我们使用静态方法时,对应的 Resolver 对象是一个全局变量 root 或 main,两者其实是同一个 Resolver 对象。

当我们使用实例方法时,首先我们需要初始化一个 Resolver 对象。对此,框架提供了一个 Resolving 协议,并扩展了默认实现,如下所示。这样的话,遵循了 Resolving 协议的类会自动扩展了一个 Resolver 类型的属性,并且执行修改类型。

public protocol Resolving {
    var resolver: Resolver { get }
}

extension Resolving {
    public var resolver: Resolver {
        return Resolver.root
    }
}

注解注入

Resolver 除了完整实现了一套服务定位架构之外,还支持了注解注入。它提供了五种类型的注解,底层使用 propertyWrapper 实现的,分别如下:

  • Injected<Service>
  • OptionalInjected<Service>
  • LazyInjected<Service>
  • WeakLazyInjected<Service>
  • InjectedObject<Service>

使用注解的前提是,服务必须提前进行注册,因为注解在初始化时会立即进行服务解析。不过,LazyInjected<Service> 和 WeakLazyInjected<Service> 并无此要求,对于它们,只需要在调用前进行服务注册即可。

设计模式

Resolver 框架其实采用了 策略模式 进行设计实现的。

当服务注册时,Resolver 依赖的是抽象类 ResolverRegistration,框架提供了三种具体类型的 ResolverRegistration。当已有类型满足不了需求时,我们可以对 Resolver 的 register 方法进行重载,并自定义 ResolverRegistration 子类。

当服务注册时,Resolver 依赖的同样是抽象类 ResolverScope,框架提供两个五种具体类型的 ResolverScope 以提供不同的解析策略。当已有类型满足不了需求时,我们同样可以自定义 ResolverScope 子类实现定制的解析策略。

总结

本文对 Resolver 源码进行了解读,分析其整体框架的设计。Resolver 的核心主要包含三个部分:ResolverRegistration、ResolverScope、Resolver,它们各有各的职责,遵循了职责单一的设计原则,并且实现了一个扩展性很强的策略模式。

我认为 Resolver 非常适合作为重构的一套辅助工具,希望后面能够在实际开发中使用一下,从而能够在实践中提高一下自己的架构设计能力。

参考

  1. Resolver
  2. 控制反转、依赖注入、服务定位
  3. Design Pattern
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容