欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

动手造*:实现一个简单的依赖注入(二) --- 服务注册优化

程序员文章站 2022-06-14 19:09:46
之前实现的那版依赖注入框架基本可用,但是感觉还是不够灵活,而且注册服务和解析服务在同一个地方感觉有点别扭,有点职责分离不够。于是借鉴 Autofac 的做法,增加了一个 `ServiceContainerBuilder` 来负责注册服务,`ServiceContainer`负责解析服务,并且增加了一... ......

动手造*:实现一个简单的依赖注入(二) --- 服务注册优化

intro

之前实现的那版依赖注入框架基本可用,但是感觉还是不够灵活,而且注册服务和解析服务在同一个地方感觉有点别扭,有点职责分离不够。于是借鉴 autofac 的做法,增加了一个 servicecontainerbuilder 来负责注册服务,servicecontainer负责解析服务,并且增加了一个 servicecontainermodule 可以支持像 autofac 中 module/registerassemblymodules 一样注册服务

实现代码

servicecontainerbuilder

增加 servicecontainerbuild 来专门负责注册服务,原来注册服务的那些扩展方法则从 iservicecontainer 的扩展方法变成 iservicecontainerbuilder 的扩展

public interface iservicecontainerbuilder
{
    iservicecontainerbuilder add(servicedefinition item);

    iservicecontainerbuilder tryadd(servicedefinition item);

    iservicecontainer build();
}

public class servicecontainerbuilder : iservicecontainerbuilder
{
    private readonly list<servicedefinition> _services = new list<servicedefinition>();

    public iservicecontainerbuilder add(servicedefinition item)
    {
        if (_services.any(_ => _.servicetype == item.servicetype && _.getimplementtype() == item.getimplementtype()))
        {
            return this;
        }

        _services.add(item);
        return this;
    }

    public iservicecontainerbuilder tryadd(servicedefinition item)
    {
        if (_services.any(_ => _.servicetype == item.servicetype))
        {
            return this;
        }
        _services.add(item);
        return this;
    }

    public iservicecontainer build() => new servicecontainer(_services);
}

iservicecontainer

增加 servicecontainerbuilder 之后就不再支持注册服务了,servicecontainer 这个类型也可以变成一个内部类了,不必再对外暴露

public interface iservicecontainer : iscope, iserviceprovider
{
    iservicecontainer createscope();
}

internal class servicecontainer : iservicecontainer
{
    private readonly ireadonlylist<servicedefinition> _services;
    
    public servicecontainer(ireadonlylist<servicedefinition> servicedefinitions)
    {
        _services = servicedefinitions;
        // ...
    }
    
    // 此处约省略一万行代码 ...
}

servicecontainermodule

定义了一个 servicecontainermodule 来实现像 autofac 那样,在某一个程序集内定义一个 module 注册程序集内需要注册的服务,在服务注册的地方调用 registerassemblymodules 来扫描所有程序集并注册自定义 servicecontainermodule 需要注册的服务

public interface iservicecontainermodule
{
    void configureservices(iservicecontainerbuilder servicecontainerbuilder);
}

public abstract class servicecontainermodule : iservicecontainermodule
{
    public abstract void configureservices(iservicecontainerbuilder servicecontainerbuilder);
}

自定义 servicecontainermodule 使用示例:

public class testservicecontainermodule : servicecontainermodule
{
    public override void configureservices(iservicecontainerbuilder servicecontainerbuilder)
    {
        servicecontainerbuilder.addsingleton<iidgenerator>(guididgenerator.instance);
    }
}

registerassemblymodules 扩展方法实现如下:

public static iservicecontainerbuilder registerassemblymodules(
    [notnull] this iservicecontainerbuilder servicecontainerbuilder, params assembly[] assemblies)
{
    #if net45
        // 解决 asp.net 在 iis 下应用程序域被回收的问题
        // https://autofac.readthedocs.io/en/latest/register/scanning.html#iis-hosted-web-applications
        if (null == assemblies || assemblies.length == 0)
        {
            if (system.web.hosting.hostingenvironment.ishosted)
            {
                assemblies = system.web.compilation.buildmanager.getreferencedassemblies()
                    .cast<assembly>().toarray();
            }
        }
    #endif

        if (null == assemblies || assemblies.length == 0)
        {
            assemblies = appdomain.currentdomain.getassemblies();
        }

    foreach (var type in assemblies.wherenotnull().selectmany(ass => ass.gettypes())
             .where(t => t.isclass && !t.isabstract && typeof(iservicecontainermodule).isassignablefrom(t))
            )
    {
        try
        {
            if (activator.createinstance(type) is servicecontainermodule module)
            {
                module.configureservices(servicecontainerbuilder);
            }
        }
        catch (exception e)
        {
            console.writeline(e);
        }
    }
    return servicecontainerbuilder;
}

使用示例

使用起来除了注册服务变化了之外,别的地方并没有什么不同,看一下单元测试代码

public class dependencyinjectiontest : idisposable
{
    private readonly iservicecontainer _container;

    public dependencyinjectiontest()
    {
        var containerbuilder = new servicecontainerbuilder();
        containerbuilder.addsingleton<iconfiguration>(new configurationbuilder().build());
        containerbuilder.addscoped<ifly, monkeyking>();
        containerbuilder.addscoped<ifly, superman>();

        containerbuilder.addscoped<hasdependencytest>();
        containerbuilder.addscoped<hasdependencytest1>();
        containerbuilder.addscoped<hasdependencytest2>();
        containerbuilder.addscoped<hasdependencytest3>();
        containerbuilder.addscoped(typeof(hasdependencytest4<>));

        containerbuilder.addtransient<wukong>();
        containerbuilder.addscoped<wujing>(serviceprovider => new wujing());
        containerbuilder.addsingleton(typeof(genericservicetest<>));
        
        containerbuilder.registerassemblymodules();

        _container = containerbuilder.build();
    }

    [fact]
    public void test()
    {
        var rootconfig = _container.resolveservice<iconfiguration>();

        assert.throws<invalidoperationexception>(() => _container.resolveservice<ifly>());
        assert.throws<invalidoperationexception>(() => _container.resolverequiredservice<idependencyresolver>());

        using (var scope = _container.createscope())
        {
            var config = scope.resolveservice<iconfiguration>();

            assert.equal(rootconfig, config);

            var fly1 = scope.resolverequiredservice<ifly>();
            var fly2 = scope.resolverequiredservice<ifly>();
            assert.equal(fly1, fly2);

            var wukong1 = scope.resolverequiredservice<wukong>();
            var wukong2 = scope.resolverequiredservice<wukong>();

            assert.notequal(wukong1, wukong2);

            var wujing1 = scope.resolverequiredservice<wujing>();
            var wujing2 = scope.resolverequiredservice<wujing>();

            assert.equal(wujing1, wujing2);

            var s0 = scope.resolverequiredservice<hasdependencytest>();
            s0.test();
            assert.equal(s0._fly, fly1);

            var s1 = scope.resolverequiredservice<hasdependencytest1>();
            s1.test();

            var s2 = scope.resolverequiredservice<hasdependencytest2>();
            s2.test();

            var s3 = scope.resolverequiredservice<hasdependencytest3>();
            s3.test();

            var s4 = scope.resolverequiredservice<hasdependencytest4<string>>();
            s4.test();

            using (var innerscope = scope.createscope())
            {
                var config2 = innerscope.resolverequiredservice<iconfiguration>();
                assert.true(rootconfig == config2);

                var fly3 = innerscope.resolverequiredservice<ifly>();
                fly3.fly();

                assert.notequal(fly1, fly3);
            }

            var flysvcs = scope.resolveservices<ifly>();
            foreach (var f in flysvcs)
                f.fly();
        }

        var genericservice1 = _container.resolverequiredservice<genericservicetest<int>>();
        genericservice1.test();

        var genericservice2 = _container.resolverequiredservice<genericservicetest<string>>();
        genericservice2.test();
    }

    public void dispose()
    {
        _container.dispose();
    }
}

reference