9月2日

@CacheAble没有写入缓存,是因为Cachemanager的名称和redisconfig的对应有问题,有可能有重名的方法名

Map<String, String> map = Maps.newHashMap();,第一次接触到这种写法,这是引用了谷歌提供的guava包,依赖如下:

1
2
3
4
5
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>17.0</version>
</dependency>

按照平常我们的习惯,在创建Map时,一般都会使用new HashMap<K, V>(),怀着好奇的心态,便看了一下源码,原来这个方法也是返回的一个HashMap,两则是一模一样的,只是这样写比较简洁而已。

1
2
3
public static <K, V> HashMap<K, V> newHashMap() {
return new HashMap<K, V>();
}

当一个接口有2个不同实现时,使用@Autowired注解时会报org.springframework.beans.factory.NoUniqueBeanDefinitionException异常信息

序列化

序列化就是一种用来处理对象流的机制,

所谓对象流也就是将对象的内容进行流化,将数据分解成字节流,以便存储在文件中或在网络上传输。

那么什么时候进行序列化呢

因为要将流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化的实现:将需要被序列化的类实现Serializable接口,该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来

构造一个ObjectOutputStream(对象流)对象,

接着,使用ObjectOutputStream对象的writeObject(Object obj)方法

**就可以将参数为obj的对象写出(即保存其状态)**要恢复的话则用输入流;

序列化分为两大部分:序列化和反序列化。序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。

反序列化就是打开字节流并重构对象。

对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。

序列化的什么特点:

如果某个类能够被序列化,其子类也可以被序列化。声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态, transient代表对象的临时数据

什么时候使用序列化:
  一:对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
  二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的”深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。

序列化:序列化是将对象转换为容易传输的格式的过程。例如,可以序列化一个对象,然后使用 HTTP 通过 Internet 在客户端和服务器之间传输该对象。在另一端,反序列化将从该流重新构造对象。
  是对象永久化的一种机制。
  确切的说应该是对象的序列化,一般程序在运行时,产生对象,这些对象随着程序的停止运行而消失,但如果我们想把某些对象(因为是对象,所以有各自不同的特性)保存下来(想想保存在哪里,就是项目文件夹呗),在程序终止运行后,这些对象仍然存在,可以在程序再次运行时读取这些对象的值,或者在其他程序中利用这些保存下来的对象。这种情况下就要用到对象的序列化。

只有序列化的对象才可以存储在存储设备上。为了对象的序列化而需要继承的接口也只是一个象征性的接口而已,也就是说继承这个接口说明这个对象可以被序列化了,没有其他的目的。

也就是说继承这个接口(implement Serializable)说明这个对象可以被序列化了,没有其他的目的

之所以需要对象序列化,是因为有时候对象需要在网络上传输,传输的时候需要这种序列化处理

从服务器硬盘上把序列化的对象取出,然后通过网络传到客户端

再由客户端把序列化的对象读入内存,执行相应的处理。


对象序列化是java的一个特征,通过该特征可以将对象写作一组字节码,当在其他位置读到这些字节码时,可以依此创建一个新的对象,而且新对象的状态与原对象完全相同。

为了实现对象序列化,要求必须能够访问类的私有变量,从而保证对象状态能够正确的得以保存和恢复。

相应的,对象序列化API能够在对象重建时,将这些值还原给私有的数据成员。

这是对java语言访问权限的挑战。

所以序列化通常用在服务器客户端的对象交换上面,另外就是在本机的存储。


MySQL 会为每个线程分配固定大小的 sort buffer 用作排序

sort buffer 是具有逻辑概念的内存区域,大小由

sort_buffer_size 参数控制,默认为 256 kb。

由于 sort buffer 大小固定,而 data(待排序的数据量)并不固定,所以根据 sort buffer 与 data(待排序数据量)的大小差值,可分为内部排序和外部排序:

data <= sort buffer:即 sort buffer 够用,这时候 MySQL 只需要在内存中进行排序即可。内部排序使用的是快速排序

data > sort buffer:这时候 sort buffer 不够用,MySQL 需要借助外部“容器”(通常是文件)进行排序。通常会将待排序数据分成多个“小文件”,对各个“小文件”进行排序,再汇总成一个有序的“大文件”。外部排序使用的是归并排序

如何验证当前执行的排序语句使用的是内部排序还是外部排序

可以通过

EXPLAIN 命令来查看,如果在分析结果中的

Extra 字段里包含

Using filesort 字眼,说明执行了外部排序操作。

当查询条件本身有索引可用的话,全字段排序的排序过程都在 sort buffer(内存)进行,回表次数为符合条件的数据个数。

当然,如果我们建立的是 citynick_name、age、phone 的联合索引,还可以实现“索引覆盖”,即在一棵索引树上取得全部所需数据,减少回表(随机读)次数。

不过针对每个查询或排序语句建立联合索引,会导致索引过多,大大降低写入更新数据的速度,以及大大提升数据所需要的存储空间。

生产上对索引的建立修改需要格外谨慎。


rowId 排序全过程:

city 索引树上找到第一条值为深圳的数据,

取得 id 之后回表(回到主键索引)取得 nick_name 这个与排序相关的字段

和主键 id 一起放入 sort buffer

从 city 索引树取下一条值为深圳的数据,

`

`

`

所有 city = 深圳 的数据都在 sort buffer 了,(sort buffer 里面的数据包含两个字段: id 和 nick_name)

对 nick_name 执行快速排序利用排序好的数据

使用主键 id 再次回表取其他字段,findById,将结果返回

优先队列排序

无论是使用全字段排序还是 rowId 排序,都不可避免了对所有符合

WHRER 条件的数据进行了排序。

设想一下,如果我们还搭配着

LIMIT 使用呢?

LIMIT 3 ,哪怕查出来的数据有 10W 行,我们也只需要前 3 行有序。

为了得到前 3 行数据,而不得不将 10W 行数据载入内存,大大降低了 sort buffer 的利用率

这时候你可能想到利用“最小堆”、“最大堆”来进行排序。

没错,这正是 MySQL 针对带有

LIMIT 的

ORDER BY 语句的优化:使用优先队列进行排序。

优先队列进行排序的流程:

在所有待排序的数据,取数量为

LIMIT (本例中为 3)的数据,构建一个堆不断的取下一行数据,更新堆节点当所有行的扫描完,得到最终的排序结果

如何选择?

现在我们知道有全字段排序和 rowId 排序,那么 MySQL 是如何在这两种排序方案中做选择呢?

由于 rowId 排序相对于全字段排序,不可避免的多了一次回表操作回表操作意味着随机读(相对于整存整取的概念而言),而随机 IO 是数据库中最昂贵的操作。

所以 MySQL 会在尽可能的情况下选择全字段排序。

那么什么情况下 MySQL 会选择 rowId 排序呢,是否有具体的值可以量度?

答案是有的,通过参数

max_length_for_sort_data 可以控制用于排序的行数据最大长度,默认值为 1024 字节。

当单行数据长度超过该值,MySQL 就会觉得如果还用全字段排序,会导致 sort buffer 容纳下的行数太少

从而转为使用 rowId 排序。

临时表排序

通常对于一个执行较慢的排序语句,在使用

EXPLAIN 进行执行过程分析的时候除了能看到Using filesort 以外,

还能看到Using temporary,

代表在排序过程中使用到了临时表。

内存临时表排序

MySQL 优先使用内存临时表。当 MySQL 使用内存临时表时,

临时表存储引擎为 memory 。

如果当前 MySQL 使用的是内存临时表的话,将会直接使用 rowId 排序,因为这时候所谓的“回表”只是在内存表中读数据,操作不涉及硬盘的随机 IO 读。

如果系统中很多需要使用临时表的排序语句执行,而又不加以限制,全都使用临时表的话,内存很快就会被打满。

所以 MySQL 提供了

tmp_table_size 参数限制了内存临时表的大小,默认值是 16M。

如果临时表大小超过了tmp_table_size,那么内存临时表就会转成磁盘临时表。

当使用磁盘临时表的时候,表储存引擎将不再是 memory,而是由

internal_tmp_disk_storage_engine 参数控制,默认为

InnoDB 。

这时候 MySQL 会根据单行大小是否超过

max_length_for_sort_data 决定采用全字段排序还是 rowId 排序。

全字段排序

全字段排序是指,只要与最终结果集有关的字段都会被放进 sort buffer,而不管该字段本身是否参与排序。」

rowId 排序

rowId 就是 MySQL 对每行数据的唯一标识符。

当数据表有主键时,rowId 就是表主键;当数据表没有主键或者主键被删除时,MySQL 会自动生成一个长度为 6 字节的 rowId 为作为 rowId。


总结

总结一下,MySQL 总是使用 「“最快”」 的排序方案:

当排序数据量不超过 sort buffer 容量时,MySQL 将会在内存使用快速排序算法进行排序(内部排序);

当排序数据量超过 sort buffer 容量时,MySQL 将会借助临时磁盘文件使用归并排序算法进行排序(外部排序)


在进行真正排序(理解什么是真正的排序时),MySQL 又会根据数据单行长度是否超过

max_length_for_sort_data而决定使用 rowId 排序还是全字段排序

优先选择全字段排序,以减少回表次数

当需要借助临时表的时候,会使用 rowId 排序方式;

内存临时表大小超过

tmp_table_size 限制时,则需要将内存临时表转换为磁盘临时表

这时候由于回表意味着随机读,所以会搭配全字段排序方式


1、Redis

redis是一款开源的Key-Value数据库,
运行在内存中,由C语言编写。企业开发通常采用Redis来实现缓存。
同类的产品还有memcache 、memcached 等。

2、Jedis

Jedis是Redis官方推出的一款面向Java的客户端,提供了很多接口供Java语言调用。可以在Redis官网下载,当然还有一些开源爱好者提供的客户端,如Jredis、SRP等等,推荐使用Jedis。

3、Spring Data Redis

Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,

RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
spring-data-redis针对jedis提供了如下功能:

  1. 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
  2. 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
    • ValueOperations:简单K-V操作
    • SetOperations:set类型数据操作
    • ZSetOperations:zset类型数据操作
    • HashOperations:针对map类型的数据操作
    • ListOperations:针对list类型的数据操作
  3. 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:
  4. 将事务操作封装,有容器控制。
  5. 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)

Redis妙用–存储用户token

在设计类似电商的系统时,一个常见的需求是每个页面都需要携带登录用户信息。

常见的解决方法有两种:使用cookie保存、使用JWT保存。

但如果系统中使用了Redis缓存,那么还可以有第三种解决方案–将用户token缓存在Redis中

header里面放Authorization,就是为了验证用户身份,现在前后端分离,有跨域问题,session经常会失效

什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域

比如说,前端域名是www.abc.com,那么在当前环境中运行的js代码,出于安全考虑,访问www.xyz.com域名下的资源,是受到限制的。现代浏览器默认都会基于安全原因而阻止跨域的ajax请求,这是现代浏览器中必备的功能,但是往往给开发带来不便。

@component是spring中的一个注解,它的作用就是实现bean的注入,在探究@component前先了解一下注解?何为注解?注解本质上就是一个类,开发中我们可以使用注解 取代 xml配置文件。

Spring @Configuration 注解介绍

指示一个类声明一个或多个@Bean方法,并且可以由Spring容器处理

以便在运行时为这些bean生成BeanDefinition和服务请求

可以看到这个 @Component 注解, 意味也将会注册为bean,其内部也可以依赖注入。


Ps:什么是依赖注入。

Class A依赖Class B的对象b,一般情况下,需要在A代码中显式的new一个B的对象,

采用依赖注入技术之后,A的代码只需要定义一个私有的B对象,啥意思

不需要直接new来获得这个对象,

而是通过相关的容器控制程序来将B对象在外部new出来并注入到A类的引用中。

COntroller需要service的服务,要在controller中注入service

private Service service;

同样的,service中需要注入Repository


为什么注入Service,不是注入Impl?

就是先画个虎头,身体没画,虎身自己慢慢画

解耦,测试方便,面向接口编程,代理方式不同

ps:面向接口编程有什么好处

面向接口编程

高内聚低耦合

设计模式之开闭原则

接口是一组规则的集合,它规定了实现本接口的类或接口必须拥有的一组规则。

—-< 前面就是接口的部分,后面自己延伸。好像不太准确,这应该是继承的关系

面向接口编程和面向对象编程是什么关系

接口就是标准规范,就是定死了一个框架,你根据这个框架去执行!

有了标准去遵守就容易扩展,我们只需要面向标准编程,而不用针对具体的实现类。

就像是只有骨架,而肉身由自己定义。


@Configuration也附带了@Component的功能。所以理论上也可以使用@Autowared功能。上述代码可以改成下面形式

jackson库中objectMapper的使用

Jackson可以轻松的将Java对象转换成json对象和xml文档,同样也可以将json、xml转换成Java对象

ObjectMapper类是Jackson库的主要类。

序列化

它称为ObjectMapper的原因是因为它将JSON映射到Java对象(反序列化),或将Java对象映射到JSON(序列化)。

Jackson ObjectMapper如何将JSON字段与Java字段匹配

三种方式

  1. Jackson通过将JSON字段的名称与Java对象中的gettersetter方法相匹配,将JSON对象的字段映射到Java对象中的字段。Jackson删除了getter和setter方法名称的“get”和“set”部分,并将剩余名称的第一个字符转换为小写。
  2. Jackson还可以通过java反射进行匹配
  3. 通过注解或者其它方式进行自定义的序列化和反序列化程序。