uni-app 的 API 与微信小程序 API 基本一致。掌握微信小程序 API 对后面的开发很有帮助。微信小程序 API 文档:https://developers.weixin.qq.com/miniprogram/dev/api/
注册微信小程序账号,获取到 AppID,我们后面配置的时候会用到。在 HBuilderX 工具栏,点击发行,选择小程序-微信。输入小程序名称和 AppID,单击发行就可以了。这样我们就会获得一个微信小程序的打包文件,接下来我们来发布微信小程序项目,打开微信小程序开发者工具,导入刚刚生成的微信小程序项目的打包文件,在微信小程序开发者工具中先测试一下,项目运行是否正常,项目测试没有问题后,点击右上角>>按钮,上传代码就可以发布微信小程序了,最后等待微信团队审核通过,别人就可以在线上访问到你的项目了。
我们希望操作系统能够同时处理多个任务,例如在播放网易云音乐的同时,也能在微信上聊天。计算机的核心处理硬件是 CPU,如果计算机只有一个 CPU,那么播放音乐和处理聊天软件都需要这个 CPU 去执行运算逻辑,那如何做到时间上的同时处理?答案是 CPU 在多个逻辑任务之间来回切换,因为切换速度太快,所以看起来做到了并发执行。传统操作系统的任务调度采用时间片轮转的方式,在执行一个任务达到时间限制时会暂停处理,然后切换到处理下一个任务。每一个任务执行的时间间隔就是一个时间片,被执行的任务处于运行状态,被暂停的任务处于就绪状态,如图所示:假设存在两个调度任务,CPU 调度产生并发的 "错觉"
Flask 是一个 Python 实现的 Web 开发微框架,但是这个“微”并不代表着 Flask 功能比较简陋、有所欠缺。微框架中的 “微” 意味着:Flask 旨在保持核心简单而易于扩展;Flask 不会替用户做出太多决策,比如使用何种数据库;Flask 的选项(比如使用何种模板引擎) 通常有多个,用户很容易替换。默认情况下,Flask 不包含数据库抽象层、模板引擎、身份认证或其它任何已有多种库可以胜任的功能,如下图所示。然而,Flask 支持用扩展来给应用添加这些功能,应用程序可以很方便的集成这些扩展。众多的扩展提供了数据库集成、表单验证、上传处理、各种各样的开放认证技术等功能。
乐观是一种积极的解决问题的态度。所谓乐观锁认为系统中的事务并发更新不会很频繁,即使冲突了也没事,大不了重新再来一次。基本思想:每次提交一个事务更新时,查看要修改的数据从上次读取以后有没有被其它事务修改过,如果修改过,那么更新就会失败。实现方案:在实体中增加一个版本控制字段,每次事务更新后就将版本 (Version) 字段的值加 1。Tips: 乐观锁本质就是版本控制管理的实现,记录的每一次更新操作都会以版本递增的方式进行记录。一个事务在更新之前,先获取记录的当前版本号,更新时,如果版本还是最新的则可以更新,否则说明有事务比你先更新,则需要放弃。或者重新查询到最新版本信息后再更新。所以,在乐观锁的实现中,冲突是常态。实现过程:在学生实体类中添加新属性,用来记录每次更新的版本号。public class Student implements Serializable {//省略…… @Versionprivate Long version;//省略…… }stu = (Student) session.get(Student.class, new Integer(1));System.out.println("当前版本号:"+stu.getVersion);//模拟延迟,如果在这个时间内有其它事务进行了更新操作,此事务的更新不会成功Thread.sleep(30000);stu.setStuName("Hibernate");transaction.commit();好了,悲观也好,乐观也好,只是一种解决问题的态度。对于这两种态度,咱们要总结一下。乐观锁:优势:性能好,并发性高。缺点:用户体验不好,可能会出现高高兴兴去更新,却告知已经有人捷足先登了。悲观锁:优势:锁住记录为我所用,没修改完成之前,其他事务只能瞪眼瞧着,时间虽然延迟,至少心里有底。缺点:并发性不好,性能不高。Hibernate 的其它性能优化:随时使用 Session.clear()及时清除 Session 缓存区的内容;1+N 问题 ( 一条 SQL 语句能解决的问题用了很多条 SQL 语句来实现) ;使用 Criteria 查询可以解决这个问题;Lazy 加载:需要时,使用 get() 方法发出 SQL 语句。使用类似于 from Student s left join s.classRoom c 的关联查询语句。缓存使用:在对象更新、删除、添加相对于查询要少得多时, 二级缓存的应用将不怕 n+1 问题,因为即使第一次查询很慢,之后直接缓存命中也是很快的,刚好又利用了 n+1。
定义:乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新的时候,才会正式对数据冲突与否进行检测。乐观锁的实现:依旧拿数据库的锁进行比较介绍,乐观锁并不会使用数据库提供的锁机制, 一般在表中添加 version 宇段或者使用业务状态来实现。 乐观锁直到提交时才锁定,所以不会产生任何死锁。Java 中的乐观锁:我们之前所学习的 CAS 原理即是乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。Tips:我们这里所说的对于乐观锁,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败。注意失败两字,失败意味着有操作,而悲观锁是等待,意味着不能同时操作。
可以看到,乐观锁虽然有缺陷,它会使更新失败,因此必须重复获取数据然后重试,但是它保证了数据的正确性和完整性。在读多写少的场景下,乐观锁不会出现太多的重试,当然如果出现了很多重试,证明场景已经可能不是读多写少了,可以尝试换方案了。乐观锁的实现也颇为简单,不需要任何第三方依赖,你完全可以自己直接实现,不过仍然有一些第三方框架提供了开箱即用的乐观锁,你可以根据自己的使用语言和生态去查找相应的乐观锁框架。
这里我们要给大家讲解一个好用的神奇的标签,就是 bgsound ,我们可以写一个这样的标签到网页的 body 标签里。 <bgsound src=""> 其中双引号中的部分,需要用你的本地音频文件目录位置进行替换。把这个标签粘贴到网页的 body 中,就可以为网页设置背景音乐了。由于是音频,所以老师就无法截图给大家说明了,大家只要按找老师说的方式去操作,就会很容易的给网页添加了背景音乐。
群聊指的是一个组内多个用户之间的聊天,一个用户发到群组的消息会被组内任何一个成员接收 。具体架构思路如下所示:群聊流程解析:群聊其实和单聊整体上思路都是一致的,都是需要保存每个用户和通道的对应关系,方便后期通过用户 ID 去查找到对应的通道,再跟进通道推送消息;那么如何把消息发送给多个组内的成员呢?其实很简单,服务端再保存另外一份映射关系,那就是聊天室和成员的映射关系。发送消息时,首先根据聊天室 ID 找到对应的所有成员,然后再跟进各个成员的 ID 去查找到对应的通道,最后由每个通道进行消息的发送;成员加入某个群聊组的时候,往映射表新增一条记录,如果成员退群的时候则删除对应的映射记录;通过上面的架构图发现,群聊和单聊相比,其实就是多了一份映射关系而已。
这是一种优化的读模式。乐观读模式相关的几个方法如下。tryOptimisticRead () 方法:非阻塞尝试乐观获取读锁,只有当写锁没有被获取时返回一个非 0 的 stamp 。乐观读取模式适用于短时间读取操作,降低竞争和提高吞吐量。在使用时一般需将数据存储到一个副本中,在后继处理中用于对比数据是否是最新状态;validate (long stamp) 方法:用于检查在获取到读锁 stamp 后,锁有没被其他写线程抢占。如果写锁没有被获取,那么 validate () 方法返回 true。可多次调用验证这一信息。另外,此类也提供了一组读写锁之间的转换方法:tryConvertToWriteLock (long stamp) 方法:尝试转换为写锁。转换条件:tryConvertToReadLock (long stamp) 方法:尝试转换为悲观读锁。tryConvertToOptimisticRead (long stamp) 方法:尝试转换为乐观读锁。注意此类的编程方法有这样一个共通特征:所有获取锁的方法,都返回一个邮戳(Stamp),Stamp 为 0 表示获取失败,其余都表示成功;所有释放锁的方法,都需要一个邮戳(Stamp),这个 Stamp 必须是和成功获取锁时得到的 Stamp 一致;下面我们举一个具体的编程例子。
本节通过GestureDetector完成一个类似微信聊天中的大图缩放功能,即通过双指往外或者向内的手势来控制图片的放大、缩小。
乐观锁的使用十分广泛,我们也推荐你在实际的开发中使用乐观锁,接下来,我们以一个例子来详细的说明一下乐观锁。我们新建一个测试数据表 imooc_order :DROP TABLE IF EXISTS imooc_order;CREATE TABLE imooc_order( id int PRIMARY KEY, price decimal(10,2), -- version 字段作为乐观锁版本控制位 version int NOT NULL DEFAULT 0);INSERT INTO imooc_order(id,price,version)VALUES (1,23.2,1);注意: 我们已经在表中添加了 version 字段
腾讯新闻<h1>习近平地方考察中,强调的重点内容有哪些</h1>网易云音乐<h1><a>网易云音乐</a></h1>
在日常工作学习中,我们经常需要把有限的内容按照一定的比例进行分开,比如一天的时间用来工作,学习,娱乐,休息这样的时间需要进行按比例合理划分。一般这样多比较占比的时候大家一般都会使用饼图表示。饼图又称饼状图,用于描述量、频率或百分比之间的相对关系。
微前端 尚处在发展时期,其核心概念和 微服务 相似。现阶段较为常用的微前端框架为 single-spa 和 qiankun,后者是基于前者实现的。该技术能做到 技术栈无关,即一个应用,能由多个不同技术的子应用构成,同时做到子应用的相互隔离,这里的隔离就可以选择采用 Web Components 实现。
1. 独立的页面维护了全局 错误码错误码由5位整数构成2. 每个接口一个独立的 参数说明页面正常情况下出参只返回业务实体异常情况才有 errCode errMsg每个接口下也可能有自己的业务错误码
咱们打开新浪微博,然后随便找个带皇冠的,按下F12键(Mac用户按command+option+i)然后选中控制台的箭头,再点击皇冠:再点开这个图片可以发现:可以看到那些大 V 图标皇冠图标以及各种微博认证等图标,都是放在了一张雪碧图里(即使再牛的大 V,身份标志也是放在雪碧图中的)。
乐观与悲观是两种不同的态度,从名字上看,二者就是以开发者的态度作为边界来分类的。乐观锁认为,同一数据在并发条件下,发生冲突是小概率事件,因此我们不加锁,而是加上版本号判断修改是否成功。悲观锁认为,同一数据在并发条件下,冲突是大概率事件,因此我们必须先加锁,不允许别人修改。悲观锁和乐观锁其实是一种思想,主要取决于开发者对待它的态度。在锁这一小节中,里面谈到的所有锁宏观上(可能实现的思想是乐观锁)来说都是悲观锁,因此一旦加锁,都会锁定数据,直到解锁才会释放。
不同的系统会有不同的默认 Button 样式,但是它们都有一个共同点——丑。相比于 TextView 而言,Button 是一个互动感很强的控件,除了设置字体字号,还需要有形状、背景、颜色、点击态等等样式变化。特别是在游戏及娱乐类 App 中 ,Button 的样式及变化效果将直接影响用户体验,所以这里将重点讲解几种设置样式的方法。
乐观锁不全依赖于数据库,一般情况下我们都是在代码层面上来完成它的,主流的设计思路是这样的:我们在数据表中添加一个字段version,version 代表版本号,字段类型为整型。当我们获取数据时,假设得到它的version字段为n,执行完其它操作对该数据进行更新时,会执行UPDATE ... SET version=n+1 WHERE version=n。如果在更新时,数据已经被别人更新过了,那么该数据的version字段已经不是n了,那么此时修改就会失败,反之修改就会成功。可以看到,乐观锁就像它的名称一样乐观,适合数据读多写少的场景,因为实际上并没锁住数据,所以性能十分可观;而悲观锁则与之相反,适合写多读少的场景,盲目的排他性一定程度上会大幅影响性能。
为了更好地理解悲观锁与乐观锁,我们通过设置一个简单的示例场景来进行分析。并且我们采用悲观锁 synchronized 和乐观锁 Atomic 操作进行分别实现。Atomic 操作类,指的是 java.util.concurrent.atomic 包下,一系列以 Atomic 开头的包装类。例如 AtomicBoolean,AtomicInteger,AtomicLong。它们分别用于 Boolean,Integer,Long 类型的原子性操作。Atomic 操作的底层实现正是利用的 CAS 机制,而 CAS 机制即乐观锁。场景设计:创建两个线程,创建方式可自选;定义一个全局共享的 static int 变量 count,初始值为 0;两个线程同时操作 count,每次操作 count 加 1;每个线程做 100 次 count 的增加操作。结果预期:最终 count 的值应该为 200。悲观锁 synchronized 实现:public class DemoTest extends Thread{ private static int count = 0; //定义count = 0 public static void main(String[] args) { for (int i = 0; i < 2; i++) { //通过for循环创建两个线程 new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { synchronized (DemoTest.class){ count++; } } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); }}结果验证:200乐观锁 Atomic 操作实现:public class DemoTest extends Thread{ //Atomic 操作,引入AtomicInteger。这是实现乐观锁的关键所在。 private static AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } //每个线程让count自增100次 for (int i = 0; i < 100; i++) { count.incrementAndGet(); } } }). start(); } try{ Thread.sleep(2000); }catch (Exception e){ e.printStackTrace(); } System.out.println(count); }}结果验证:200代码解读:此处主要关注两个点,第一个是 count 的创建,是通过 AtomicInteger 进行的实例化,这是使用 Atomic 的操作的入口,也是使用 CAS 乐观锁的一个标志。第二个是需要关注 count 的增加 1 调用是 AtomicInteger 中 的 incrementAndGet 方法,该方法是原子性操作,遵循 CAS 原理。
本小节通过一个具体的例子,讲解 CSRF 工具的步骤。案例的假设如下:存在一个银行网站,提供在线转账功能;用户登录银行后,才可以使用在线转账功能;银行中存在两个账户:受害者账户、攻击者账户;存在一个恶意网站,当访问该恶意网站时,会自动向银行网站提出转账请求。如果受害者没有退出银行网站的情况下访问恶意网站,因为受害者已经通过了银行网站的身份验证,因此发出的转账请求会通过银行网站的授权,即攻击者完成了攻击。
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样其他线程想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和 CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。
首先在项目初期,测试人员提前介入,进行接口测试,模拟客户端与服务端的交互。有问题提前抛出来。保证接口调用是没问题的。其次,接口测试完成之后再进行系统测试,会轻松很多。明白了各个接口在做什么,各个参数的模拟实际上就是业务场景的模拟。系统测试出现一些问题可以更快速的定位是客户端还是服务端出问题。另外,接口是获取和操作资源的方式,而大部分系统和产品中,资源一般都是产品的核心,比如微信核心资源就是通讯录关系链和聊天记录等,因此资源是必测的。而接口中大部分的内容是数据,通过数据的对比我们能推测到系统和产品的逻辑,测接口就是测逻辑。最后接口中的返回相对单纯,不像 web 页面,HTML 代码中有太多 UI 的东西,UI 最不稳定,变化太快,接口相对稳定一点点,但是里面的干扰信息更少,断言相对容易很多。
只需要配置常规的邮件收发信息即可。实例:# 网易邮箱发件服务器spring.mail.host=smtp.163.com# 网易邮箱发件端口spring.mail.prot=25# 发件人账号spring.mail.username=taqsxxkj@163.com# 发件授权密码,注意授权码是用于登录第三方邮件客户端的专用密码spring.mail.password=123456spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory# Spring Boot Admin 发件收件信息spring.boot.admin.notify.mail.from=taqsxxkj@163.comspring.boot.admin.notify.mail.to=taqsxxkj@163.comspring.boot.admin.notify.mail.cc=taqsxxkj@163.com此处特别注意发件授权密码不是普通邮箱的登录密码,而是授权密码,以网易邮箱为例在下图位置设置。网易邮箱授权密码设置
业务场景: 模拟微信聊天,每个客户端和服务端建立连接,并且可以实现点对点通信(单聊),点对多点通信(群聊)。设计思路: 我们要实现的是点(客户端)对点(客户端)的通讯,但是我们大部分情况下接触的业务都是客户端和服务端之间的通讯,客户端只需要知道服务端的 IP 地址和端口号即可发起通讯了,那么客户端和客户端应该怎么去设计呢?思考:难道是手机和手机之间建立通讯连接,互相发送消息吗?这种方案显然不是很好的方案,第一: 客户端和客户端之间通讯,首先需要确定对方的 IP 地址和端口号,显然不是很现实。第二: 即使有办法拿到对方的 IP 地址和端口号,那么每个点(客户端)既作为服务端还得作为客户端,无形之中增加了客户端的压力。其实,我们可以使用服务端作为中转站,由服务端主动往指定客户端推送消息,如果是这种模式的话,那么 Http 协议是无法支持的,Http 是无状态的,只能一请求一响应的模式,只能使用 TCP 协议去实现了。
本节内容功能稍微有点多,主要是实现了群聊的几个核心功能,分别是创建群组、查看群组列表、加入群组、退出群组、查看成员列表、群发消息。功能希望大家可以亲自动手实现一遍。
ViewPager 是一种可以让用户通过左右滑动来切换页面的控件,通过它我们可以展示超过屏幕尺寸大小的内容,在某种程度上它可以说是实现多页面的最佳方式,同时 ViewPager 还支持任意动态的添加/删除页面。比如我们可以将不同的类别的内容分别放在不同页面当中,然后通过滑动切换不同的类别从而给用户展示不同的页面,这个在类似百度App等新闻类App中非常适用。在 ViewPager 中插入“娱乐”、“国际”、“体育”、“星座”等等新闻类别,然后在不同的 View 中展示不同的新闻内容,还可以根据用户的喜好动态增加/删除某些页面,接下来就一起来看看如何完成多页视图。
工厂模式是平时开发过程中最常见的设计模式。工厂模式解决类的实例化问题,它属于创建型模式。工厂模式也经常会和其他设计模式组合使用。试想你去麦当劳买一个汉堡。你只需要告诉收银员要一个xx汉堡。过一会就会有一个此类型的汉堡被制作出来。而你完全不需要知道这个汉堡是怎么被制作出来的。这个例子中你就是客户端代码,麦当劳就是工厂,负责生产汉堡。汉堡是接口,而具体的某一种汉堡,比如说香辣鸡腿堡,就是实现了汉堡接口的类。我们继续通过另外一个例子,深入理解工厂模式。现在我们给某款音乐软件开发一个推荐功能。需求是能够根据用户选择的音乐风格,推荐不同风格的歌曲清单。那么你打算怎么实现呢?
简易框架主要模拟 3 个组件,分别对应 Hibernate 中的 Configuration、SessionFactory、Session 组件。Configuration 组件的作用:解析配置文件中的信息。Tips: 简易框架中,保持配置文件格式及内容和 Hiberante 的主配置文件一致。查看一下 Configuration 类的结构:public class Configuration { //保存配置文件中的 property 元素信息 private Map<String,String> pros; //保存配置文件中的映射类 private List<String> mappings; public Configuration() { this.pros=new HashMap<String, String>(); this.mappings=new ArrayList<String>(); } /** * 读取配置文件 */ public void configure() throws DocumentException { configure("/hibernate.cfg.xml"); } private void configure(String resource) throws DocumentException { InputStream stream =Configuration.class.getResourceAsStream(resource); doConfigure( stream); } /** * 使用 DOM4j XML 解析器解决配置文件中的信息 */ private void doConfigure(InputStream stream) throws DocumentException { SAXReader saxReader=new SAXReader(); Document doc= saxReader.read(stream); Element rootEle= doc.getRootElement(); Element sfEle= rootEle.addElement("session-factory"); List<Element> propertys= sfEle.elements("property"); for (Element ele : propertys) { this.pros.put(ele.attributeValue("name"), ele.getText()); } List<Element> mappings= sfEle.elements("mapping"); for (Element m : mappings) { this.mappings.add(m.attributeValue("class")); } } /** *创建会话工厂 * */ public SessionFactory buildSessionFactory() { return new SessionFactory(this.pros,this.mappings); }}Hibernate 的主配置文件中有很多配置项,因受限于文章篇幅和本文初设目标,这里只解析 2 类信息:数据库连接信息;实体类路径信息。基础好的学习者可以查阅 Hibernate 的源代码。