我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Laravel中的事件溯源实例代码分析

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Laravel中的事件溯源实例代码分析

这篇文章主要介绍了Laravel中的事件溯源实例代码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel中的事件溯源实例代码分析文章都会有所收获,下面我们一起来看看吧。

我们将新建一个 Laravel 项目,但我将使用 Jetstream,因为我想启用身份验证和团队结构和功能。 一旦你创建了这个项目, 请在你的IDE中打开它。(这里的正确答案当然是 PHPStorm ), 现在我们已经准备好在 Laravel 深入事件溯源。

我们希望为应用程序创建一个附加模型,这是唯一的一个。这是一个 Celebration 模型,你可以使用以下的Artisan命令创建它:

php artisan make:model Celebration -m

修改你的迁移文件 up 方法,它看起来应该像这样:

public function up(): void{    Schema::create('celebrations', static function (Blueprint $table): void {        $table->id();        $table->string('reason');        $table->text('message')->nullable();        $table            ->foreignId('user_id')            ->index()            ->constrained()            ->cascadeOnDelete();        $table            ->foreignId('sender_id')            ->index()            ->constrained('users')            ->cascadeOnDelete();        $table            ->foreignId('team_id')            ->index()            ->constrained()            ->cascadeOnDelete();        $table->timestamps();    });}

我们有一个庆祝的原因 reason,一个简单的句子,然后是我们可能希望与庆祝活动发送的可选消 message。除此之外,我们有三个关系,正在庆祝的用户,发送庆祝的用户,以及他们所在的团队。使用 Jetstream,一个用户可以属于多个团队,并且可能存在两个用户在同一个团队中的情况
,我们要确保我们在正确的团队中公开庆祝他们。

一旦我们有了这个设定,让我们看看模型本身:

declare(strict_types=1);namespace App\Models;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\Relations\HasMany;final class Celebration extends Model{    use HasFactory;    protected $fillable = [        'reason',        'message',        'user_id',        'sender_id',        'team_id',    ];    public function user(): BelongsTo    {        return $this->belongsTo(            related: User::class,            foreignKey: 'user_id',        );    }    public function sender(): BelongsTo    {        return $this->belongsTo(            related: User::class,            foreignKey: 'sender_id',        );    }    public function team(): BelongsTo    {        return $this->belongsTo(            related: Team::class,            foreignKey: 'team_id',        );    }}

我们可以将这些关系映射到其他相关的模型上。尽管,默认情况下,我会将关联关系的另一端添加到每一个模型中,使它们的关联关系更加清楚,无论它是否严格需要这些关联关系。这是我养成的习惯,为了帮助他人理解模型本身。

现在我们有了从模型视角创建的我们的应用基础。我想我们需要一些安装一些对我们有帮助的软件包(依赖)。对于我的应用,我使用 Laravel Livewire 来控制 UI 。但是我并不会在本教程详细介绍这个包,因为我想确保我能专注于讲事件溯源这个方面。

与我创建的大多数项目一样,无论大小,我都为应用程序采用了模块化布局——一个领域驱动模型设计 ( Domain Driven Design ) 方法。这只是我做的事情,不要觉得你自己必须遵循这个,因为它是非常主观的。

我的下一步是设置我的域,对于这个演示,我只有一个域:文化。在文化中,我为我可能需要的一切创建了名称空间。但我会经历它,这样你就明白了过程

第一步是安装一个软件包,使我能够在Laravel中使用事件源。为此,我使用了一个 Spatie package包,它为我做了大量的后台工作

composer require spatie/laravel-event-sourcing

安装后,请确保按照包的安装说明进行操作,因为配置和迁移需要发布。正确安装后,运行迁移,使数据库处于正确状态。

php artisan migrate

现在我们可以开始思考我们要如何实现事件溯源。你可以通过两种方式实现这一点:投影仪来投影你的状态或聚合。

Projector 是一个位于你的应用程序中并处理你调度的事件的类。然后,这些将更改你的应用程序的状态。这不仅仅是简单地更新你的数据库。它位于中间,捕获一个事件,存储它,然后进行所需的更改 —— 然后 “投射” 应用程序的新状态

另一种方法,我的首选方法,聚合 - 这些是像投影仪一样为你处理应用程序状态的类。我们不是在我们的应用程序中自己触发事件,而是将其留给聚合为我们做。把它想象成一个中继,你要求中继做某事,它会为你处理。

在我们创建第一个聚合之前,需要在后台做一些工作。我非常喜欢为每个聚合创建一个事件存储,以便查询更快,并且该存储不会很快填满。这在包文档中进行了解释,但我将亲自引导你完成它,因为它在文档中并不是最清楚的。

第一步是创建模型和迁移,因为你将来需要一种方法来查询它以进行报告等。运行以下 artisan 命令来创建这些:

php artisan make:model CelebrationStoredEvent -m

以下代码是你在 up 方法中进行迁移所需的代码:

public function up(): void{    Schema::create('celebration_stored_events', static function (Blueprint $table): void {        $table->id();        $table->uuid('aggregate_uuid')->nullable()->unique();        $table        ->unsignedBigInteger('aggregate_version')        ->nullable()        ->unique();        $table->integer('event_version')->default(1);        $table->string('event_class');        $table->json('event_properties');        $table->json('meta_data');        $table->timestamp('created_at');        $table->index('event_class');        $table->index('aggregate_uuid');    });}

如你所见,我们为我们的活动收集了大量数据。现在模型要简单得多。它应该如下所示:

declare(strict_types=1);namespace App\Models;use Spatie\EventSourcing\StoredEvents\Models\EloquentStoredEvent;final class CelebrationStoredEvent extends EloquentStoredEvent{    public $table = 'celebration_stored_events';}

当我们扩展 EloquentStoredEvent 模型时,我们需要做的就是改变它正在查看的表。模型的其余功能已经在父级上到位。

要使用这些模型,你必须创建一个存储库来查询事件。这是一个非常简单的存储库 —— 然而,这是一个重要的步骤。我将我的添加到我的域代码中,位于 class="lazy" data-src/Domains/Culture/Repositories/ 下,但您可以随意添加对您最有意义的位置:

declare(strict_types=1);namespace Domains\Culture\Repositories;use App\Models\CelebrationStoredEvent;use Spatie\EventSourcing\StoredEvents\Repositories\EloquentStoredEventRepository;final class CelebrationStoredEventsRepository extends EloquentStoredEventRepository{    public function __construct(        protected string $storedEventModel = CelebrationStoredEvent::class,    ) {        parent::__construct();    }}

既然我们有了存储事件和查询它们的方法,我们可以继续我们的聚合本身。同样,我将我的存储在我的域中,但可以随意将你的存储在你的应用程序上下文中。

declare(strict_types=1);namespace Domains\Culture\Aggregates;use Domains\Culture\Repositories\CelebrationStoredEventsRepository;use Spatie\EventSourcing\AggregateRoots\AggregateRoot;use Spatie\EventSourcing\StoredEvents\Repositories\StoredEventRepository;final class CelebrationAggregateRoot extends AggregateRoot{    protected function getStoredEventRepository(): StoredEventRepository    {        return app()->make(            abstract: CelebrationStoredEventsRepository::class,        );    }}

到目前为止,除了为我们连接到正确的事件存储之外,此聚合不会执行任何操作。要让它开始跟踪事件,我们首先需要创建它们。但在此之前,我们需要停下来想一想。我们希望在活动中存储哪些数据?我们想要存储我们需要的每一个属性吗?或者我们是否希望存储一个数组,就像它来自一个表单一样?我两种方法都不用,因为为什么要保持简单呢?我在所有事件中使用数据传输对象,以确保始终维护上下文并始终提供类型安全。

我构建了一个软件包,让我做这件事更容易。可以通过以下 Composer 命令安装它:

composer require juststeveking/laravel-data-object-tools

和以前一样, 我默认将我的数据对象保存在我的领域, 但你可以添加到对你最有意义的地方。 我创建了一个名为 Celebration 的数据对象,可以传递给事件和聚合器:

declare(strict_types=1);namespace Domains\Culture\DataObjects;use JustSteveKing\DataObjects\Contracts\DataObjectContract;final class Celebration implements DataObjectContract{    public function __construct(        private readonly string $reason,        private readonly string $message,        private readonly int $user,        private readonly int $sender,        private readonly int $team,    ) {}    public function userID(): int    {        return $this->user;    }    public function senderID(): int    {        return $this->sender;    }    public function teamUD(): int    {        return $this->team;    }    public function toArray(): array    {        return [            'reason' => $this->reason,            'message' => $this->message,            'user_id' => $this->user,            'sender_id' => $this->sender,            'team_id' => $this->team,        ];    }}

当我升级到 PHP 8.2 时,这会容易得多,因为我可以创建只读类 - 是的,我的包已经支持它们。

现在我们有了我们的数据对象。我们可以回到我们想要存储的事件。我已经调用了我的CelebrationWasCreated,因为事件名称应该总是过去时。让我们看看这个事件:

declare(strict_types=1);namespace Domains\Culture\Events;use Domains\Culture\DataObjects\Celebration;use Spatie\EventSourcing\StoredEvents\ShouldBeStored;final class CelebrationWasCreated extends ShouldBeStored{    public function __construct(        public readonly Celebration $celebration,    ) {}}

因为我们使用的是数据对象,所以我们的类保持干净。所以,现在我们有了一个事件——以及一个可以发送的数据对象,我们需要考虑如何触发它。这让我们回到了聚合本身,所以让我们在聚合上创建一个可以用于此目的的方法:

declare(strict_types=1);namespace Domains\Culture\Aggregates;use Domains\Culture\DataObjects\Celebration;use Domains\Culture\Events\CelebrationWasCreated;use Domains\Culture\Repositories\CelebrationStoredEventsRepository;use Spatie\EventSourcing\AggregateRoots\AggregateRoot;use Spatie\EventSourcing\StoredEvents\Repositories\StoredEventRepository;final class CelebrationAggregateRoot extends AggregateRoot{    protected function getStoredEventRepository(): StoredEventRepository    {        return app()->make(            abstract: CelebrationStoredEventsRepository::class,        );    }    public function createCelebration(Celebration $celebration): CelebrationAggregateRoot    {        $this->recordThat(            domainEvent: new CelebrationWasCreated(                celebration: $celebration,            ),        );        return $this;    }}

在这一点上,我们有一种方法来要求一个类记录事件。但是,这一事件还不会持续下去 —— 那是以后的事。此外,我们不会以任何方式改变应用程序的状态。那么,我们该如何做这项活动采购工作呢?这一部分是关于 Livewire 中的实现的,我现在将向你介绍它。

我喜欢通过调度一个事件来管理这个过程,因为它更高效。如果你考虑如何与应用程序交互,你可以从 Web 访问它,通过 API 端点发送请求,或者发生 CLI 命令可能运行的事件 —— 可能是一个 Cron 作业。在所有这些方法中,通常,你需要即时响应,或者至少您不想等待。我将在我的 Livewire 组件上向你展示我为此使用的方法:

public function celebrate(): void{    $this->validate();    dispatch(new TeamMemberCelebration(        celebration: Hydrator::fill(            class: Celebration::class,            properties: [                'reason' => $this->reason,                'message' => $this->content,                'user' => $this->identifier,                'sender' => auth()->id(),                'team' => auth()->user()->current_team_id,            ]        ),    ));    $this->closeModal();}

在这一点上,我们有一种方法来要求一个类记录事件。但是,这一事件还不会持续下去 —— 那是以后的事。此外,我们不会以任何方式改变应用程序的状态。那么,我们该如何做这项活动采购工作呢?这一部分是关于 Livewire 中的实现的,我现在将向你介绍它。

我喜欢通过调度一个事件来管理这个过程,因为它更高效。如果你考虑如何与应用程序交互,你可以从 Web 访问它,通过 API 端点发送请求,或者发生 CLI 命令可能运行的事件 —— 可能是一个 Cron 作业。在所有这些方法中,通常,你需要即时响应,或者至少你不想等待。我将在我的 Livewire 组件上向你展示我为此使用的方法:

public function celebrate(): void{    $this->validate();    dispatch(new TeamMemberCelebration(        celebration: Hydrator::fill(            class: Celebration::class,            properties: [                'reason' => $this->reason,                'message' => $this->content,                'user' => $this->identifier,                'sender' => auth()->id(),                'team' => auth()->user()->current_team_id,            ]        ),    ));    $this->closeModal();}

当我验证来自组件的用户输入,可以分派处理一个新的作业,然后结束这个流程。我使用我的包将一个新的数据对象传递给作业。它有一个 Facade,可以让我用一系列属性来为类添加——到目前为止它工作得很好。那么这是怎么实现的呢?让我们来看看。

declare(strict_types=1);namespace App\Jobs\Team;use Domains\Culture\Aggregates\CelebrationAggregateRoot;use Domains\Culture\DataObjects\Celebration;use Illuminate\Bus\Queueable;use Illuminate\Contracts\Queue\ShouldQueue;use Illuminate\Foundation\Bus\Dispatchable;use Illuminate\Queue\InteractsWithQueue;use Illuminate\Queue\SerializesModels;use Illuminate\Support\Str;final class TeamMemberCelebration implements ShouldQueue{    use Queueable;    use Dispatchable;    use SerializesModels;    use InteractsWithQueue;    public function __construct(        public readonly Celebration $celebration,    ) {}    public function handle(): void    {        CelebrationAggregateRoot::retrieve(            uuid: Str::uuid()->toString(),        )->createCelebration(            celebration: $this->celebration,        )->persist();    }}

我们的工作将数据对象接受到它的构造函数中,然后在处理它时存储它。处理作业时,它使用 CelebrationAggregateRoot 按 UUID 检索聚合,然后调用我们之前创建的 createCelebration 方法。在它调用了这个方法之后 - 它在聚合本身上调用了 persist。这就是将为我们存储事件的内容。但是,同样,我们还没有改变我们的应用程序状态。我们所做的只是存储一个不相关的事件而不是创建我们想要创建的庆祝活动?那么我们缺少什么?

我们的事件也需要处理。在另一种方法中,我们使用投影仪来处理我们的事件,但我们必须手动调用它们。这是一个类似的过程,但是我们的聚合正在触发事件,我们仍然需要一个投影仪来处理事件并改变我们的应用程序状态。

让我们创建我们的投影仪,我称之为处理程序 —— 因为它们处理事件。但我会让你决定如何命名你的。

declare(strict_types=1);namespace Domains\Culture\Handlers;use Domains\Culture\Events\CelebrationWasCreated;use Spatie\EventSourcing\EventHandlers\Projectors\Projector;use Infrastructure\Culture\Actions\CreateNewCelebrationContract;final class CelebrationHandler extends Projector{    public function __construct(        public readonly CreateNewCelebrationContract $action,    ) {}    public function onCelebrationWasCreated(CelebrationWasCreated $event): void    {        $this->action->handle(            celebration: $event->celebration,        );    }}

我们的投影机 / 处理程序,无论你选择如何称呼它,都将从容器中为我们解析 - 然后它将寻找一个以 on 为前缀的方法,后跟事件名称本身。所以在我们的例子中,onCelebrationWasCreated。在我的示例中,我使用一个动作来执行事件中的实际逻辑 - 单个类执行一项可以轻松伪造或替换的工作。所以再一次,我们把树追到下一个班级。动作,这对我来说是这样的:

declare(strict_types=1);namespace Domains\Culture\Actions;use App\Models\Celebration;use Domains\Culture\DataObjects\Celebration as CelebrationObject;use Illuminate\Database\Eloquent\Model;use Infrastructure\Culture\Actions\CreateNewCelebrationContract;final class CreateNewCelebration implements CreateNewCelebrationContract{    public function handle(CelebrationObject $celebration): Model|Celebration    {        return Celebration::query()->create(            attributes: $celebration->toArray(),        );    }}

这是当前执行的操作。如你所见,我的操作类本身实现了一个合同 / 接口。这意味着我将接口绑定到我的服务提供者中的特定实现。这使我可以轻松地创建测试替身 / 模拟 / 替代方法,而不会对需要执行的实际操作产生连锁反应。这不是严格意义上的事件溯源,而是通用编程。我们确实拥有的一个好处是我们的投影仪可以重放。因此,如果出于某种原因,我们离开了 Laravel Eloquent,也许我们使用了其他东西,我们可以创建一个新的操作 - 将实现绑定到我们的容器中,重放我们的事件,它应该都能正常工作。

在这个阶段,我们正在存储我们的事件并有办法改变我们的应用程序的状态 —— 但是我们做到了吗?我们需要告诉 Event Sourcing 库我们已经注册了这个 Projector/Handler 以便它知道在事件上触发它。通常我会为每个域创建一个 EventSourcingServiceProvider,这样我就可以在一个地方注册所有的处理程序。我的看起来如下:

declare(strict_types=1);namespace Domains\Culture\Providers;use Domains\Culture\Handlers\CelebrationHandler;use Illuminate\Support\ServiceProvider;use Spatie\EventSourcing\Facades\Projectionist;final class EventSourcingServiceProvider extends ServiceProvider{    public function register(): void    {        Projectionist::addProjector(            projector: CelebrationHandler::class,        );    }}

剩下的就是确保再次注册此服务提供者。我为每个域创建一个服务提供者来注册子服务提供者 —— 但这是另一个故事和教程。

在这个阶段,我们正在存储我们的事件,并有一种办法改变我们的应用程序的状态——但是我们做到了吗?我们需要告诉 Event Sourcing 库,我们已经注册了 Projector/Handler 以便它知道在事件上触发它。通常,我会为每个域创建一个EventSourcingServiceProvider,以便可以在一个位置注册所有处理程序。如下:

declare(strict_types=1);namespace Domains\Culture\Providers;use Domains\Culture\Handlers\CelebrationHandler;use Illuminate\Support\ServiceProvider;use Spatie\EventSourcing\Facades\Projectionist;final class EventSourcingServiceProvider extends ServiceProvider{    public function register(): void    {        Projectionist::addProjector(            projector: CelebrationHandler::class,        );    }}

剩下确保此服务提供者重新注册。我为每个域创建一个 Service Provider 来注册子服务提供者--但这是另一个故事和教程。

现在,当我们把它们放在一起时。我们可以要求我们的聚合创建一个庆祝活动,它将记录事件并将其保存在数据库中,并且作为副作用,我们的处理程序将被触发,随着新的变化改变应用程序的状态。

关于“Laravel中的事件溯源实例代码分析”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“Laravel中的事件溯源实例代码分析”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Laravel中的事件溯源实例代码分析

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Laravel中的事件溯源实例代码分析

这篇文章主要介绍了Laravel中的事件溯源实例代码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel中的事件溯源实例代码分析文章都会有所收获,下面我们一起来看看吧。我们将新建一个 Laravel
2023-07-04

Node.js事件循环实例代码分析

本篇内容主要讲解“Node.js事件循环实例代码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Node.js事件循环实例代码分析”吧!通过代码模拟 eventloopconst eventl
2023-07-04

laravel的执行流程实例代码分析

这篇文章主要讲解了“laravel的执行流程实例代码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“laravel的执行流程实例代码分析”吧!1.index.php$app = requi
2023-07-04

Laravel事件监听方法实例分析

这篇文章主要介绍了Laravel事件监听方法实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Laravel事件监听方法实例分析文章都会有所收获,下面我们一起来看看吧。Laravel Event & Lis
2023-06-30

Laravel枚举包standards实例代码分析

这篇文章主要讲解了“Laravel枚举包standards实例代码分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Laravel枚举包standards实例代码分析”吧!PrinsFran
2023-07-04

Node.js事件循环机制实例代码分析

这篇文章主要介绍“Node.js事件循环机制实例代码分析”,在日常操作中,相信很多人在Node.js事件循环机制实例代码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Node.js事件循环机制实例代码分析
2023-07-04

React实现合成事件的源码分析

React 中的事件,是对原生事件的封装,叫做合成事件。抽象出一层合成事件,是为了做兼容,抹平不同浏览器之间的差异。本文将从事件绑定和事件触发角度,带大家解读下源码,感兴趣的可以了解一下
2022-12-08

jQuery中的事件实例分析

这篇“jQuery中的事件实例分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“jQuery中的事件实例分析”文章吧。一、概
2023-06-30

Vue组件中的自定义事件源码分析

这篇文章主要介绍“Vue组件中的自定义事件源码分析”,在日常操作中,相信很多人在Vue组件中的自定义事件源码分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue组件中的自定义事件源码分析”的疑惑有所帮助!
2023-06-29

深入分析React源码中的合成事件

合成事件不是浏览器本身触发的事件,自己创建和触发的事件。本文将从源码角度带大家一起深入了解下React中的合成事件,需要的可以参考一下
2022-11-13

vue2.x中keep-alive源码解析(实例代码)

Keep-Alive模式避免频繁创建、销毁链接,允许多个请求和响应使用同一个HTTP链接,这篇文章主要介绍了vue2.x中keep-alive源码解析,需要的朋友可以参考下
2023-02-14

react源码中的生命周期和事件系统实例解析

这篇文章主要为大家介绍了react源码中的生命周期和事件系统实例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-03

Java notify唤醒源代码的示例分析

这期内容当中小编将会给大家带来有关Java notify唤醒源代码的示例分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Java notify唤醒在此对象监视器上等待的单个线程。相关的问题需要我们不断的
2023-06-17

laravel中软删除的实例分析

这篇文章主要介绍“laravel中软删除的实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“laravel中软删除的实例分析”文章能帮助大家解决问题。在laravel中,软删除指的是数据表记录并
2023-06-29

Android源码分析——ViewGroup的事件分发机制(二)

通过前一篇博客View的事件分发机制,从dispatchTouchEvent说起(一)的介绍相信大家对 Android View 事件的分发机制有了很深的理解。我们知道 Android 中 View 是存在于 Activity。 今天我们继
2022-06-06

Java中Handler源码的示例分析

这篇文章主要介绍了Java中Handler源码的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。从很早开始就认识到 Handler 了,只不过那时修为尚浅,了解的不够深
2023-06-02

java中CopyOnWriteArrayList源码的示例分析

这篇文章将为大家详细讲解有关java中CopyOnWriteArrayList源码的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。简介CopyOnWriteArrayList是ArrayList的
2023-06-29

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录