一:概述
自叙:这次更新比较慢,一部分是因为工作的原因,家庭的原因,一部分是因为这些代码,遇到我很多不懂的。
无论什么原因。我都会坚持下好每篇博客,哪怕是没有观众。
这是缓存主体代码情况。看到缓存包下的decorators(装饰者),CacheBuilder 这个类,是调用这些的入口。装饰者。装饰模式是java中的设计模式。实不相瞒,看这个设计模式我又看了会。
我感觉我没特别的理解。但是我会在这个文章中,尽可能表达自己对装饰者模式的理解。
看代码的过程中,能给自己找到问题并且能搞明白问题。我觉得一个很有意思的过程。下面我列下类我看重的东西
|
用处 | 问题 |
|
|
|
|
|
|
|
|
|
|
|
什么是序列化?为什么序列化?如何实现序列化?平时操作数据库的操作进行了序列化吗? |
|
|
1:弱引用是啥?什么是引用?java 中有哪些引用? |
|
||
|
||
|
||
其他的不是很难,就不列举了。。。。 |
二: mybatis 缓存模块的装饰者模式
- 装饰模式,我尝试过多个渠道去理解他,问过人,看过博客,但是没有一种方式在我自己的心里生根发芽的。我想如果一个东西,没有入心。无论别人怎么说的话,自己理解起来还是很渣。我把mybatis里面的缓存图花了点时间画了下。
org.apache.ibatis.cache.decorators 这个目录下面的所有包全是装饰器,是装饰包。看上图,这些装饰器都有一个属性,属性传入的是一个接口类型的装饰类。这个装饰类是通过构造方法赋值的。其实我们细看,我们这些装饰类都实现了Cache 接口中的方法的
- 那么我们搞这么多的装饰器作用是什么?因为每个装饰器的作用都是不一样的。增强的功能也是不一样的。delegate(委托),decorators(装饰者),我认为装饰者是对委托进来的对象进行了功能增强。
- 如下面的代码运行的结果: 我是希望我定义的一个缓存类,不单单只有一般缓存的功能,还可以有序列化,和同步的功能,那么下面这样组合一下。就可以达到我们的效果。从代码层次看,我们很清晰可以看到我们这个缓存类增强了那些功能。
- 纠结这个装饰者模式,我纠结了很久的。
- 如果说,装饰者是对委托进来的对象进行增强。我们没有其他的方式来做这个事吗?
- 1:我想也是有的,比如说继承。我们重写父类的方法,在重新的方法中,进行功能的增加后调用父类的方法。也能达到对应的效果。
- 2:但是说,如果我的缓存,可以定时,可以实例化,又可以使用先进先出策略。。。。等,我可能通过继承,就没那么灵活实现这些功能。
- 3:通过装饰者模式,我们是可以很清晰看到我们增强了什么功能。我们做了些什么事。每个功能又是比较独立。便于组合。
- mybatis缓存CacheBuilder 这个构造器,就是把我们的装饰器连接起来的。就我标识的这些方法中。
- 我个人的理解。mybatis缓存。可能最初只有个序列化的装饰器,但是随着业务和场景的逐步增多。装饰者是可以不改变原来任何代码,就对我们缓存体系进行增强。所以可以看中装饰者模式是对现有功能不足的补充,并且是最小破坏的补充。并且组合也是灵活多变。可以按照不同的要求,进行组合。我平时开发的项目中,就没有用过装饰者,从来都没有用过装饰者模式的。所以写这个,我是想让自己的理解更深刻。
三:mybatis的装饰者
BlockingCache:简单的阻塞装饰器,这个类中代码我都写了注释的。我简单说下我看的过程中遇到的问题。acquireLock方法,调用这个方法的时候,就创建了一个同步计数器
- 看这个方法前,大家先看ConcurrentHashMap 类的
-
putIfAbsent的返回值是 上一次设置的值如下图,key=2,我是第一次设置的,那么他上一次设置的值是没有,没有默认就是null,再看属性p1 ,p1是我对key=1的第二次操作,第二次前的一次操作是给值2,所以我们打印,p1的时候显示的是“2” remove的返回值是什么。remove返回的就是当前对象删除的值。如果我remove是不存在的key,那么返回的肯定是null,如果我remove的是一个存在的key,如key=1,的值,那么返回是2。看懂这个对下面获取锁的方法和释放锁是有重大帮助的。
acquireLock 方法,我在这个里面写了我自己循环思考的逻辑。我认为对我自己是理解了,希望看的你如果不懂也能明白。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* <p>Simple blocking decorator
* 简单的阻塞装饰器
*
* <p>Simple and inefficient version of EhCache's BlockingCache decorator.
*
* It sets a lock over a cache key when the element is not found in cache.
* 当元素在缓存中找不到的时候,它会对缓存键设置一个锁
* This way, other threads will wait until this element is filled instead of hitting the database.
*这样,其他线程将等待直到这个元素被填满,而不是到达数据库
* <p>By its nature, this implementation can cause deadlock when used incorrecly.
* 就其本质而言,这个实现在使用不当时可能会导致死锁。
* @author Eduardo Macarron
*
* 这个比较有意思,里面有锁。实不相瞒,获取那个锁的那个方法,我差点就转里面出不来了。
* 幸亏有个好领导,指导了一句
*
*/
public class BlockingCache implements Cache {
/**
* 阻塞等待超时时间
*/
private long timeout;
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
/**
* 缓存键与 CountDownLatch 对象的映射
*/
private final ConcurrentHashMap<Object, CountDownLatch> locks;
/**
* 释放锁,并且使计数器减1
* @param key 键
*/
private void releaseLock(Object key) {
CountDownLatch latch = locks.remove(key);
if (latch == null) {
// 检测到试图释放未获得的锁。这是不应该发生的。
throw new IllegalStateException("Detected an attempt at releasing unacquired lock. This should never happen.");
}
latch.countDown();
}
/**
* 获取值
* @param key
* The key 键
* @return
*/
@Override
public Object getObject(Object key) {
acquireLock(key);
Object value = delegate.getObject(key);
if (value != null) {
releaseLock(key);
}
return value;
}
/**
* 获取锁
* @param key key
*/
private void acquireLock(Object key) {
// 创建同步计数器
CountDownLatch newLatch = new CountDownLatch(1);
// 循环,基本算死循环
while (true) {
// 假设key是1,第一次进来的时候,调用 CountDownLatch latch = locks.putIfAbsent(key, newLatch); latch的值的是null。那么这个while死循环就会跳出去。
// 当key=1,第二次进来的时候,如果第一次的还没有释放锁正在执行缓存操作时,那么 CountDownLatch latch = locks.putIfAbsent(key, newLatch);返回的是第一次进来设置的对象new CountDownLatch(1),
// 此时 latch 不等于null,往下执行,要不是超时抛异常,要不一直等着,
// 此时,第一次进来的那个现在。活干完了。执行releaseLock方法。这个方法,执行了 CountDownLatch latch = locks.remove(key);
// 执行这个会删除locks中对应的键key=1,并且返回的是第一次自己设置的new CountDownLatch(1),然后,把这个计数器进行 latch.countDown();计算会变成0.
// 那么第二次进来的那个现在对应的latch.await()这个方法,会让程序继续执行。那么第二次进来的 key=1,执行 CountDownLatch latch = locks.putIfAbsent(key, newLatch),会返回null,因为key=1
// 的值没有的,以上的分析,就完成了阻塞
CountDownLatch latch = locks.putIfAbsent(key, newLatch);
if (latch == null) {
break;
}
try {
// 如果设置了超时时间,
if (timeout > 0) {
// latch 的计数值变成0的时候,返回true,返回false,如果超时了但是计数器还是没有变成0,就返回false,此时就抛出异常。
boolean acquired = latch.await(timeout, TimeUnit.MILLISECONDS);
if (!acquired) {
throw new CacheException(
"Couldn't get a lock in " + timeout + " for the key " + key + " at the cache " + delegate.getId());
}
} else {
// 没有设置超时时间
latch.await();
}
} catch (InterruptedException e) {
throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
}
}
}
public BlockingCache(Cache delegate) {
this.delegate = delegate;
// ConcurrentHashMap 支持检索的完全并发性和更新的高预期并发性的哈希表
this.locks = new ConcurrentHashMap<>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
/**
* put之前应该要获取一下锁,要不然,最后释放锁的时候会异常。
* @param key
* Can be any object but usually it is a {@link CacheKey}
* 可以是任何对象,但是通常是一个缓存的key
* @param value
* The result of a select.
*/
@Override
public void putObject(Object key, Object value) {
try {
// <2.1> 添加缓存
delegate.putObject(key, value);
} finally {
// <2.2> 释放锁
releaseLock(key);
}
}
@Override
public Object removeObject(Object key) {
// despite of its name, this method is called only to release locks
// 尽管它的名字是这样的,但调用这个方法只是为了释放锁
releaseLock(key);
return null;
}
@Override
public void clear() {
delegate.clear();
}
public long getTimeout() {
return timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
}
FifoCache 先进先出的 缓存淘汰机制。 上代码
- 这个类我们思考的点是,如何做到先进先去的?还有Deque 这个类挺陌生的,得和他好好认识下。好,带着着2个问题。我们开始
- 解决问题1:看代码发现,这个类有设置最大的长度。putObject方法是放缓存值的方法,这个方法会调用cycleKeyList,这个方法会把新添加的key放到双端队列的最后面keyList,然后当这个链的长度大于设置的最大长度的
- 时候就开始删除链最前面的key、下面代码比较清晰,其实removeObject方法是有点问题的。
- Deque 双端队列。
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.util.Deque;
import java.util.LinkedList;
import org.apache.ibatis.cache.Cache;
/**
* FIFO (first in, first out) cache decorator.
*
* 先进先出的 缓存淘汰机制。
*
* @author Clinton Begin
*/
public class FifoCache implements Cache {
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
/**
* 双端队列,记录缓存键的添加
*/
private final Deque<Object> keyList;
/**
* 队列上限
*/
private int size;
public FifoCache(Cache delegate) {
this.delegate = delegate;
this.keyList = new LinkedList<>();
this.size = 1024;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(int size) {
this.size = size;
}
@Override
public void putObject(Object key, Object value) {
// 循环 keyList
cycleKeyList(key);
delegate.putObject(key, value);
}
@Override
public Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
// 此处应该是有问题的,应该把 keyList 中的也进行移除。要不然不准确
return delegate.removeObject(key);
}
@Override
public void clear() {
// 清除所有缓存和对应的key
delegate.clear();
keyList.clear();
}
private void cycleKeyList(Object key) {
// 把key添加到最后面
keyList.addLast(key);
if (keyList.size() > size) {
// 如果大于默认长度1024,删除第一个
Object oldestKey = keyList.removeFirst();
delegate.removeObject(oldestKey);
}
}
}
LoggingCache 日志缓存装饰器,代码比较简单
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
/**
* 打印日志的 Cache 实现类
*
* @author Clinton Begin
*/
public class LoggingCache implements Cache {
/**
* MyBatis Log 对象
*/
private final Log log;
/**
* 装饰的 Cache 对象:
* delegate 属性,被装饰的 Cache 对象
*/
private final Cache delegate;
/**
* 统计请求缓存的次数
*/
protected int requests = 0;
/**
* 统计命中缓存的次数
*/
protected int hits = 0;
public LoggingCache(Cache delegate) {
this.delegate = delegate;
this.log = LogFactory.getLog(getId());
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
// 请求次数 ++
requests++;
// 获得缓存
final Object value = delegate.getObject(key);
// 如果命中缓存,则命中次数 ++
if (value != null) {
hits++;
}
if (log.isDebugEnabled()) {
log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
}
return value;
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
/**
* 命中比率的统计
* @return 命中率
*/
private double getHitRatio() {
return (double) hits / (double) requests;
}
}
LruCache 最近最少使用的缓存装饰器。
- 这个类是比较不容易的。我看的过程中。
- 问题是:这个类是如何记录到那个是最少用的对象,他是如何进行记录的?带着这个问题。我看了代码,并且做了实验。下面这个连接是我自己测下来写文章。
- https://mp.csdn.net/editor/html/113871176 这篇文章大家应该可以能理解出最老的值那个list是如何来的
- 上LruCache 的代码。
- keyMap 这个属性的值是得在缓存是key数量大于我们默认1024,才会给keyMap添加值。我想如果你认真看了我的注释和对应的博客。你一定能看明白这个装饰者。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.ibatis.cache.Cache;
/**
* Lru (least recently used) cache decorator.
* 最近最少使用的缓存装饰器。
*
*
* @author Clinton Begin
*/
public class LruCache implements Cache {
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
/**
* 基于 LinkedHashMap 实现淘汰机制
*/
private Map<Object, Object> keyMap;
/**
* 最老的键,即要被淘汰的
*/
private Object eldestKey;
public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
public void setSize(final int size) {
// 这个是重点: LinkedHashMap的一个构造函数,当参数accessOrder为true时,即问顺序排序
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
// LinkedHashMap自带的判断是否删除最老的元素方法,默认返回false,即不删除老数据
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
@Override
public void putObject(Object key, Object value) {
// 添加到缓存
delegate.putObject(key, value);
// 循环 keyMap
cycleKeyList(key);
}
@Override
public Object getObject(Object key) {
keyMap.get(key); // touch
return delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
keyMap.clear();
}
/**
* 他怎么知道那个是最老的
* @param key
*/
private void cycleKeyList(Object key) {
// 添加到 keyMap 中
keyMap.put(key, key);
if (eldestKey != null) {
// 如果超过上限,则从 delegate 中,移除最少使用的那个
delegate.removeObject(eldestKey);
eldestKey = null;
}
}
}
ScheduledCache 定时清空整个容器的 Cache 实现类
代码还是比较简单,没深度
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.util.concurrent.TimeUnit;
import org.apache.ibatis.cache.Cache;
/**
* 定时清空整个容器的 Cache 实现类
*
* 其实 是有问题的。如果我 把缓存放进去的时候是1小时前,但是我一年都不进行放值了。
* 其实这个定时器也是没有起效的。因为他的定时是依赖 这个类中部分方法的操作才实行的。
*
*
* @author Clinton Begin
*/
public class ScheduledCache implements Cache {
/**
* 被装饰的 Cache 对象
*/
private final Cache delegate;
/**
* 清空间隔,单位:毫秒,
* 默认一小时
*/
protected long clearInterval;
/**
* 最后清空时间,单位:毫秒
*/
protected long lastClear;
public ScheduledCache(Cache delegate) {
this.delegate = delegate;
this.clearInterval = TimeUnit.HOURS.toMillis(1);
this.lastClear = System.currentTimeMillis();
}
public void setClearInterval(long clearInterval) {
this.clearInterval = clearInterval;
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
// 判断是否要全部清空
clearWhenStale();
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
// 判断是否要全部清空
clearWhenStale();
delegate.putObject(key, object);
}
@Override
public Object getObject(Object key) {
return clearWhenStale() ? null : delegate.getObject(key);
}
@Override
public Object removeObject(Object key) {
// 判断是否要全部清空
clearWhenStale();
return delegate.removeObject(key);
}
@Override
public void clear() {
// 记录清空时间
lastClear = System.currentTimeMillis();
// 全部清空
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
/**
* 当前系统时间和上次的时间间隔大于1小时,就进行清理
* @return
*/
private boolean clearWhenStale() {
if (System.currentTimeMillis() - lastClear > clearInterval) {
clear();
return true;
}
return false;
}
}
SerializedCache 支持序列化的缓存装饰者
- 代码没什么特别难看明白的,无非是放缓存的时序列化,取数据的时候反序列化。但是问题来了。为什么要做序列化,其实工作这么多了,以前曾遇到过,没序列化出错的情况,后来也没特别深入的去想为什么?
- 刚好这次看到了。我要认真想下,序列化是个什么意思,为啥要序列化?
- 从代码上面看,Serializable就是一个空接口。
- 我是这样测试的,我写一个类实现序列化,一个不实现序列化结果如下。下面先列出不实例化的结果
- 实现序列化的接口,进行流操作的时候,结果如下。
- 经过上面的测试。我是这样想的,要看api文档,文档上面列清楚了。如果操作流的时候,不实现实例化接口,是不行。直接会报错。那么我可以这样想。因此也就是操作流对象需要的时候我们就实现就好。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.io.SerialFilterChecker;
/**
* 支持序列化值的 Cache 实现类
*
* @author Clinton Begin
*/
public class SerializedCache implements Cache {
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
public SerializedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public String getId() {
System.out.println("SerializedCache");
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public void putObject(Object key, Object object) {
if (object == null || object instanceof Serializable) {
delegate.putObject(key, serialize((Serializable) object));
} else {
throw new CacheException("SharedCache failed to make a copy of a non-serializable object: " + object);
}
}
@Override
public Object getObject(Object key) {
Object object = delegate.getObject(key);
return object == null ? null : deserialize((byte[]) object);
}
@Override
public Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
/**
* ObjectOutputStream
* ByteArrayOutputStream
* 序列化
*
* @param value
* @return
*/
private byte[] serialize(Serializable value) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos)) {
oos.writeObject(value);
oos.flush();
return bos.toByteArray();
} catch (Exception e) {
throw new CacheException("Error serializing object. Cause: " + e, e);
}
}
private Serializable deserialize(byte[] value) {
SerialFilterChecker.check();
Serializable result;
try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
ObjectInputStream ois = new CustomObjectInputStream(bis)) {
result = (Serializable) ois.readObject();
} catch (Exception e) {
throw new CacheException("Error deserializing object. Cause: " + e, e);
}
return result;
}
public static class CustomObjectInputStream extends ObjectInputStream {
public CustomObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Resources.classForName(desc.getName());
}
}
}
上面总结就是:个别对象,流对象,看业务情景。如果涉及操作流的就需要序列化,如果不实现序列化操作流是会异常的。
SynchronizedCache 同步缓存装饰类
这个类没什么难的。简单就是加了同步关键字
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import org.apache.ibatis.cache.Cache;
/**
* 同步的cache 实现类
*
* 感觉就是把所有的方法变成了同步而已。
*
* @author Clinton Begin
*/
public class SynchronizedCache implements Cache {
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
public SynchronizedCache(Cache delegate) {
this.delegate = delegate;
}
@Override
public String getId() {
System.out.println("SynchronizedCache");
return delegate.getId();
}
@Override
public synchronized int getSize() {
return delegate.getSize();
}
@Override
public synchronized void putObject(Object key, Object object) {
delegate.putObject(key, object);
}
@Override
public synchronized Object getObject(Object key) {
return delegate.getObject(key);
}
@Override
public synchronized Object removeObject(Object key) {
return delegate.removeObject(key);
}
@Override
public synchronized void clear() {
delegate.clear();
}
@Override
public int hashCode() {
return delegate.hashCode();
}
@Override
public boolean equals(Object obj) {
return delegate.equals(obj);
}
}
SoftCache 软引用缓存装饰器
什么是引用?java中有哪些引用?
-
在"think in java"这本书里面讲:他们把引zhi用dao叫做"句柄 那么句柄是什么意思:
-
在java中任何东西都可以看作是对象,但是操作的标识实际是指向一个对象的“句柄”,在其他java参考书中,有人把句柄称为“引用”, 甚至一个指针,比如说: 用遥控器(句柄),操作电视机(对象),只要握住这个遥控器,就相当于掌握了电视连接的渠道,但是 一旦需要“换频道”或者“关小声音”,我们实际操作的是遥控器(句柄),再由遥控器操作电视机(对象),如果在房间四处走走,我们握住的是遥控器,而不是电视机。
-
那么引用有哪些种类? 强引用,软引用,弱引用,虚引用(用的极少)
-
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它,当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题
-
对象只具有软引用,则内存空间充足时,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没 有回收它,该对象就可以被程序使用
-
软引用对象是在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用, JVM什么时候扫描回收对象是JVM自己的状态决定的。就算扫描到软引用对象也不一定会回收它,只有内存不够的时候才会回收 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
- 这块的代码,和WeakCache的区别在于加了同步关键词,为什么要加?我还没明白。
-
/** * Copyright 2009-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.cache.decorators; import java.lang.ref.ReferenceQueue; import java.lang.ref.SoftReference; import java.util.Deque; import java.util.LinkedList; import org.apache.ibatis.cache.Cache; /** * Soft Reference cache decorator * 软引用缓存装饰器 * Thanks to Dr. Heinz Kabutz for his guidance here. * * @author Clinton Begin */ public class SoftCache implements Cache { /** * 强引用的键的队列 */ private final Deque<Object> hardLinksToAvoidGarbageCollection; /** * 被 GC 回收的 WeakEntry 集合,避免被 GC。 */ private final ReferenceQueue<Object> queueOfGarbageCollectedEntries; /** * 装饰的 Cache 对象 */ private final Cache delegate; /** * {@link #hardLinksToAvoidGarbageCollection} 的大小 */ private int numberOfHardLinks; public SoftCache(Cache delegate) { this.delegate = delegate; this.numberOfHardLinks = 256; this.hardLinksToAvoidGarbageCollection = new LinkedList<>(); this.queueOfGarbageCollectedEntries = new ReferenceQueue<>(); } @Override public String getId() { return delegate.getId(); } @Override public int getSize() { removeGarbageCollectedItems(); return delegate.getSize(); } public void setSize(int size) { this.numberOfHardLinks = size; } @Override public void putObject(Object key, Object value) { removeGarbageCollectedItems(); delegate.putObject(key, new SoftEntry(key, value, queueOfGarbageCollectedEntries)); } @Override public Object getObject(Object key) { Object result = null; @SuppressWarnings("unchecked") // assumed delegate cache is totally managed by this cache // 假设委托缓存完全由这个缓存管理 SoftReference<Object> softReference = (SoftReference<Object>) delegate.getObject(key); if (softReference != null) { // 软引用里面对应的值被清除了 result = softReference.get(); if (result == null) { // 缓存中也进行移除 delegate.removeObject(key); } else { // See #586 (and #335) modifications need more than a read lock // TODO 添加到这个里面去。但是这里操作,为啥加同步锁,同步锁加加了,说明肯定是发生过事故,那么上面情况下会出现并发修改呢, synchronized (hardLinksToAvoidGarbageCollection) { hardLinksToAvoidGarbageCollection.addFirst(result); if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) { hardLinksToAvoidGarbageCollection.removeLast(); } } } } return result; } @Override public Object removeObject(Object key) { removeGarbageCollectedItems(); return delegate.removeObject(key); } @Override public void clear() { // 加了同步,为啥这边加了同步? synchronized (hardLinksToAvoidGarbageCollection) { hardLinksToAvoidGarbageCollection.clear(); } removeGarbageCollectedItems(); delegate.clear(); } private void removeGarbageCollectedItems() { SoftEntry sv; // queueOfGarbageCollectedEntries 队列里面是否有被gc回收的引用,如果没有返回null while ((sv = (SoftEntry) queueOfGarbageCollectedEntries.poll()) != null) { delegate.removeObject(sv.key); } } /** * 和弱引用相识。 */ private static class SoftEntry extends SoftReference<Object> { private final Object key; SoftEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) { super(value, garbageCollectionQueue); this.key = key; } } }
WeakCache 弱引用缓存装饰器。
这个块的东西。难点在于garbageCollectionQueue,我试图去弄明白这个里面设置值的过程,很遗憾。没看到对应的代码。只在jdk对应的api中,和方法说明上看到这个是在引用被GC会收的时候,会设置值到队列中。
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Deque;
import java.util.LinkedList;
import org.apache.ibatis.cache.Cache;
/**
* Weak Reference cache decorator.
* Thanks to Dr. Heinz Kabutz for his guidance here.
*
* WeakCache 弱引用缓存装饰器。
* 感谢Heinz Kabutz博士的指导。
*
*
* 弱引用,是不是会对应强引用,那么引用是什么意思。java中有哪些引用
*
* 有强引用,弱引用(无论内存充足与否,Java GC后对象如果只有弱引用将会被自动回收),软引用。
* @author Clinton Begin
*/
public class WeakCache implements Cache {
/**
* 队列。
* 支持两端元素插入和移除的线性集合。
* 名称deque是“双端队列”的缩写,通常发音为“deck”。
* 大多数Deque实现对它们可能包含的元素的数量没有固定的限制,
* 但是该接口支持容量限制的deques以及没有固定大小限制的deques
*/
private final Deque<Object> hardLinksToAvoidGarbageCollection;
/**
* {@link #hardLinksToAvoidGarbageCollection} 的大小
*/
private int numberOfHardLinks;
/**
* 被 GC 回收的 WeakEntry 集合,避免被 GC。
* 弱引用,这个里面的值是如何被赋值上的?TODO?
*/
private final ReferenceQueue<Object> queueOfGarbageCollectedEntries;
/**
* 装饰的 Cache 对象
*/
private final Cache delegate;
public WeakCache(Cache delegate) {
this.delegate = delegate;
this.numberOfHardLinks = 256;
this.hardLinksToAvoidGarbageCollection = new LinkedList<>();
this.queueOfGarbageCollectedEntries = new ReferenceQueue<>();
}
@Override
public String getId() {
return delegate.getId();
}
/**
* 获取大小,获取大家的时候为什么药移除已经被GC回收的weakEntry
* @return 大小
*/
@Override
public int getSize() {
// 移除已经被 GC 回收的 WeakEntry
removeGarbageCollectedItems();
return delegate.getSize();
}
public void setSize(int size) {
this.numberOfHardLinks = size;
}
/**
*
*
* @param key
* Can be any object but usually it is a {@link CacheKey}
* 可以是任何对象,但是通常是一个缓存的key
* @param value
* The result of a select.
*/
@Override
public void putObject(Object key, Object value) {
// 移除已经被 GC 回收的 WeakEntry
removeGarbageCollectedItems();
// 添加到 delegate 中
// queueOfGarbageCollectedEntries ,类属性的队列
delegate.putObject(key, new WeakEntry(key, value, queueOfGarbageCollectedEntries));
}
/**
* 这个方法是没啥难度的。
* @param key
* The key 键
* @return 拿到的值
*/
@Override
public Object getObject(Object key) {
Object result = null;
// assumed delegate cache is totally managed by this cache
// 假设委托缓存完全由这个缓存管理
@SuppressWarnings("unchecked")
// 获得值的 WeakReference 对象
WeakReference<Object> weakReference = (WeakReference<Object>) delegate.getObject(key);
if (weakReference != null) {
// 获得值
result = weakReference.get();
if (result == null) {
// 为空,从 delegate 中移除 。为空的原因是,意味着已经被 GC 回收
delegate.removeObject(key);
} else {
// 非空,添加到 hardLinksToAvoidGarbageCollection 中,避免被 GC
// 添加到 hardLinksToAvoidGarbageCollection 的队头
hardLinksToAvoidGarbageCollection.addFirst(result);
// 超过上限,移除 hardLinksToAvoidGarbageCollection 的队尾
if (hardLinksToAvoidGarbageCollection.size() > numberOfHardLinks) {
hardLinksToAvoidGarbageCollection.removeLast();
}
}
}
return result;
}
@Override
public Object removeObject(Object key) {
// 移除已经被 GC 回收的 WeakEntry
removeGarbageCollectedItems();
// 移除出 delegate
return delegate.removeObject(key);
}
@Override
public void clear() {
// 清空 hardLinksToAvoidGarbageCollection
hardLinksToAvoidGarbageCollection.clear();
// 移除已经被 GC 回收的 WeakEntry
removeGarbageCollectedItems();
// 清空 delegate
delegate.clear();
}
/**
* 移除已经被 GC 回收的键
* 重点: queueOfGarbageCollectedEntries 这个的赋值,应该是GC自动的,我能力有限目前是没有看到对应的代码
*/
private void removeGarbageCollectedItems() {
WeakEntry sv;
//poll: 轮询此队列以查看引用对象是否可用。 如果没有进一步延迟可用,那么它将从队列中删除并返回。 否则,此方法立即返回null 。
while ((sv = (WeakEntry) queueOfGarbageCollectedEntries.poll()) != null) {
delegate.removeObject(sv.key);
}
}
/**
* 弱引用
*
*
*/
private static class WeakEntry extends WeakReference<Object> {
private final Object key;
/**
*
* @param key 缓存key
* @param value 对象
* @param garbageCollectionQueue 这个把存放value引用的队列。问题是,GC的时候,怎么把被清理的数据value放到这个引用队列的
*/
private WeakEntry(Object key, Object value, ReferenceQueue<Object> garbageCollectionQueue) {
super(value, garbageCollectionQueue);
this.key = key;
}
}
}
TransactionalCache 二级缓存
- 这个看着是挺简单的。这让我觉得是我自己还没完全看明白。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.decorators;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
/**
* The 2nd level cache transactional buffer.
* <p>
* This class holds all cache entries that are to be added to the 2nd level cache during a Session.
* Entries are sent to the cache when commit is called or discarded if the Session is rolled back.
* Blocking cache support has been added. Therefore any get() that returns a cache miss
* will be followed by a put() so any lock associated with the key can be released.
*
* TransactionalCache 二级缓存
* *这个类保存了会话期间所有要添加到二级缓存中的缓存条目。
* *当commit被调用时,表项被发送到缓存中;如果会话被回滚,表项被丢弃。
* *添加了阻塞缓存支持。因此,任何返回缓存未命中的get()方法
* *后面跟着一个put(),因此任何与该密钥相关的锁都可以被释放。
*
* * 在二级缓存中使用,可一次存入多个缓存,移除多 个缓存
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class TransactionalCache implements Cache {
private static final Log log = LogFactory.getLog(TransactionalCache.class);
/**
* 委派设计模式
*/
private final Cache delegate;
/**
* 提交前是否clear
*/
private boolean clearOnCommit;
//待提交的Map缓存
private final Map<Object, Object> entriesToAddOnCommit;
//记录缓存未命中的CacheKey对象:就是我们查询缓存的时候,如果没有,我们加入这个队列进行统计使用
private final Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<>();
this.entriesMissedInCache = new HashSet<>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public Object getObject(Object key) {
// issue #116
// 根据key从委托缓存对象中查询
Object object = delegate.getObject(key);
if (object == null) {
// 查询不到。往entriesMissedInCache存放key,也就是entriesMissedInCache是放未命中的键的集合
entriesMissedInCache.add(key);
}
// issue #146
// 提交前是否清除为true,就返回null
if (clearOnCommit) {
return null;
} else {
// 否则返回查询到的对象。
return object;
}
}
@Override
public void putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
public void clear() {
clearOnCommit = true;
entriesToAddOnCommit.clear();
}
/**
* 提交
*/
public void commit() {
if (clearOnCommit) {
// 清除委托缓存中所有的对象
delegate.clear();
}
// 提交到真的缓存中
flushPendingEntries();
reset();
}
/**
* 回滚
*/
public void rollback() {
// 把未命中的缓存key,从真正的缓存中进行删除
unlockMissedEntries();
// 重置 待提交的缓存数据,提交清除标识,缓存未命中的CacheKey对象
reset();
}
/**
* 重置
*/
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
private void flushPendingEntries() {
// 循环 待提交的Map缓存 entriesToAddOnCommit
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
// 循环 未命中的key 集合
for (Object entry : entriesMissedInCache) {
// 如果待提交的集合没有这个值,就把这个key对应的值在真正缓存对象中置为null
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
private void unlockMissedEntries() {
// 循环 缓存未命中的CacheKey对象
for (Object entry : entriesMissedInCache) {
try {
// 从真正的缓存里移除。
delegate.removeObject(entry);
} catch (Exception e) {
log.warn("Unexpected exception while notifying a rollback to the cache adapter. "
+ "Consider upgrading your cache adapter to the latest version. Cause: " + e);
}
}
}
}
TransactionalCacheManager
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.cache.decorators.TransactionalCache;
/**
* 事务缓存管理。
*
* @author Clinton Begin
*/
public class TransactionalCacheManager {
private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
/**
* 获取指定缓存对象,缓存key对应的值。
* @param cache 缓存对象
* @param key 缓存key
* @return key对应的值
*/
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
/**
* 获取事务缓存对象
* @param cache 缓存对象
* @return 事务缓存对象
*/
private TransactionalCache getTransactionalCache(Cache cache) {
return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
}
PerpetualCache 持久的缓存。里面的内容比较简单
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache.impl;
import java.util.HashMap;
import java.util.Map;
import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.cache.CacheException;
/**
* @author Clinton Begin
*/
public class PerpetualCache implements Cache {
/**
* 标识
*/
private final String id;
/**
* 缓存容器
*/
private final Map<Object, Object> cache = new HashMap<>();
public PerpetualCache(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public int getSize() {
return cache.size();
}
@Override
public void putObject(Object key, Object value) {
cache.put(key, value);
}
@Override
public Object getObject(Object key) {
return cache.get(key);
}
@Override
public Object removeObject(Object key) {
return cache.remove(key);
}
@Override
public void clear() {
cache.clear();
}
@Override
public boolean equals(Object o) {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
if (this == o) {
return true;
}
if (!(o instanceof Cache)) {
return false;
}
Cache otherCache = (Cache) o;
return getId().equals(otherCache.getId());
}
@Override
public int hashCode() {
if (getId() == null) {
throw new CacheException("Cache instances require an ID.");
}
return getId().hashCode();
}
}
四: CacheKey 缓存键,
-
因为 MyBatis 中的缓存键不是一个简单的 String ,而是通过多个对象组成。
-
所以 CacheKey 可以理解成将多个对象放在一起,计算其缓存键。
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.cache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
import org.apache.ibatis.reflection.ArrayUtil;
/**
* 因为 MyBatis 中的缓存键不是一个简单的 String ,而是通过多个对象组成。
* 所以 CacheKey 可以理解成将多个对象放在一起,计算其缓存键。
*
* 如何做到的? todo
* @author Clinton Begin
*/
public class CacheKey implements Cloneable, Serializable {
private static final long serialVersionUID = 1146682552656046210L;
public static final CacheKey NULL_CACHE_KEY = new CacheKey() {
@Override
public void update(Object object) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
@Override
public void updateAll(Object[] objects) {
throw new CacheException("Not allowed to update a null cache key instance.");
}
};
// multiplier 默认乘数,
private static final int DEFAULT_MULTIPLIER = 37;
// 默认hashCode
private static final int DEFAULT_HASHCODE = 17;
// 乘数
private final int multiplier;
// hashCode
private int hashcode;
private long checksum;
private int count;
// 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
// is not always true and thus should not be marked transient.
private List<Object> updateList;
public CacheKey() {
this.hashcode = DEFAULT_HASHCODE;
this.multiplier = DEFAULT_MULTIPLIER;
this.count = 0;
this.updateList = new ArrayList<>();
}
public CacheKey(Object[] objects) {
this();
updateAll(objects);
}
/**
*
* @return 更新总count
*/
public int getUpdateCount() {
return updateList.size();
}
/**
* 更新方法
* @param object 对象
*
* 复合赋值运算符
* a+=30 -》a=a+30
* a-=30 -》a=a-30
* a*=30 -》a=a*30
*/
public void update(Object object) {
// 对象如果是null,然后1,不是就计算对象的hashCode
int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
// 计算次数++
count++;
// checksum 为 baseHashCode 的求和
checksum += baseHashCode;
// 计算新的 hashcode 值 ;baseHashCode=baseHashCode*count
baseHashCode *= count;
hashcode = multiplier * hashcode + baseHashCode;
// 添加 object 到 updateList 中
updateList.add(object);
}
public void updateAll(Object[] objects) {
for (Object o : objects) {
update(o);
}
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof CacheKey)) {
return false;
}
final CacheKey cacheKey = (CacheKey) object;
if (hashcode != cacheKey.hashcode) {
return false;
}
if (checksum != cacheKey.checksum) {
return false;
}
if (count != cacheKey.count) {
return false;
}
for (int i = 0; i < updateList.size(); i++) {
Object thisObject = updateList.get(i);
Object thatObject = cacheKey.updateList.get(i);
// 比较 updateList 数组
if (!ArrayUtil.equals(thisObject, thatObject)) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
return hashcode;
}
@Override
public String toString() {
StringJoiner returnValue = new StringJoiner(":");
returnValue.add(String.valueOf(hashcode));
returnValue.add(String.valueOf(checksum));
updateList.stream().map(ArrayUtil::toString).forEach(returnValue::add);
return returnValue.toString();
}
@Override
public CacheKey clone() throws CloneNotSupportedException {
CacheKey clonedCacheKey = (CacheKey) super.clone();
clonedCacheKey.updateList = new ArrayList<>(updateList);
return clonedCacheKey;
}
}
这一块的重点是:理解符合运算。其他没啥难的。
* 复合赋值运算符 * a+=30 -》a=a+30 * a-=30 -》a=a-30 * a*=30 -》a=a*30
五: 总结,这今天元宵节晚上8点。我才把这一篇更新完。是因为我看的过程中确实也遇到自己不懂的。毕竟水。所以耗时比较多。还有个原因。节后上班了。作为外包,得好好干活的。智能下班了。看看学学下。最近看到身边很多其他外包走了。甲方有些也走了。心里有点空。心里不太踏实。哎!加油吧。当实力不允许的时候。我想只能自己多努力努力。我就想把要看代码这件事做好。偏执的我加油,别被外界影响太多!