失踪人口回归,距离上篇博客已经过去了整整两年,过去两年坎坎坷坷地在忙一些有意义或没意义的事情,现在总算进入到了一个新的暂稳态。这篇文章写一下工作后接触到的一些新技能点,同时也给人生新阶段的前半年留个记录:)
PS:本文类似于目录式的记录,而非百科全书式的堆砌概念。
一、微服务初印象
此前在学校里自己写一些小项目时,虽说用到了 SpringBoot 等框架,但写过来写过去还是一个单体程序,在一个程序里完成各种功能,压根没有实际上手过诸如微服务、分布式之类的项目。所以,当工作后第一次接触到以一个业务来拆分服务、各服务间通过 feign、RPC 等进行调用时,感觉还是非常的新鲜的。机缘巧合下,读到了周志明老师的《凤凰架构 构建可靠的大型分布式系统》,其中第二章 “演进中的架构” 按照 “原始分布式 - 单体系统 - SOA - 微服务 - 无服务” 的脉络进行讲述,读完后感觉茅塞顿开。由此想到,服务架构的发展历史其实与个人对编程的接触还是有着一定的相似性的,比如当一个单体系统越来越大时,无论企业还是个人,直观的感受还是想对其进行拆分。
1. 技能点
1.1 微服务
“微服务是一种通过多个小型服务组合来构建单个应用的架构风格,这些服务围绕业务能力而非特定的技术标准来构建。各个服务可以采用不同的编程语言,不同的数据存储技术,运行在不同的进程之中。服务采取轻量级的通信机制和自动化的部署机制实现通信与运维”。
1.2 Feign(如何在服务间发起 HTTP 请求)
Feign 是一个 HTTP 请求调用的轻量级框架,可以以 Java 接口注解的方式调用 HTTP 请求。Feign 通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,封装了HTTP 调用流程。
1.3 gRPC(如何在不同语言的服务间发起调用)
在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。
1.4 Protocol buffers(对传输数据进行压缩处理,加快网络数据传输)
Protocol buffers 是 Google 开源的一套成熟的结构数据序列化机制,简单理解为其比 JSON 具有更高的压缩效率,更适合网络数据传输。
二、Java 之无穷无尽的语法糖
讲道理,上一次系统的去学 Java,还是在大二的时候心血来潮花了一个月狂刷完 Java SE 相关语法,然而两年的时间让我对语法的遗忘还是比较多的。当时还用最原始的 Servlet 开发 Web 程序,自己买云服务器装 Tomcat,然后把 war 包扔到 Tomcat 里,启动一下就跑起来了。之后又想到这玩意写起来太费劲,又去学了 Spring,写了几个 demo 出来,还挺好玩,但每次那么多配置文件写起来真的不优雅。再之后听说了大名鼎鼎的 SpringBoot,“约定大于配置”的理念让大部分烦人的配置文件都销声匿迹。但回过头来看,不过是当初的 Servlet,还是之后的 Spring 全家桶,对于当时的我来说就真的只是会用而已。至于什么 AOP、IOC 等等理念,当时的我是看不懂也不想去看懂,无他,就是因为没有具体的实践场景驱使我去钻研。这也让我在工作最开始的几个月里,遇到一些奇奇怪怪的 bug 时,总是很难自己定位出来,从而不得不求助同事。当我看到他们熟练地在 IDEA 里切来切去、打个断点、定位异常,最后解决问题时,作为小菜鸡的我,深深地感受到了工作年限给人带来的技术成长,也感受到了不能只停留在对框架和工具的使用上,还应当适度了解其原理,不求了解后能再造成更好的轮子,只求出现一些比较复杂的问题时,能自己定位出来。
因此,我将 Java 语言的学习分为两块:Java SE 基础语法 和 Spring 框架,前者是作为编写每一行代码所需的必要条件,而后者则是在做业务开发时躲不掉的一个工具。下面是一些之前不太理解但工作后接触较多的语法。
1. 语法糖
1.1 枚举类
// 一个简单的枚举类
public enum XxxEnum {
STATUS_0(0, "状态0"),
STATUS_1(1, "状态1"),
;
private int code;
private String desc;
public static XxxEnum fromCode(Integer code) {
for (XxxEnum xxxEnum : XxxEnum.values()) {
if (code != null && code == xxxEnum.getCode()) {
return xxxEnum;
}
}
return STATUS_0;
}
}
1.2 异常处理
万恶之源:NullPointerException;
解决之道:每个方法入口记得进行参数校验。
1.3 打日志
少用 System.out.println(); 多用 @Slf4j 注解直接打日志 log.info 、log.warn ,打异常日志时,Exception e 不用占位符 {} 。
打日志的时机:1、可能出现错误点;2、调用外部接口时的返回体;3、正常流程执行的标识。
1.4 反射
初次接触时感觉很牛X的样子,多看两遍后发现其作用就是:通过 Class 实例获取 class 信息(但感觉破坏了类的封装性)
平时使用不多,一些框架里用的多,之后再拜读吧。
1.5 注解(搞懂后发现其确实是 Java 的一大亮点)
// 一个简单的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}
1.6 泛型(搞懂后就知道一些看起来很炫的代码里的 T、K、E、?等是什么)
泛型就是类型参数化
延伸出一些常用的初始化方法:
// Google 提供的一些写法
List<String> strList = Lists.newArrayList();
Map<String, Object> result = Maps.newHashMap();
HashSet<String> set01 = Sets.newHashSet("aa", "bb", "cc");
1.7 序列化
序列化:把一个 Java 对象变成二进制内容,本质上就是一个
byte[]
数组反序列化:把一个二进制内容(也就是
byte[]
数组)变回 Java 对象为什么要序列化:序列化后可以把
byte[]
保存到文件中,或者把byte[]
通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了注意:反序列化时不调用构造方法,可设置 private static final long
serialVersionUID
作为版本号(非必需)
1.8 单元测试(引入 Junit 依赖)
public class FactorialTest {
@Test
void testFact() {
assertEquals(1, Factorial.fact(1));
assertEquals(2, Factorial.fact(2));
assertEquals(6, Factorial.fact(3));
assertEquals(3628800, Factorial.fact(10));
assertEquals(2432902008176640000L, Factorial.fact(20));
}
}
1.9 maven
Maven使用 groupId,artifactId 和 version唯一定位一个依赖
这个网站讲挺好的,推荐:Maven 3.1
2.0 lambda 表达式(写的人是爽了,不懂的人是真的看着难受)
其思想:函数式编程
经常使用的流式API:Stream;推荐博客:使用Stream - 廖雪峰的官方网站
Stream API提供了一套新的流式处理的抽象序列;
Stream API支持函数式编程和链式操作;
Stream可以表示无限序列,并且大多数情况下是惰性求值的。
常用操作:
转换操作:map(),filter(),sorted(),distinct();
合并操作:concat(),flatMap();
并行处理:parallel();
聚合操作:reduce(),collect(),count(),max(),min(),sum(),average();
其他操作:allMatch(), anyMatch(), forEach()。
TODO(挖坑,闲了再写):
- Spring 相关
- 事务相关
- 分布式
- 云原生
参考文章: