OpenHarmony南向开发实例:【游戏手柄】

 介绍

基于TS扩展的声明式开发范式编程语言,以及OpenHarmony的分布式能力实现的一个手柄游戏。

完成本篇Codelab需要两台开发板,一台开发板作为游戏端,一台开发板作为手柄端,实现如下功能:

  • 游戏端呈现飞机移动、发射子弹等效果。
  • 游戏端分布式拉起手柄端FA。
  • 手柄端与游戏端建立连接,发送指令给游戏端,比如移动飞机,发射子弹和释放技能等。

最终效果图如下:

搭建OpenHarmony环境

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以RK3568开发板为例,参照以下步骤进行:

  1. [获取OpenHarmony系统版本]:标准系统解决方案(二进制)。

    以3.1版本为例:

  2. 搭建烧录环境。

    1. [完成DevEco Device Tool的安装]
    2. [完成RK3568开发板的烧录]
    3. 鸿蒙开发指导:qr23.cn/AKFP8k参考前往复制转到。
  3. 搭建开发环境。

    1. 开始前请参考[工具准备],完成DevEco Studio的安装和开发环境配置。
    2. 开发环境配置完成后,请参考[使用工程向导]创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。
    3. 工程创建完成后,选择使用[真机进行调测]。 2.鸿蒙next文档籽料+mau123789直接去v拿取

搜狗高速浏览器截图20240326151547.png

分布式组网

本章节以系统自带的音乐播放器为例(具体以实际的应用为准),介绍如何完成两台设备的分布式组网。

  1. 硬件准备:准备两台烧录相同的版本系统的RK3568开发板A、B。

  2. 开发板A、B连接同一个WiFi网络。

    打开设置-->WLAN-->点击右侧WiFi开关-->点击目标WiFi并输入密码。

  3. 将设备A,B设置为互相信任的设备。

    • 找到系统应用“音乐”。

    • 设备A打开音乐,点击左下角流转按钮,弹出列表框,在列表中会展示远端设备的id。

    • 选择远端设备B的id,另一台开发板(设备B)会弹出验证的选项框。

    • 设备B点击允许,设备B将会弹出随机PIN码,将设备B的PIN码输入到设备A的PIN码填入框中。

    配网完毕。

代码结构解读

  • [HandleEtsOpenHarmony]
  • [GameEtsOpenHarmony]

本篇Codelab只对核心代码进行讲解,对于完整代码,我们会在参考章节中提供下载方式,首先介绍一下整个工程的代码结构:

└── HandleGameApplication│── GameEtsOpenHarmony│  └── HandleEtsOpenHarmony

其中HandleEtsOpenHarmony为手柄端工程代码,GameEtsOpenHarmony为游戏端工程代码。

HandleEtsOpenHarmony

  • MainAbility:存放应用主页面。

    • pages/index.ets:应用主页面。
    • common/images:存放图片资源的目录。
  • ServiceAbility:存放ServiceAbility相关文件。

    • service.ts:service服务,用于跨设备连接后通讯。

GameEtsOpenHarmony

  • MainAbility:存放应用主页面。

    • pages/index.ets:应用主页面。
    • common/images:存放图片资源。
  • model:存放获取组网内的设备列表相关文件。

    • RemoteDeviceModel.ets:获取组网内的设备列表。
    • GameElement.ets:游戏端界面元素的实体类,用于封装子弹、飞机等元素的属性。
  • ServiceAbility:存放ServiceAbility相关文件。

    • service.ts:service服务,用于跨设备连接后通讯。

实现手柄端功能

  1. 实现布局和样式。

    手柄端有两个功能:向游戏端发送指令和实时获取游戏端得分数据。界面上有三个功能组件:蓝色图形组件用于控制游戏端飞机移动方向,黄色图形组件用于发射子弹,绿色图形组件用于释放技能,效果图如下:

    主要代码如下:

    @Entry
    @Component
    struct Index {
    ...build() {Stack() {...Text('score:' + this.score)...Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Start, justifyContent: FlexAlign.SpaceBetween }) {Stack() {Image('/common/images/bigcircle.png').width(300).height(300)Image('/common/images/smallcircle.png').width(140).height(140).position({ x: this.smallPosX, y: this.smallPosY }) // 30+75-35}...Row() {Image('/common/images/a.png').width(160).height(160).margin({ right: 20, bottom: 80 })Image('/common/images/b.png').width(200).height(200)}.alignItems(VerticalAlign.Bottom)...}}
    }

  2. 实现摇杆功能。

    给摇杆(蓝色小圆图形)添加TouchEvent,动态改变摇杆position属性使摇杆跟随手指移动,主要代码如下:

    onTouchEvent(event: TouchEvent) {switch (event.type) {case TouchType.Down:this.startX = event.touches[0].screenX;this.startY = event.touches[0].screenY;break;case TouchType.Move:this.curX = event.touches[0].screenX;this.curY = event.touches[0].screenY;this.getSmallCurrentPos(this.curX - this.smallR - 60, this.curY - this.smallR - 60)angle = Math.round(this.calculateAngle());break;default:break;}
    }
    

  3. 计算摇杆偏移角度。

    主要代码如下:

    calculateAngle() {var angle = 0var degree = Math.atan(this.getDisAbsY() / this.getDisAbsX()) * 180 / Math.PIvar quadrant = this.quadrant();switch (quadrant) {case this.QUADRANT_1:// 向右上移动angle = degree;break;case this.QUADRANT_2:// 向左上移动angle = 180 - degree;break;case this.QUADRANT_3:// 向左下移动angle = -180 + degree;break;case this.QUADRANT_4:// 向右下移动angle = -degree;break;default:angle = 0;break;}return angle;
    }

  4. 连接游戏端Service。

    当手柄端被游戏端拉起时,获取游戏端传递的数据:游戏端deviceId和分数score。然后通过deviceId连接游戏端Service,主要代码如下:

    aboutToAppear() {// 当被拉起时,通过want传递的参数同步对端界面UIawait featureAbility.getWant((error, want) => {// 远端被拉起后,连接游戏端的serviceif (want.parameters.deviceId) {let remoteDeviceId = want.parameters.deviceIdconnectRemoteService(remoteDeviceId)}});
    }async function connectRemoteService(deviceId) {
    ...await featureAbility.connectAbility({'deviceId': deviceId,'bundleName': "com.huawei.cookbook",'abilityName': "com.huawei.cookbook.ServiceAbility",},{onConnect: onConnectCallback,onDisconnect: onDisconnectCallback,onFailed: onFailedCallback,},);
    }

  5. 通过RPC发送数据到游戏端。

    连接游戏端Service之后,摇杆角度angle和操作类型actionType(1为发射子弹,2为释放技能)发送给游戏端,主要代码如下:

    async function sendMessageToRemoteService() {
    ...let option = new rpc.MessageOption();let data = new rpc.MessageParcel();let reply = new rpc.MessageParcel();data.writeInt(actionType);data.writeInt(angle);await mRemote.sendRequest(1, data, reply, option);
    }

实现游戏端功能

  1. 实现布局和样式。

    游戏界面主要由玩家飞机、敌机、子弹和道具(降落伞)等组成,由于敌机和子弹都是多个的,所以使用ForEach来实现,主要代码如下:

    @Entry
    @Component
    struct Index {build() {Stack() {... ForEach(this.bullets, item => {Image(item.imgSrc).width(item.imgWidth).height(item.imgHeight).position({ x: item.positionX, y: item.positionY })}, item => item.timestamp.toString())ForEach(this.enemyPlanes, item => {Image(item.imgSrc).width(item.imgWidth).height(item.imgHeight).position({ x: item.positionX, y: item.positionY })}, item => item.timestamp.toString())Image('/common/images/planeOne.png').width(this.planeSize).height(this.planeSize).position({ x: this.planePosX, y: this.planePosY }).onTouch((event: TouchEvent) => {this.onTouchEvent(event)})Image('/common/images/props.png').width(this.propsSize).height(this.propsSize).position({ x: this.propsPosX, y: this.propsPosY })...}.height('100%').width('100%')}
    }

  2. 实现游戏端元素动画效果。

    飞机、子弹和道具等元素的移动是通过动态改变Image的position属性来实现的。使用定时器setInterval每隔16ms重新设置界面元素position属性的值,主要实现代码如下:

     startGame() {var that = thissetInterval(function () {   // 每60*16ms创建一个敌机if (that.num % 60 == 0) {that.createEnemyPlane()}// 移动子弹var bulletsTemp: GameElement[] = []for (var i = 0; i < that.bullets.length; i++) {var bullet = that.bullets[i]bullet.positionY -= 8// 当子弹移除屏幕外的时候,释放掉if (bullet.positionY > 0) {bulletsTemp.push(bullet)}}that.bullets = bulletsTemp// 移动飞机var enemyPlanesTemp: GameElement[] = []for (var j = 0; j < that.enemyPlanes.length; j++) {var enemyPlane = that.enemyPlanes[j]enemyPlane.positionY += 6// 当飞机移除屏幕外的时候,释放掉if (enemyPlane.positionY < that.screenHeight) {enemyPlanesTemp.push(enemyPlane)}}that.enemyPlanes = enemyPlanesTemp// 每隔 500*16ms显示降落伞if (that.num % 500 == 0) {that.getPropsFlag = truethat.propsPosY = -that.propsSizethat.propsPosX = Math.round((Math.random() * (that.screenWidth - that.propsSize)))}// 刷新道具位置if (that.propsPosY < that.screenHeight) {that.propsPosY += 6}that.checkCollision()}, 16);}

  3. 判断元素是否发生碰撞。

    在setInterval中改变元素位置的时候同时检测元素之间是否发生碰撞,子弹和敌机发生碰撞则分数值改变(摧毁小飞机加50分,摧毁大飞机加100分),玩家飞机和道具发生碰撞则道具加1,主要实现代码如下:

    checkCollision() {...for (var i = 0; i < this.enemyPlanes.length; i++) {var enemy = this.enemyPlanes[i];for (var j = 0; j < this.bullets.length; j++) {var bullet = this.bullets[j];var inside = this.isInside(bullet, enemy);// 发生碰撞if (inside) {enemy.imgSrc = '/common/images/boom.png'if (enemy.flag == 1) {this.score += 50sendMessageToRemoteService(that.score)} else if (enemy.flag == 2) {this.score += 100sendMessageToRemoteService(that.score)}// 清除子弹this.enemyPlanes.splice(i, 1);i--;enemy.flag = 3// 清除被子弹打中敌机that.bullets.splice(j, 1);j--;}}}// 飞机和降落伞是否发生碰撞var isGetProps = this.isInside(myPlane, props);if (isGetProps && this.getPropsFlag) {this.getPropsFlag = falsethis.bombNum++this.propsPosY = 2000}}

  4. 获取设备列表。

    点击界面右上角的“电脑”图标,调用registerDeviceListCallback()发现设备列表,并弹出设备列表选择框DeviceListDialog ,选择设备后拉起远端FA。DeviceListDialog 主要代码如下:

    @CustomDialog
    export struct DeviceListDialog {controller: CustomDialogControllerbuild() {Column() {Text("选择设备").fontWeight(FontWeight.Bold).fontSize(20).margin({ top: 20, bottom: 10 })List() {ForEach(deviceList, item => {ListItem() {Stack() {Text(item).fontSize(12).margin({ top: 10 })}.onClick(() => {startRemoteAbility(item)this.controller.close();}).padding({ left: 30, right: 30 })}}, item => item.toString())}.height("30%").align(Alignment.TopStart)
    ...}}
    }

  5. 拉起手柄端FA。

    点击设备列表获取远程设备id后,拉起手柄端FA,代码如下:

    function startRemoteAbility(deviceId) {var params = {deviceId: localDeviceId}var wantValue = {bundleName: 'com.huawei.cookbook',abilityName: 'com.huawei.cookbook.MainAbility',deviceId: deviceId,parameters: params};featureAbility.startAbility({want: wantValue}).then((data) => {console.info('[game] featureAbility.startAbility finished, localDeviceId=' + localDeviceId + '----deviceId:' + deviceId);// 拉起远端后,连接远端serviceconnectRemoteService(deviceId)});
    }

  6. 连接手柄端Service。

    拉起手柄端FA后,连接手柄端Service,代码如下:

    async function connectRemoteService(deviceId) {// 连接成功的回调async function onConnectCallback(element, remote) {mRemote = remote;}
    ...if (remoteDeviceModel.deviceList.length === 0) {return;}await featureAbility.connectAbility({'deviceId': deviceId,'bundleName': "com.huawei.cookbook",'abilityName': "com.huawei.cookbook.ServiceAbility",},{onConnect: onConnectCallback,onDisconnect: onDisconnectCallback,onFailed: onFailedCallback,},);
    }

  7. 通过RPC发送数据到手柄端。

    通过RPC将游戏分数发送给手柄端,主要代码如下:

    async function sendMessageToRemoteService(score) {console.log('[game]connectRemoteService sendMessageToRemoteService:')if (mRemote == null) {return;}let option = new rpc.MessageOption();let data = new rpc.MessageParcel();let reply = new rpc.MessageParcel();data.writeInt(score);await mRemote.sendRequest(1, data, reply, option);
    }
    

  8. Service发布公共事件。

    通过Service接收手柄端数据,然后使用CommonEvent模块将数据发送给FA,主要代码如下:

    class GameServiceAbilityStub extends rpc.RemoteObject {
    ...onRemoteRequest(code, data, reply, option) {console.log('[game]Service onRemoteRequest');var publishCallBack;if (code === 1) {// 读取手柄端发送的数据let actionType = data.readInt();let angle = data.readInt();reply.writeInt(100);var params = {actionType: actionType,angle: angle,}var options = {code: 1,data: 'init data',isOrdered: true,bundleName: 'com.huawei.cookbook',parameters: params}publishCallBack = function () {}// 发布公共事件commonEvent.publish("publish_action", options, publishCallBack);} return true;}
    }

  9. FA订阅公共事件。

    订阅公共事件,接收从Service发送的公共事件数据,actionType 为操作类型(1表示发送子弹指令,2表示释放技能指令),angle 为飞机移动的角度。接收到数据后执行手柄端发送的指令:移动玩家飞机、发射子弹和释放技能摧毁所有敌机,主要代码如下:

    subscribeEvent() {
    ...// 订阅公共事件回调function SubscribeCallBack(err, data) {let msgData = data.data;let code = data.code;
    ...// 处理接收到的数据datathat.actionType = data.parameters.actionType;that.angle = data.parameters.angle;if (that.actionType == 1) {that.createBullet()}if (that.actionType == 2) {if (that.bombNum > 0) {that.bombNum--that.destroyAllEnemy()}}if (that.angle != 0) {that.movePlaneByHandle()}}//创建订阅者回调function CreateSubscriberCallBack(err, data) {subscriber = data;//订阅公共事件commonEvent.subscribe(subscriber, SubscribeCallBack);}//创建订阅者commonEvent.createSubscriber(subscribeInfo, CreateSubscriberCallBack);
    }

鸿蒙开发岗位需要掌握那些核心要领?

目前还有很多小伙伴不知道要学习哪些鸿蒙技术?不知道重点掌握哪些?为了避免学习时频繁踩坑,最终浪费大量时间的。

自己学习时必须要有一份实用的鸿蒙(Harmony NEXT)资料非常有必要。 这里我推荐,根据鸿蒙开发官网梳理与华为内部人员的分享总结出的开发文档。内容包含了:【ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战】等技术知识点。

废话就不多说了,接下来好好看下这份资料。

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。鸿蒙OpenHarmony知识←前往。下面是鸿蒙开发的学习路线图。

针对鸿蒙成长路线打造的鸿蒙学习文档。鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

其中内容包含:

《鸿蒙开发基础》鸿蒙OpenHarmony知识←前往

  1. ArkTS语言
  2. 安装DevEco Studio
  3. 运用你的第一个ArkTS应用
  4. ArkUI声明式UI开发
  5. .……

《鸿蒙开发进阶》鸿蒙OpenHarmony知识←前往

  1. Stage模型入门
  2. 网络管理
  3. 数据管理
  4. 电话服务
  5. 分布式应用开发
  6. 通知与窗口管理
  7. 多媒体技术
  8. 安全技能
  9. 任务管理
  10. WebGL
  11. 国际化开发
  12. 应用测试
  13. DFX面向未来设计
  14. 鸿蒙系统移植和裁剪定制
  15. ……

《鸿蒙开发实战》鸿蒙OpenHarmony知识←前往

  1. ArkTS实践
  2. UIAbility应用
  3. 网络案例
  4. ……

最后

鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

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

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

相关文章

Windows 安装 Node.js 开发环境

一、简介 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;主要功能是编写像 web 服务器一样的网络应用。它使用事件驱动、非阻塞式 I/O 模型&#xff0c;可以优化应用程序的传输量和规模&#xff0c;非常适合在分布式设备上运行数据密集型的实时应用。 …

【C++】适配器· 优先级队列 仿函数 反向迭代器

目录 适配器&#xff1a;适配器的应用&#xff1a;1. 优先级队列&#xff1a;仿函数&#xff1a;更深入的了解仿函数&#xff1a;一个关于不容易被注意的知识点&#xff1a; 2. 反向迭代器&#xff1a;&#xff08;list为例&#xff09; 适配器&#xff1a; 我们先来谈来一下容…

【网络编程】如何创建一个自己的并发服务器?

hello &#xff01;大家好呀&#xff01; 欢迎大家来到我的网络编程系列之如何创建一个自己的并发服务器&#xff0c;在这篇文章中&#xff0c;你将会学习到在Linux内核中如何创建一个自己的并发服务器&#xff0c;并且我会给出源码进行剖析&#xff0c;以及手绘UML图来帮助大家…

素描石膏像:写实与抽象的美学对话

正文&#xff1a; 素描&#xff0c;作为绘画艺术的基础&#xff0c;承载着艺术家对世界的独特理解和表达。在素描石膏像的创作中&#xff0c;写实与抽象两种风格的碰撞&#xff0c;不仅展现了艺术家的技巧&#xff0c;更是一场美学的对话。 一、写实风格&#xff1a;细节的捕…

基于HMM隐马尔可夫模型的金融数据预测算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于HMM隐马尔可夫模型的金融数据预测算法.程序实现HMM模型的训练&#xff0c;使用训练后的模型进行预测。 2.测试软件版本以及运行结果展示 MATLAB2022A版本运…

CentOS 7 文件权限管理详解

CentOS 7 文件权限管理详解 在 CentOS 7 系统中,文件权限管理是一项至关重要的任务,它确保了系统安全、数据完整性和用户隐私。本文将详细介绍 CentOS 7 中的文件权限管理,包括相关文件和命令的使用。 一、文件权限基础 在 Linux 系统中,每个文件和目录都有与之关联的权…

简单工厂模式抽象工厂模式

一.简单工厂模式 1.什么是工厂模式 工厂模式的核心就是把对象的生产过程交给工厂来完成&#xff0c;当外部需要一个对象时&#xff0c;由工厂提供接口来获取对象&#xff0c;这样一来即使生产对象的过程变了&#xff0c;仍然不影响外部对对象的获取和使用。 2.举个栗子 定义…

云计算笔记

RAID的组合方式 RAID0&#xff1a;多个硬盘同时工作&#xff0c;可提供性能&#xff0c;无冗余机制 RAID1&#xff1a;数据保存多份&#xff0c;提供冗余机制&#xff0c;性能受到影响 RAID3&#xff1a;存在数据盘和单独校验盘&#xff0c;数据写入 至数据盘后需要运算且将…

吃透2000-2024年600道真题和解析,科学高效通过2025年AMC8竞赛

为帮助孩子科学、有效备考AMC8竞赛&#xff0c;我整理了2000-2004年的全部AMC8真题&#xff08;完整版共600道&#xff0c;且修正了官方发布的原试卷中的少量bug&#xff09;&#xff0c;并且独家制作成多种在线练习&#xff0c;利用碎片化时间&#xff0c;8个多月的时间足以通…

Django中的实时通信:WebSockets与异步视图的结合【第167篇—实时通信】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 在现代Web应用程序中&#xff0c;实时通信已经成为了必不可少的功能之一。无论是在线聊天、…

爆肝3k字!掌握Spring与Redis的高效交互:从Jedis到Spring Data Redis

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

甲辰年三月初九有感

甲辰年三月初九有感 雨多汇成洪&#xff0c;梅酸长几成&#xff1f; ​久旱微巨调&#xff0c;点积多少恒。 ​时光沙漏里&#xff0c;情境入己梦。 望山山外山&#xff0c;​登顶人为峰。

Spring Boot 多环境配置:YML 文件的三种高效方法

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Linux内核之WRITE_ONCE用法实例(四十八)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

单链表实现通讯录-三万字

声明 这一篇文章我会从单链表的概念&#xff0c;单链表的原理&#xff0c;一直到通讯录项目单链表的实现&#xff0c;再把单链表的专用题型系统的讲解一下&#xff08;文章较长&#xff09;。同时建议学习单链表之前可以学习一下顺序表&#xff0c;作为知识铺垫顺序表&#xf…

FreeRTOS 4.16作业

1、总结keil5下载代码和编译代码需要注意的事项 1&#xff09;使用STM32Cubemx创建工程 2&#xff09;先build编译排除错误 3&#xff09;点击魔术棒&#xff0c;选择Debug选项&#xff0c;找到使用的仿真器&#xff0c;选择ST-LINK仿真器&#xff0c;点击settings&#xff…

62、ARM/STM32开发板按键中断相关学习20240416

实现开发板上三个按键按下后触发中断&#xff0c;控制LED灯的亮灭。 【本次实验现象为&#xff1a;按键1&#xff08;key1&#xff09;控制开灯&#xff0c;key3控制关灯&#xff0c;key2按下LED灯闪烁五次】 代码&#xff1a; 头文件mykey.h: #ifndef __MYKEY_H__ #define…

【Java】@RequestMapping注解在类上使用

RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一。这个注解会将 HTTP 请求映射到控制器&#xff08;controller类&#xff09;的处理方法上。 Request Mapping 基础用法 在 Spring MVC 应用程序中&#xff0c;RequestDispatcher (在 Front Controller 之下) 这…

Google Guava第五讲:本地缓存实战及踩坑

本地缓存实战及踩坑 本文是Google Guava第五讲,先介绍为什么使用本地缓存;然后结合实际业务,讲解如何使用本地缓存、清理本地缓存,以及使用过程中踩过的坑。 文章目录 本地缓存实战及踩坑1、缓存系统概述2、缓存架构演变2.1、无缓存架构2.2、引入分布式缓存问题1:为什么选…

【HCIP学习】OSPF协议基础

一、OSPF基础 1、技术背景&#xff08;RIP中存在的问题&#xff09; RIP中存在最大跳数为15的限制&#xff0c;不能适应大规模组网 周期性发送全部路由信息&#xff0c;占用大量的带宽资源 路由收敛速度慢 以跳数作为度量值 存在路由环路可能性 每隔30秒更新 2、OSPF协议…