zoukankan      html  css  js  c++  java
  • netcore3.0 IServiceCollection 依赖注入系统(二)

    上一文中我们主要介绍了依赖注入的几个接口和重要类ServiceDescriptor,该类是服务的描述,所有的服务都保存在IServiceCollection接口中,也就是我们的服务注册就是在该接口中

    下面介绍下IServiceCollection接口的实现类ServiceCollection

    /// <summary>
        /// Default implementation of <see cref="IServiceCollection"/>.
        /// </summary>
        public class ServiceCollection : IServiceCollection
        {
            private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
    
            /// <inheritdoc />
            public int Count => _descriptors.Count;
    
            /// <inheritdoc />
            public bool IsReadOnly => false;
    
            /// <inheritdoc />
            public ServiceDescriptor this[int index]
            {
                get
                {
                    return _descriptors[index];
                }
                set
                {
                    _descriptors[index] = value;
                }
            }
    
            /// <inheritdoc />
            public void Clear()
            {
                _descriptors.Clear();
            }
    
            /// <inheritdoc />
            public bool Contains(ServiceDescriptor item)
            {
                return _descriptors.Contains(item);
            }
    
            /// <inheritdoc />
            public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
            {
                _descriptors.CopyTo(array, arrayIndex);
            }
    
            /// <inheritdoc />
            public bool Remove(ServiceDescriptor item)
            {
                return _descriptors.Remove(item);
            }
    
            /// <inheritdoc />
            public IEnumerator<ServiceDescriptor> GetEnumerator()
            {
                return _descriptors.GetEnumerator();
            }
    
            void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item)
            {
                _descriptors.Add(item);
            }
    
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
    
            /// <inheritdoc />
            public int IndexOf(ServiceDescriptor item)
            {
                return _descriptors.IndexOf(item);
            }
    
            /// <inheritdoc />
            public void Insert(int index, ServiceDescriptor item)
            {
                _descriptors.Insert(index, item);
            }
    
            /// <inheritdoc />
            public void RemoveAt(int index)
            {
                _descriptors.RemoveAt(index);
            }
        }

    该类内部有List<ServiceDescriptor>类型的字段_descriptors,用来保存我们注入的服务信息

    我们已经把服务注入好了,那怎么从服务容器里面获取对应的服务实现呢,下面详细介绍:

    //
        // 摘要:
        //     Defines a mechanism for retrieving a service object; that is, an object that
        //     provides custom support to other objects.
        public interface IServiceProvider
        {
            //
            // 摘要:
            //     Gets the service object of the specified type.
            //
            // 参数:
            //   serviceType:
            //     An object that specifies the type of service object to get.
            //
            // 返回结果:
            //     A service object of type serviceType. -or- null if there is no service object
            //     of type serviceType.
            object GetService(Type serviceType);
        }

    这里有个重要的接口IServiceProvider,我们的服务解析就是从该接口中获取的

    netcore的服务解析类为:ServiceProvider,

    看下详细代码:

    /// <summary>
        /// The default IServiceProvider.
        /// </summary>
        public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback, IAsyncDisposable
        {
            private readonly IServiceProviderEngine _engine;
    
            private readonly CallSiteValidator _callSiteValidator;
    
            internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
            {
                IServiceProviderEngineCallback callback = null;
                if (options.ValidateScopes)
                {
                    callback = this;
                    _callSiteValidator = new CallSiteValidator();
                }
    
                switch (options.Mode)
                {
                    case ServiceProviderMode.Default:
    #if !NETCOREAPP
                        _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
    #else
                        if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled"))
                        {
                            _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                        }
                        else
                        {
                            // Don't try to compile Expressions/IL if they are going to get interpreted
                            _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                        }
    #endif
                        break;
                    case ServiceProviderMode.Dynamic:
                        _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                        break;
                    case ServiceProviderMode.Runtime:
                        _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                        break;
    #if IL_EMIT
                    case ServiceProviderMode.ILEmit:
                        _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
                        break;
    #endif
                    case ServiceProviderMode.Expressions:
                        _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
                        break;
                    default:
                        throw new NotSupportedException(nameof(options.Mode));
                }
    
                if (options.ValidateOnBuild)
                {
                    List<Exception> exceptions = null;
                    foreach (var serviceDescriptor in serviceDescriptors)
                    {
                        try
                        {
                            _engine.ValidateService(serviceDescriptor);
                        }
                        catch (Exception e)
                        {
                            exceptions = exceptions ?? new List<Exception>();
                            exceptions.Add(e);
                        }
                    }
    
                    if (exceptions != null)
                    {
                        throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());
                    }
                }
            }
    
            /// <summary>
            /// Gets the service object of the specified type.
            /// </summary>
            /// <param name="serviceType">The type of the service to get.</param>
            /// <returns>The service that was produced.</returns>
            public object GetService(Type serviceType) => _engine.GetService(serviceType);
    
            /// <inheritdoc />
            public void Dispose()
            {
                _engine.Dispose();
            }
    
            void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
            {
                _callSiteValidator.ValidateCallSite(callSite);
            }
    
            void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
            {
                _callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
            }
    
            /// <inheritdoc/>
            public ValueTask DisposeAsync()
            {
                return _engine.DisposeAsync();
            }
        }

    ServiceProvider构造函数接收两个参数IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options,

    第一个参数是我们注册的所有服务,第二个参数是一些配置项

    /// <summary>
        /// Options for configuring various behaviors of the default <see cref="IServiceProvider"/> implementation.
        /// </summary>
        public class ServiceProviderOptions
        {
            // Avoid allocating objects in the default case
            internal static readonly ServiceProviderOptions Default = new ServiceProviderOptions();
    
            /// <summary>
            /// <c>true</c> to perform check verifying that scoped services never gets resolved from root provider; otherwise <c>false</c>. Defaults to <c>false</c>.
            /// </summary>
            public bool ValidateScopes { get; set; }
    
            /// <summary>
            /// <c>true</c> to perform check verifying that all services can be created during <code>BuildServiceProvider</code> call; otherwise <c>false</c>. Defaults to <c>false</c>.
            /// NOTE: this check doesn't verify open generics services.
            /// </summary>
            public bool ValidateOnBuild { get; set; }
    
            internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Default;
        }

    先看下构造函数的第一段代码

    if (options.ValidateScopes)
    {
        callback = this;
        _callSiteValidator = new CallSiteValidator();
    }

    CallSiteValidator用来做一些验证操作

    IServiceProviderEngine接口:

     

    ServiceProviderEngine抽象类:

    internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory
        {
            private readonly IServiceProviderEngineCallback _callback;
    
            private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
    
            private bool _disposed;
    
            protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback)
            {
                _createServiceAccessor = CreateServiceAccessor;
                _callback = callback;
                Root = new ServiceProviderEngineScope(this);
                RuntimeResolver = new CallSiteRuntimeResolver();
                CallSiteFactory = new CallSiteFactory(serviceDescriptors);
                CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
                CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());
                RealizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
            }
    
            internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; }
    
            internal CallSiteFactory CallSiteFactory { get; }
    
            protected CallSiteRuntimeResolver RuntimeResolver { get; }
    
            public ServiceProviderEngineScope Root { get; }
    
            public IServiceScope RootScope => Root;
    
            public void ValidateService(ServiceDescriptor descriptor)
            {
                if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType)
                {
                    return;
                }
    
                try
                {
                    var callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain());
                    if (callSite != null)
                    {
                        _callback?.OnCreate(callSite);
                    }
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e);
                }
            }
    
            public object GetService(Type serviceType) => GetService(serviceType, Root);
    
            protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);
    
            public void Dispose()
            {
                _disposed = true;
                Root.Dispose();
            }
    
            public ValueTask DisposeAsync()
            {
                _disposed = true;
                return Root.DisposeAsync();
            }
    
            internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
            {
                if (_disposed)
                {
                    ThrowHelper.ThrowObjectDisposedException();
                }
    
                var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
                _callback?.OnResolve(serviceType, serviceProviderEngineScope);
                DependencyInjectionEventSource.Log.ServiceResolved(serviceType);
                return realizedService.Invoke(serviceProviderEngineScope);
            }
    
            public IServiceScope CreateScope()
            {
                if (_disposed)
                {
                    ThrowHelper.ThrowObjectDisposedException();
                }
    
                return new ServiceProviderEngineScope(this);
            }
    
            private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
            {
                var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
                if (callSite != null)
                {
                    DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite);
                    _callback?.OnCreate(callSite);
                    return RealizeService(callSite);
                }
    
                return _ => null;
            }
        }

    ServiceProviderEngine抽象类用于解析服务对应的实现

    private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;

    该委托解析出对应的服务实现

    private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
            {
                var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
                if (callSite != null)
                {
                    DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite);
                    _callback?.OnCreate(callSite);
                    return RealizeService(callSite);
                }
    
                return _ => null;
            }
    protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);

    里面的抽象方法RealizeService正是解析出服务的实现,具体的实现都各自的实现类中查看

     ServiceCallSite抽象类:用于服务的创建

     CallSiteFactory对注入的服务IEnumerable<ServiceDescriptor> descriptors进行处理,根据服务的类型获取对应的ServiceCallSite

    ServiceCallSite可以理解为以哪种方式解析该服务

    如果ServiceDescriptor的ImplementationInstance不为null,则返回ConstantCallSite

    如果ServiceDescriptor的ImplementationFactory不为null,则返回FactoryCallSite

    如果ServiceDescriptor的ImplementationType不为null,则返回ConstructorCallSite

    再回到ServiceProviderEngine抽象类中

    ServiceProviderEngine类中有CallSiteRuntimeResolver类型的RuntimeResolver属性

    internal abstract class CallSiteVisitor<TArgument, TResult>
        {
            private readonly StackGuard _stackGuard;
    
            protected CallSiteVisitor()
            {
                _stackGuard = new StackGuard();
            }
    
            protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
            {
                if (!_stackGuard.TryEnterOnCurrentStack())
                {
                    return _stackGuard.RunOnEmptyStack((c, a) => VisitCallSite(c, a), callSite, argument);
                }
    
                switch (callSite.Cache.Location)
                {
                    case CallSiteResultCacheLocation.Root:
                        return VisitRootCache(callSite, argument);
                    case CallSiteResultCacheLocation.Scope:
                        return VisitScopeCache(callSite, argument);
                    case CallSiteResultCacheLocation.Dispose:
                        return VisitDisposeCache(callSite, argument);
                    case CallSiteResultCacheLocation.None:
                        return VisitNoCache(callSite, argument);
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }
    
            protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
            {
                switch (callSite.Kind)
                {
                    case CallSiteKind.Factory:
                        return VisitFactory((FactoryCallSite)callSite, argument);
                    case  CallSiteKind.IEnumerable:
                        return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
                    case CallSiteKind.Constructor:
                        return VisitConstructor((ConstructorCallSite)callSite, argument);
                    case CallSiteKind.Constant:
                        return VisitConstant((ConstantCallSite)callSite, argument);
                    case CallSiteKind.ServiceProvider:
                        return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
                    case CallSiteKind.ServiceScopeFactory:
                        return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument);
                    default:
                        throw new NotSupportedException($"Call site type {callSite.GetType()} is not supported");
                }
            }
    
            protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)
            {
                return VisitCallSiteMain(callSite, argument);
            }
    
            protected virtual TResult VisitDisposeCache(ServiceCallSite callSite, TArgument argument)
            {
                return VisitCallSiteMain(callSite, argument);
            }
    
            protected virtual TResult VisitRootCache(ServiceCallSite callSite, TArgument argument)
            {
                return VisitCallSiteMain(callSite, argument);
            }
    
            protected virtual TResult VisitScopeCache(ServiceCallSite callSite, TArgument argument)
            {
                return VisitCallSiteMain(callSite, argument);
            }
    
            protected abstract TResult VisitConstructor(ConstructorCallSite constructorCallSite, TArgument argument);
    
            protected abstract TResult VisitConstant(ConstantCallSite constantCallSite, TArgument argument);
    
            protected abstract TResult VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, TArgument argument);
    
            protected abstract TResult VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, TArgument argument);
    
            protected abstract TResult VisitIEnumerable(IEnumerableCallSite enumerableCallSite, TArgument argument);
    
            protected abstract TResult VisitFactory(FactoryCallSite factoryCallSite, TArgument argument);
        }

    该类可以根据对应的ServiceCallSite解析出服务的实现

    ServiceProviderEngine类中的默认实现为CallSiteRuntimeResolver

    ServiceProviderEngine抽象类的GetService流程为:

    internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; }
    private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
    public object GetService(Type serviceType) => GetService(serviceType, Root);
    internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
    {
       if (_disposed)
       {
          ThrowHelper.ThrowObjectDisposedException();
       }
    
       var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
       _callback?.OnResolve(serviceType, serviceProviderEngineScope);
       DependencyInjectionEventSource.Log.ServiceResolved(serviceType);
       return realizedService.Invoke(serviceProviderEngineScope);
    }
    private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
    {
       var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
       if (callSite != null)
       {
          DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite);
          _callback?.OnCreate(callSite);
          return RealizeService(callSite);
       }
       return _ => null;
    }
    protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);

    我们再来看下ServiceProviderEngine抽象类的各个具体实现

     1、DynamicServiceProviderEngine 

      

    internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine
        {
    #if IL_EMIT
            public ILEmitResolverBuilder ResolverBuilder { get; }
    #else
            public ExpressionResolverBuilder ResolverBuilder { get; }
    #endif
    
            public CompiledServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
            {
    #if IL_EMIT
                ResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
    #else
                ResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
    #endif
            }
    
            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
            {
                var realizedService = ResolverBuilder.Build(callSite);
                RealizedServices[callSite.ServiceType] = realizedService;
                return realizedService;
            }
        }
    internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine
        {
            public DynamicServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
            {
            }
    
            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
            {
                var callCount = 0;
                return scope =>
                {
                    if (Interlocked.Increment(ref callCount) == 2)
                    {
                        Task.Run(() => base.RealizeService(callSite));
                    }
                    return RuntimeResolver.Resolve(callSite, scope);
                };
            }
        }

    DynamicServiceProviderEngine中

    如果Interlocked.Increment(ref callCount) == 2为false,则调用ServiceProviderEngine类中RuntimeResolver属性的Resolve方法来解析服务

    如果为true,则调用CompiledServiceProviderEngine中的RealizeService方法解析服务

    这里涉及到ILEmitResolverBuilder和ExpressionResolverBuilder两个类

    要深入了解里面怎么解析出服务的,自己研究下代码吧。

    2、 ExpressionsServiceProviderEngine

      

    internal class ExpressionsServiceProviderEngine : ServiceProviderEngine
        {
            private readonly ExpressionResolverBuilder _expressionResolverBuilder;
            public ExpressionsServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
            {
                _expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
            }
    
            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
            {
                var realizedService = _expressionResolverBuilder.Build(callSite);
                RealizedServices[callSite.ServiceType] = realizedService;
                return realizedService;
            }
        }

    3、 ILEmitServiceProviderEngine

      

    internal class ILEmitServiceProviderEngine : ServiceProviderEngine
        {
            private readonly ILEmitResolverBuilder _expressionResolverBuilder;
            public ILEmitServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
            {
                _expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
            }
    
            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
            {
                var realizedService = _expressionResolverBuilder.Build(callSite);
                RealizedServices[callSite.ServiceType] = realizedService;
                return realizedService;
            }
        }

    4、 RuntimeServiceProviderEngine

    internal class RuntimeServiceProviderEngine : ServiceProviderEngine
        {
            public RuntimeServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
            {
            }
    
            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
            {
                return scope =>
                {
                    Func<ServiceProviderEngineScope, object> realizedService = p => RuntimeResolver.Resolve(callSite, p);
    
                    RealizedServices[callSite.ServiceType] = realizedService;
                    return realizedService(scope);
                };
            }
        }

    我们重新回到ServiceProvider类中来

    internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
            {
                IServiceProviderEngineCallback callback = null;
                if (options.ValidateScopes)
                {
                    callback = this;
                    _callSiteValidator = new CallSiteValidator();
                }
    
                switch (options.Mode)
                {
                    case ServiceProviderMode.Default:
    #if !NETCOREAPP
                        _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
    #else
                        if (RuntimeFeature.IsSupported("IsDynamicCodeCompiled"))
                        {
                            _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                        }
                        else
                        {
                            // Don't try to compile Expressions/IL if they are going to get interpreted
                            _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                        }
    #endif
                        break;
                    case ServiceProviderMode.Dynamic:
                        _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
                        break;
                    case ServiceProviderMode.Runtime:
                        _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
                        break;
    #if IL_EMIT
                    case ServiceProviderMode.ILEmit:
                        _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
                        break;
    #endif
                    case ServiceProviderMode.Expressions:
                        _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
                        break;
                    default:
                        throw new NotSupportedException(nameof(options.Mode));
                }
    
                if (options.ValidateOnBuild)
                {
                    List<Exception> exceptions = null;
                    foreach (var serviceDescriptor in serviceDescriptors)
                    {
                        try
                        {
                            _engine.ValidateService(serviceDescriptor);
                        }
                        catch (Exception e)
                        {
                            exceptions = exceptions ?? new List<Exception>();
                            exceptions.Add(e);
                        }
                    }
    
                    if (exceptions != null)
                    {
                        throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());
                    }
                }
            }

    从构造函数我们看出,根据参数ServiceProviderOptions的Mode属性,来获取对应的IServiceProviderEngine接口实现

    ServiceProvider类的GetService方法就是调用不同IServiceProviderEngine的GetService方法获取服务的。

  • 相关阅读:
    Mycat分布式数据库&.NET链接mysql
    MySQL安装
    Apache 安装配置及开启PHP模块支持
    XSS MSMQ FASTDFS RABBITMQ
    json表单回填(表单反序列化回填值)
    解决80端口被占用
    Oracle for .net & ServiceStack.OrmLite
    MBTiles 离线地图演示
    Java网络编程TCP程序,服务器和客户机交互流程以及基本操作步骤。
    如何使用和理解java的反射机制
  • 原文地址:https://www.cnblogs.com/lanpingwang/p/12539650.html
Copyright © 2011-2022 走看看