0%

本章是整理知识内容,为强化知识长期更新。

Redis

  • Redis是一个速度非常快的非关系型数据库(non-relationl databases),它可以是存储键对值(key-vakue)方式存储数据。它拥有5中数据类型。同时可以将存储在内存中的数据持久化到因硬盘中。
  • Redis所有的数据都是以唯一的key字符串为名称,然后可以通过这个key来获取对应的value。
结构类型 结构存储的值 结构的读写能力
STRING 可以是字符串、整数或者浮点类型 对整个字符串或者字符串的其中一部分执行操作;对整数和浮点数据执行自动或者自减操作
LIST 一个链表、链表每个节点上都包含了一个字符串 从链表的两端推入或者弹出元素;根据偏移量对链表记性修剪trim;读取单个或者多个元素;根据值查询或者移除元素。
SET 包含字符串的无序集合,并且每个被包含的字符串都是唯一约束 添加、查询、移除单个元素;检查一个元素是否存在于集合中;计算交集、并集、差集;从集合里面随机获取元素。
HASH 包含键值对的无序散列表 添加、查询、移除单个键值对;获取全部键对值。
ZSET(有序集合) 字符串与浮点分数之间的有序映射,元素的排列顺序由分值大决定。 添加、查询、移除单个元素;根据分值范围或者成员来获取元素。

String

  • 字符串是Redis当中最简单的数据结构。Redis的字符串是动态的字符串,可以被反复修改。内部实现类似Java的ArrayList,采用预分配空间的方式来减少内存频繁分配。预分配大小方式根据字符串的大小决定,字符串小于1m则扩容空间是加倍。若字符串超过1m,扩容一次只会多扩1m的空间。字符串最大长度是512m。
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
192.168.31.7:6379> keys * # 检查当前空间key
(empty list or set) # 空的
# 当个字符串读写
192.168.31.7:6379> set redis-name redis:latest
OK
192.168.31.7:6379> keys *
1) "redis-name"
192.168.31.7:6379> get redis-name
"redis:latest"
192.168.31.7:6379> set redis-version 5.0.3
OK
# 批量获取字符串
192.168.31.7:6379> mget redis-name redis-version
1) "redis:latest"
2) "5.0.3"
# 批量写入字符串
192.168.31.7:6379> mset os mac language Chinese
OK
192.168.31.7:6379> keys *
1) "redis-version"
2) "language"
3) "os"
4) "redis-name"
192.168.31.7:6379> mget redis-name redis-version os language
1) "redis:latest"
2) "5.0.3"
3) "mac"
4) "Chinese"
192.168.31.7:6379>
阅读全文 »

mybatis小试牛刀 在此基础上添加log日志。

0x01 测试控制台的显示内容

1
2
3
User(id=1, name=TOM, age=12, gender=0)
User(id=2, name=JON, age=13, gender=1)
User(id=3, name=King, age=11, gender=0)

0x02 增加日志输出

根据官方文档显示,支持的log有很多实现,这里使用Slf4j + Logback 多组合。

  • 根据之前的项目,这里偷懒重新创建一个项目并把之前的代码都复制过来。
  • 添加maven依赖。
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
 <properties>
<mybatis.version>3.5.0</mybatis.version>
<junit.version>4.12</junit.version>
<lmbok.version>1.18.4</lmbok.version>
<mysql.version>5.1.47</mysql.version>
<h2.version>1.4.197</h2.version>
<!-- 新增内容 -->
<slf4j.version>1.7.25</slf4j.version>
<logback.version>1.2.3</logback.version>
<!-- pom文件需要指定打包编码集,[WARNING] File encoding has not been set, using platform encoding GBK, i.e. build is platform dependent! -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>


<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
  • 添加logback配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>z201.github.cn %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
  • 再次运行单元测试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
z201.github.cn 15:08:30.046 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
z201.github.cn 15:08:30.049 [main] DEBUG org.apache.ibatis.logging.LogFactory - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter.
z201.github.cn 15:08:30.076 [main] DEBUG o.a.i.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
z201.github.cn 15:08:30.076 [main] DEBUG o.a.i.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
z201.github.cn 15:08:30.076 [main] DEBUG o.a.i.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
z201.github.cn 15:08:30.076 [main] DEBUG o.a.i.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
z201.github.cn 15:08:30.155 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Opening JDBC Connection
z201.github.cn 15:08:30.356 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Created connection 1839168128.
z201.github.cn 15:08:30.356 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [conn0: url=jdbc:h2:mem:mybatis_learning_01_db user=ROOT]
z201.github.cn 15:08:30.358 [main] DEBUG c.z201.mybatis.learning.UserDao.list - ==> Preparing: SELECT * FROM `user`
z201.github.cn 15:08:30.384 [main] DEBUG c.z201.mybatis.learning.UserDao.list - ==> Parameters:
z201.github.cn 15:08:30.411 [main] DEBUG c.z201.mybatis.learning.UserDao.list - <== Total: 3
z201.github.cn 15:08:30.414 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [conn0: url=jdbc:h2:mem:mybatis_learning_01_db user=ROOT]
z201.github.cn 15:08:30.415 [main] DEBUG o.a.i.t.jdbc.JdbcTransaction - Closing JDBC Connection [conn0: url=jdbc:h2:mem:mybatis_learning_01_db user=ROOT]
z201.github.cn 15:08:30.415 [main] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1839168128 to pool.
User(id=1, name=TOM, age=12, gender=0)
User(id=2, name=JON, age=13, gender=1)
User(id=3, name=King, age=11, gender=0)
z201.github.cn 15:08:30.416 [main] DEBUG cn.z201.mybatis.test.MybatisTest01 - end...

接着上章内容mybatis源码环境搭建,根据官方文档,运行一个demo项目。

项目结构

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
mybatis-learning/mybatis-learning-01
├── mybatis-learning-01.iml
├── pom.xml #项目依赖管理 1
└── src
├── main
│   ├── java
│   │   ├── cn
│   │   │   └── z201
│   │   │   └── mybatis
│   │   │   └── learning
│   │   │   ├── SetUpSessionFactory.java # mybatis 初始化 3
│   │   │   ├── User.java # 实体 5
│   │   │   └── UserDao.java # 实体dao 6
│   │   └── package-info.java
│   └── resources
│   ├── init_table.sql # h2数据库初始化sql 4
│   ├── mybatis
│   │   └── UserDao.xml # dao 对应的mapper 7
│   └── mybatis-config.xml # mybatis 配置文件 2
└── test
└── java
└── cn
└── z201
└── mybatis
└── test
├── MybatisTest01.java # 单元测试 8
└── package-info.java

15 directories, 11 files

根据上面注释的顺序,我们依次介绍项目代码。

阅读全文 »

mybatis学习笔记。

0x00 阅读源代码

下载源代码

  • 代码仓库地址 https://github.com/mybatis/mybatis-3.git
  • 阅读版本号 myabtis-3.5.0
  • 下载版本git clone -b mybatis-3.5.0 https://github.com/mybatis/mybatis-3.git
    • 由于github下载速度是在太慢了,这里用gitee克隆一个镜像。
    • 下载版本git clone -b mybatis-3.5.0 https://gitee.com/Z201/mybatis-3.git

查看源代码结构

  • 查看项目主要文件夹
1
2
3
4
5
6
7
tree -d L 2
.
├── src
│   ├── main # 源代码
│   ├── site # 站点稳当
│   └── test # 单元测试
└── travis # 官方的ci集成
  • 查看项目源码主目录
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
tree -d src/main -L 5
src/main
└── java
└── org
└── apache
└── ibatis
├── annotations # 注解
├── binding # 代理
├── builder # 构造
├── cache # 缓存
├── cursor # 返回值类型为游标的方法
├── datasource # 数据源
├── exceptions # 异常
├── executor # 数据操作具体执行
├── io # 文件流
├── jdbc # jdbc模块
├── lang # 工具
├── logging # 日志
├── mapping # 映射、参数、结果集。
├── parsing # 解析器
├── plugin # 插件
├── reflection # 反射
├── scripting # sql解析
├── session # 接口层会话
├── transaction # 事物
└── type # 类型
  • 将项目导入idea中,就可以看到完整的源代码了。

0x01 根据官方文档快速入门

强烈推荐认真阅读mybatis的官方文档。

本章是整理知识内容,为强化知识长期更新。

Git速度查询表

下面是常用 的Git 命令清单。几个专用名词的译名如下:

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

最小配置

为什么要最小配置,每次提交代码需要告诉git。所以需要简单设置下user信息。

1
2
git config --global user.name 'your name'
git config --global user.email 'your email'

这里需要注意config的三个作用域

1
2
3
git config --local  #某个git本地仓库有效
git config --global #当前用户所有仓库有效,就是你系统的登陆用户。
git config --system #对系统所有的仓库都有效果。

我们一般只会使用local 和 global这两个配置。当我们配置好user.name user.email之后,我们可以检查下配置信息。

1
2
3
git config --list --local # 注意,必须在某个仓库里面才能看到。
git config --list --global
git config --list --system

创建一个本地仓库

一般来说git仓库有两种情况,一种是没有仓库,另外一种就是已经有仓库了。这里对第一种情况演示,因为工作中习惯在web上创建仓库。这里还是了解下。

1
git init git_learning 

创建一个README文件。

1
touch README.md

这里需要说明下,本地仓库创建完成后需要与远程仓库进行管理。

1
git remote add origin github.com:yourname/git_learning.git # 这里的yourname 是你的git账号

建议手动去github上面创建一个叫git_learning的仓库。上面的命令就是做下关联。

1
2
3
git add . # 将当前文件添加至暂存区
git commit -m "upload file" # 将暂存区的文件提交到本地仓库
git push origin mastet # 将本地仓库的改动信息推送到远程仓库中

可控一个远程仓库

1
2
# 克隆一个仓库
git clone www.**/**.git

文件追踪

有些时候不想提交部分文件,但是git add .的时候添加进去了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 添加文件到暂存区
git add [file] [file]

## 添加文件夹暂存区
git add [dir]

## 添加当前目录所有文件暂存区
git add .

## 如果是对所有文件都取消跟踪的话,就是
git rm -r --cached .    ## 不删除本地文件
git rm -r --f .    ## 删除本地文件

##对某个文件取消跟踪
git rm --cached readme1.txt ## 删除readme1.txt的跟踪,并保留在本地。
git rm --f readme1.txt ## 删除readme1.txt的跟踪,并且删除本地文件s

提交仓库

如果发现message写错了,可以修改下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 修改最新一次commit提交message。
git commit --amend

# 提交暂存区的指定文件到仓库区
git commit [file1] [file2] ... -m [message]

# 将本地暂存区的文件推送到本地仓库。 当修改已经通过`git add <change file>`将其添加到`stage`,可以通过`git commit -m "<message>"`为这所有已经进入`stage`的改变添加一个`commit`信息。
git commit -m

#可以直接使用`git commit -am "<message>"`,将所有修改,但未进`stage`的改动加入`stage`,并记录`commit`信息。(某种程度上相当于`git add`和`git commit -m`的组合技,前提是被改动文件已经是`tracked`)
git commit -am

#这个命令经常会出现在本地仓库与远程仓库发生冲突的时候,需要强制推送更新。
git push -f origin master

# 推送删除远程分支操作
git push origin : xxxx

# 推送删除远程分支操作
git push origin --delete xxxx

  • 如果想修改以往的commit的message。注意是不连续的commit。
    • 首先查看log 里面的信息,然后获取commitid进行合并
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Dubbo概述

  • 服务治理(SOA)治理框架
  • Duboo是一个分布式服务框架
    • 远程通讯:提供多种基于长链接的NIO框架抽象封装,包括多种线程模型、序列化“请求-响应”的信息交换方案。
    • 集群容错:提供基于接口方法的透明远程调用过程,包括多协议支持、软负载均衡、失败容错、地址路由、动态配置等集群支持。
    • 自动发现:基于注册中心目录服务,使服务消费方能动态地查找提供方,使地址透明,使服务提供方可以平滑增或减少机器。
    • 介绍:
    • 致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
    • Dubbo的命名:澳大利亚达博市
    • 同类型框架:
      • Apache Thrift、Hessian、RMI、WebService(原生)、HSF(淘宝,不开源)、JSF(京东,不开源)。
    • Dubbo涉及的基础知识
      • 远程调用方面:RMI、Hassion、WebService、Thrift进行底层调用。
      • 通讯交互:HTTP、Mian、netty。
      • 序列化:Hession2、Java、Json。
      • 容器:Jetty、Spring。
      • 负载均衡:zookeeper、Redis。(大部分都是zookeeper)
    • 简单的使用场景
      • 作为对内提供服务应用的容器。
      • 拆分负载Web应用到服务容器。
      • 应用负载均和协调。
      • 引用服务处理。

架构节点(抽象概念)

  • Provider:暴露服务的服务提供方。
  • Consumer:调用远程服务的服务消费方。
  • Registry:服务注册于发现服务
  • Monitor:统计服务的调用次调用时间监控中心
  • Container:服务运行容器
    • 节点调用关系
    • 服务提供-服务提供者在启动的时候,向注册中心提供自己提供的服务。
    • 服务消费-服务消费者在启动的时候,向注册中心订阅自己所需要的服务。
      • 注册中心异步返回服务提供者地址列表给消费者,如果有变动,注册中心将给予长连接推送变更数据给消费者。
    • 服务统计-服务者和消费者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
      • 异步方式
    • 服务调用关系
      • 服务消费者,从提供者地址列表中,基于[软负载均衡算法],选一台提供者进行调用,如果调用失败,在选另一台调用。

Dubbo源码相关

  • 源码地址git clone https://github.com/alibaba/dubbo

  • 初次编码不执行Test mvn clean install -Dmaven.test.skip=true

  • JS错误不修改,请自行忽视。

  • 修复mavan的警告
    * 地址:dubbo-parent pom.xml
    * maven-jar-plugin version:2.4
    * maven-surefire-plugin Version 2.19.1
    * maven-deploy-plugin Version 3.3.9

  • 项目导入后修复Dubbo XML校验错误提示

  • 需要导入dubbo.xsd文件

  • 下载dubbo-2.5.3.jar 版本尽量保持源码同步,解压dt后导入eclipse中。

    • Preferences-XML-XML Catalog 添加一个自定义的:

      1
      <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dubbo SYSTEM"http://code.alibabatech.com/schema/dubbo">
  • 关于maven-dependency-plugin错误 可能是IDE错误提示,空白处添加空格保存自动消失。

    • 错误信息如下:Artifact has not been packaged yet. When used on reactor artifact, unpack should be executed after packaging: see MDEP-98. (org.apache.maven.plugins:maven-dependency-plugin:2.8:unpack:unpack:package)

      • 修复方案更改插件配置:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <?xml version="1.0" encoding="utf-8"?>

      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <version>2.10</version>
      <executions>
      <execution>
      <id>unpack</id>
      <phase>packaged</phase>
      <goals>
      <goal>unpack</goal>
      </goals>
      </execution>
      </executions>
      </plugin>
  • Dubbo源码模块

    • Config配置层(Dubbo-config)
    • Proxy服务代理层(Dubbo-rpc)
    • Registry注册中心层(Dubbo-registry)
    • Cluster路由层(Dubbo-cluster)
    • Monitor监控层(Dubbo-monitor)
    • Protocol远程调用层(Dubbo-rpc)
    • Exchange信息交换层(Dubbo-remoting)
    • Transport网络传输层(Dubbo-remoting)
    • Serialize数据序列化层(Dubbo-common)

Zookeeper

(测试方案采用zookeeper做均衡负载,简单介绍下配置信息)

  • 下载地址 http://apache.fayea.com/zookeeper/zookeeper-3.4.8/zookeeper-3.4.8.tar.gz
  • 基本配置:
    • 单机模式:修改zookeeper-3.4.8/conf zoo.cfg(原始文件名zoo_sanple.cfg复制一份重命名)
      • 编辑内容(仅供参考,具体环境自行修改)
      • tickTime = 2000
        • tickTime:基本事件单元,以毫秒为单位。这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
      • initLimit= 5
        • initLimit:这个配置项用来配置Zookeeper接受客户端初始化连接时最长能忍受多少个心跳时间间隔数,当已超过5个心跳的时间(也就是tickTime)长度后Zookeeper服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度是5 * 2000=4s。
      • dataDir = D:\zookeeper\data
        • 顾名思义就是 Zookeeper 保存数据快照的目录,默认情况下,Zookeeper 将写数据的日志文件也保存在这个目录里)。
      • dataLogDir= D:\zookeeper\log
        • 顾名思义就是 Zookeeper 保存日志的目录。
      • synclimit = 5
        • 这个配置项表示Leader与Follower之间发送消息,请求和应答时间长长度,最长不能超过多少个tickTime的时间长度,总的时间长度是2 * 2000 = 4s。
      • clientPort = 2181
        • 这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。
      • Server(待续)
        • 格式:server.id=host:port:port(两个port保证可以正常使用就行)
          • id:通常为整数,建议使用整数。
          • host:服务器的IP地址。
          • port: Follower端口
          • port: Leader选举投票。
        • ZooKeeper建议使用hostname,而非ip。这需要对主机的/etc/hostname和/etc/hosts做host绑定(不用的OS不同修改方式)。
      • 创建一个myid文件(放在 dataDir文件下面)
        • 写入一行数据(请查阅zoo.cfg文件)
          • 写入当前Zookeeper id位置的数据即可。表示当前系统环境Zookeeper是哪一个Server(通讯用的)。

Dubbo程序运行

  • 测试运行,默认配置即可。注意与zookeeper一台机器。
  • 运行Dubbo-admin本机tomcat启动启动成功后127.0.0.1:8080/dubbo-admin(注意默认请设置跟目录,否则部分功能不发正常显示)
    • 默认用户名密码(root/root,guest/guest)

本章是整理知识内容,为强化知识长期更新。

面向对象思想

  • 面向对象是一个思想,时间万物皆可以被看做一个对象。

封装

  • 隐藏对象的属性和实现的具体细节,只对外暴露公共访问方式。

继承

  • 当多个类出现相同代码逻辑时,我们通常将相同的代码重构到一个类中,如果是绑定关系就可以使用继承。
  • Java中类是单继承。多继承会导致棱形问题。
  • 继承是面向对象的四大特性之一,用来表示类之间的 is-a 关系,可以解决代码复用的问题。虽然继承有诸多作用,但继承层次过深、过复杂,也会影响到代码的可维护性。
    • 可以利用组合(composition)、接口、委托(delegation)三个技术手段,一块儿来解决刚刚继承存在的问题。

多态

  • 一个事物的的多种状态,比如女人、男人都是人的性别。人的性别就分为女人、男人。

抽象

  • 在逻辑上看似相关的,想要把他们联系起来。这样可以提高效率。矩形、圆形,都可以具有周长和面积两个方法,但是计算的方式完全不同,矩形和圆形之间肯定不能构成子父类的关系,那么只能是同时去继承一个父类。这时,就引出了抽象的概念。

总结

  • 封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式来访问内部信息或者数据。它需要编程语言提供权限访问控制语法来支持,例如 Java 中的 private、protected、public 关键字。封装特性存在的意义,一方面是保护数据不被随意修改,提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。
  • 抽象可以通过接口类或者抽象类来实现,但也并不需要特殊的语法机制来支持。抽象存在的意义,一方面是提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动范围;另一方面,它也是处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息。
  • 继承是用来表示类之间的 is-a 关系,主要是用来解决代码复用的问题。
  • 多态是指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。多态这种特性也需要编程语言提供特殊的语法机制来实现。主要解决扩展性问题。

接口与抽象类

如果我们要表示一种 is-a 的关系,并且是为了解决代码复用的问题,我们就用抽象类;如果我们要表示一种 has-a 关系,并且是为了解决抽象而非代码复用的问题,那我们就可以使用接口。

  • 接口:它是一种自上而下的设计思路。我们在编程的时候,一般都是先设计接口,再去考虑具体的实现。

    • 实际开发过程中,容易过度使用比如给每个类都定义接口。
    • 基于接口而非实现编程”这条原则的英文描述是:“Program to an interface, not an implementation”
    • 从本质上来看,“接口”就是一组“协议”或者“约定”,是功能提供者提供给使用者的一个“功能列表”。“接口”在不同的应用场景下会有不同的解读,比如服务端与客户端之间的“接口”,类库提供的“接口”,甚至是一组通信的协议都可以叫作“接口”。
  • 抽象类 :抽象类是一种自下而上的设计思路,先有子类的代码重复,然后再抽象成上层的父类(也就是抽象类)

    • 抽象类不允许被实例化,只能被继承。它可以包含属性和方法。方法既可以包含代码实现,也可以不包含代码实现。不包含代码实现的方法叫作抽象方法。子类继承抽象类,必须实现抽象类中的所有抽象方法。接口不能包含属性,只能声明方法,方法不能包含代码实现。类实现接口的时候,必须实现接口中声明的所有方法。

    • 每个优秀的程序员都知道,不应该定义一个attackBaghdad() ‘袭击巴格达‘ 的方法,而是应该把城市作为函数的参数 attack(city)。

重写和重载

  • 重载:同一个类中,方法名相同,参数个数或者类型不相同,返回类型可以不相同。
  • 重写:类的继承关系中体现,子类重写父类的方法。
区别点 重载方法 重写方法
发生范围 同一个类 子类
参数列表 必须修改 一定不能修改
返回类型 可修改 子类方法返回值类型应比父类方法返回值类型更小或相等
异常 可修改 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
访问修饰符 可修改 一定不能做更严格的限制(可以降低限制)
发生阶段 编译期 运行期
  • 重写(override)

    • 存在父类和子类之间。
    • 方法名、参数、返回值相同。
    • 方法被final修饰不能被重写。
    • 子类重写父类方法后,不能抛出比父类方法的异常。子类不能缩写父类的方法访问权限
  • 重载(overload)

    • 参数类型、个数、顺序至少有一个不相同。
    • 不能重载只有返回值不同的方法名。
    • 存在于父类和子类、同类中。
  • Constructor 不能被 override(重写),但是可以 overload(重载),所以你可以看到⼀个类中有多个构造函数的情况。

介绍下 Java 基本数据类型

  • Java中存在8个原生数据类型,同时又分成四种:整形、浮点型、char、Boolean。它们之间存在自动类型转换,规则是从小到大。并且都存在自动装箱拆箱特性,但是这种操作是隐式操作而且在某些情况会导致CG压力增大。
类型 存储需求 取值范围
int 4字节 -2 147 483 638 ~ 2 147 483 637
short 2字节 -32 768 ~ 32 767
long 8字节 -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807
byte 1字节 - 128 ~ 127
  • 整型的范围与运行Java运行的硬件没有关系,所有的数据类型所占的字节数量与平台无关。
类型 存储需求 取值范围
float 4字节 大约 $\pm$ 3.402 823 37F + 38F (有效位数为7~8位)
double 8字节 大约 $\pm$ 1.797 693 134 862 315 70E + 308 (有效位数为15位)
  • double这种类型的精度是float的两倍。

  • 所有浮点数值计算都遵循IEEE 754规范,下面是溢出和出错的情况的三种特殊的浮点数值。

    • 正无穷大
    • 负无穷大
    • NaN ( 不是一个数字 )
    • 一个整整数除以0的结果为正无穷大,计算0/0或者负数的平方根结果为NaN。
  • char类型

    • char类型表示单个字符,属于Unicode编码表。因为历史原因,不建议在程序中使用。除非确实要对UTF-16代码单元进行操作。
    • char字节大小
      • Java中无论是汉字还是英文字母都是用Unicode编码来表示的,一个Unicode是16位,每字节是8位,所以一个Unicode码占两字节。但是英文字母比较特殊,源自于8位(1字节)的ASCII吗,于是在Unicode码仅使用了低8位(1字节)就可以表示。
  • boolean类型

    • 布尔类型,只有两个值false、true。基本用于判定条件。
    • boolean字节大小
      • Java规范中并未明确规定boolean类型的大小。
  • 自动类型转换

    • 整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。

      转换从低级到高级。

      1
      byte,short,char—> int —> long—> float —> double 
    • 不能对boolean进行类型转换、不能把对象类型转换成不相关的对象、把大容量的对象转换成小容量对象时需要强制类型转换、转换过程中间可能出现精度损失。

装箱和拆箱boxing or unboxing

装箱:将基础类型用它们对应类型包装起来

拆箱:将包装类型转换成基本数据类型

原语 对应的JDK类
int java.lang.Integer
short java.lang.Short
long java.lang.Long
byte java.lang.Byte
char java.lang.Character
double java.lang.Double
float java.lang.Float
boolean java.lang.Boolean
  • Java中只有原生数据类型是特殊的,它们不是对象。其它的都是对象。那么就一个尴尬的问题,集合类都是存放的对象,JDK5之后考虑到这个问题就自动进行逆行拆箱装箱的操作。

    1
    2
    3
    //比如所在泛型中是不能存放原生数据类型的,如要要存放原生数据类型的数据,需要装箱。
    Collection<int> c = new ArrayList<int>(); //这是无法编译成功的。
    Collection<Integer> cc = new ArrayList<Integer>(); //这样才行。
  • 每个 JDK 类都提供了相应方法来解析它的内部表示,并将其转换为相应的原语类型。

  • 但是注意装箱拆箱操作其实是非常消耗内存的举动,在该过程中可能会生成生成无用对象增加GC压力。所以尽量避免这中操作。

    1
    2
    3
    4
    Integer sum = 0;
    for(int index = 1000; index < 5000; index ++){
    sum+=index;
    }

    比如这种,每次sum都需要自动拆箱。

  • 默认情况下整数的类型都是int、浮点型的数都是double。

    1
    float d = 1.1f; //在后面添加f,大小写不区分。隐式强制类型转换
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Java集合

​ 集合是Java提供的工具包、包含常用的数据结构:集合、链表、队列、栈、数组、映射等。Collection的包是java.util.*。

Collection

Map

实际上还有Cloneable、Serializable接口是都集合类需要实现的,通用所以不不画上去了。

Java集合主要划分4个部分:

  • List(列队)
  • Set(集合)
  • Map(映射)
  • 工具类(Iterator迭代器、Enumeration枚举类、Arrays、Collections)

划重点

  • Conllection
    • List
      • ArrayList
      • Vector
        • Stack
      • LinkedList
    • Set
      • HashSet
      • TreeSet
      • LinkedHashSet
    • Queue
  • Map
    • HashMap
    • HashTable
    • TreeMap
  • 工具
    • Arrays
    • Collections
    • Enumeration

Java类库中具体集合

集合类型 概括
ArrayList 一种可以动态增长和缩减的索引序列,访问速度很快但是插入和删除比ArrayList慢。
LinkedList 一种可以在任何位置进行高效地插入和删除操作的有序序列,但是访问比较ArrayList慢。
CopyOnWriteArrayList CopyOnWriteArrayList相当于线程安全的ArrayList,它实现了List接口,支持高并发。
ArrayDeque 一种循环数组实现的双端序列。
HashSet 一种没有重复元素的无序集合。
TreeSet 一种有序集合。
EnumSet<E extends Enum> 一种包含枚举类型的集合。
LinkedHashSet 一种可以记住元素插入顺序的集合。
PriorityQueue 一种允许高效删除最小元素的集合。
HashMap<K , V> 一个存储 键 / 值 关联的数据结构。
TreeMap<K , V> 一种键值有序排列的映射表。
EnumMap<K extends Enum, V> 一种键属于枚举类型的映射表。
LinkedHashMap<K ,V > 一种可以记住键 / 值 项添加顺序的映射表。
WeakHashMap<K , V > 一种其值无用武之地后可以被垃圾回收回收的映射表。
IdentityHashMap<K , V> 一种用 == 而不是用用 equals 比较的映射表。
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Mavan常用命令以及用法

  • 常用命令就要先了解Maven的生命周期
    • Maven的生命周期就是对所有的构建过程进行抽象和统一。包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有的构建步骤。
    • Maven的生命周期是抽象的,即生命周期不做任何实际的工作,实际任务由插件完成,类似于设计模式中的模板方法。所以必须有对应的插件才能执行对于的步骤。
    • Maven有三套相互独立的生命周期,分别是clean、default和site。每个生命周期包含一些阶段(phase),阶段是有顺序的,后面的阶段依赖于前面的阶段。
      • clean生命周期:清理项目,包含三个phase。
        • 1.)pre-clean:执行清理前需要完成的工作。
        • 2)clean:清理上一次构建生成的文件。
        • 3)post-clean:执行清理后需要完成的工作。
      • default生命周期:构建项目,重要的phase如下。
        • 1)validate:验证工程是否正确,所有需要的资源是否可用。
        • 2)compile:编译项目的源代码。
        • 3)test:使用合适的单元测试框架来测试已编译的源代码。这些测试不需要已打包和布署。
        • 4)Package:把已编译的代码打包成可发布的格式,比如jar。
        • 5)integration-test:如有需要,将包处理和发布到一个能够进行集成测试的环境。
        • 6)verify:运行所有检查,验证包是否有效且达到质量标准。
        • 7)install:把包安装到maven本地仓库,可以被其他工程作为依赖来使用。
        • 8)Deploy:在集成或者发布环境下执行,将最终版本的包拷贝到远程的repository,使得其他的开发者或者工程可以共享。
      • site生命周期:建立和发布项目站点,phase如下。
        • 1)pre-site:生成项目站点之前需要完成的工作。
        • 2)site:生成项目站点文档。
        • 3)post-site:生成项目站点之后需要完成的工作。
        • 4)site-deploy:将项目站点发布到服务器
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

整体架构

基础层

处理层

接口层

Mybatis执行流程

  • 获取sqlSessionFactory对象:解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;注意:MappedStatement:代表一个增删改查的详细信息。
  • 获取sqlSession对象,返回一个DefaultSQlSession对象,包含Executor和Configuration;这一步会创建Executor对象;
  • 获取接口的代理对象(MapperProxy),getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象。

Mybatis拦截器

代理对象里面包含了,DefaultSqlSession(Executor)

  • 执行增删改查方法。
    • 调用DefaultSqlSession的增删改查(Executor)
    • 创建一个StatementHandler对象、且同时创建出ParameterHandler和ResultSetHandler。
    • 调用StatementHandler预编译参数以及设置参数值;使用ParameterHandler来给sql设置参数
    • 调用StatementHandler的增删改查方法;
    • ResultSetHandler封装结果
  • MyBatis 拦截签名 拦截器签名是一个名为 @Intercepts 的注解,该注解中可以通过 @Signature 配置多个签名。@Signature 注解中则包含三个属性
    • type: 拦截器需要拦截的接口,有 4 个可选项,分别是:Executor、ParameterHandler、ResultSetHandler 以及 StatementHandler。
    • method: 拦截器所拦截接口中的方法名,也就是前面四个接口中的方法名,接口和方法要对应上。
    • args: 拦截器所拦截方法的参数类型,通过方法名和参数类型可以锁定唯一一个方法。
  • 被拦截的对象
    • org.apache.ibatis.executor.Executor
    • org.apache.ibatis.executor.statement.StatementHandler
    • org.apache.ibatis.executor.statement.ParameterHandler
    • org.apache.ibatis.executor.resultset.ResultSetHandler
  • Executor
    • update:该方法会在所有的 INSERT、 UPDATE、 DELETE 执行时被调用,如果想要拦截这些操作,可以通过该方法实现。
    • query:该方法会在 SELECT 查询方法执行时被调用,方法参数携带了很多有用的信息,如果需要获取,可以通过该方法实现。
    • queryCursor:当 SELECT 的返回类型是 Cursor 时,该方法会被调用。
    • flushStatements:当 SqlSession 方法调用 flushStatements 方法或执行的接口方法中带有 @Flush 注解时该方法会被触发。
    • commit:当 SqlSession 方法调用 commit 方法时该方法会被触发。
    • rollback:当 SqlSession 方法调用 rollback 方法时该方法会被触发。
    • getTransaction:当 SqlSession 方法获取数据库连接时该方法会被触发。
    • close:该方法在懒加载获取新的 Executor 后会被触发。
    • isClosed:该方法在懒加载执行查询前会被触发。
  • StatementHandler
    • prepare:该方法在数据库执行前被触发。
    • parameterize:该方法在 prepare 方法之后执行,用来处理参数信息。
    • batch:如果 MyBatis 的全剧配置中配置了 defaultExecutorType=”BATCH”,执行数据操作时该方法会被调用。
    • update:更新操作时该方法会被触发。
    • query:该方法在 SELECT 方法执行时会被触发。
    • queryCursor:该方法在 SELECT 方法执行时,并且返回值为 Cursor 时会被触发。
  • ParameterHandler
    • getParameterObject:在执行存储过程处理出参的时候该方法会被触发。
    • setParameters:设置 SQL 参数时该方法会被触发。
  • ResultSetHandler
    • handleResultSets:该方法会在所有的查询方法中被触发(除去返回值类型为 Cursor 的查询方法),一般来说,如果我们想对查询结果进行二次处理,可以通过拦截该方法实现。
    • handleCursorResultSets:当查询方法的返回值类型为 Cursor 时,该方法会被触发。
    • handleOutputParameters:使用存储过程处理出参的时候该方法会被调用。

MyBatis插件

MyBatis 将插件单独分离出一个模块,位于 org.apache.ibatis.plugin 包中,在该模块中主要使用了两种设计模式:代理模式和责任链模式。

  • 插件接口

    • org.apache.ibatis.plugin.Interceptor

    • intercept:它将直接覆盖你所拦截的对象,有个参数Invocation对象,通过该对象,可以反射调度原来对象的方法;

    • plugin:target是被拦截的对象,它的作用是给被拦截对象生成一个代理对象;

    • setProperties:允许在plugin元素中配置所需参数,该方法在插件初始化的时候会被调用一次;

  • MyBatis允许我们自定义 Interceptor 拦截 SQL 语句执行过程中的某些关键逻辑,允许拦截的方法有:Executor 类中的 update()、query()、flushStatements()、commit()、rollback()、getTransaction()、close()、isClosed()方法,ParameterHandler 中的 setParameters()、getParameterObject() 方法,ResultSetHandler中的 handleOutputParameters()、handleResultSets()方法,以及StatementHandler 中的parameterize()、prepare()、batch()、update()、query()方法。

MyBatis中的设计模式

  • 工厂模式
    • 工厂模式在 MyBatis 中的典型代表是 SqlSessionFactory。
    • SqlSession 是 MyBatis 中的重要 Java 接口,可以通过该接口来执行 SQL 命令、获取映射器示例和管理事务,而 SqlSessionFactory 正是用来产生 SqlSession 对象的,所以它在 MyBatis 中是比较核心的接口之一。
    • 工厂模式应用解析:SqlSessionFactory 是一个接口类,它的子类 DefaultSqlSessionFactory 有一个 openSession(ExecutorType execType) 的方法,其中使用了工厂模式。
  • 建造者模式
    • MyBatis 中的典型代表是 SqlSessionFactoryBuilder。
    • 普通的对象都是通过 new 关键字直接创建的,但是如果创建对象需要的构造参数很多,且不能保证每个参数都是正确的或者不能一次性得到构建所需的所有参数,那么就需要将构建逻辑从对象本身抽离出来,让对象只关注功能,把构建交给构建类,这样可以简化对象的构建,也可以达到分步构建对象的目的,而 SqlSessionFactoryBuilder 的构建过程正是如此。
    • 在 SqlSessionFactoryBuilder 中构建 SqlSessionFactory 对象的过程是这样的,首先需要通过 XMLConfigBuilder 对象读取并解析 XML 的配置文件,然后再将读取到的配置信息存入到 Configuration 类中,然后再通过 build 方法生成我们需要的 DefaultSqlSessionFactory 对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}

public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}

  • 单例模式
    • 单例模式在 MyBatis 中的典型代表是 ErrorContext。
    • 使用 private 修饰的 ThreadLocal 来保证每个线程拥有一个 ErrorContext 对象,在调用 instance() 方法时再从 ThreadLocal 中获取此单例对象。
1
2
3
4
5
6
7
8
9
10
11
public class ErrorContext {

private static final String LINE_SEPARATOR = System.lineSeparator();
// 每个线程存储的容器
private static final ThreadLocal<ErrorContext> LOCAL = ThreadLocal.withInitial(ErrorContext::new);

public static ErrorContext instance() {
return LOCAL.get();
}
}

  • 适配器模式

    • MyBatis 中的典型代表是 Log。
      • SLF4J
      • Apache Commons Logging
      • Log4j 2
      • Log4j
      • JDK logging
  • 代理模式

    • 代理模式在 MyBatis 中的典型代表是 MapperProxyFactory。
  • 模版方法模式

    • 模板方法在 MyBatis 中的典型代表是 BaseExecutor。
  • 装饰器模式

    • 装饰器模式在 MyBatis 中的典型代表是 Cache。

Mybatis一些疑问

#{} 于 ${}

使用#{}可以有效的防止SQL注入,提高系统安全性。

  • #{}是预编译,${}是字符串替换。
    • Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;
    • Mybatis在处理${}时,就是把${}替换成变量的值。

MyBatis Dao 接口的工作原理

  • Dao 接口的全限定名,就是映射文件中的 namespace 的值,接口的方法名,就是映射文件中 Mapper 的 Statement 的 id 值,接口方法内的参数,就是传递给 SQL 的参数。Mapper 接口是没有实现类的,当调用接口方法时,接口全限定名 + 方法名拼接字符串作为 key 值,可唯一定位一个 MapperStatement。在 MyBatis 中,每一个 select、insert、update、delete 标签,都会被解析为一个 MapperStatement 对象。

Dao 接口里的方法可以重载吗

  • Mapper 接口的工作原理是 JDK 动态代理,MyBatis 运行时会使用 JDK 动态代理为 Mapper 接口生成代理对象 proxy,代理对象会拦截接口方法,转而执行 MapperStatement 所代表的 SQL,然后将 SQL 执行结果返回。所以是不能重载的

本章是整理知识内容,为强化知识长期更新。

Spring Web MVC

Spring Web 模型-视图-控制器 (MVC) 框架围绕DispatcherServlet将请求分派给处理程序而设计。

常用注解

  • @Controller

    • 声明在类上,该注解表明该类扮演控制器的角色,类似Action。
  • @RestController

    • RestController是Controller超子集,相当于@RequestMapping方法默认采用@ResponseBody注解。
  • @RequestMapping

    • 该注解是用来映射一个URL到一个类或一个特定的方处理法上。
    • RequestMapping属性
      • path / method 指定方法的映射路径
      • params / headers 请求映射范围
      • consumes / produces 请求与响应格式的限制范围
    • Restfull风格的使用。
      • restfull 支持的请求头GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE 。
      • 通常情况下只使用 GET,PUT,POST,DELETE。
        • GET 通常用来获取数据。
        • PUT 通常用来新增数据。
        • POST 通常用来更新数据。
        • DELETE 通常用来删除数据。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public Object get(){
    return "200";
    }

    @RequestMapping(value = "/post", method = RequestMethod.POST)
    public Object post(){
    return "200";
    }

    @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
    public Object delete(){
    return "200";
    }

    @RequestMapping(value = "/put", method = RequestMethod.PUT)
    public Object put(){
    return "200";
    }

DispatcherServlet

  • Root WebApplicationcontext 是可以在不同的 Servlet WebApplicationcontext共享,但是反来不醒,因此通常将web相关的代码放到Servlet WebApplicationcontext一些基础代码放到Root WebApplicationcontext 。也可以让Root WebApplicationcontext 托管所有的bean,这样可以避免Servlet WebApplicationcontext未初始化全部bean的问题。

源码分析

  • DispatcherServlet是继承了FrameworkServlet,而FrameworkServlet又继承了HttpServletBean。追溯到最上层其实就是Servlet。通过源码可以发现最核心的一个方法doService()

  • 处理流程

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);

// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}

try {
//进入doDispatch
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}



/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;

WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

try {
ModelAndView mv = null;
Exception dispatchException = null;

try {
// 先处理是否Multipart的请求,如果是则会解析,并且返回一个解析后的请求。
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 寻找对应的handler
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 没有找到则直接返回默认404视图。
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
// 如果上面的流程都执行完成,在执行真实的handle
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 如果是异步的就直接结束。
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 视图解析
applyDefaultViewName(processedRequest, mv);
//执行后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}

// 响应视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
// 没有抛异常也会判断是否异步,如果是异步也会执行拦截器。
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}


/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历所有的HandlerMapping找出对应的HandlerExecutionChain
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}




/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
// 取出所有拦截器,遍历拦截器
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}

Spring作用域。

  • 当定义一个Bean时,可以给这个Bean声明一个作用域。通过@Scope注解声明在类上。

    • Bean默认作用域是singleton 单例模式,在每个Spring Ioc容器中的一个Bean定义一个实例,也就是无状态Bean,也就是线程不安全的。这个单一实例会被存储到单例缓存(singleton cache)中,并且所有针对该bean的后续请求、访问和引用都将返回被缓存的对象实例。相当于一次创建多次使用。
    • prototype 原型模式,在每个Spring Ioc容器中的一个Bean定义多实例,就相当与Java 中new 操作。通常作为有状态的Bean。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。
  • spring mvc增加的作用域。

    • request、session、global session 仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架)。
      • 如果不是在web下实现而是通过ApplicationContext这种实现,尝试使用这些作用域将抛出异常IllegalStateException未知作用域。
      • request 对每一次HTTP请求都会产生一个新的bean。Spring容器会根据loginAction bean定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。
      • session 对每一次HTTP请求都会产生一个新的bean,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
      • ‍global session 作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。在一个标准的基于Servlet的web应用,并且定义了一个或多个具有global session作用域的bean,系统会使用标准的HTTP Session作用域,并且不会引起任何错误。
    • 作用域依赖
      • Spring IoC容器除了管理对象(bean)的实例化,同时还负责协作者(或者叫依赖)的实例化。如果将一个Http request范围的bean注入到另一个bean中,那么需要注入一个AOP代理来替代被注入的作用域bean。也就是说需要注入一个代理对象,该对象具有与被代理对象一样的公共接口,而容器则可以足够智能的从相关作用域中(比如一个HTTP request)获取到真实的目标对象,并把方法调用委派给实际的对象。

阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Spring

  • Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

Spring IOC = Spring Bean + Spring Context特性

  • 主要由以下几个模块组成:
    • Spring Core:核心类库,提供IOC服务
    • Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等)
    • Spring AOP:AOP服务切面编程
    • Spring Type Conversion:类型转换
    • Spring Data Binding :数据绑定
    • Spring Express Language:Spring 表达式
    • Spring Resources:资源管理
    • Spring Events:事件
    • Spring i18n:国际化
    • Spring Validation:校验
    • Spring Data Access:数据存储
      • JDBC Java API:对JDBC的抽象,简化了数据访问异常的处理
      • Transactions:事务抽象 EJB简化版本
      • O/R Mapping:映射
      • DAO Support:对JDBC的抽象,简化了数据访问异常的处理
      • XML Marshalling 编列
    • Spring Integration
      • Remoting:远程调用
      • JMS:Java消息服务
      • JCA:Java连接架构
      • JMS:Java管理扩展
      • Email:Java 邮件客户端
      • Tasks:本地任务
      • Scheduling:本地调度
      • Caching:缓存抽象
      • Test : Spring 测试
        • Mock Objects:模拟对象框架
        • Test Context Framework:TestContext框架
        • Spring Mvc Test:Spring MVC 测试
        • WebTestClient :Web 测试客户端
    • Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传
      • Spring Servlet Support
        • Spring MVC:提供面向Web应用的Model-View-Controller实现
        • WebSocket
        • SockJS
      • Spring Reactive
        • Spring WebFlux
        • WebClient
        • WebSocket
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Mysql基础架构图

mysql基础架构图

简单介绍上图相关模块功能

  • 连接器:管理连接,权限验证

  • 查询缓存:命中缓存则直接返回结果

  • 语法解析:词法分析,语法分析

  • 查询优化:执行计划生成,索引选择

  • 执行器:操作引擎,返回结果

  • 存储引擎:存储数据,提供读写结构。

连接器

我们需要连接到mysql服务端才能进行各种操作。

1
2
# 使用密码登录到时候我们会使用这种格式。
mysql -h$ip -P$port -u$user -p

执行上述命令会提示输入密码。输入完密码后就进入了mysql。

  • 如果用户名或者密码输入错误,会提示一个Access denied for user到错误,然后客户端程序结束执行。

  • 如果用户名和密码认证通过,连接器会去权限表中查询该用户拥有的所有权限。此后该连接里面的权限逻辑判断都会依赖认证成功时候读取到的权限。

因此当修改用户的权限之后,如果被修改用户的在登录状态是不会修改已经连接的权限。需要让被修改权限用户从新登录。

  • 实际上连接的方式有多种,上面通过密码的方式只是其中的一种方式。当客户端与服务端mysql进程进程建立连接,服务器的进程就会创建一个单独的线程来专门处理与这个客户端的交互,当该客户断开与服务端连接时,服务端并不会马上销毁对应的交互线程,而是缓存起来。当另一个新的客户端连接进来的时候,在把这个缓存的线程分配给新的客户端。这样就避免的频繁创建和修改线程,节省系统的开销。但是连接线程多了也会影响服务端,所以也有默认的参数限制客户端的连接数量。
1
2
# 这里是查询服务端存在的连接。
SHOW PROCESSLIST;

我使用的阿里云的数据库,这里展示下结果集。这里需要注意,登录的账号需要有PROCESS的权限否则只能看到自己的连接信息(线程),我这里采用的root所以看到全部到。

连接完成后如果没有执行后续操作则 Command会显示Sleep的状态,长时间Sleep会导致连接自动断开。默认的时间是8小时,也就是参数 wait_timeout。当客户端连接断开后,若客户端再次发出请求就会提示一个

Lost connection to MySQL server during query,此时就只能重新建立连接了。

半双工
  • MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。
  • 服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。
长连接与短连接
  • 在数据库里面,长连接是指数据库建立连接,如果客户端有持续的请求则使用同一个连接,短连接是指每次执行几次短的查询就断开连接,下次查询再次创建连接。由于创建的过程复杂,一般来说尽量减少创建连接的动作,尽可能使用长连接。特别是在开发的时候出现大伙突然连接不上测试库,此时就检查下连接是不是太多了。因为连接多了会导致内存消耗特别快。

    • mysql的执行过程中临时使用的内存管理是在连接线程对象里面,服务端断开的时候才会释放,为了避免内存消耗过大,每次执行较大的操作之后,可以通过mysql_reset_connection来重新初始化连接资源。这个操作是不需要重新做权限,只是恢复到创建的初始状态。
  • MySQL mysql_reset_connection 官方文档

查询缓存

一个不被建议使用的功能,在新版的8.0中已经被删除了。总结就是弊大于利。

  • 建立完成后,假设执行一次SELECT语句,执行逻辑就会到第二部查询缓存。mysql拿到一个查询之后,会先到缓存中寻找释放有完全对应的,因为之前执行的查询语句和结果集会直接被缓存起来,以Keys-value的形式。keys是查询语句-value是查询结果集。如果能命中这个key则直接返回value。咋一看挺有用的,但实际上查询缓存的实效的太频繁。并且keys的命中条件太不聪明了,如果两次查询语句在任何的字符上存在不通(空格、大小写)都不会缓存命中。为什么会频繁失效,因为如果对查询语句中的表进行更新,就会导致缓存失效。比如INSERT、UPDATE 、DELETE 、TRUNCATE TABLE 、ALTER TABLE 等等就会导致缓存失效。

语法解析

如果没有命中查询缓存,就要开始执行sql语句了。mysql需要做什么,因此需要对sql进行解析。MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。

  • 本质上发送过来的是一个文本信息,这里就需要对该文本信息进行编译,涉及到词法解析语法解析语义解析等阶段。需要注意的是,这里会对sql语句进行一些检查,比如设计的相关表、表字段。这里检查通过的才会进入查询优化模块。
  • 根据SQL语言的功能可以划分成4个部分
    • DDL,英文叫做 Data Definition Language,也就是数据定义语言,它用来定义我们的数据库对象,包括数据库、数据表和列。通过使用 DDL,我们可以创建,删除和修改数据库和表结构。
    • DML,英文叫做 Data Manipulation Language,数据操作语言,我们用它操作和数据库相关的记录,比如增加、删除、修改数据表中的记录。
    • DCL,英文叫做 Data Control Language,数据控制语言,我们用它来定义访问权限和安全级别。
    • DQL,英文叫做 Data Query Language,数据查询语言,我们用它查询想要的记录,它是 SQL 语言的重中之重。在实际的业务中,我们绝大多数情况下都是在和查询打交道,因此学会编写正确且高效的查询语句,是学习的重点。

查询优化

通过之前的语法解析,基本可以判定语法树是合法的,此时mysql就知道文本内容要做什么。

  • mysql会对查询语句进行优化,优化的结果就是生成一个执行计划,这个计划会表明使用了那些查询索引,表之间的连接是什么样子的。有时候出现多种执行方式,只是效率不通,优化器会决定使用哪一个执行方案,这个时候就涉及到EXPLAIN语句,该语句可以查看sql到执行计划,这将涉及到查询优化。
  • MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划。
    • 重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)
    • 优化函数
    • 提前终止查询(比如:使用Limit时,查找到满足数量的结果集后会立即终止查询)
    • 优化排序(在老版本MySQL会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于I/O密集型应用,效率会高很多。

执行器

进入到执行sql到环节。

  • 通过之前到查询优化,进入执行阶段,此时会先判断该连接用户是否有相关表的权限,如果没有会返回权限错误。如果有权限就会打开表,执行器会根据表定义的引擎去使用这个引擎提供的接口完成流程。

存储引擎

关于存储引擎,这里明确提醒。不是三种也不是四种。而是多种因为随时可能出现新的引擎。

  • 我们常用的就是InnoDBMyISAM,其它的不常见就不做过多介绍。mysql现在默认的引擎是InnoDB所以主要也是了解InnoDB
1
2
# 查看当前服务支持的存储引擎
SHOW ENGINES;

阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Redis 简介

Redis 是完全开源免费的,遵守 BSD 协议,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

  • Redis 与 其他 key - value 缓存产品有以下三个特点:
    • Redis 支持数据持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
    • Redis 不仅仅支持简单的 key - value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储
    • Redis 支持数据的备份,即 master - slave 模式的数据备份。
  • Redis是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快,读的速度是 110000 次 /s, 写的速度是 81000 次 /s。另一方面是因为它的数据结构。键值对是按一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是 Redis 快速处理数据的基础。
  • Redis使用基于哈西槽(slot)的数据划分方式。

Redis数据类型与数据结构

数据结构时间复杂度

名称 时间复杂度
哈希表 O(1)
跳表 O(logN)
双向链表 O(N)
压缩列表 O(N)
整数数组 O(N)

Redis事务

严格上来说redis是伪事物,

  • Redis事务功能是通过MULTI、EXEC、DISCARD和WATCH 四个原语实现的。Redis会将一个事务中的所有命令序列化,然后按顺序执行。
    1. redis 不支持回滚“Redis 在事务失败时不进行回滚,而是继续执行余下的命令”, 所以 Redis 的内部可以保持简单且快速。
    2. 如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
    3. 如果在一个事务中出现运行错误,那么正确的命令会被执行。
  • MULTI命令用于开启一个事务,它总是返回OK。 MULTI执行之后,客户端可以继续向服务器发送任意多条命令,这些命令不会立即被执行,而是被放到一个队列中,当EXEC命令被调用时,所有队列中的命令才会被执行。
  • EXEC:执行所有事务块内的命令。返回事务块内所有命令的返回值,按命令执行的先后顺序排列。当操作被打断时,返回空值 nil 。
  • 通过调用DISCARD,客户端可以清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出。
  • WATCH 命令可以为 Redis 事务提供 check-and-set (CAS)行为。 可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行,监控一直持续到EXEC命令。

Redis单线程

  • 我们通常说,Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等,其实是由额外的线程执行的。Redis 的单线程设计机制以及多路复用机制
  • Linux 中的 IO 多路复用机制
    • Linux 中的 IO 多路复用机制是指一个线程处理多个 IO 流,就是我们经常听到的 select/epoll 机制。简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中,同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求。一旦有请求到达,就会交给 Redis 线程处理,这就实现了一个 Redis 线程处理多个 IO 流的效果。
  • Redis 的网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程

Redis单线程处理IO请求性能瓶颈

  • 任意一个请求在server中一旦发生耗时,都会影响整个server的性能,也就是说后面的请求都要等前面这个耗时请求处理完成,自己才能被处理到。耗时的操作包括以下几种:
    • 操作bigkey:写入一个bigkey在分配内存时需要消耗更多的时间,同样,删除bigkey释放内存同样会产生耗时;
    • 使用复杂度过高的命令:例如SORT/SUNION/ZUNIONSTORE,或者O(N)命令,但是N很大,例如lrange key 0 -1一次查询全量数据;
    • 大量key集中过期:Redis的过期机制也是在主线程中执行的,大量key集中过期会导致处理一个请求时,耗时都在删除过期key,耗时变长;
    • 淘汰策略:淘汰策略也是在主线程执行的,当内存超过Redis内存上限后,每次写入都需要淘汰一些key,也会造成耗时变长;
    • AOF刷盘开启always机制:每次写入都需要把这个操作刷到磁盘,写磁盘的速度远比写内存慢,会拖慢Redis的性能;
    • 主从全量同步生成RDB:虽然采用fork子进程生成数据快照,但fork这一瞬间也是会阻塞整个线程的,实例越大,阻塞时间越久;
  • 并发量非常大时,单线程读写客户端IO数据存在性能瓶颈,虽然采用IO多路复用机制,但是读写客户端数据依旧是同步IO,只能单线程依次读取客户端的数据,无法利用到CPU多核。
  • Redis在4.0推出了lazy-free机制,把bigkey释放内存的耗时操作放在了异步线程中执行,降低对主线程的影响。
  • Redis在6.0推出了多线程,可以在高并发场景下利用CPU多核多线程读写客户端数据,进一步提升server性能,当然,只是针对客户端的读写是并行的,每个命令的真正操作依旧是单线程的。
阅读全文 »

本章属于持续学习、长期更修。

HashMap

特征

  • HashMap 允许null键和null值,null键哈西值为0.
  • HashMap 并不是有序的存放。在使用iterate迭代的时候,并不能得到存放顺序。
  • HashMap使用它的内部类Node <K,V>来存储映射。
  • HashMap将entries存储到多个单链表中,称为存储桶或存储桶。默认的箱数为16,默认负载因子0.75,它的扩展系数为2,当键值对数量大于阈值,则容量扩容到原来的2倍。
  • HashMap不是线程安全的,对于多线程环境,您应该使用ConcurrentHashMap类或使用Collections.synchronizedMap()方法获取同步映射。
  • 底层实现是链表,但是jdk1.8后添加了红黑谁的转换。
  • HashMap的Key用存放,所以key默认不允许重复的,如果想重复就重写key的hashcode和equals方法。
  • 查找方法,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。

源码分析

HashMap中的常量以及变量

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
// 未指定容量大小的情况下,默认初始化16。容量都是2的幂。第一次扩容大概率情况下是64。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 容量最大长个数。
static final int MAXIMUM_CAPACITY = 1 << 30;
// 默认负载因子,
static final float DEFAULT_LOAD_FACTOR = 0.75f;
// HashMap使用单链表来存储元素,这些元素称为bin或buckets。当我们调用put方法时,key的hashCode用于确定将用于存储映射的存储区。
// 链表转换红黑树的阀值。当某个bin\buckets的长度大于8的时候进行转换。
static final int TREEIFY_THRESHOLD = 8;
// 红黑树转换链表的阀值。当某个bin\buckets的长度小于8的时候进行转换。
static final int UNTREEIFY_THRESHOLD = 6;
// 桶中bin最小hash容量,如果大于这个值会进行resize扩容操作,此值至少是TREEIFY_THRESHOLD的4倍
static final int MIN_TREEIFY_CAPACITY = 64;

// 被transient修饰的变量不回被序列化。
// HashMap内部类实现了Map的内部类Entry,用于存储K,V,第一次使用的时候被创建,根据需要可以进行resize。分配长度为2的冥次方
transient Node<K,V>[] table;
// 当被调用entrySet时被赋值。通过keySet()方法可以得到map key的集合,通过values方法可以得到map value的集合
transient Set<Map.Entry<K,V>> entrySet;

// size表示HashMap中存放KV的数量(为链表和树中的KV的总和)。
transient int size;

// 操作次数,通常用过fail-fast。每次扩容和更改map结构的计数器
transient int modCount;

// threshold=capacity*loadFactor threshold表示当HashMap的size大于threshold时会执行resize操作。
int threshold;

// Load Factor用于确定何时重新散列HashMap并增加存储桶大小。存储桶或容量的默认值为16,负载系数为0.75。通过乘以容量和负载因子来计算重新散列的阈值
final float loadFactor;

Node

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
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; // node哈希值
final K key;
V value;
Node<K,V> next; // 下一个node的地址

Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}

public final K getKey() { return key; }
public final V getValue() { return value; }
// 这里重写方法,所以map.toString()不是内存地址。
public final String toString() { return key + "=" + value; }

// 这里重写了hashCode()方法
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}

public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}

// 这里重写了equals()方法
public final boolean equals(Object o) {
if (o == this)
return true;
//Map.Entry 相等的条件:键相等、值相等、个数相等、顺序相等
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
阅读全文 »

本章属于持续学习、长期更修。

HashSet

特性

  • HashSet不允许重复数据。
  • HashSet不允许null,因为null也是可以重复的。
  • HashSet不保证数据的插入顺序。
  • HashSet不是线程安全的,如果想使用线程的安全的HashSet可以通过Collections.synchronizedSet 来获取线程安全的HashSet。也可以使用CopyOnWriteArraySet。但是性能会有很大的损失。
  • HashSet迭代器方法是快速失败的。因此,在创建迭代器之后对集合进行任何结构修改都会抛出ConcurrentModificationException。
  • HashSet支持泛型,这是在运行时避免ClassCastException的推荐方法。
  • HashSet使用HashMap存储元素,因此对象应该提供hashCode()和equals()方法的良好实现,以避免不必要的结果。

源码分析

刚去杭州面试的第二家公司,做笔试的时候的时候遇到的。最近几天在整理以前的笔记发现了。简单记录下。

互换两个变量的值

1
2
3
int a=10;
int b=12;
// 通过两个变量之间的运算输出结果 a = 12 b = 10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void swapTest1(){
int a=10;
int b=12;
a=a^b; //a=1010^b=1100;
b=a^b; //a=0110^b=1100;
a=a^b; //a=0110^b=1010;
//a=1100=12;b=1010;
System.out.println(a);
System.out.println(b);
}

@Test
public void swapTest2(){
int a = 10;
int b = 12;
a = a + b; // 22 = 10 + 12
b = a - b; // 10 = 22 - 12
a = a - b; // 12 = 22 - 10
System.out.println(a);
System.out.println(b);
}

2018年在公司负责了一段时间老项目改造升级工作。因此记录下折腾的过程。

  • Spring boot 最近挺火的,之前的工作并没有机会去实战。但是即使不使用Spring boot 也是可以做零配置的。

约定大于配置

  • servlet3.*的规范已经支持不使用web.xml了。
  • annotation的配置方式在Spring其实也存在很久了。
阅读全文 »

最近开始用mac来开发。记录下环境安装过程方便以后快速恢复熟悉的开发环境。

这里提前修改一下控制台前缀

1
2
3
4
5
[dev mc]$ sudo vim /etc/bashrc
#修改以下内容
PS1='\h:\W \u\$ '
#修改成
PS1='[dev \u]$ '

好用的工具

  • pages 同事推荐的软件暂时用来写周报。
  • brew 这是一个神器,和linux 上的yum 类似的工具。
  • sublime text 我之前在windows上都是使用notepad++。上App Store上没找到,该用这个了。

pages

  • 直接在app store安装就好了

brew

  • 安装过程使用终端安装。
1
2
#  这里并没有换行,复制的时候请注意。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
  • 基本操作方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 更新
brew update
# 安装软件
brew install xxx(软件名)

#常见命令整理如下,以下命令都可带上 cask 参数:

brew search name:联网搜索软件是否存在 brew 中
brew install name:安装软件
brew upgrade name:更新软件
brew uninstall name:卸载软件
brew reinstall name:重新安装软件
brew info name:查看软件安装地址
brew cleanup:清理缓存等
brew doctor:查看建议,例如升级等

mvnd

maven-mvnd,可以读作 Maven Daemon,译作 Maven 守护版,旨在为 Maven 提供更快的构建速度,灵感借鉴了 Gradle 和 Takari(Maven 生命周期优化器)。

1
2
3
4
5
6
# Windows
choco install mvndaemon
# Linux
sdk install mvnd
# macOS
brew install mvndaemon/homebrew-mvnd/mvnd

mycli

python编写的mysql客户单工具

1
brew install mycli
阅读全文 »

本章内容阐述个人理解,以及使用方法。若有不正确的内容欢迎斧正。睡觉之前赶快记录下来免得有忘记了。

什么是Log?

  • 日志记录的是什么?市面上流行的软件基本都有日志记录功能,作为开发人员的好帮手。日志可以有效的记录程序运行的状态以及运行的异常,比如说操作系统蓝屏了、手机死机了、软件卡死、程序崩溃、写了BUG等等都会有对应的日志留下。

如何利用log?

  • 笔者是一个Java开发人员,本章内容代码部分已Java代码用例。大家都知道日志非常重要,但是经验不足的开发人员往往忽视了这个重要的手段,比如在控制台输出日志,所有输出信息都集中在一起。排除问题非常困难,一旦出现了线上异常信息首先要看的就是日志信息。所以有必要就日志手段多了解下。

我是如何操作的?

  • 最在开发一个客户端应用,并不是GUI那种程序。功能不多但是日志信息却很多,循环监控数据,所以运行期产生的日志也有点多,开发的时候毕竟简单调试还能接受,运行起来日志过于集中眼睛疼,为了方便调试以及排除问题,考虑先将日志经拆分。不同业务或者类型使用不同的log文件来记录。
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

以下部分超链接需要科学上网。

ModBus

modbus是一种通讯协议,是Modicon公司1979年为使用可编程逻辑控制器(PLC)通讯而发表。Modbus是工业领域通讯协议的业界标准之一,目前也我国国家标准工业通讯协议中的重要成员之一。

介绍

  • Modbus采用主从(Master-Salve)通信模式,仅有主设备(Master)能对传输进行初始化,从设备(Slave)根据主设备的请求进行应答。 __在主从关系中,通信总是成对发生。一个设备必须发起请求,然后等待响应 - 并且发起设备(主设备)负责发起每次交互。 通常,主设备是人机界面(HMI)或监控和数据采集(SCADA)系统,从设备是传感器、可编程逻辑控制器(PLC)或可编程自动化控制器(PAC)。 这些请求和响应的内容以及发送这些消息的网络层由协议的不同层来定义__。

    • 在串行链路的主从通信中,Modbus主设备可以连接一个或N(最大为247)个从设备,主从设备之间的通信包括单播模式和广播模式。
    • 在广播模式中,Modbus主设备可同时向多个从设备发送请求(设备地址0用于广播模式),从设备对广播请求不进行响应。
    • 在单播模式中,主设备发送请求至某个特定的从设备(每个Modbus从设备具有唯一地址),请求的消息帧中会包含功能代码和数据,比如功能代码“01”用来读取离散量线圈的状态。从设备接到请求后,进行应答并把消息反馈主设备。
    • __主设备不发送数据,从数据是不会自己发出数据的;即必须是主设备发出查询请求才能建立通讯。 __
  • 消息帧校验方式

    • ASCII消息帧:LRC(纵向冗长检测)
    • CRCi消息帧:CRC(循环冗长检测)
  • ModBus是OSI模型第七层上即__应用层__报文传输协议。

  • ModBus支持多工业设备、包括PLCDSC变频器、智能仪表等都在应用。

  • ModBus支持多种电气接口,如RS-232RS-485等,还可以在各种介质上传送,如双绞线、光纤、无线。

  • ModBus协议完全免费;帧格式简单,紧凑。

  • ModBus对线缆的要求、长度、波特率,终端电阻,接地,连接器,通信状态的LED指示,都有电气标准。

  • ModBus数据可以分为两大类,分别为Coil和Register,每一种数据,根据读写方式的不同,又可细分为两种(只读,读写)。

    • Coli是位(bit)变量。
    • Register是整型(word ,即 16 - bit )。
Primary tabels Object Type Type Of comments
Discretes Input 离线输入量 Single bit 只读 IO系统提供这种类型的数据
Coils 线圈 Single bit 只写 通用应用程序改变这种类型的数据
Input Registers 输入寄存器 16-bit word 只读 IO系统提供这种类型的数据
Holding Registers 保持寄存器 16-bit word 只写 通用应用程序改变这种类型的数据
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

  • MySQL基础(一) 已经介绍了MYSQL最常用基础的操作,由于篇幅已经很大,所以分开记录。

MySql基础操作

子查询与连接

在MYSQL实际操作中,最大的操作就是查询,下面就关于子查询和链接做记录。

子查询

  • 子查询(SubQuery)是指出现在其它SQL语句中的SELECT子句。比如
1
2
SELECT * FROM table_1 AS t1 WHERE columns_1 = (SELECT columns_2 FROM table_2 AS t2);
# 这里的SELECT * FROM table_1 AS t1 称之为 Outer Query/Outer Statement;SELECT columns_2 FROM table_2 AS t2 成为 SubQuery。
  • 子查询严格以上是指嵌套在查询内部,且必须始终出现在圆括号内
  • 子查询可以包含多个关键字:DISTINCTGROUP BYORDER BYLIMIT等函数。
  • 子查询的外层可以是:INSERTUPDATESELECT DELETESETDO等操作。
  • 子查询可以返回结果,这个结果可以是标量一行一列,或者子查询。
  • 子查询的引发条件:这里需要特别注意,并不是显示的才是子查询。
    • 使用比较运算符的子查询:= , > , < , >= , <= , <> , != , <=>
    • 使用结构语法:operand comparison_operator subquery ,可以使用一下修饰符来配合比较运算符。
      • operand comparison_operator ANY(subquery)
      • operand comparison_operator SOME(subquery)
      • operand comparison_operator ALL(subquery)
运算符\关键字 ANY SOME ALL
>>= 最大值 最小值 最大
<<= 最大值 最大值 最小值
= 任意值 任意值
<>!= 任意值
阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Mysql介绍

  • Mysql数据库是一种C\S结构,就是客户端和服务端。
  • 关键字
    1. DB : DataBase 数据库
    2. DBMS:Database Management System数据库管理系统
    3. DBS:Database System = DBMS + DB数据库系统
    4. DBA:Database Administrator 数据库管理员
    5. 行\列:二维表中存储的数据。
      1. row\record (一条记录)。
      2. column\field(一个字段)。
  • SQL

SQL:Structured Query Language,结构化查询语言(数据以查询为主,99% 都是在进行查询操作),SQL是关系型数据库的操作指令,是一种约束,但不强制,类似与W3C,所以不同的关系型数据SQL语句可能会有一定的差异性。

  • DDL:Data Definition Language数据定义语言,用来维护存储数据的结构(数据库、表),代表指令为createdropalter等。
  • DML:Data Manipulation Language,数据操作语言,用来对数据进行操作(表中的内容)代表指令为insertdeleteupdate等,不过在 DML 内部又单独进行了一个分类,即 DQL(Data Query Language),数据查询语言,代表指令为select
  • DCL:Data Control Language,数据控制语言,主要是负责(用户)权限管理,代表指令为grantrevoke等。
  • TPL:``,事物控制语言

Mysql基本操作

本章部分内容在Centos中实现,Centos7-MySql部署记录

交互方式

阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Stack

  • 栈是元素的集合,其包含了两个基本操作:push 操作可以用于将元素压入栈,pop 操作可以将栈顶元素移除。
  • 遵循后入先出(LIFO)原则。
  • 时间复杂度:
  • 索引: O(n)
  • 搜索: O(n)
  • 插入: O(1)
  • 移除: O(1)

Stack

Collection成员之一,继承了Vector,通过重写Vector来实现LIFO(Last-in-First-out 后进先出)特性。

概述

  • Java Stack是LIFO对象。它扩展了Vector类。

源码分析Stack

阅读全文 »

本章是整理知识内容,为强化知识长期更新。

Vector

概述

  • Vector是Java Collection Franmework成员。
  • 列队允许添加重复元素
  • 列队允许null的存在
  • 列队从0开始,也就是列队头部的下标是0。
  • 列队支持泛型,这样可以避免ClassCastException异常。

与ArrayList的区别

阅读全文 »