@CacheAble没有写入缓存,是因为Cachemanager的名称和redisconfig的对应有问题,有可能有重名的方法名
Map<String, String> map = Maps.newHashMap();
,第一次接触到这种写法,这是引用了谷歌提供的guava包,依赖如下:
1 | <dependency> |
按照平常我们的习惯,在创建Map时,一般都会使用new HashMap<K, V>()
,怀着好奇的心态,便看了一下源码,原来这个方法也是返回的一个HashMap,两则是一模一样的,只是这样写比较简洁而已。
1 | public static <K, V> HashMap<K, V> newHashMap() { |
当一个接口有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(内存)进行,回表次数为符合条件的数据个数。
当然,如果我们建立的是 city、nick_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提供了如下功能:
- 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
- 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
- ValueOperations:简单K-V操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:针对map类型的数据操作
- ListOperations:针对list类型的数据操作
- 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:
- 将事务操作封装,有容器控制。
- 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
Redis妙用–存储用户token
在设计类似电商的系统时,一个常见的需求是每个页面都需要携带登录用户信息。
常见的解决方法有两种:使用cookie保存、使用JWT保存。
但如果系统中使用了Redis缓存,那么还可以有第三种解决方案–将用户token缓存在Redis中。
header里面放Authorization,就是为了验证用户身份,现在前后端分离,有跨域问题,session经常会失效
什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
@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字段匹配
三种方式
- Jackson通过将JSON字段的名称与Java对象中的getter和setter方法相匹配,将JSON对象的字段映射到Java对象中的字段。Jackson删除了getter和setter方法名称的“get”和“set”部分,并将剩余名称的第一个字符转换为小写。
- Jackson还可以通过java反射进行匹配
- 通过注解或者其它方式进行自定义的序列化和反序列化程序。