Laravel Kernel引导流程分析

Laravel Kernel引导流程分析

代码展示

protected function sendRequestThroughRouter($request)
{# $this->app->instance('request', $request);# Facade::clearResolvedInstance('request');// 主要是这句代码$this->bootstrap();# return (new Pipeline($this->app))#            ->send($request)#            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)#            ->then($this->dispatchToRouter());
}
public function bootstrap()
{if (! $this->app->hasBeenBootstrapped()) {$this->app->bootstrapWith($this->bootstrappers());}
}
protected function bootstrappers()
{######################################################################$bootstrappers = [#    \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,#    \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,#    \Illuminate\Foundation\Bootstrap\HandleExceptions::class,      #    \Illuminate\Foundation\Bootstrap\RegisterFacades::class,#    \Illuminate\Foundation\Bootstrap\RegisterProviders::class,      #    \Illuminate\Foundation\Bootstrap\BootProviders::class,#];#####################################################################return $this->bootstrappers;
}
public function bootstrapWith(array $bootstrappers)
{$this->hasBeenBootstrapped = true;foreach ($bootstrappers as $bootstrapper) {$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);$this->make($bootstrapper)->bootstrap($this);$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);}
}

$this->make($bootstrapper)->bootstrap($this):会先创建$bootstrapper对象,在执行对象的引导方法,参数为应用对象

处理流程

  1. 加载并设置应用的系统环境变量(IlluminateFoundationBootstrapLoadEnvironmentVariables)

    public function bootstrap(Application $app)
    {// /var/www/laravel/bootstrap/cache/config.php 存在则直接返回if ($app->configurationIsCached()) {return;}$this->checkForSpecificEnvironmentFile($app);try {// 委托Dotenv来临时设置此次请求的系统环境变量,默认传参依次为'/var/www/laravel'和'.env'(new Dotenv($app->environmentPath(), $app->environmentFile()))->load();} catch (InvalidPathException $e) {//}
    }
    protected function checkForSpecificEnvironmentFile($app)
    {// cli模式下,并且存在--env参数(类似命令为: cammond --env=example)if (php_sapi_name() == 'cli' && with($input = new ArgvInput)->hasParameterOption('--env')) {// 将系统环境文件(类似:/var/www/laravel/.env.example)设置为$app应用的environmentFile属性,供后面使用$this->setEnvironmentFilePath($app, $app->environmentFile().'.'.$input->getParameterOption('--env'));}if (! env('APP_ENV')) {return;}$this->setEnvironmentFilePath($app, $app->environmentFile().'.'.env('APP_ENV'));
    }
    

    (new Dotenv($app->environmentPath(), $app->environmentFile()))->load()

    public function __construct($path, $file = '.env')
    {// 类似/var/www/laravel/.env$this->filePath = $this->getFilePath($path, $file);// 创建加载器,委托Loader处理$this->loader = new Loader($this->filePath, true);
    }
    protected function getFilePath($path, $file)
    {if (!is_string($file)) {$file = '.env';}$filePath = rtrim($path, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file;return $filePath;
    }
    public function load()
    {return $this->loadData();
    }
    protected function loadData($overload = false)
    {$this->loader = new Loader($this->filePath, !$overload);return $this->loader->load();
    }
    

    new Loader($this->filePath, !$overload)

    public function __construct($filePath, $immutable = false)
    {$this->filePath = $filePath;$this->immutable = $immutable;
    }
    public function load()
    {$this->ensureFileIsReadable();$filePath = $this->filePath;$lines = $this->readLinesFromFile($filePath);foreach ($lines as $line) {// 如果行不是注释行且含有=号,则进行if (!$this->isComment($line) && $this->looksLikeSetter($line)) {$this->setEnvironmentVariable($line);}}return $lines;
    }
    // 将文件按行的形式读入到数组并返回
    protected function readLinesFromFile($filePath)
    {$autodetect = ini_get('auto_detect_line_endings');ini_set('auto_detect_line_endings', '1');$lines = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);ini_set('auto_detect_line_endings', $autodetect);return $lines;
    }
    public function setEnvironmentVariable($name, $value = null)
    {// 检测过滤校验环境变量list($name, $value) = $this->normaliseEnvironmentVariable($name, $value);// 当immutable为真时,不覆盖对应的环境变量if ($this->immutable && $this->getEnvironmentVariable($name) !== null) {return;}// apache运行环境下,尝试临时覆盖系统环境变量if (function_exists('apache_getenv') && function_exists('apache_setenv') && apache_getenv($name)) {apache_setenv($name, $value);}// 尝试临时设置当前请求的系统环境变量if (function_exists('putenv')) {putenv("$name=$value");}// 赋值全局变量$_ENV[$name] = $value;$_SERVER[$name] = $value;
    }
    
  2. 将应用配置文件目录(/var/www/laravel/config)下所有php文件返回的数组载入到$config对象(IlluminateFoundationBootstrapLoadConfiguration)

    public function bootstrap(Application $app)
    {$items = [];// /var/www/laravel/bootstrap/cache/config.php文件[配置文件的缓存合集,加快加载速度]存在则载入,并标记已加载if (file_exists($cached = $app->getCachedConfigPath())) {$items = require $cached;$loadedFromCache = true;}// 构建config对象,并注入到服务容器$app->instance('config', $config = new Repository($items));if (! isset($loadedFromCache)) {// 将系统的配置文件载入到$config对象$this->loadConfigurationFiles($app, $config);}// 设置$this['env']为系统环境变量app.env,没有则默认为production$app->detectEnvironment(function () use ($config) {return $config->get('app.env', 'production');});date_default_timezone_set($config->get('app.timezone', 'UTC'));mb_internal_encoding('UTF-8');
    }$config = new \Illuminate\Config\Repository($items)
    public function __construct(array $items = [])
    {$this->items = $items;
    }protected function loadConfigurationFiles(Application $app, RepositoryContract $repository)
    {foreach ($this->getConfigurationFiles($app) as $key => $path) {// 此操作将在$repository对象里面构造一个多维数组属性$this->items,值为相应的系统配置文件返回的数组,后续可以直接通过get获取$repository->set($key, require $path);}
    }
    /** $files数组形式如下['app' => '/var/www/laravel/config/app.php','auth' => '/var/www/laravel/config/auth.php','xx.file' => '/var/www/laravel/config/xx/file.php','xx.yy.file' => '/var/www/laravel/config/xx/yy/file.php',]
    */
    protected function getConfigurationFiles(Application $app)
    {$files = [];// 系统配置文件的路径(/var/www/laravel/config)$configPath = realpath($app->configPath());// 文件相关的操作委托给Finder类(很强大)来处理,Finder实现了IteratorAggregate的getIterator方法foreach (Finder::create()->files()->name('*.php')->in($configPath) as $file) {// 迭代/var/www/laravel/config下面嵌套的层层子目录构造成.形式的目录$directory = $this->getNestedDirectory($file, $configPath);$files[$directory.basename($file->getRealPath(), '.php')] = $file->getRealPath();}return $files;
    }$repository->set($key, require $path)
    // 构造将.形式转变为相应层级的数组$this->items。比如:$key='xx.yy.file',$value='/var/www/laravel/config/xx/yy/file.php',将会构建为:$this->items['xx']['yy']['file'] = $value返回的数组。
    public function set($key, $value = null)
    {$keys = is_array($key) ? $key : [$key => $value];foreach ($keys as $key => $value) {Arr::set($this->items, $key, $value);}
    }
    

    根据默认的系统配置文件目录,以上操作的结果如下:
    $config对象(new Repository)里面的$this->items数组属性,后期可以通过$config->get()来获取

    $this->items['app'] = /var/www/laravel/config/app.php返回的数组;
    $this->items['auth'] = /var/www/laravel/config/auth.php返回的数组;
    $this->items['broadcasting'] = /var/www/laravel/config/broadcasting.php返回的数组;
    $this->items['cache'] = /var/www/laravel/config/cache.php返回的数组;
    $this->items['database'] = /var/www/laravel/config/database.php返回的数组;
    $this->items['filesystems'] = /var/www/laravel/config/filesystems.php返回的数组;
    $this->items['mail'] = /var/www/laravel/config/mail.php返回的数组;
    $this->items['queue'] = /var/www/laravel/config/queue.php返回的数组;
    $this->items['services'] = /var/www/laravel/config/services.php返回的数组;
    $this->items['session'] = /var/www/laravel/config/session.php返回的数组;
    $this->items['view'] = /var/www/laravel/config/view.php返回的数组;假如有这样的文件(/var/www/laravel/config/xx/yy/zz/file.php),返回['a'=>'hello,world!']数组
    将得到:$this->items['xx']['yy']['zz']['file'] = ['a'=>'hello,world!'];
    获取方式: $config->get('xx.yy.zz.file.a', $default),直接返回'hello,world!';
    
  3. 设置应用的错误异常等处理事件(IlluminateFoundationBootstrapHandleExceptions)

    public function bootstrap(Application $app)
    {$this->app = $app;error_reporting(-1);set_error_handler([$this, 'handleError']);set_exception_handler([$this, 'handleException']);register_shutdown_function([$this, 'handleShutdown']);if (! $app->environment('testing')) {ini_set('display_errors', 'Off');}
    }
    public function handleError($level, $message, $file = '', $line = 0, $context = [])
    {if (error_reporting() & $level) {throw new ErrorException($message, 0, $level, $file, $line);}
    }
    public function handleException($e)
    {if (! $e instanceof Exception) {$e = new FatalThrowableError($e);}$this->getExceptionHandler()->report($e);if ($this->app->runningInConsole()) {$this->renderForConsole($e);} else {$this->renderHttpResponse($e);}
    }
    // 核心代码,获取的\App\Exceptions\Handle对象
    protected function getExceptionHandler()
    {// make时将会直接调用$this->bindings['Illuminate\Contracts\Debug\ExceptionHandler']['concrete'](此代码位于/var/www/laravel/bootstrap/app.php,应用对象化后,直接注入到服务容器的几个单例),返回\App\Exceptions\Handle对象,并将此对象注入到服务容器[参考]return $this->app->make(ExceptionHandler::class);
    }
    protected function renderHttpResponse(Exception $e)
    {$this->getExceptionHandler()->render($this->app['request'], $e)->send();
    }
    // \App\Exceptions\Handle
    public function render($request, Exception $e)
    {$e = $this->prepareException($e);if ($e instanceof HttpResponseException) {return $e->getResponse();} elseif ($e instanceof AuthenticationException) {return $this->unauthenticated($request, $e);} elseif ($e instanceof ValidationException) {return $this->convertValidationExceptionToResponse($e, $request);}return $this->prepareResponse($request, $e);
    }
    protected function renderHttpException(HttpException $e)
    {$status = $e->getStatusCode();view()->replaceNamespace('errors', [resource_path('views/errors'),__DIR__.'/views',]);if (view()->exists("errors::{$status}")) {return response()->view("errors::{$status}", ['exception' => $e], $status, $e->getHeaders());} else {return $this->convertExceptionToResponse($e);}
    }
    public function handleShutdown()
    {if (! is_null($error = error_get_last()) && $this->isFatal($error['type'])) {$this->handleException($this->fatalExceptionFromError($error, 0));}
    }
    
  4. 根据配置项设置应用的 Facades(IlluminateFoundationBootstrapRegisterFacades)

    public function bootstrap(Application $app)
    {Facade::clearResolvedInstances();Facade::setFacadeApplication($app);// 将配置文件/var/www/laravel/config/app.php返回数组的键为aliases的值赋给\Illuminate\Foundation\AliasLoader的aliases属性,并进行注册AliasLoader::getInstance($app->make('config')->get('app.aliases', []))->register();
    }
    public static function clearResolvedInstances()
    {static::$resolvedInstance = [];
    }
    public static function setFacadeApplication($app)
    {static::$app = $app;
    }\Illuminate\Foundation\AliasLoader
    public static function getInstance(array $aliases = [])
    {if (is_null(static::$instance)) {return static::$instance = new static($aliases);}$aliases = array_merge(static::$instance->getAliases(), $aliases);static::$instance->setAliases($aliases);return static::$instance;
    }
    private function __construct($aliases)
    {$this->aliases = $aliases;
    }
    public function getAliases()
    {return $this->aliases;
    }
    public function setAliases(array $aliases)
    {$this->aliases = $aliases;
    }
    public function register()
    {if (! $this->registered) {$this->prependToLoaderStack();$this->registered = true;}
    }
    protected function prependToLoaderStack()
    {// 将$this->load注册到自动加载器的最前面,失败时抛异常spl_autoload_register([$this, 'load'], true, true);
    }
    public function load($alias)
    {// $facadeNamespace = 'Facades\\',估计是框架内部使用的,以后再看吧if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {$this->loadFacade($alias);return true;}if (isset($this->aliases[$alias])) {return class_alias($this->aliases[$alias], $alias);}
    }
    

    Facade的本质

    实际上是通过$app->make('config')->get('app.aliases', [])取出config/app.php文件里面的aliases数组并实例化AliasLoader,再将AliasLoader->load方法放到spl自动加载器最前面,最后通过class_alias($this->aliases[$alias], $alias)。当调用Cache::Method时,会触发Facdes的__callStatic魔术方法,此方法会调用相应对象里面的方法。

  5. 注入配置项的服务提供者(IlluminateFoundationBootstrapRegisterProviders)

    public function bootstrap(Application $app)
    {$app->registerConfiguredProviders();
    }
    public function registerConfiguredProviders()
    {(new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))->load($this->config['app.providers']);
    }
    public function getCachedServicesPath()
    {return $this->bootstrapPath().'/cache/services.php';
    }// 先取services缓存文件,再对\Illuminate\Foundation\ProviderRepository进行实例化,随后加载系统配置文件(./config/app.php)里面的providers数组
    (new ProviderRepository($this, new Filesystem, $this->getCachedServicesPath()))->load($this->config['app.providers'])
    public function __construct(ApplicationContract $app, Filesystem $files, $manifestPath)
    {$this->app = $app;$this->files = $files;$this->manifestPath = $manifestPath;
    }
    public function load(array $providers)
    {$manifest = $this->loadManifest();if ($this->shouldRecompile($manifest, $providers)) {$manifest = $this->compileManifest($providers);}foreach ($manifest['when'] as $provider => $events) {$this->registerLoadEvents($provider, $events);}foreach ($manifest['eager'] as $provider) {// 直接注册服务(将直接调用服务的register方法)$this->app->register($provider);}$this->app->addDeferredServices($manifest['deferred']);
    }
    public function loadManifest()
    {if ($this->files->exists($this->manifestPath)) {$manifest = $this->files->getRequire($this->manifestPath);if ($manifest) {return array_merge(['when' => []], $manifest);}}
    }
    public function shouldRecompile($manifest, $providers)
    {return is_null($manifest) || $manifest['providers'] != $providers;
    }
    protected function compileManifest($providers)
    {$manifest = $this->freshManifest($providers);foreach ($providers as $provider) {$instance = $this->createProvider($provider);// 延迟加载的服务if ($instance->isDeferred()) {foreach ($instance->provides() as $service) {$manifest['deferred'][$service] = $provider;}// 注册延迟的事件$manifest['when'][$provider] = $instance->when();}// 即时加载的服务else {$manifest['eager'][] = $provider;}}return $this->writeManifest($manifest);
    }
    protected function freshManifest(array $providers)
    {return ['providers' => $providers, 'eager' => [], 'deferred' => []];
    }
    public function createProvider($provider)
    {return new $provider($this->app);
    }
    public function isDeferred()
    {return $this->defer;
    }
    public function writeManifest($manifest)
    {if (! is_writable(dirname($this->manifestPath))) {throw new Exception('The bootstrap/cache directory must be present and writable.');}$this->files->put($this->manifestPath, '<?php return '.var_export($manifest, true).';');return array_merge(['when' => []], $manifest);
    }
    protected function registerLoadEvents($provider, array $events)
    {if (count($events) < 1) {return;}$this->app->make('events')->listen($events, function () use ($provider) {$this->app->register($provider);});
    }
    public function addDeferredServices(array $services)
    {$this->deferredServices = array_merge($this->deferredServices, $services);
    }
    

    大致流程

    通过/var/www/laravel/bootstrap/cache/services.php等实例化IlluminateFoundationProviderRepository,并加载$this->config['app.providers']数组。实例化app.providers各服务提供者,根据其defer属性将服务进行分类(延迟服务|即时服务),从而得到一个$manifest数组(格式如services.php,延迟处理:deferred=>注册延迟的服务,以后再进行调用;when=>注册延迟的事件;即时处理:eager=>直接进行注册调用等),并重新写入到services.php,然后根据此文件进行相应的处理。

  6. 启动服务提供者的boot方法等操作(IlluminateFoundationBootstrapBootProviders)

    public function bootstrap(Application $app)
    {$app->boot();
    }
    public function boot()
    {if ($this->booted) {return;}// 可以通过应用的booting方法来注册服务启动前的事件监听者$this->fireAppCallbacks($this->bootingCallbacks);// 尝试调用所有的服务提供者的boot方法array_walk($this->serviceProviders, function ($p) {$this->bootProvider($p);});$this->booted = true;// 可以通过应用的booted方法来注册服务启动后的事件监听者,若已经启用了,则直接出发事件$this->fireAppCallbacks($this->bootedCallbacks);
    }
    protected function fireAppCallbacks(array $callbacks)
    {foreach ($callbacks as $callback) {call_user_func($callback, $this);}
    }
    protected function bootProvider(ServiceProvider $provider)
    {if (method_exists($provider, 'boot')) {return $this->call([$provider, 'boot']);}
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/396352.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android RecyclerView (一) 使用完全解析

转载请标明出处&#xff1a; http://blog.csdn.net/lmj623565791/article/details/45059587&#xff1b; 本文出自:【张鸿洋的博客】 概述 RecyclerView出现已经有一段时间了&#xff0c;相信大家肯定不陌生了&#xff0c;大家可以通过导入support-v7对其进行使用。 据官方的…

Asix下日志包冲突

为什么80%的码农都做不了架构师&#xff1f;>>> Class org.apache.commons.logging.impl.SLF4JLogFactory does not implement org.apache.commons.logging. 最近集成asix包的时候发生如下错误&#xff0c;原因是程序运行时logFactoryImple加载了JBOSS下面的sff4j包…

kubernetes中mysql乱码_在kubernetes中部署tomcat与mysql集群-Go语言中文社区

在kubernetes中部署tomcat与mysql集群之前必须要有以下这些基础&#xff1a;1. 已安装、配置kubernetes2. 集群中有tomcat与mysql容器镜像3. 有docker基础具体步骤部署tomcat创建tomcat RC对象我们想要在kubernetes集群中配置tomcat服务器&#xff0c;首先要保证集群中的节点上…

【原】Jenkins持续集成环境搭建之创建java项目的job【centos6.5 java maven git 项目】...

一、构建一个maven项目在jenkins主页上&#xff0c;左侧&#xff0c;选择“新建”&#xff0c;然后填写项目名称&#xff0c;选择“构建一个maven项目”二、Git配置保存之后&#xff0c;进入详细配置页面&#xff1a;这里的源码管理&#xff1a;选择git&#xff0c;输入代码的g…

Linux内核分析作业第八周

进程的切换和系统的一般执行过程 一、进程调度的时机 中断处理过程&#xff08;包括时钟中断、I/O中断、系统调用和异常&#xff09;中&#xff0c;直接调用schedule()&#xff0c;或者返回用户态时根据need_resched标记调用schedule()&#xff1b; 内核线程可以直接调用sched…

iOS--数据存储NSUserDefaults

2019独角兽企业重金招聘Python工程师标准>>> 今天去面试&#xff0c;被问道NSUserDefaults的存取并手写出来&#xff0c;一时想不起来&#xff0c;回来之后看看之前的笔记&#xff0c;稍作一些整理 NSUserDefaults是一个单例&#xff0c;在整个程序中只有一个实例对…

mysql5.6热升级_Mysql5.6主从热备配置

数据库是应用系统的核心&#xff0c;为了保证数据库的安全采用主从热备是很常见的方法&#xff0c;也就是主数据库DDL、DML都将被同步到从数据库。一、 实验环境操作系统&#xff1a;windowsserver 2008 R2数据库&#xff1a;mysql-advanced-5.6.21-winx64二、 准备工作1、…

dhcp服务

安装与配置 配置文件 修改配置文件 复制这个文件到另一端 打开另一端的配置文件 原端输入这些命令可以去掉英文 然后vim进入另一端配置文件 全局配置不在{}内的 分发范围是指哪个ip到哪个ip的范围 指定固定电脑获取固定位置 原端修改配置文件 下面进行启动dhcp 克隆一台虚拟机&…

java有什么压力_编程语言的心智负担!你学编程得有多大的压力快来测试一下...

很多编程语言对比的文章&#xff0c;总喜欢比较各种编程语言的性能、语法、IO模型。本文将从心智负担这个角度去比较下不同的编程语言和技术。内存越界如&#xff1a;C语言、C(C with class)C/C可以直接操作内存&#xff0c;但编程必须要面对内存越界问题。发生内存越界后&…

mapper mysql 主键_实现通用mapper主键策略兼容mysql和oracle

【原创文章&#xff0c;转载请注明原文章地址&#xff0c;谢谢&#xff01;】1.直接用官方提供的注解方法是无法达到兼容效果的2.跟踪源码看看是否有其他方法3.这里有个genSql&#xff0c;可以看一下这个类4.创建一个自定义的处理类实现GenSql(代码中是我实际项目中用到的策略&…

java 面试题 由浅入深_面试官由浅入深的面试套路

阅读文本大概需要3分钟。从上图看来面试官面试是有套路的&#xff0c;一不小心就一直被套路。0x01&#xff1a;Thread面试官&#xff1a;创建线程有哪几种方式&#xff1f;应聘者&#xff1a;继承Thread类、实现Runable接口、使用j.u.c中的线程池面试官&#xff1a;继承Thread类…

java string转long报错_java.lang.Integer cannot be cast to java.lang.Long解决办法

你好我是辰兮&#xff0c;本次是项目遇到的java.lang.Integer cannot be cast to java.lang.Long异常以及相对应的解决方案。文章目录一、实战问题用postman测试数据报错&#xff0c;类型转换异常&#xff01;如何将Integer类型转换成长整形 &#xff1f;先转成String型&#x…

pyqt 界面关闭信号_木辛老师的编程课堂之Python和Qt实战慕课软件开发:增加关闭按钮...

软件实战开始&#xff0c;快速提供编程能力&#xff1b;通过实战&#xff0c;分析产品需求&#xff0c;梳理设计需求&#xff0c;提升项目分析和架构的能力。快点跟着木辛老师一起学习吧&#xff01;请点击右上角“关注”按钮关注我们哟&#xff1a;跟着木辛老师学习Python编程…

最全面的几何画板实用教程视频免费下载

不同的选择就会有不同的人生夜&#xff0c;这里小编为奋斗在教学一线的老师们送个大福利&#xff0c;这也是老师们充实自己的好去处。作为数学老师一枚&#xff0c;在平时的教学中应该用到很多教学辅助软件&#xff0c;而几何画板就是其中一款。众所周知&#xff0c;几何画板是…

Redis(1):简介

2019独角兽企业重金招聘Python工程师标准>>> Redis之父Salvatore Sanfilippo于2009年将Redis开源。VMware公司从2010年开始赞助Redis的开发&#xff0c;Salvatore Sanfilippo和Pieter Noordhuis(另一名主要的代码贡献者)同年加入VMware&#xff0c;全职开发Redis。R…

java中类型转换的造型_Java总结篇系列:类型转换/造型

Java中&#xff0c;经常可以遇到类型转换的场景&#xff0c;从变量的定义到复制、数值变量的计算到方法的参数传递、基类与派生类间的造型等&#xff0c;随处可见类型转换的身影。Java中的类型转换在Java编码中具有重要的作用。首先&#xff0c;来了解下数据类型的基本理解&…

Jenkins --SVN

项目名称&#xff1a;XXX 源码管理&#xff1a; None 发布之前&#xff0c;获取源码 编译获取后的代码&#xff0c;指定vs版本 将源码拷贝至jenkins工作控件 d:\jenkins\workspace\.. 删除指定文件 用管理员命令 将Jenkins工作空间的代码发布至指定路径转载于:https://www.cnbl…

keil5图标变成白色_电脑桌面图标全部变成白色的解决办法

系统桌面图标全部变成一个样子的白色图标&#xff0c;这是怎么回事&#xff1f;电脑桌面的图标全部变成白色该如何解决&#xff1f;下面为大家解答。解决办法&#xff1a;1.首先尝试最简单的方法操作看看&#xff0c;登录到系统桌面&#xff0c;右键桌面空白处点击打开“个性化…

epoll哪些触发模式_5.epoll的水平触发和边缘触发

本篇是多路复用的第五篇&#xff0c;主要来讲解epoll的水平触发和边缘触发是怎么回事。一、概念介绍EPOLL事件有两种模型&#xff0c;水平出发和边缘触发&#xff0c;如下所示&#xff1a;1. Level Triggered (LT) 水平触发1. socket接收缓冲区不为空 有数据可读 读事件一直触发…

HC系列蓝牙模块连接单片机与电脑,传输数据(蓝牙心电测试)

毕设做无线心电监护。有线的做出来了&#xff0c;AD8232MCULabVIEW上位机。pcb还没时间搞&#xff0c;这个9*7*2.5cm拿来测试能用。 自己做了AD8232的模拟前端&#xff0c;打的板子还没到没法测试。 虽然比较水&#xff0c;但看起来任务也完成的差不多了&#xff0c;于是就想加…