生活加油,摘一句子,
如果有一天你不再寻找爱情,只是去爱;你不再渴望成功,只是去做;你不再追逐成长,只是去修;一切才真正开始——纪伯伦
2019.9.9
书的pdf资源在最后。
第一章,后端技术导言
一,后端基础设施
1,请求统一入口一-API 网关:
在移动 APP 的开发过程中,通常后端提供的接口 需要以下功能的支持。:
一般的做法是, 使用 Nginx 做负载均衡 ,然后在每个业务应用里做 API 接口的访问权 限控制和用户鉴权 ,更优化一点的方式则是把后两者做成公共类库供所有业务调用
2,业务应用和后端基础框架:
业务应用分为在线业务应用和内部业务应用 ,分别介绍如下。 · 在线业务应用 : 直接面向互联网用户的应用、接口等 ,典型的特点就是请求量大、 高并发、对故障的容忍度低。 · 内部业务应用 :主要面向公司内部用户的应用 。 比如,内部数据管理平台、广告投 放平台等。 相比在线业务应用,其特点是数据保密性高、压力小、并发量小、允许 故障的发生。
业务应用是基于后端的基础框架进行开发的,针对 Java 后端框架:
MVC 框架 : 是统一开发流程、提高开发效率、屏蔽一些关键细节的 Web/后端框架。 典型的 MVC 框架如 SpringMVC、 Jersey、国人开发的 JFinal 以及阿里的 WebX。
loC 框架 : 可实现依赖注入/控制反转的框架。 Java 中最流行的 Spring 框架的核心 就是 IoC 功能。
ORM 框架 : 是能够屏蔽底层数据库细节、提供统一的数据访问接口的数据库操作框 架,另外也能够支持客户端主从、分库、分表等分布式特性。 MyBatis 是目前最流行的 ORM 框架。 此外, Spring ORM 中提供的 JdbcTemplate 也很不错
缓存框架 : 对 Redis、 Memcached 这些缓存软件操作的统一封装,能够支持客户端 分布式方案 、 主从等。 一般使用 Spring 的 RedisTemplate 即
3,缓存、数据库、搜索引擎、消息队列
缓存、数据库、搜索引擎、消息队列这 4 者都是应用依赖的后端基础服务 ,
缓存: 缓存通常被用来解决热点数据的访问问题,是提高数据查询性能的强大武器。 在高并发的后端应用中,将数据持久层的数据加载缓存中,能够隔离高井发请求 与后端数据库,避免数据库被大量请求击垮。 目前除了内存中的本地缓存,比较普 遍使用的缓存软件有 Memcached 和 Redis, 其中 Redis 已经成为最主流的缓存软件
数据库: 数据库可以说是后端应用最基本的基础设施。 基本上绝大多数业务数据都 是持久化存储在数据库中的。 主流的数据库包括传统的关系型数据库( MySQL, PostgreSQL )以及最近几年开始流行的 NoSQL ( MongoDB、 HBase ) 。
搜索引擎 :搜索引擎是针对全文检索以及数据各种维度查询设计的软件。 目前用得 比较多的开源软件是 Solr 和 Elasticsearch,它们都是基于 Lucene 来实现的,不同之 处主要在于 Term Index 的存储、分布式架构的支持等。 Elasticsearch 由于对集群的 良好支持以及高性能的实现,已经逐渐成为搜索引擎的主流开源方案
消息队列 :数据传输的一种方式就是通过消息队列。 目前用得比较普遍的消息队列 包括为日志设计的 Kafka 以及重事务的 RabbitMQ 等。
4,文件存储
文件存储需要满足的特性有 : 可靠性、 容灾性、 稳定
5,统一认证中心
统一认证中心,主要是对 APP 用户、内部用户 、 APP 等的认证服务,包括
用户的注册、登录验证、 Token 鉴权。
内部信息系统用户的管理和登录鉴权。
APP 的管理,包括 APP 的 secret 生成、 APP 信息的验证(如验证接口签名)等。
通过统一认证中心构建移动 APP 的单点登录 也是 水到渠成的事情:模仿 Web 的机制,将认证后的信息加密存储到本地存储中 ,供多个 APP 使用。
6,单点登录系统:
通俗地说就是只需要一次用户 登录,就能够进入多个业务应用(权限可以不相同),非常方便用户操作。 而在移动互联 网公司中,内部的各种管理、 信息系统,甚至外部应用同样也需要单点登录系统。
7,统一配置中心 :
在 java后端应用中, 一种读/写配置比较通用的方式就是将配置文件写在 Propeties,能够在线动态修改配置文件并生效。
在很多业务中,定时调度是一个非常普遍的场景, 比如定时去抓取数据、 定时刷新 订单的状态等。 通常的做法就是针对各自的业务,依赖 Linux 的 Cron 机制或者 Java 中的 Quartz 来实现调度
8,服务治理框架
对于外部 API 调用或者客户端对后端 API 的访问,可以使用 HTTP 协议或者阻STful (当然也可以直接通过最原始的 Socket 来调用) 。 但对于内部服务间的调用, 一般都是通 过 RPC 机制来进行的。 目前主流的 RPC 协议有:
RMI。
Hessian。
Turift。
Dubbo
9,统一的调度中心
在很多业务中,定时调度是一个非常普遍的场景, 比如定时去抓取数据、 定时刷新 订单的状态等。 通常的做法就是针对各自的业务,依赖 Linux 的 Cron 机制或者 Java 中的 Quartz 来实现调度
10,统一的日志服务
可以通过实现 Log4j 或者 LogBack 的 appender 来实现统一 日志框架,然后通过 RPC 调 用将日志打印到日志服务器上。
11,数据基础设施
数据高速公路,离线数据分析,实时数据分析。
二,后端技术概览。
软件开发的核心原则;
Don‘t Repeat Yourself 《不要重复自己 ,即不要做重复性劳动,也是 现在所说的“极客文化”的一种。 代码重复、 工作重复在软件开发中都是不合理的 存在,利用各种手段消除这些重复是软件开发的一个核心工作准则
Keep it simple stupid:《保持简单,愚蠢 ,KISS原则,。 在做软件设计的工作中,很多时候都不要想得 过于复杂,也不要过度设计和过早优化,用最简单且行之有效的方案也就避免了复 杂方案带来的各种额外成本。 这样既有利于后续的维护 , 也有利于进一步的扩展。
You Ain’t Gonna Need It:《你不需要它 ,即 YAGNI 原则。 只需要将应用程序必需的功能包含进来, 而不要试图添加任何其他你认为可能需要的功能。 因为在一个软件中,往往 80% 的 请求都花费在 20% 的功能上。
Done is better than perfect:《完成总比完美好 ,在面对一个开发任务时,最佳的思路就是先把东西做出 来,再去迭代优化。 如果一开始就面面俱到, 考虑到各种细节,那么很容易钻牛角 尖而延误项目进度。
Choose the most suitable things:《选择最合适的东西, 这是在做方案选择、 技术选型时候的一个很重要 的原则。 在面对许多技术方案、开源实现的时候,务必做到不能盲目求新,要选择 最合适的而非被吹得天花乱坠的
软件开发的过程管理:
项目管理 : 项目管理对于一个软件的开发是非常重要的 , 能够保证项目进度有条不 紊地进行, 在可控的时间内以一定的质量交付。 瀑布开发模型、螺旋开发模型是传 统的项目管理模型。 在互联网的开发工作中,敏捷开发则是比较受推崇的开发方式。 所谓的敏捷开发即快速实现原型,然后快速迭代。 Scrum 是目前普遍流行的敏捷开 发方式之一。
测试驱动开发 :在平时的开发过程中, 目前比较流行也是行之有效的一种方式就是 Test Driven Develop,即测试驱动开发。 这种方式的核心就是编写单元测试。 简单来 讲,就是先完成某一个功能的单元测试用例,然后在逐步消除测试用例的编译错误 的过程中完成功能的开发。
持续集成 : 某一个软件功能完成开发之后,后续还有测试、预发布、部署等过程。 整个过程被称为集成,而持续集成指的是无须人工干预即可不断地进行这个过程。 Jenkins 、 Quick Build 都是比较典型的持续集成工具。
日常的开发工具:
·编辑器: 开发中现在用得比较多的编辑器包括 Emacs、 Vim 和 SublimeText
·源码版本管理::代码的版本管理工具由 CVS 到 SVN 再到现在的 Git
·项目工具: GitHub 是一个第三方 Git 中央仓库
应用的运行环境
Linux :说到后端服务器肯定绕不开 Linux。 至少现在互联网的后端服务绝大多数都 是部署在 Linux 的各种服务器版本中的。 其中 CentOS、 Ubuntu 以及 Debian 是用得 比较多的版本。
应用服务器 : 就 Java 来讲,很多时候开发的都是 Web 应用,以 HTTP 协议对外提 供服务。 除了对性能要求比较苛刻的情况下需要自己构建 HTTP 服务外,大部分情 况需要依赖于支持 Java 程序的应用服务器。 目前最常用的有: Tomcat、 Jetty
负载均衡 : 在高并发流量环境下,后端服务会以集群的模式对外提供服务。 在集群 的前面,需要负载均衡器将请求分配到集群的各个节点上。 LVS 是最流行的四层负 载均衡软件, HAPr。可是另一个既支持四层又支持七层负载均衡的软件, Nginx 则 是七层负载均衡最流行的解决方案
虚拟化 : 虚拟化技术是前几年经常用来做私有云的一种技术,即将自己的物理主机 通过虚拟化技术分裂为多个虚拟主机,以隔离资源
常用第二方服
laaS: Infrastructure as a Service,是云计算最开始的一种模式,现在基本上所有的云 服务商都提供 IaaS 服务
PaaS : Platform as a Service,即只需要提交代码到指定的运行环境,其他的诸如代 码打包、 部署、 IP 绑定等都由平台完成。
域名: 有一个可以提供服务的应用后,域名也是一个必需的基础设施
CON :内容分发网络,即就近请求的一种技术实现。 服务提供方将会被大量访问的 内容在全国的多个节点都做缓存
邮件发送 :这主要依赖邮件服务器,通过 SMTP 协议就可以实现发送。
短信发送 :使用短信发送验证码、营销短信是很常见的应用场景
消息推送 :应用上,推送已经成为一个标配功能。 目前个推应该是第三方推 送服务中的佼佼者
开放平台 :通过开放平台,可以使用 OAuth 等协议获取用户在第三方平台上的信息, 以实现第三方平台登录等。 目前,微博、微信、 QQ 是最常见的第三方登录方式,
支付接口 : 支付接口是很多内置购买功能软件的必备组件。
计算机基础科学知识
数据结构,数据结构是组成程序的基础。 经典的数据结构有字符串、数组、链表、 I喻希表、树(二叉树、平衡树、红黑树、 B 树)、堆枝、队列、图
算法,:经典的排序和查找算法在平时的开发工作中经常会用到,如冒泡排序、插入 排序、选择排序、归并排序、快速排序、希尔排序、堆排序以及二分查找等。 此外, 在函数/方法的算法实现中要注意递归和迭代各自的优缺点。 而衡量算法性能的主 要因素包含空间复杂度和时间复杂度。
业务相关算法,,业务中还会经常涉及一些更复杂的算法, 如压缩算法、 LRU 缓存算法、缓存一致性、编译原理中的状态机等。
计算机网络,TCP/IP 协议是网络最根本的协议, 其七层/四层协议拢的设计都是非 常精华的东西,连接的建立、断开以及连接的各种状态的转换都是排查、解决网络 问题的根本依据
设计模式。经典的工厂模式、简单工厂模式、单例模式、 观察者模式、代理模式、建筑者模式、 门面模式、适配器模式、装饰器模式
数据处理相关技能
高速缓存 :目前用得最广泛的缓存软件 Redis 能够支持丰富的数据结构,如字符串 、 列表、有序集合等多种数据的存储。 了解缓存实现的原理、内存淘汰的策略能够更 好地使用缓存:
数据库 : 掌握数据库的很大一个关键点就在于对索引的使用 ,可以说,正确地使用 索引就基本等于掌握了数据库的使用。 目前绝大多数数据库都使用 B 树作为索引 的数据结构,目的就是为了利用磁盘顺序读/ 写的特性
搜索引擎 : 搜索引擎主要应对全文检索以及多维度查询 的业务场景。 掌握搜索引擎 使用的数据结构、集群方式、配置的关键点,有助于更好地使用搜索引擎服务于业 务应用。
消息队列 :消息队列有两种角色,即生产者和消费者 ,两种角色对于消息队列的需 求也不一样。 其中,对于消费者来说,消息消费的方式包括发布-订阅和队列两种。 消息队列在语义保证上分为 At Most Once、 At Least Once、 Exactly Once 共 3 种模式
数据存储和处理 :数据存储下来最终还是要用于做分析和处理的。 数据的处理分为 离线处理和实时处理
Java 编程知识
IDE :目前用得最多的java IDE 当属 Eclipse 和 Intellij IDEA
核心语法 :目前用得最多的当属 JDK 6 的 Java 语法。 而 Java 7 则又引入了 try-withresource、 switch string、 diamonds 等语法; Java 8 则又引入了 Lambda、 Stream 等语法。
集合类 :集合类是 Java 语言中非常精华的部分,包括 HashMap、 ArrayList、 LinkedList、 Hash Set、 TreeSet 以及线程安全的 Concurren旧ashMap、 ConcurrentLinkedQueue 等线程安 全集合。 了解它们的实现原理、查询、 修改的性能和使用场景是非常必要的
·工具类: Google Guava、 Apache Commons、 FastJson 提供了很多 JDK 本身没有的 工具类、 集合等。
高级特性 :抛开java核心的基本编程,并发编程、泛型、网络编程、序列化 RPC 都属于 Java 的高级编程特性。
Java EE : Java EE 现在是 Java 应用最普遍的一个领域。 Servlet 是 Java EE 中最根本的 组件之一。 而 Servlet 3.0 带来的异步 Servlet 提高了其处理请求的性能。
项目构建 : 目前用得最多的 Java 项目构建工具包括 Maven 和 Gradle
编程框架 : Spring 是 Java 编程中避不开的一个框架,发展到现在除了 Spring 核心的 IoC、 AOP 之外, Spring MVC、 Spring Data、 Spring Cloud 等都给 Java 开发者们带 来了开发上的便利,大大提高了开发效率。 除此之外, 框架 ORM 中MyBatis 也是 Java 领域比较火的框架之一
测试 : 测试是任何编程都需要的一步。 黑盒测试主要指的是通常进行的功能测试, 自盒测试则主要指的是对代码功能、质量进行的测试。 此外,关键的单元测试则是 开发工程师需要着重注意的地方,“测试驱动开发,JU nit 是目前 Java 中实现单元测试的主流方案。
虚拟机实现 : Java 的虚拟机实现除了我们常用的 Hotspot 外,还有 JRockit、 J9 以及 移动平台的 DalvKit。
类加载机制: JVM 的类加载机制遵循双亲委派原则,即当前类加载器需要先去请求 父加载器加载当前类,无法完成才自己去尝试进行加载。
运行时内存组成 :程序计数器、堆械、方法区、堆 、堆外内存,共同组成了 JVM 的运行时内存。
Java 内存模型 : Java 的主内存+线程私有内存的模型是线程安全问题产生的根本。
GC 原理和调优 :如果想在某些场景下发挥 GC 的最大性能,能做的就是 对 GC 的各种参数做优化配置,如新生代和老年代的垃圾回收器选择、各种垃圾回 收参数的配置等。
性能调忧和监控工具 : JDK 自带了很多强大的调优和监控工具,包括 jmap、 jstack、 jcmd、 JConsole、 jinfo 等
系统架构演化
单体应用 : 当应用规模、团队规模比较小的时候,只需要一个包括了所有功能的应用。 这样可减少部署节点,也减少部署成本。 此时, 对数据库的 ORM 操作是架构实现 的关键点。 ·
垂直应用 : 当应用的用户规模越来越大,请求量越来越高的时候,单体应用增加节 点带来的资源浪费会凸现出来,因为绝大多数接口请求量并不是特别大,根本没必 要扩充到多个节点,完全可以将单体应用拆分成互不相关的几个应用,分别对外提 供服务。 此时,加速每个应用开发的 MVC 框架是架构实现的关键点。 ·
分布式服务 : 当垂直应用越来越多时,应用之间的交互不可避免。 要考虑抽离核心 业务单独部署,逐渐形成稳定的服务中心。 而随着团队规模的相应扩大,服务会随 着团队的增多变得越来越多,粒度会变得越来越小,也就逐步形成了分布式服务的 架构,而当粒度细到某种程度、服务数量多到一定程度 ,则可以称之为微服务。 即 在设计好业务边界之后将原来的单体应用分解成一个个细粒度的服务,彼此之间通 过某种方式进行通信。微服务架构的关键在于如何做好服务的治理、调度、维护工作。 目前, Dubbo 算是微服务架构中用得比较多的框架,但 Dubbo 仅仅解决了微服务 架构中的一部分问题。 另外, Spring Cloud 则基本上涵盖了微服务架构的各个方面
典型的部署架构
对于 Web 应用来说, LVS+Nginx+Tomcat+MySQL+Redis 即可构成一个简单、通用的部 署架构。
LVS 作为最前置的节点 ,负责在网络第四层转发流量、负载均衡。 多个 LVS 使用 Keepalived 互为主备实现高可用。
Nginx 作为反向代理 ,负责在网络第七层转发流量、负载均衡。
Tomcat 作为业务容器 ,主要的应用代码都放在这里面。
Redis 作为缓存 , 隔离高并发请求和后端数据库。
MySQL 以主从模式对数据做持久化 。其中,虚线部分是数据库层,采用的是主从模式。 也可以使用 Redis Cluster ( Codis 等)以及 MySQL Cluster ( Cobar 等)来替换。
第2章 Java 项目与工程化
项目构建
传统构建工具一-Ant
主流构建工具一-Maven :Maven 是继 Ant 后出现的一款基于约定优于配置原则的项 目构建,约定的一些规范无须再配置,例如其约定好的生 命周期、项目结构等。 当然, Maven 也提供了打破默认约定的配置方法。 工具。
依赖管理 : Maven 能够帮助我们解决软件包依赖的管理问题,不再需要提交大量的 jar 包,引入第三方库也不需要关心其依赖
规范目录结构 : 标准的目录结构有助于项目构建的标准化,使得项目更整洁,还可 通过配置 profile 根据环境的不同读取不同的配置文件。
完整的项目构建阶段 : Maven 能够对项目完整阶段进行构建。
支持多种插件 : 面向不同类型的工程项目提供不同的插件。
方便集成 : 能够集成在 IDE 中方便使用,和其他自动化构建工具也都能配合使用
配置文件 :Maven 基于 POM (Progect Object Model )进行。 一个项目所有的配置都被放置在 porn. xml 文件中,包括定义项目的类型、名字,管理依赖关系,定制插件的行为等
Maven 使用 groupld:artifactld:version 三者来标识一个唯一的二进制版本,可以缩写 为 GAV。
packaging 代表打包方 式,可选的值有 porn、 jar、 war、 ear、 custom,默认值是 jar。
properties 是全局属性的配置。
dependencies 是对于依赖的管理。
plugins 是对于插件的管理。 此外,可以通过 parent 实现 POM 的继承以完成统一配置管理,子 POM 中的配置优先 级高于父 POM。
标准 Web 项目结构 :
src/main/java Java 代码目录
src/main/resources 配置文件目录
src/main/webapp webapp 根目录
src/test/java测试代码目录
src/test/resources 测试配置目录
target/ classes 代码编译结果目标目录
target/test-classes 测试代码编译结果目标目
依赖管理 :
依赖管理是通过 < dependencies 〉来定义的,:
一项 jar 包依赖可以由 groupld:artifactld:version 标识。
完整的标识为: groupld:artifactldtype:classifier:version。
依赖在编译部署中参与的情况可以由 scope 来指定,分为 compile、 test、 provided、 system、 import, 默认值是 compile。 其中的 import 是在 Maven 2.0.9 后引人的,仅 仅支持在 < dependencyManagement > 中使用,导人外部的依赖版本管理。
依赖是一个树状结构, 采用最近侬赖原则也可以通过 exclusions标签来排除一些包。 这里的最近依赖指的是在依赖树中, 离当前节点最近的依赖优先级高,同样远时第 一个优先。
常用插件 Maven 提供了很多插件方便开发工作。
• maven-source-plugin : 源码发布插件,绑定在 compile 阶段,执行 jar goal,将源码 以 jar 包的形式发布出去。
• maven-javadoc-plugin: javadoc 插件,将源码的 javadoc 发布出去。
• maven-tomcat7-plugin :此插件可以直接使用 Tomcat 运行 Web 项目,常用的命令 是 mv口 tomcat7 :ru口。 同样的还有 jetty-maven-plugin。
• maven-shade-plugin :此插件是 Maven 常用的打包插件, 一般将其绑定在 package 阶段,执行其 shade goal。 其能够将源码和依赖的第三方资游、打包在一起以供独立 足行。 • maven-assesmbly-plugin :和 maven-shade-plugin 一样也是打包插件,但是其功能更 加强大,输出压缩包格式除了 jar 外,还支持 tar、 zip、 gz 等。• maven-gpg-plugin :此插件是 jar 包的签名插件,可以对自己发布的 jar 包进行签名。
新兴构建工具一-Gradle
代码版本控制
集中式代码版本管理一-SVN
分布式代码版本管理一-Git :Git 是一个分布式版本控制系统,相比 SVN 的集中式版本控制, 每个工作计算机都可 以作为版本库,可以不依赖远程仓库服务器离线工作。 此外,它的每一个版本库都保存了 完整信息,且是按照元数据的方式存储的。
与常用命令 :
git clone [repository url] )复制项目的Git的地址,在windowcmd下使用clone命令,可以将远程仓库的的项目拷贝到本地。
git checkout [branchName] )切换项目分支
git add [fileName] )增加或改动了一些文件。这里也可以使用 git add -a 添加所有变动到暂存区。
git commit - m )提交文件到本地仓库。可以使用 git commit -a 跳过 git add 步骤直接 commit
git pull )从远程库更新代码并合并
git push origin master )提交到远程仓库。
git merge [third repossitory]/master allow-unrelated-histories ) 合并不相关的分支。
服务器搭建 :因此需要一套基于 Git 的工作流来规范整个协同流程。
GitHub Workflow:
)检出新的分支。
)在开发分支上完成开发工作, commit 并 Push 到远程仓库分支中。
) 向主分支发起 Pull request。
)在 Pull request 中发起讨论和修改。
)将开发分支部署到测试环境,经测试无误后 merge 回主分支。
GitFlow:
主分支 :主分支是所有开发活动的核心分支。 所有的开发活动产生的输出物最终都 会反映到主分支的代码中。
master 分支: 存放的是随时可供在生产环境中部署的代码
develop 分支: 保存当前最新开发成果的分支。 通常这个分支上的代码也是可进行 每日/夜间发布的代码
辅助分支 : 用于组织解决特定问题的各种软件开发活动的分支。 辅助分支主要用于 组织软件新功能的并行开发、简化新功能开发代码的追踪、辅助完成版本发布工作 以及对生产代码的缺陷进行紧急修复工作
feature 分支: 用于开发新功能时所使用的分支。 从 develop 分支发起 feature 分支, 代码必须合并回 develop 分支。 此分支甚至可以仅仅保存在开发者向己的代码库里 而不提交。
release 分支: 用于辅助版本发布的分支。 从 develop 分支派生,必须合并回 develop 分支和 master 分支。 此分支是为发布新的产品版本而设计的,当 develop 上开发的 功能基本成形且可以发布的时候,就可以派生,LH release 版本
hotfix 分支: 用于修正生产代码中有缺陷的分支。 从 master 分支派生且必须合并回 master 分支和 develop 分支。
git push 之前需要先 git pull 更新代码。
配置GIt:
取得项目的Git仓库:
在工作目录中初始化一个新仓库,对现有的项目使用Git管理,只需要此项目所在的目录执行, git init # 在当前目录新建一个Git代码库, git init [projectName] #新建一个目 录并初始化为 Git 代码库
从现有仓库克隆,git clone https://github.com/LIRUILONGS/parents;在当前目录下创建parents目录,并保存下载 。
将记录每次更新到仓库
检查当前文件状态, git status
追踪新文件,暂存以修改文件,
git add .#添加当前目 录的所有文件到暂存区域
git add --a #添加所有文件和目录到暂存区域
查看巳暂存和未暂存的更新、提交之间的差异。
git diff #查看尚未暂存的文件更新了哪些部分
git diff 一cached [file] #查看已经暂存起来的文件和上次提交时的快照之间的差异
git di ff [branchl] [branch2] #显示两次提交之间的差异
提交更新,每次准备提交前,先用 git status 看下,是不是都已暂存了,然后再运行提交命令 git commit 提交更新
git commit [file1] [file2] #会提示输入本次提交说明
git commit -m [messag] #直接附带提交说明
git commit --amend #修改最后一次提交
git commit -v #提交时显示所有 diff 信息
git commit --amend -m [message]
移除文件。要从 Git 中移除某个文件(包括暂存区域和工作目录),就必须要从已追踪文件清单中 移除( 确切地说,是从暂存区域移除), 然后提交。
git rm [filel][file2]。 如果文件已经存放到暂存区域,则必须要强制删除选项-f。
移动文件:
要在 Git 中对文件改名,可以运行如下命令 :git mv file_from file_to
回滚文件:
git branch backup #先备份到一个新分支
git l og #找到妥回滚的版本
git reset --hard [版本号]#回滚
远程仓库:
)查看当前的远程仓库。
可以用 git remote 命令,它会列出每个远程仓库的简 短名字。 在克隆完某个项目后,至少可以看到一个名为 origin 的远程仓库,也可以
使用 git remote -v 显示对应的克隆地址
)添加远程仓库
git remote add [shortname] [url] ,url 也可以是一个本地 Git 项目文件夹,
)从远程同步信息
git fetch [remote] #下载仓库的所有变动
git pull [remote] [branch] #取回远程仓库的变化并合并本地分支
)推送数据到远程仓库。
git push [remote-name] [branch-name]
git push origin master #把本地的 master 分支推送到 origin 服务器上
git push -u origin master //push 同时设直默认追踪分支
当本地的版本落后于远程仓库,但是想要用旧版本覆盖远程版本的话,
git push --force origin master
推送所有分支到远程仓库 :
git push [remote] --all
)远程仓库的删除和重命名。
用 git remote rename 命令修改某个远程仓库在本地的简短名称,
使用 git remote rm 命令删除远程仓库。
)检出远程仓库的某一分支。git checkout -b [local.branch] [remote .branch]
分支的使用
git branch #列出本地分支
git branch -r #列出i2l年呈分支
git branch -a #列出所有本地分支和远程分支
git branch - v #查看各个分支最后一个提交对象的信息
git branch --merge #查看已经合并到当前分支的分支
git branch --no-merge #查看未合并到当前分支的分支 git branch [branch-name] #新建分支,但仍然停留在当前分支
git branch [branch] [commit] #新建一个分支,指向指定
commit git branch -m [old_branch_name] [new_branch_name] #重命名分支
git checkout [branch-name] #切换到分支
git checkout -b [branch-name ]#新建并切换到该分支
git checkout -b [branchl] [branch2] #基于 branch2 新建 branchl 分支,并切换
git branch d [branch-name] #删除分支
git branch -D [branch-name] #强制删除分支
git merge [bra口ch-name] #将分支合并到当前分支
git rebase [branch-name] #将 banch-name 分支上超前的提交,变基到当前分支
git branch -set-upstream [branch] [remote-branch] #建立现有分支和指定远程分 支的追踪关系 #删除远程分支
git push origin --delete [branch-name]
git push origin : [branch-name]
git branch -dr [remote/branch-name]
标签的使用
git tag #列出现有标签
git tag [tag] #新建标签 git tag [tag] #新建一个 tag 在当前 commit
git tag [tag] [commit ]#新建一个 tag 在指定 commit
git tag -a [tag]咐’ tag comment ’ #新建带注释标签
git checkout - b [branch] [tag] #新建一个分支,指向某个 tag
git show [tag] #查看 tag 信息
git checkout [tag] #切换到标签 git push [remote] [tag] #推送分支到源上
git push [remote] --tags #一次性推送所有分支
git tag -d [tag] #删除标签 git push origin : refs/tags/vO .l #删除远程标签
日志
git log #显示当前分支的版本历史
git log --stat #显示 commit 历 史,以及每次 commit 发生变史的文件,每次提交的文件增删 数量#显示某个文件的版本历史,包括文件改名
git log --follow [file] git whatchanged [file]
git blame [file ]#显示指定文件由谁何时修改过
git log -p [file] #显示指定文件相关的每一次 diff
git show [commit] #显示每次提交的元数据和内容变化
git show --name-only [commit] #显示某次提交发生变化的文件
git show [commit] : [filename] #显示某次提交某个文件的内容
git reflog #显示当前分文的最近几次提交
撤销
git status # 查看状态,
git clean -f -d #清空未进入暂存区域的改动
git reset --hard #重置暂存区域和工作区域到上一次commit
git pull 从远程库更新代码并合并
git push 提交代码
git checkout [file] #恢复暂存区域的指定文件到工作区域
git checkout [commit] [file] #恢复某个 commit 的指定文件到工作区域
git checkout . #恢复上一个 commit 的所有文件到工作区域
git reset - - hard #重直暂存区域和工作区域到上一次 commit
git reset [commit] [file ]#重直当前分支到 commit ,重直暂存区域,但工作区域不变
git reset -soft #只回退 commit , 此时可以直接
git commit git reset - - hard [commit ] #重直当前分支的 HEAD 为指定 commit ,同时重直暂存区域和工作区域,与指定 commit 一致
git reset - - keep [commit] #重直当前 HEAD 为指定 commit ,但保持暂存区域和工作区域 不变
git revert [commit] #新建一个 comm工t 撤销指定 commit,后者的所有变化都将被前者抵消, 并且应用到当前分
git reset HEAD" #回退所有内容到上一个版本
git reset HEAD" [file] #回退文件的版本到上一个版本
git reset - -soft HEAD~3 #向前回退到第 3 个版本
git clean -f -d #清空未进入暂存区域的改动
其他
git help #获取命令的帮助信息
git archive #生成一个可供发布的压缩包
代码质量保证
使用单元测试保证代码质量,单元测试,即Unit Test ,指的是对代码的各个接口的测试,所谓测试驱动开发也依赖于此。
· 在某些阶段必然被调用的代码。
对使用了 Spring 的项目写测试用例的时候,需要依赖 SpringJUnit4ClassRunner (需要引人 spring-test 依赖) : package com.qst.Service;
import com.qst.pojo.Buser;
import com.qst.service.before.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @Description : Service层测试
* @Author: Liruilong
* @Date: 2019/9/5 20:38
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring/applicationContext.xml")
public class UserServiceTest {
@Autowired
private UserService userService;
// 用户注册
@Test
public void register() throws Exception{
Buser buser1 = new Buser();
buser1.setBpwd("123");
buser1.setBemail("2423423");
System.out.println("注册状态:" +userService.register(buser1));
}
JUnit 还有一种比较高级的参数化测试,即需要多组参数验证的测试用例,
package com.qst.main;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
/**
* @Description :
* @Author: Liruilong
* @Date: 2019/9/10 15:30
*/
@RunWith(Parameterized.class)
public class Parameteried {
private int param;
private boolean result;
// 为每组数据构建测试用例
public Parameteried(int param, boolean result) {
this.param = param;
this.result = result;
}
// 生成测试数据
@Parameterized.Parameters
public static Collection genparams(){
return Arrays.asList( new Object[][]{{1,true}, {2, false}});
}
@Test
public void test(){
Assert.assertEquals(this.param % 2 == 1, this.result);
}
}
对运行时间异常有要求的测试,
Method 级(用在实例方法) 。
• @Before 在每个测试方法执行前执行。
• @After 在每个测试方法执行后执行。
Class 级(用在 static 方法) 。
@BeforeClass 在第一个测试方法运行前执行。
@AfterClass 在最后一个测试方法运行后执行。
package com.qst.main;
import org.junit.*;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
/**
* @Description :Junit学习
* @Author: Liruilong
* @Date: 2019/9/10 14:17
*/
public class Mian {
// Class级,用在static方法
@BeforeClass
public static void initClass(){
System.out.println("在第一个测试方法运行之前执行 ");
}
@AfterClass
public static void afterClass(){
System.out.println("在最后一个测试方法运行后执行");
}
// Methon级,用在实例方法
@Before
public void initMethod(){
System.out.println("在每个方法执行前执行");
}
@After
public void afterMethod(){
System.out.println("在每个方法执行后执行");
}
@Test
public void testAdd()throws Exception{
//断言是编写测试用例的核心实现方式,即期望值是多少,测试的结果是多少,以此来判断测试是否通过。
Assert.assertEquals(2, add(1, 1));
}
// 对运行级别有要求的测试,测试运行时间超时则失败
@Test(timeout = 200)
public void testTimeout(){
try{
TimeUnit.MILLISECONDS.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}
}
// 期待抛出异常的测试用例,测试方法中如果不抛出异常则失败
@Test(expected = IndexOutOfBoundsException.class)
public void testException(){
new LinkedList().get(0);
}
// 忽略掉这个测试用例
@Ignore("ignore the test")
@Test
public void ignoreTest() throws Exception{
Assert.assertEquals(2, add(1, 1));
}
public int add(int n1, int n2){
return n1 + n2;
}
}
Mock测试:
在写单元测试时会经常碰到一些依赖于外部环境的场景,比如数据库查询、网络请求等。 有时候,这些外部依赖并非是一直可用的或者暂时没有开发完成的 经常会对测试产生影 响。 而 Mock 测试工具则是为了解决这个问题开发的。 }肝a 中常用的 Mock 测试框架主要是 Easy Mock 和 PowerMock。 这里的 Mock 就是打桩( Stub )或者模拟,当你调用一个不好在 测试中创建的对象时, Mock 框架为你模拟一个和真实对象类似的替身来完成相应的行为。
package com.qst.Service;
import com.qst.dao.UserMapper;
import com.qst.pojo.Buser;
import com.qst.service.before.UserService;
import com.qst.service.before.UserServiceimpl;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.lang.reflect.Field;
@Test
public void setUp() throws Exception {
// 创建mock对象
UserMapper userMappermock = EasyMock.createMock(UserMapper.class);
Buser buserMock = EasyMock.createMock(Buser.class);
// 将得到的Mock对象注入被测试对象
Field userMapperFieid = UserServiceimpl.class.getDeclaredField("userMapper");
//将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
userMapperFieid.setAccessible(true);
//set(Object obj, Object value) 将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
userMapperFieid.set(userServiceimpl, userMappermock);
// 设置mock对象的期望值,
// expect()方法指定要模拟方法的方法和参数
// andReturn()方法定义方法的返回值,times()方法定义mock对象可以调用多少次
EasyMock.expect(buserMock.getBemail()).andReturn("123").times(1);
EasyMock.expect(userMappermock.login(new Buser())).andReturn(buserMock);
//mock对象的replay.录制mock对象的预期行为
EasyMock.replay(buserMock);
EasyMock.replay(userMappermock);
// 断言
Assert.assertTrue(userServiceimpl.login(new Buser()).equals(buserMock));
Assert.assertEquals(userServiceimpl.login(new Buser()), buserMock);
// 验证mock对象是否被调用
EasyMock.verify(buserMock);
EasyMock.verify(userMappermock);
}
嗯,不是太明白,以后用的时候在弄明白。^_^
衡量单元测试的标准:
有了单元测试,那么就需要有一个能够衡量单元测试好坏的标准,即测试覆盖率。 即 你写的单元测试对你的项目中代码的覆盖程度。 对于很多比较严格的企业来说,对测试覆 盖率的要求经常是达到 98% 以上。
目前常用的测试覆盖率工具有 EMMA、 Cobertura 等。 使用方法基本类似,可以使用 IDE 的相关插件,也可以使用相应的 Maven 插件。 但相比 Cobertura, EMMA 的覆盖率更 为严格,并且支持 test 代码和 Java 代码不在一个工程下的单元测试覆盖率统计。
https://blog.csdn.net/sanhewuyang/article/details/94553492《阿里巴巴 Java 开发手册 》学习笔记
第三章,开发框架:
在一个可用的 Java Web 应用中 , 一般由以下几种框架构成。
• loC 框架: 依赖注入/控制反转 即将依赖从代码层面转移到了容器配置层面。 其 是 Java 中使用得最普遍的框架。
• ORM 框架:对象关系映射, 即将数据库的表映射到 Java 中对象的一种数据库操作框架。
• Log 框架: 日志框架。 即记录应用运行、异常 日 志的框架。
• Web 框架: 一般指的是 Model-View-Controller 的分层 Web 开发框架,将业务代码 做了逻辑分层,各司其职,能够做到灵活的配置和扩展。、
依赖注入:IoC,控制翻转( Inversion of Control ),又叫依赖注入( Dependency. I叫ect ) 。 即将代码里对象之间的依赖关系转移到容器中,这样就能够很灵活地通过面向接口的编程方式改 变真正的实现类。
对于依赖注入, JSR-330 ( Dependency I时ection for Java )做了一些规范。 该规范主要是 面向依赖注入使用者的,而对注入器实现、 配置并未做详细要求。 目前 Spring、 Guice 已经 开始兼容该规范。 JSR-330 规范并未按 JSR 惯例发布规范文档,只发布了 API 源码。 其指定 了获取对象的一种方法, 该方法与构造器、工厂以及服务定位器(例如 JNDI )这些传统方法相比可以获得更好的可重用性、可测试性以及可维护性。此方法的处理过程就是依赖注入。
常见的IOC 框架:Google Guice。PicoContainer。 Dagger。Spring Framework。
基本所有的IOC框架都支持构造器,setter,基本值注入.
循环依赖问题;
通过替换为 Provider
如果不依赖 Provider,对于构造器中的循环依赖是无法解决的,会抛出异常。
对于方法或字段注入的情况,将其依赖的一边放置到单例作用域中(可以缓存), 使得循环依赖能够被注入器解析。 class TestA{
@Inject TestB b;
}
class TestB{
@Inject TestC c;
}
class TestC{
@Inject TestA a;
}
JSR-330 依赖注入规范 :如果要使用 JSR-330 提供的注解等功能,可引人依赖:
javax.inject
javax.inject
1
提供的注解:
对象关系映射:ORM ( Object Relational Mapping ),对象关系映射,是一种为了解决面向对象与关系 型数据库不匹配而出现的技术,便开发者能够用面向对象的方式使用关系型数据库
最常用的 ORM框架主要是 MyBatis (前身是 iBATIS )和 Hibernate
一般的 ORM 包括以下几个部分。 · 一个规定Mapping Metadata 的工具,即数据库中的表、列与对象以及对象属性的映射。 . 一个对持久类对象进行 CRUD 操作的 API。 · 一个语言或 API 用来规定与类和类属性相关的查询。 · 一种技术可以让 ORM 的实现同事务对象一起进行缓存、延迟加载等操作。
mybatis配置信息:My Batis 也支持注解映射 Mapper:
select * from noticetable
insert into noticetable (id,ntitle,ncontent,ntime)
values (null,#{ntitle},#{ncontent},now())
delete from noticetable where id = #{id}
select * from noticetable where id = #{id}
Hibernate配置信息:
com.mysql.cj.jdbc.Driver
jdbc:mysql://localhost:3306/dbtest?characterEncoding=utf-8&serverTimezone=UTC&allowMultiQueries=true
root
asd123asd
org.hibernate.dialect.MySQL5Dialect
true
true
update
none
3,日志
Java 中已经有很多成熟的日志框架:JDK Logging。Apache Log4j。Apache Log4j2。Logback。
还有两个用于实现日志统一的框架 Apache 的 Commons Logging、 SLJ4J。 与上述 框架的不同之处在于,这两个框架只是一个门面,并没有日志框架的具体实现,可以认为 是日志接口框架。
这些日志框架来说, 一般会解决日志中的以下问题:
日志的级别: 定义日志级别来区分不同级别日志的输出路径、 形式等,帮助我们适 应从开发调试到部署上线等不同阶段对日志输出粒度的不同需求。
日志的输出目的地:包括控制台、文件、 GUI 组件,甚至是套接口服务器、 UNIX Syslog 守护进程等。
日志的输出格式:日志的输出格式( JSON、 XML )
日志的输出优化 :缓存、异步等。
JDK Logging :JDK Logging 就是 JDK 自带的日志操作类,在 java.util.logging 包下面,通常被简称为JUL。
Log4j :Log4j应该是目前Java开发中用得最广泛的日志框架
log4j有三个部分:
1.loggers 负责捕获日志信息。
2.appenders 负责输出信息到不同的目的地
3.layouts 负责使用不同的样式输出日志
log4j框架中有两种对象:
核心对象:框架的支撑对象,是框架必不可少的组成部分。
支撑对象:这些都是框架可选的对象,用于提供额外重要的工作。
核心对象包括下面几种类型 :
logger对象 ,是最高的层,负责通过不同的风格转化日志信息,他提供给appender对象发布前的信息。(这里的层是指所处的位置)
layout对象 ,用于提供格式化日志信息的风格,在发布日志信息前,使其变得可读,可重用。
appender对象 ,这个对象属于底层的对象,它负责发布信息到不同的目的地,比如数据库,文件,控制台,UNIXsyslog等等。
配置:Log4j 支持 XML、 Proerties 配置,通常使用 Properties:默认情况下,logManager对象会在CLASSPATH目录下寻找
# Define the root logger with appender X
log4j.rootLogger = DEBUG, X
# Set the appender named X to be a File appender
log4j.appender.X=org.apache.log4j.FileAppender
# Define the layout for X appender
log4j.appender.X.layout=org.apache.log4j.PatternLayout
log4j.appender.X.layout.conversionPattern=%m%n
首先日志的级别为DEBUG,另外添加了一个appender,名字是X, 设置appender的名字是X,并且定义该appender的实现为org.apache.log4j.FileAppender,即文件读写方式。 为X设置显示的方式--layout为PatternLayout
如果 Log4j 文件不直接存储于 classpath 下的话, 可以使用 PropertyConfigurator 来进行 配置:
在Java文件中输出日志消息:
# Define log file location
log=f:/
# Define the root logger with appender X
log4j.rootLogger = DEBUG, FILE
# Set the appender named X to be a File appender
# 输出到制指定文件
log4j.appender.FILE=org.apache.log4j.FileAppender
# 输出到控制台
#log4j.appender.FILE=org.apache.log4j.ConsoleAppender
# Define the output file
log4j.appender.FILE.file=${log}/log.txt
# Define the layout for X appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n
package com.Log4j;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
/**
* @Description : log4j学习
* @Author: Liruilong
* @Date: 2019/9/15 22:35
*/
public class Log4jDemo {
private static Logger logger = Logger.getLogger(Log4jDemo.class);
public static void main(String[] args) {
// PropertyConfigurator.configure("F:\\idea_workspace\\frameDemo\\src\\log4j.properties");
logger.error("错误日志");// 指定错误事件
logger.debug("debug日志");//指定的信息事件的粒度是DEBUG,在调试应用的时候会有帮助。
logger.warn("warn日志");//输出具有潜在风险的信息
logger.trace("trace日志");//指定比DEBUG更粗粒度的调试日志。
logger.fatal("fatal日志");//指定严重的错误事件,该事件会导致应用暂停。
}
}
Logback :Logback 是由 Log4j 创始人设计的又一个开源日志组件,相对 Log4j而言,在各个方面很大改进。
Log back 当前分成 3 个模块。
logback-core 是其他两个模块的基础模块。
logback-classic 是 Log4j 的一个改良版本。 logback-classic 完整实现 SLF4J API,使你 可以很方便地更换成其他日志系统,如 Log4j或 JDK14 Logging。
logback-access 访问模块与 Serviet 容器集成提供通过 HTTP 来访问日志的功能。
统一日志 API 的门面框架:
J ava开发中经常提到面向接口编程,所以我们应该按照一套统一的API来进行日志编程, 以实际的日志框架来实现这套 API,这样的话, 即使更换日志框架, 也可以做到无缝切换。Apache Commons Logging 经常被简称为 JCL,是 Apache 开源的日志门面框架。 Spring 中使用的日志框架就是 JCL, 使用起来非常简单 :
commons-logging
commons-logging
1.2
让 JCL 使用其他日志实现框架。
SLF4J :SLF4J ( Simple Logging Facade for Java )为 1盯a 提供了简单日志 Facade,允许用户以自 己的喜好,在工程中通过 SLF4J 接入不同的日志实现。 与 JCL 不同的是, SLF4J 只提供接口, 没有任何实现(可以认为 Logback 是默认的实现) 。
使用 JUL 作为日 志实现,需要引人 slf4j-jdk14包。
使用 Log4j 作为日志实现,需要引人 slf4j-log4j12 和 Log4j 两个 jar 包。
使用 Log4j2 作为日志实现,需要引人 log4j-slf4j-impl 依赖。
使用 Logback 作为日 志实现,只需要引人 Logback 包。
统一日志框架的使用
配置好 Logback 的依赖。
org.slf4j
slf4j-api
1.7.28
ch.qos.logback
logback-core
1.2.3
ch.qos.logback
logback-classic
1.2.3
)切换 Log4j 到 SLF4J。
org.slf4j
slf4j-api
1.7.28
log4j
log4j
1.2.17
org.slf4j
slf4j-log4j12
1.7.12
切换 JUL 到 SLF4J
org.slf4j
slf4j-api
1.7.28
org.slf4j
jul-to-slf4j
1.7.28
切换 JCL 到 SLF4J
org.slf4j
jcl-over-slf4j
1.7.25
commons-logging
commons-logging
1.2
org.slf4j
jul-over-slf4j
1.7.28
链接:https://pan.baidu.com/s/19i1xpL16a6lCNxQotQ8QWg 提取码:a0ph