Laravel学习笔记之IoC Container实例化源码解析

   2016-09-18 0
核心提示:说明:本文主要学习Laravel容器的实例化过程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the Base Path等四个过程。同时并把自己的一点研究心得分享出来,希望对别人有所帮助。开发环境:

说明:本文主要学习Laravel容器的实例化过程,主要包括Register Base Bindings, Register Base Service Providers , Register Core Container Aliases and Set the Base Path等四个过程。同时并把自己的一点研究心得分享出来,希望对别人有所帮助。

开发环境:Laravel5.3 + PHP7 + OS X10.11

Laravel的入口文件是public/index.php文件,首先第一步加载composer的autoload文件:

// bootstrap/autoload.php
require __DIR__.'/../vendor/autoload.php';

关于composer自动加载原理可看这篇文章: Laravel学习笔记之Composer自动加载

然后开始实例化Application容器得到全局变量$app:

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

输入的是project的根路径,研究下\Illuminate\Foundation\Application的构造函数源码:

public function __construct($basePath = null)
    {
        $this->registerBaseBindings();

        $this->registerBaseServiceProviders();

        $this->registerCoreContainerAliases();

        if ($basePath) {
            $this->setBasePath($basePath);
        }
    }

Create Application过程中做了4件事:

1. register base bindings.

 2. register base service providers(\Illuminate\Events\EventServiceProvider and \Illuminate\Routing\RoutingServiceProvider).

 3. register core service aliases 
('app', 'auth', 'auth.driver', 'blade.compiler', 'cache', 'cache.store', 'config', 'cookie', 'encrypter', 'db', 'db.connection', 
'events', 'files', 'filesystem', 'filesystem.disk', 'filesystem.cloud', 'hash', 'translator', 'log', 'mailer', 
'auth.password', 'auth.password.broker', 'queue', 'queue.connection', 'queue.failer', 'redirect', 'redis', 'request', 
'router', 'session', 'session.store', 'url', 'validator', 'view'), and these core service will be registered later.

 4. set the base path, including 
'path' = __DIR__ . '/app', 'path.base' = __DIR__ , 'path.lang' = __DIR__ . '/resources/lang',
'path.config' = __DIR__ . '/config', 'path.public' = __DIR__ . '/public', 'path.storage' = __DIR__ . '/storage', 
'path.database' = __DIR__ . '/database', 'path.resources' = __DIR__ . '/resources', 
'path.bootstrap' = __DIR__ . '/bootstrap'. U can get theses path everywhere in the way, 
e.g.  public_path('/js/app.js') === __DIR__ . '/public/js/app.js';

1. Register Base Bindings

基础绑定主要是绑定当前Application对象进容器,绑定的是同一对象,但给了两个名字:

$this->instance('app', $this);

$this->instance('Illuminate\Container\Container', $this);

OK, 那instance()是如何绑定服务的?

\Illuminate\Foundation\Application是extends from the \Illuminate\Container\Container,看instance()源码:

/**
     * Register an existing instance as shared in the container.
     *
     * @param  string  $abstract
     * @param  mixed   $instance
     * @return void
     */
    public function instance($abstract, $instance)
    {
        // $abstract如果是string,截取右边的'\', 如\Illuminate\Foundation\Application => Illuminate\Foundation\Application
        $abstract = $this->normalize($abstract);
        
        if (is_array($abstract)) {
            list($abstract, $alias) = $this->extractAlias($abstract);

            $this->alias($abstract, $alias);
        }

        unset($this->aliases[$abstract]);

        $bound = $this->bound($abstract);

        $this->instances[$abstract] = $instance;

        if ($bound) {
            $this->rebound($abstract);
        }
    }

分解代码,看别名的注册:

if (is_array($abstract)) {
        list($abstract, $alias) = $this->extractAlias($abstract);

        $this->alias($abstract, $alias);
    }
    
        ...
        
    protected function extractAlias(array $definition)
    {
        return [key($definition), current($definition)];
    }   
    public function alias($abstract, $alias)
    {
        $this->aliases[$alias] = $this->normalize($abstract);
    }

如果$abstract是数组, e.g. $this->instance(['app' => '\Illuminate\Foundation\Application'], $this) ,则 \Illuminate\Foundation\Application 是alias name,存入Container class的$aliases[ ]属性中,这样存入值是:

$aliases = [
    '\Illuminate\Foundation\Application' => 'app',
];

然后在注册到属性$instances[ ]中,则上面的绑定代码类似于;

// 这里加个别名
$this->instances['app' => '\Illuminate\Foundation\Application'] = (new \Illuminate\Foundation\Application($path = __DIR__));
$this->instances['Illuminate\Container\Container'] = (new \Illuminate\Foundation\Application($path = __DIR__));

可以PHPUnit测试下别名这个feature:

public function testAlias ()
{
    // make()是从Container中解析出service,与instance正好相反
    $object1 = App::make('app');
    $object2 = App::make('\Illuminate\Foundation\Application');
    $this->assertInstanceOf(\Illuminate\Foundation\Application::class, $object1);
    $this->assertInstanceOf(\Illuminate\Foundation\Application::class, $object2);
}

由于不是单例绑定singleton(),这里$object1与$object2都是\Illuminate\Foundation\Application的对象,但不是同一对象。singleton()和make()稍后讨论下。

同时检查下之前是否已经绑定了,如果已经绑定了,则执行之前rebinding()的回调函数,主要是执行Container的$reboundCallbacks[ ]属性值。Container提供了rebinding()函数供再一次 补充 绑定(如再给'app'绑定一些之前绑定没有的的行为),PHPUnit测试下:

public function testReboundCallbacks() 
{
    // Arrange
    $container = new Container;
    
    // Actual
    $container->instance('app', function(){
        return 'app1';
    });
    $a = 0
    $container->rebinding('app', function() use ($a) {
        $a = 1;
    });
    // 再次绑定时,触发上一次rebinding中绑定该'app'的回调
    $container->instance('app', function () {
        return 'app2';
    });
    
    // Assert
    $this->assertEqual(1, $a);
}

Container的作用是供service的绑定和解析,绑定有三种方法:bind(),singleton(),instance();解析是make(),稍后讨论下容器中最重要的这几个feature。

2. Register Base Service Providers

绑定了名为'app','IlluminateContainerContainer'的两个service后(尽管绑定的service相同),看下绑定了两个基础service provider:

$this->register(new \Illuminate\Events\EventServiceProvider($this));
$this->register(new \Illuminate\Routing\RoutingServiceProvider($this));

两个基础的service provider is: IlluminateEventsEventServiceProvider和IlluminateRoutingRoutingServiceProvider。看下是如何注册两个service provider:

public function register($provider, $options = [], $force = false)
    {
        if (($registered = $this->getProvider($provider)) && ! $force) {
            return $registered;
        }
        
        if (is_string($provider)) {
            $provider = $this->resolveProviderClass($provider);
        }

        if (method_exists($provider, 'register')) {
            $provider->register();
        }
        
        foreach ($options as $key => $value) {
            $this[$key] = $value;
        }

        $this->markAsRegistered($provider);

        // If the application has already booted, we will call this boot method on
        // the provider class so it has an opportunity to do its boot logic and
        // will be ready for any usage by the developer's application logics.
        if ($this->booted) {
            $this->bootProvider($provider);
        }

        return $provider;
    }

首先检查是否已经注册了,如果注册了就直接返回,主要是检查Application class 的$serviceProviders[ ]的值,看下代码:

if (($registered = $this->getProvider($provider)) && ! $force) {
        return $registered;
    }

    ...

    public function getProvider($provider)
    {
        $name = is_string($provider) ? $provider : get_class($provider);

        return Arr::first($this->serviceProviders, function ($value) use ($name) {
            return $value instanceof $name;
        });
    }

如果输入的是字符串,就直接 new $provider($this) 生成对象,所以上面两个注册可以这么写:

$this->register(\Illuminate\Events\EventServiceProvider::class);
$this->register(\Illuminate\Routing\RoutingServiceProvider::class);

然后执行service provider中的register()方法,稍后看下两个base service provider注册了哪些service。

然后把注册过的service provider标记为 provided ,就是写入到$serviceProviders[ ]中,而开始是先检查$serviceProviders[ ]中,有没有已经注册过的将要注册的service。看下markAsRegistered()源码:

protected function markAsRegistered($provider)
    {
        $this['events']->fire($class = get_class($provider), [$provider]);

        $this->serviceProviders[] = $provider;

        $this->loadedProviders[$class] = true;
    }

这里还用了刚注册的'events' service来触发该service provider已经注册的事件,并把该service provider写入到已经加载的属性中loadedProviders[ ].

然后检查程序是否已经启动,如果已经启动完成了,再执行每一个service provider中的boot()方法, 这里会发现为啥每一个service provider里经常出现register()和boot()方法,并且register()是注册服务的,等所有服务注册完,再去boot()一些东西。 当然,这里程序刚刚注册第一个EventServiceProvider,程序离完全启动还早着呢。不过,可以先看下这里的bootProvider()方法源码:

protected function bootProvider(ServiceProvider $provider)
    {
        if (method_exists($provider, 'boot')) {
            return $this->call([$provider, 'boot']);
        }
    }
    /**
     * Call the given Closure / class@method and inject its dependencies.
     *
     * @param  callable|string  $callback
     * @param  array  $parameters
     * @param  string|null  $defaultMethod
     * @return mixed
     */
    public function call($callback, array $parameters = [], $defaultMethod = null)
    {
        if ($this->isCallableWithAtSign($callback) || $defaultMethod) {
            return $this->callClass($callback, $parameters, $defaultMethod);
        }

        $dependencies = $this->getMethodDependencies($callback, $parameters);

        return call_user_func_array($callback, $dependencies);
    }

重点看下call()这个Container另一个重要的函数,如果这么调用call(EventServiceProvider@register),那就通过Container::callClass()来解析出class和method,然后在调用call(),看下callClass()源码:

protected function callClass($target, array $parameters = [], $defaultMethod = null)
    {
        $segments = explode('@', $target);
        $method = count($segments) == 2 ? $segments[1] : $defaultMethod;

        if (is_null($method)) {
            throw new InvalidArgumentException('Method not provided.');
        }

        // 然后在这样调用call([$class, $method], $parameters)
        return $this->call([$this->make($segments[0]), $method], $parameters);
    }

也就是说,如果call(EventServiceProvider@register)这种方式的话先转化成call([$class, $method], $parameters)来调用,当然要是直接这种方式就不用在转换了。这里是通过[(new EventServiceProvider($app)), 'boot']类似这种方式来调用的。在调用boot()时有依赖怎么办?使用[$class, $method]通过getMethodDependencies($parameters)来获取$dependencies,看下getMethodDependencies($parameters)源码:

protected function getMethodDependencies($callback, array $parameters = [])
    {
        $dependencies = [];

        foreach ($this->getCallReflector($callback)->getParameters() as $parameter) {
            $this->addDependencyForCallParameter($parameter, $parameters, $dependencies);
        }

        return array_merge($dependencies, $parameters);
    }
    protected function getCallReflector($callback)
    {
        if (is_string($callback) && strpos($callback, '::') !== false) {
            $callback = explode('::', $callback);
        }

        if (is_array($callback)) {
            return new ReflectionMethod($callback[0], $callback[1]);
        }

        return new ReflectionFunction($callback);
    }
    protected function addDependencyForCallParameter(ReflectionParameter $parameter, array &$parameters, &$dependencies)
    {
        if (array_key_exists($parameter->name, $parameters)) {
            $dependencies[] = $parameters[$parameter->name];

            unset($parameters[$parameter->name]);
        } elseif ($parameter->getClass()) {
            $dependencies[] = $this->make($parameter->getClass()->name);
        } elseif ($parameter->isDefaultValueAvailable()) {
            $dependencies[] = $parameter->getDefaultValue();
        }
    }

这里是通过PHP的Reflector Method来获取依赖,依赖如果是对象的话再继续make()自动解析出service,是个外部传进来的值则代入,有默认值传默认值。反射(Reflector)是PHP的一个重要的高级特性,值得研究。

总的来说,在boot()方法中如果有dependency,container会自动解析,不管该dependency是不是某个service。这就是Method Injection,我们知道Dependency Injection有两种:Constructor Injection and Method Injection,这里可看到Method Injection是如何实现的。

OK,然后看下两个service provider注册了些什么?

首先注册EventServiceProvider中提供的service,看有哪些:

public function register()
{
    $this->app->singleton('events', function ($app) {
        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {
            return $app->make('Illuminate\Contracts\Queue\Factory');
        });
    });
}

OK,只有一个名为'events'的service注册到容器中了,并且是单例注册的。看下singleton()的源码:

public function singleton($abstract, $concrete = null)
    {
        $this->bind($abstract, $concrete, true);
    }
    
    public function bind($abstract, $concrete = null, $shared = false)
    {
        $abstract = $this->normalize($abstract);

        $concrete = $this->normalize($concrete);

        // 如果是数组,抽取别名并且注册到$aliases[]中,上文已经讨论
        if (is_array($abstract)) {
            list($abstract, $alias) = $this->extractAlias($abstract);

            $this->alias($abstract, $alias);
        }

        $this->dropStaleInstances($abstract);

        if (is_null($concrete)) {
            $concrete = $abstract;
        }

        // If the factory is not a Closure, it means it is just a class name which is
        // bound into this container to the abstract type and we will just wrap it
        // up inside its own Closure to give us more convenience when extending.
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete', 'shared');

        // If the abstract type was already resolved in this container we'll fire the
        // rebound listener so that any objects which have already gotten resolved
        // can have their copy of the object updated via the listener callbacks.
        if ($this->resolved($abstract)) {
            $this->rebound($abstract);
        }
    }
    
    protected function dropStaleInstances($abstract)
    {
        unset($this->instances[$abstract], $this->aliases[$abstract]);
    }

singleton()实际上就是$shared = true 的bind()。同时舍弃掉$instances[]中已经注册过的名为$abstract的service,当然别名数组也别忘了舍弃。

如果$concrete没有提供,则使用$abstract自动补全$concrete,并且使用getClosure()封装下做个Closure:

protected function getClosure($abstract, $concrete)
    {
        // $c 就是$container,即Container Object,会在回调时传递给这个变量
        return function ($c, $parameters = []) use ($abstract, $concrete) {
            $method = ($abstract == $concrete) ? 'build' : 'make';

            return $c->$method($concrete, $parameters);
        };
    }

$concrete没有提供绑定的情况,如:$this->singleton(IlluminateContainerContainer::class); 只提供了$abstract.

这里,就是向$bindings[ ]中注册下,现在它的值类似这样:

$bindings = [
    'events' => [
        'concrete' => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make('Illuminate\Contracts\Queue\Factory');});
                      },
        'shared'   => true,
    ],
];

已经说了singleton()和binding()注册的区别就是'shared'的值不一样,如果是$this->app->binding('events', Closure),则$bindings[ ]值是:

$bindings = [
    'events' => [
        'concrete' => function ($app) {
                        return (new Dispatcher($app))->setQueueResolver(function () use ($app) {return $app->make('Illuminate\Contracts\Queue\Factory');});
                      },
        'shared'   => false,
 
    ],
 
];

OK,看下RoutingServiceProvider中注册了些什么service?

上文说过,Application中register()会调用service provider中的register()方法,看下\Illuminate\Routing\RoutingServiceProvider源码就发现其注册了几个service: 'router', 'url', 'redirect', Psr\Http\Message|ServerRequestInterface::class, Psr\Http\Message\ResponseInterface::class, Illuminate\Contracts\Routing\ResponseFactory::class

只有 Illuminate\Contracts\Routing\ResponseFactory::class 是singleton(),其余是bind(),e.g. 'router' service source code:

$this->app['router'] = $this->app->share(function ($app) {
    return new Router($app['events'], $app);
});

为什么说是bind()?并且$this->app['router']是啥意思?

OK, 看下share()的源码:

public function share(Closure $closure)
{
    return function ($container) use ($closure) {
        static $object;
 
        if (is_null($object)) {
            $object = $closure($container);
        }
 
        return $object;
    };
}

share()仅仅执行$closure()并传入$container,所以上面的 'router' service 代码类似于:

$this->app['router'] = new Router($app['events'], $app);

$this->app是Container对象,而Container implement ArrayAccess这个Interface,实现对类的属性做数组式访问,所以Container必须实现四个方法:

@link http://php.net/manual/en/arrayaccess.offsetset.php
public function offsetExists($offset);
public function offsetGet($offset);
public function offsetSet($offset, $value);
public function offsetUnset($offset);

这里是对$this->app赋值,所以看下offsetSet()源码:

public function offsetSet($key, $value)
{
    if (! $value instanceof Closure) {
        $value = function () use ($value) {
            return $value;
        };
    }
 
    $this->bind($key, $value);
}

这里是用bind()来绑定到container中,所以上文中说是bind(),而不是其他。所上文的代码类似于这样:

$this->app['router'] = new Router($app['events'], $app);
  
is like:
  
$object = new Router($app['events'], $app);
$this->bind('router', function () use ($object) {return $object});

总的来说,就是通过注册EventServiceProvider and RoutingServiceProvider来绑定了一些service, e.g. 'events', 'router' and so on.

3. Register Core Container Aliases

由于PHP使用namespace来命名class,有时类名很长,所以需要做个别名alias图方便。看下registerCoreContainerAliases()的源码:

public function registerCoreContainerAliases()
    {
        $aliases = [
            'app'                  => ['Illuminate\Foundation\Application', 'Illuminate\Contracts\Container\Container', 'Illuminate\Contracts\Foundation\Application'],
            'auth'                 => ['Illuminate\Auth\AuthManager', 'Illuminate\Contracts\Auth\Factory'],
            'auth.driver'          => ['Illuminate\Contracts\Auth\Guard'],
            'blade.compiler'       => ['Illuminate\View\Compilers\BladeCompiler'],
            'cache'                => ['Illuminate\Cache\CacheManager', 'Illuminate\Contracts\Cache\Factory'],
            'cache.store'          => ['Illuminate\Cache\Repository', 'Illuminate\Contracts\Cache\Repository'],
            'config'               => ['Illuminate\Config\Repository', 'Illuminate\Contracts\Config\Repository'],
            'cookie'               => ['Illuminate\Cookie\CookieJar', 'Illuminate\Contracts\Cookie\Factory', 'Illuminate\Contracts\Cookie\QueueingFactory'],
            'encrypter'            => ['Illuminate\Encryption\Encrypter', 'Illuminate\Contracts\Encryption\Encrypter'],
            'db'                   => ['Illuminate\Database\DatabaseManager'],
            'db.connection'        => ['Illuminate\Database\Connection', 'Illuminate\Database\ConnectionInterface'],
            'events'               => ['Illuminate\Events\Dispatcher', 'Illuminate\Contracts\Events\Dispatcher'],
            'files'                => ['Illuminate\Filesystem\Filesystem'],
            'filesystem'           => ['Illuminate\Filesystem\FilesystemManager', 'Illuminate\Contracts\Filesystem\Factory'],
            'filesystem.disk'      => ['Illuminate\Contracts\Filesystem\Filesystem'],
            'filesystem.cloud'     => ['Illuminate\Contracts\Filesystem\Cloud'],
            'hash'                 => ['Illuminate\Contracts\Hashing\Hasher'],
            'translator'           => ['Illuminate\Translation\Translator', 'Symfony\Component\Translation\TranslatorInterface'],
            'log'                  => ['Illuminate\Log\Writer', 'Illuminate\Contracts\Logging\Log', 'Psr\Log\LoggerInterface'],
            'mailer'               => ['Illuminate\Mail\Mailer', 'Illuminate\Contracts\Mail\Mailer', 'Illuminate\Contracts\Mail\MailQueue'],
            'auth.password'        => ['Illuminate\Auth\Passwords\PasswordBrokerManager', 'Illuminate\Contracts\Auth\PasswordBrokerFactory'],
            'auth.password.broker' => ['Illuminate\Auth\Passwords\PasswordBroker', 'Illuminate\Contracts\Auth\PasswordBroker'],
            'queue'                => ['Illuminate\Queue\QueueManager', 'Illuminate\Contracts\Queue\Factory', 'Illuminate\Contracts\Queue\Monitor'],
            'queue.connection'     => ['Illuminate\Contracts\Queue\Queue'],
            'queue.failer'         => ['Illuminate\Queue\Failed\FailedJobProviderInterface'],
            'redirect'             => ['Illuminate\Routing\Redirector'],
            'redis'                => ['Illuminate\Redis\Database', 'Illuminate\Contracts\Redis\Database'],
            'request'              => ['Illuminate\Http\Request', 'Symfony\Component\HttpFoundation\Request'],
            'router'               => ['Illuminate\Routing\Router', 'Illuminate\Contracts\Routing\Registrar'],
            'session'              => ['Illuminate\Session\SessionManager'],
            'session.store'        => ['Illuminate\Session\Store', 'Symfony\Component\HttpFoundation\Session\SessionInterface'],
            'url'                  => ['Illuminate\Routing\UrlGenerator', 'Illuminate\Contracts\Routing\UrlGenerator'],
            'validator'            => ['Illuminate\Validation\Factory', 'Illuminate\Contracts\Validation\Factory'],
            'view'                 => ['Illuminate\View\Factory', 'Illuminate\Contracts\View\Factory'],
        ];

        foreach ($aliases as $key => $aliases) {
            foreach ($aliases as $alias) {
                $this->alias($key, $alias);
            }
        }
    }

给class name注册个别名,并且在相同数组里有着共同的别名,e.g. 'IlluminateFoundationApplication', 'IlluminateContractsContainerContainer' and 'IlluminateContractsFoundationApplication' share the same alias name 'app'.

4. Set the Base Path

Application Constructor里需要传入一个path这个原料来构造类,这里path是这个project的当前绝对路径。同时绑定一些常用的文件夹路径供将来使用,看下构造函数中源码:

public function __construct($basePath)
{
      ...
      
    if ($basePath) {
        $this->setBasePath($basePath);
    }
}
public function setBasePath($basePath)
{
    $this->basePath = rtrim($basePath, '\/');
 
    $this->bindPathsInContainer();
 
    return $this;
}
protected function bindPathsInContainer()
{
    $this->instance('path', $this->path());
    $this->instance('path.base', $this->basePath());
    $this->instance('path.lang', $this->langPath());
    $this->instance('path.config', $this->configPath());
    $this->instance('path.public', $this->publicPath());
    $this->instance('path.storage', $this->storagePath());
    $this->instance('path.database', $this->databasePath());
    $this->instance('path.resources', $this->resourcePath());
    $this->instance('path.bootstrap', $this->bootstrapPath());
}

instance()上文已经讨论过,所以这里的$instances[ ]类似于这样:

$instances = [
    'path'           => __DIR__ . '/app',
    'path.base'      => __DIR__ . '/',
    'path.lang'      => __DIR__ . '/resources/lang',
    'path.config'    => __DIR__ . '/config',
    'path.public'    => __DIR__ . '/public',
    'path.storage'   => __DIR__ . '/storage',
    'path.database'  => __DIR__ . '/database',
    'path.resources' => __DIR__ . '/resources',
    'path.bootstrap' => __DIR__ . '/bootstrap',
];

OK,看下bootstrap/app.php文件,在得到$app这个实例化对象后,再单例绑定Two Kernel and One Exception:

$app->singleton(
    Illuminate\Contracts\Http\Kernel::class,
    RightCapital\Admin\Http\Kernel::class
);
 
$app->singleton(
    Illuminate\Contracts\Console\Kernel::class,
    RightCapital\Admin\Console\Kernel::class
);
 
$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    RightCapital\Admin\Exceptions\Handler::class
);

最后,就得到一个塞满好几个service的容器了,而未被实例化前是个空Container.整个的Application的实例化过程分析就OK了。

总结:本文主要学习了Application的实例化过程,主要学习了实例化过程中向这个IoC(Inversion of Control) Container绑定了哪些service,并讨论了绑定的三个方法:bind(),singleton(),instance(),解析方法make()留到单独研究Container时再讨论吧。下次分享下Container学习心得,并写上PHPUnit测试,到时见。

 
标签: IOC Laravel
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 微信小程序踩坑(二)——微信小程序recorderMa
    目录写在前面RecorderManager录音相关innerAudioContext播放相关写在前面关于微信小程序的录音和语音方面,踩了很多坑,记录一下recorderManager相关文档innerAudioContext相关文档RecorderManager录音相关在使用RecorderManager相关方法之前,在page外先定义
    02-09
  • =======服务端的数据发送和JSonStreamObject的
    现在IOCP的功能还剩下服务端数据的返回。还是采用netty的方式。netty返回数据的调用是这样的contenxt.write(TObject obj);这样将obj对象发送给客户端。 1.将回传的对象进行编码成buffer2.通过socket进行传送. 下面我贴出回传数据的过程.procedure TClientCo
    02-09
  • delphi之IOCP学习(一)
    delphi之IOCP学习(一)
       困扰已久的网络通信(IOCP:完成端口),今天终于揭开她的神秘面纱了,之前百度N久还是未能理解IOCP,网络上好多博文都没有贴出源码,初学者很难正在理解IOCP并自己写出通信例子 ,经过努力,今天自己终于做出了简单的测试程序,下面贴出源码,水平有限
    02-09
  • ===接收数据的解码器(Decoder)和数据处理">Delp
    今天完成了第三点,初步按照netty 的做法制作了Decoder,由于我现在用的2007还没有泛型,所有我使用的返回TObject做法我先介绍下netty的处理数据的流程1.IOCP接收的数据。2.写入到套接字对应的缓存。3.调用Decoder,进行解码。4.如果解码成功调用套接字对应的
    02-09
  • 高吞吐量的一个日志函数类_用于IOCP (Delphi)
           在开发服务器端程序的时候,日志是必须的一个功能。由于服务器端的要频繁的把数据写入日志,开始的时候用了一个很简单日志函数就是直接把日志字符写入文件中。然后关闭连接。一直也应用良好。但做压力测试的时候,因为要每个连接的数据都要写入
    02-09
  • visual studioC#调用MATLAB生成的DLL
    visual studioC#调用MATLAB生成的DLL
    之前看到一个很好的博客,写的非常清楚,但是现在找不到了~~所以就大概写一下自己的操作过程,所以,及时写博客记录下来,是很有帮助的,放了一个月的假,差点忘光了。1.首先MATLAB要安装MCR.Install 即MATLAB编译器,并配置环境变量;2.注册mwcomutil.dll  
    02-09
  • r语言与dataframe r语言与bioconductor百度云
    r语言与dataframe r语言与bioconductor百度云
    什么是DataFrame引用 r-tutor上的定义:DataFrame 是一个表格或者类似二维数组的结构,它的各行表示一个实例,各列表示一个变量。没错,DataFrame就是类似于Excel表格和MySQL数据库一样是一个结构化的数据体。而这种结构化的数据体是当代数据流编程中的中流
    02-09
  • ASP.Net Core-依赖注入IoC
    一、IocIoC全称Inverse of Control,控制反转。类库和框架的不同之处在于,类库是实现某种单一功能的API,框架是针对一个任务把这些单一功能串联起来形成一个完整的流程,这个流程在一个引擎驱动下被执行。IoC的总体设计是要把在应用程序的流程控制转移到框架
    02-09
  • C# IoC 容器 c罗
     Unity是Unity是微软patterns practices组用C#实现的轻量级,可扩展的依赖注入容器,它为方便开发者建立松散耦合的应用程序, 有以下优点:        1.简化了对象的创建,特别是针对分层对象结构和依赖关系;   2.需求的抽象,允许开发人员在运行时或
    02-09
  • inversify 强大&&轻量级的基于typescript 的ioc
    inversify 强大轻量级的基于typescript 的ioc 框架,以前有介绍过一个typedi 的类似框架 inversify github 的star比typedi 多很多参考使用配置tsconfig.json {    "compilerOptions": {        "target": "es5",        "lib": ["es6"],      
    02-08
点击排行