【Java】SPI 机制

SPI 简介

https://dubbo.apache.org/zh/docsv2.7/dev/source/dubbo-spi/

SPI 全称为 Service Provider Interface,是一种服务发现机制。它是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的 SPI 机制,而是对其进行了增强,使其能够更好的满足需求。

https://www.jianshu.com/p/46b42f7f593c

img

Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制。

系统设计的各个抽象,往往有很多不同的实现方案,在面向的对象的设计里,一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。

Java SPI 就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要。所以SPI的核心思想就是解耦

阅读全文

【Linux】tmux 常用命令

tmux 简介

https://zhuanlan.zhihu.com/p/98384704

tmux 是一个 terminal multiplexer(终端复用器),它可以启动一系列终端会话。它解绑了会话和终端窗口。关闭终端窗口再打开,会话并不终止,而是继续运行在执行。将会话与终端窗后彻底分离。

安装 tmux

1
2
3
4
5
6
7
8
# Ubuntu 或 Debian
$ sudo apt-get install tmux

# CentOS 或 Fedora
$ sudo yum install tmux

# Mac
$ brew install tmux

常用命令

开启会话

1
2
3
4
5
6
7
8
# 启动tmux
$ tmux

# 退出
$ exit 或 Ctrl+D

# 启动命名tmux
$ tmux new -s <name>
阅读全文

【MacOS】MacOS 安装 Homebrew

有趣的Homebrew 命名及 keg-only 的意思

Homebrew 简介

Homebrew是一款MacOS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能,类似于CentOS下的apt-get/yum。只需简单的一条指令就可以实现包管理,而不用你关心各种依赖和文件路径的情况,十分方便快捷。

Homebrew 名称由来

https://zhuanlan.zhihu.com/p/196667957

首先, brew 本身是酿造、酿酒的意思,会用这个字的原因是 homebrew 的安装方式为下载 source code 回来做编译,由于是在自己电脑做 local compile 编译套件,所以这个工具叫做 homebrew 自家酿酒

酿酒需要有配方 formula,当你需要安装套件时,流程就是下 brew 命令去根据配方 formula, 酿造出一桶( keg)酒来。所以 keg 指的是整个编译完成的套件资料夹。

再来,放置套件的位置在 /usr/local/Cellar/(或**/opt/homebrew/Cellar/**), Cellar 就是地窖,一桶一桶酿好的酒当然要存放在地窖里,所以编译安成的套件资料夹 keg 预设目录在 /usr/local/Cellar/

回到「keg-only」整个词,字面上意思现在就很清楚,表示这个套件只会存放在桶子里,不会跑出桶子外。实际上的行为是 brew 不会帮你做 symlink/usr/local,避免你的原生系统内还有一套 readline 而打架,所以提示消息说 readline 套件是 keg-only

https://www.zhihu.com/people/morlay

brew cask(木桶) 是对于 brew 的扩展,可以采用 brew 的方式安装图形界面的软件。brew cask 仅仅是下载解压已经编译好了的应用包 (.dmg/.pkg),并放在统一的目录中( /opt/homebrew-cask/Caskroom ),省掉了自己去下载、解压、拖拽等步骤。

脚本配置安装

https://zhuanlan.zhihu.com/p/98384704

使用作者@Mintimate配置的脚本安装:

1
$ /bin/zsh -c "$(curl -fsSL 'https://host.mintimate.cn/fileHost/download/MTEyMjMz')"

卸载homebrew:

1
$ /bin/zsh -c "$(curl -fsSL 'https://host.mintimate.cn/fileHost/download/MjIzMzQ0')"

Homebrew 常用命令

安装任意包

1
$ brew install <packageName>

示例:安装node

1
$ brew install node

卸载任意包

1
$ brew uninstall <packageName>

查询可用包

1
$ brew search <packageName>

查看已安装包列表

1
$ brew list

查看任意包信息

1
$ brew info <packageName>

更新Homebrew

1
$ brew update

查看Homebrew版本

1
$ brew -v

Homebrew帮助信息

1
$ brew -h

https://www.jianshu.com/p/de6f1d2d37bf

阅读全文

【MacOS】Iterm2 常用快捷键

image-20210821160959204

标签

1
2
3
4
5
6
7
8
9
新建标签:command + t

关闭标签:command + w

切换标签:command + 数字 command + 左右方向键

切换全屏:command + enter

查找:command + f

command + f 选中文本后按Tab键自动高亮当前文本后的内容,再按Enter键将高亮文本自动保存到剪切板上。

  • fn + 左右方向键:移动到行首/尾
  • command + 左右方向键:切换窗口
  • option + 左右方向键:移动到下一个单词(需要设置映射keys替换默认的esc + b/f

分屏

1
2
3
4
5
6
7
8
9
垂直分屏:command + d

水平分屏:command + shift + d

切换屏幕:command + option + 方向键 command + [ 或 command + ]

查看历史命令:command + ;

查看剪贴板历史:command + shift + h

其他

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
清除当前行:ctrl + u

到行首:ctrl + a

到行尾:ctrl + e

前进后退:ctrl + f/b (相当于左右方向键)

上一条命令:ctrl + p

搜索命令历史:ctrl + r

删除当前光标的字符:ctrl + d

删除光标之前的字符:ctrl + h

删除光标之前的单词:ctrl + w

删除到文本末尾:ctrl + k

交换光标处文本:ctrl + t

清屏1:command + r

清屏2:ctrl + l

⌘ + 数字在各 tab 标签直接来回切换

选择即复制 + 鼠标中键粘贴,这个很实用

⌘ + f 所查找的内容会被自动复制

⌘ + d 横着分屏 / ⌘ + shift + d 竖着分屏

⌘ + r = clear,而且只是换到新一屏,不会想 clear 一样创建一个空屏

ctrl + u 清空当前行,无论光标在什么位置

输入开头命令后 按 ⌘ + ; 会自动列出输入过的命令

⌘ + shift + h 会列出剪切板历史

可以在 Preferences > keys 设置全局快捷键调出 iterm,这个也可以用过 Alfred 实现

原文地址:https://cnbin.github.io/blog/2015/06/20/iterm2-kuai-jie-jian-da-quan/

【Linux】Linux/Mac 配置 zsh + oh-my-zsh

安装 zsh

查看当前Shell

1
echo $SHELL

查看自己操作系统上都安装了哪些shell:

1
$ cat /etc/shells

显示如下:

1
2
3
4
5
6
/bin/bash
/bin/csh
/bin/ksh
/bin/sh
/bin/tcsh
/bin/zsh # 默认没有

安装 zsh

https://www.jianshu.com/p/57acb275806c

1
2
3
4
5
6
7
8
# macOS
$ brew install zsh zsh-completions

# Ubuntu
$ sudo apt-get install zsh

# CentOS
$ sudo yum -y install zsh

注意:默认安装的zsh为旧版本,不支持powerlevel10k等主题,需要手动安装最新版的zsh:https://blog.csdn.net/weixin_42000303/article/details/106027827

切换为默认 shell

1
$ chsh -s /bin/zsh

切换后重新打开终端

阅读全文

【ZooKeeper】ZooKeeper

Apache ZooKeeper - Wikipedia

ZooKeeper 简介

ZooKeeper 是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。ZooKeeper安装见Linux 开发环境配置文档

image-20210824153402010

本文档参考尚硅谷ZooKeeper教程:https://www.bilibili.com/video/BV1to4y1C7gw?p=2

ZooKeeper 工作机制

ZooKeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。ZooKeeper服务器采用NIO与客户端进行通讯

ZooKeeper = 文件系统 + 通知机制

image-20210824154028992

阅读全文

【JavaWeb】Web 中访问资源路径问题汇总

Web中 / 斜杠的不同意义

  • 浏览器内代表所有资源:/**
  • 服务器内代表所有资源:/

在Web中,/ 是绝对路径

  • / 如果被浏览器解析,得到的地址是:http://ip:port/ (指写在静态html代码中,无法被服务器解析,只能被浏览器解析)
1
<a href="/">斜杠</a>
  • / 如果被服务器解析,得到的地址是:http://ip:port/工程路径/
1
2
3
4
5
6
7
8
// 映射
<url-pattern>/servlet1<url-pattern>

// 获取绝对路径
servletContext.getRealPath("/");

// 请求转发
request.getRequestDispacther("/");
  • 特殊情况:response.sendRedirect("/");会将斜杠发送给浏览器解析,得到http://ip:port/ ,因此需要再加上工程名response.sendRedirect("/projectName/xxx");

/WEB-INF/目录下的资源文件,客户端无法直接访问(即不能在浏览器中输入url直接跳转),而只能在servlet程序中跳转

在IDEA中,"/“代表的项目文件路径为”target/项目名-1.0-SNAPSHOT/"

在Web应用的前端程序(.jsp)中:

  • 不以 / 开始的相对路径找资源时以当前资源的路径为基准,容易出现问题(不推荐使用)
  • 以 / 开始的相对路径找资源时以http://ip:port/为基准,不包含当前项目名称路径,因此需要在资源前加上${pageContext.request.contextPath}/以使程序能找到"target/项目名-1.0-SNAPSHOT/"下的资源文件(项目名-1.0-SNAPSHOT为Maven工程打包后生成的工程根目录)。例如若想在.jsp文件中引入css文件的路径,需要写 href="${pageContext.request.contextPath}/css/style.css"

image-20210604105603326

阅读全文

【Netty】Netty

img

Netty 概述

原生 NIO 存在的问题

NIO的介绍见【Java】NIO

  • NIO 的类库和 API 繁杂,使用麻烦:需要熟练掌握 SelectorServerSocketChannelSocketChannelByteBuffer等。
  • 需要具备其他的额外技能:要熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网络编程非常熟悉,才能编写出高质量的 NIO 程序。
  • 开发工作量和难度都非常大:例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等等。
  • JDK NIOBug:例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU100%。直到 JDK1.7 版本该问题仍旧存在,没有被根本解决。

Netty 官网说明

官网:https://netty.io/

Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.

img

Netty 的优点

NettyJDK 自带的 NIOAPI 进行了封装,解决了上述问题。

  • 设计优雅:适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型-单线程,一个或多个线程池。
  • 使用方便:详细记录的 Javadoc,用户指南和示例;没有其他依赖项,JDK5(Netty3.x)6(Netty4.x)就足够了。
  • 高性能、吞吐量更高:延迟更低;减少资源消耗;最小化不必要的内存复制。
  • 安全:完整的 SSL/TLSStartTLS 支持。
  • 社区活跃、不断更新:社区活跃,版本迭代周期短,发现的 Bug 可以被及时修复,同时,更多的新功能会被加入。

Netty 版本说明

Netty 版本分为 Netty 3.xNetty 4.xNetty 5.x。目前推荐使用的是 Netty 4.x的稳定版本。

阅读全文

【Java】NIO

I/O 模型

I/O 模型简介

I/O 模型简单的理解:就是用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能。

共有三种I/O 模型:

https://blog.csdn.net/tomcyndi/article/details/79087578

1. 同步阻塞:在调用read方法时,stream里没有数据可读,线程停止向下执行,直至stream有数据。

阻塞:体现在这个线程不能干别的了,只能在这里等着

同步:是体现在消息通知机制上的,即stream有没有数据是需要我自己来判断的。

2. 同步非阻塞:调用read方法后,如果stream没有数据,方法就返回,然后这个线程就就干别的事(例如NIO中Selector所在的线程不会一直阻塞在某一个IO,而是会循环判断当前有没有能处理的IO,没有就做别的事,过一段时间再判断一下有没有要处理的IO,此时仍然是同步的,因为线程一直在循环)。

非阻塞:体现在这个线程可以去干别的,不需要一直在这等着

同步:体现在消息通知机制,这个线程仍然要定时的读取stream,判断数据有没有准备好,client采用循环的方式去读取,可以看出CPU大部分被浪费了

3. 异步非阻塞:服务端调用read方法,若stream中无数据则返回,程序继续向下执行。当stream中有数据时,操作系统会负责把数据拷贝到用户空间,然后通知这个线程,这里的消息通知机制就是异步。而不是像NIO那样,自己起一个线程去监控stream里面有没有数据。

BIO、NIO、AIO

Java 共支持 3 种网络编程模型 I/O 模式:BIONIOAIO

  1. Java BIO:同步并阻塞(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。

image-20210813230944094

阅读全文

【Spring】Spring 常用注解

Spring 注解

事务注解

  • @EnableTransactionManagement,会额外加载 4 个 bean
    • BeanFactoryTransactionAttributeSourceAdvisor 事务切面类
    • TransactionAttributeSource 用来解析事务属性
    • TransactionInterceptor 事务拦截器
    • TransactionalEventListenerFactory 事务监听器工厂
  • @Transactional

核心

  • @Order

切面

  • @EnableAspectJAutoProxy
    • 会加载 AnnotationAwareAspectJAutoProxyCreator,它是一个 bean 后处理器,用来创建代理
    • 如果没有配置 @EnableAspectJAutoProxy,又需要用到代理(如事务)则会使用 InfrastructureAdvisorAutoProxyCreator 这个 bean 后处理器

组件扫描与配置类

  • @Component
  • @Controller
  • @Service
  • @Repository
  • @ComponentScan
  • @Conditional
  • @Configuration
    • 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法
    • @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
    • @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
    • @Configuration 中如果含有 BeanFactory 后处理器, 则实例工厂方法会导致 MyConfig 提前创建, 造成其依赖注入失败,解决方法是改用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 Mapper 扫描可以改用注解方式
  • @Bean
  • @Import
    • 四种用法
      ① 引入单个 bean
      ② 引入一个配置类
      ③ 通过 Selector 引入多个类
      ④ 通过 beanDefinition 注册器
    • 解析规则
      • 同一配置类中, @Import 先解析 @Bean 后解析
      • 同名定义, 默认后面解析的会覆盖前面解析的
      • 不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先? (虽然覆盖方式能解决)
      • 采用 DeferredImportSelector,因为它最后工作, 可以简单认为先解析 @Bean, 再 Import
  • @Lazy
    • 加在类上,表示此类延迟实例化、初始化
    • 加在方法参数上,此参数会以代理方式注入
  • @PropertySource

依赖注入

  • @Autowired
  • @Qualifier
  • @Value

Spring MVC

  • @RequestMapping,可以派生多个注解如 @GetMapping 等
  • @RequestBody
  • @ResponseBody,组合 @Controller => @RestController
  • @ResponseStatus
  • @ControllerAdvice,组合 @ResponseBody => @RestControllerAdvice
  • @ExceptionHandler
  • @PathVariable
  • @CrossOrigin

Spring Boot

  • @SpringBootApplication
  • @EnableAutoConfiguration
  • @SpringBootConfiguration
  • @ConditionalOnClass,classpath 下存在某个 class 时,条件才成立
  • @ConditionalOnMissingBean,beanFactory 内不存在某个 bean 时,条件才成立
  • @ConditionalOnProperty,配置文件中存在某个 property(键、值)时,条件才成立
  • @ConfigurationProperties,会将当前 bean 的属性与配置文件中的键值进行绑定
  • @EnableConfigurationProperties,会添加两个较为重要的 bean
    • ConfigurationPropertiesBindingPostProcessor,bean 后处理器,在 bean 初始化前调用下面的 binder
    • ConfigurationPropertiesBinder,真正执行绑定操作