前言:当今spring三级缓存已经成为java程序员必备知识点了,今天我就用几段"通俗易懂"的代码带大家领略spring三级缓存的设计思想,包你一看就会。
下图是两种比较常见的循环依赖场景
进入正题
场景一循环依赖之构造函数注入
新建两个类 ClassA,ClassB
public class ClassA {
ClassB classB;
public ClassA(ClassB classB) {
this.classB = classB;
}
}
public class ClassB {
ClassA classA;
public ClassB(ClassA classA) {
this.classA = classA;
}
}
编写运行类
public class Main {
//bean定义 要装载的bean
static Map<String, String> beanDefine = new ConcurrentHashMap<>(8);
//一级缓存
static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(8);
//二级缓存
static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(8);
static {
beanDefine.put("classA", "com.code.learn2.ClassA");
beanDefine.put("classB", "com.code.learn2.ClassB");
}
public static void main(String[] str) throws NoSuchMethodException, ClassNotFoundException {
beanDefine.forEach((k, v) -> {
//bean 的实例化 装载
System.out.println("实例化"+k);
try {
getConstructors(v);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
});
}
public static void getConstructors(String name) throws ClassNotFoundException {
Class<?> aClass = Class.forName(name);
//获取构造函数
Constructor<?>[] constructors = aClass.getConstructors();
//这里直接取第一个 参数也直接取第一个,因为只有一个参数 qaq, 偷懒的写法,spring 会解析你构造方法里面的所有参数
//并帮你自动注入
Class<?>[] classes = constructors[0].getParameters()[0].getDeclaringExecutable().getParameterTypes();
//同样的取第一个
Class<?> constructorsArg = classes[0];
//此时可以知道class的构造函数的第一个参数 constructorsArg,classA对应的也就是classB,反之奕然
//实例化构造函数需要的参数
System.out.println("实例化"+aClass+" 构造行数参数类型:"+constructorsArg);
//到这若不处理,开始陷入循环。。。我上你家,你上我家,你休想能找到我
getConstructors(constructorsArg.getName());
//此时得借助个工具让B知道A的位置 从而让两人真正的相遇 但大家仔细想 都是构造器注入的话 避免不了有一方要先初始化,那这不是死循环了不是......
}
}
由此可以看出来循环依赖类通过构造器来注入是处理不了的,加多少缓存都不行
场景二之Autowire,Resource注入
autowire,resource 只是类型查找方式不同而已,实际最后还是调用反射给属性赋值的哦
field.set(bean, value);
field.set(target, getResourceToInject(target, requestingBeanName));
修改下ClassA , ClassB 删去构造函数
public class ClassA {
ClassB classB;
}
public class ClassB {
ClassA classA;
}
只用一级缓存就能搞定的事
public static Object getConstructors(String beanName,String type) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<?> aClass = Class.forName(type);
//获取构造函数
Constructor<?>[] constructors = aClass.getConstructors();
if (constructors[0].getParameters().length == 0) {
//构造函数无参数 直接实例化
Object bean = constructors[0].newInstance();
//这里加到一级缓存 spring初始化是加到三级缓存
singletonObjects.put(beanName,bean);
//模拟 populateBean(beanName, mbd, instanceWrapper) 进行属性注入 ...
//获取要注入的属性 spring通过注解 @Value @Autowired @Resource 等注解来识别需要注入的属性我们这里简单点扫描到的都注入
for (Field declaredField : bean.getClass().getDeclaredFields()) {
System.out.println("要注入的属性" + declaredField.getName() + ",类型:" + declaredField.getType().getName());
//一级缓存查找 有则直接注入返回
if (singletonObjects.containsKey(declaredField.getName())) {
declaredField.set(bean,singletonObjects.get(declaredField.getName()));
continue;
}
//都没找到 实例化要注入的属性 检查是否在beanDefine里面
if (!beanDefine.containsKey(declaredField.getName())) {
throw new IllegalAccessException(declaredField.getName() + " not beanDefine");
}
Object obj = getConstructors(declaredField.getName(),declaredField.getType().getName());
//注入
declaredField.set(bean,obj);
}
//注入结束bean就算实例化完成了因为bean没有其它操作连更新singletonObjects都不用
return bean;
}
return null;
}
运行结果
两级缓存场景
两级能搞定的事三级或者一定也能搞定,不具备代表性,此处略过。
三级缓存的应用场景
新建 ProxyInterface ,ClassBProxy 模拟classB的代理场景
public interface ProxyInterface {
public void setTargetClass(Object o);
}
public class ClassBProxy extends ClassB implements ProxyInterface{
public Object source;
@Override
public void setTargetClass(Object o) {
this.source = o;
}
}
新建ClassC 依赖ClassB
public class ClassC {
ClassB classB;
}
修改classB
public class ClassB {
ClassA classA;
ClassB classB;
}
依赖结构如图 C 依赖B ,B有代理,同时B依赖A ,A依赖B, B还自我依赖
运行代码
public class Main {
//bean定义 要装载的bean
static Map<String, String> beanDefine = new ConcurrentHashMap<>(8);
//一级缓存
static Map<String, Object> singletonObjects = new ConcurrentHashMap<>(8);
//二级缓存
static Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(8);
// //三级缓存
static Map<String, Object> singletonFactories = new HashMap<>(8);
//标识创建中
static List<String> createBean = new ArrayList<>();
//存放代理beanProxy
static Map<String, Object> proxyMap = new ConcurrentHashMap<>(8);
//存放被代理的bean
static List<String> proxyList = new ArrayList<>();
static {
//这些扫描添加的工作spring会自动帮我们完成
beanDefine.put("classA", "com.code.learn2.ClassA");
beanDefine.put("classB", "com.code.learn2.ClassB");
beanDefine.put("classC", "com.code.learn2.ClassC");
proxyList.add("classB");
proxyMap.put("classB", new ClassBProxy());
}
public static void main(String[] str) {
beanDefine.forEach((k, v) -> {
//bean 的实例化 装载
try {
//遍历顺序 C B A
getConstructors(k, v);
} catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
});
}
public static Object getConstructors(String beanName, String type) throws ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
if(singletonObjects.containsKey(beanName)){
return singletonObjects.get(beanName);
}
System.out.println("实例化" + beanName + ",类型:" + type);
Class<?> aClass = Class.forName(type);
//获取构造函数
Constructor<?>[] constructors = aClass.getConstructors();
//标识为创建中
createBean.add(beanName);
//构造函数无参数 直接实例化
Object bean = constructors[0].newInstance();
//这里加到三级缓存
singletonFactories.put(beanName, bean);
//模拟 populateBean(beanName, mbd, instanceWrapper) 进行属性注入 ...
//获取要注入的属性 spring通过注解 @Value @Autowired @Resource 等注解来识别需要注入的属性我们这里简单点扫描到的都注入
for (Field declaredField : bean.getClass().getDeclaredFields()) {
System.out.println("要注入的属性" + declaredField.getName() + ",类型:" + declaredField.getType().getName());
//一级缓存找到 直接返回
if (singletonObjects.containsKey(declaredField.getName())) {
System.out.println("一级缓存找到注入属性:"+declaredField.getName()+"类型:"+singletonObjects.get(declaredField.getName()).toString());
declaredField.set(bean, singletonObjects.get(declaredField.getName()));
System.out.println("注入属性" + declaredField.getName() + ",类型:" + singletonObjects.get(declaredField.getName()).toString());
continue;
}
//bean在创建中 并且二级缓存包含bean
if (createBean.contains(declaredField.getName()) && earlySingletonObjects.containsKey(declaredField.getName())) {
System.out.println("二级缓存找到注入属性:"+declaredField.getName()+"类型:"+earlySingletonObjects.get(declaredField.getName()).toString());
declaredField.set(bean, earlySingletonObjects.get(declaredField.getName()));
System.out.println("注入属性" + declaredField.getName() + ",类型:" + earlySingletonObjects.get(declaredField.getName()).toString());
continue;
}
//三级缓存找到
if (createBean.contains(declaredField.getName()) && singletonFactories.containsKey(declaredField.getName())) {
//判断要不要生成代理
//spring getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)
System.out.println("三级缓存找到注入属性:"+declaredField.getName()+"类型:"+singletonFactories.get(declaredField.getName()).toString());
if (proxyList.contains(declaredField.getName())) {
ProxyInterface proxy = (ProxyInterface) proxyMap.get(declaredField.getName());
//绑定被代理类
proxy.setTargetClass(bean);
declaredField.set(bean, proxy);
System.out.println("生成代理对象:"+declaredField.getName()+"类型:"+proxy.toString());
System.out.println("注入属性" + declaredField.getName() + ",类型:" + proxy.toString());
//bean 已改变更新到二级缓存 同时移除三级缓存
earlySingletonObjects.put(declaredField.getName(), proxy);
singletonFactories.remove(declaredField.getName());
continue;
}
declaredField.set(bean, singletonFactories.get(declaredField.getName()));
System.out.println("注入属性" + declaredField.getName() + ",类型:" + singletonFactories.get(declaredField.getName()).toString());
continue;
}
//都没找到 实例化要注入的属性 检查是否在beanDefine里面
if (!beanDefine.containsKey(declaredField.getName())) {
throw new IllegalAccessException(declaredField.getName() + " not beanDefine");
}
Object obj = getConstructors(declaredField.getName(), declaredField.getType().getName());
//注入
declaredField.set(bean, obj);
}
//尝试生成代理类 spring中的 initializeBean(beanName, exposedObject, mbd);
if (proxyList.contains(beanName)) {
ProxyInterface proxy = (ProxyInterface) proxyMap.get(beanName);
//绑定被代理类
proxy.setTargetClass(bean);
bean = proxy;
}
//移除创建中标识
createBean.remove(beanName);
//注入结束 判断二级缓存有没有值,此时二级缓存缓存的是最新的对象
Object instance = earlySingletonObjects.get(beanName);
if (instance == null) {
//未发生改变 直接将原始bean添加到一级缓存
singletonObjects.put(beanName, bean);
} else {
//将最新的值加入到一级缓存
singletonObjects.put(beanName, instance);
}
//移除二三级缓存
earlySingletonObjects.remove(beanName);
singletonFactories.remove(beanName);
return bean;
}
}
运行结果
项目完整结构截图
结语: 可以看出 spring直接使用三级缓存实际上是考虑到了bean初始化时可能存在的多种依赖情况, 以及满足bean的设计规范等。三级缓存的设计看似提升了代码复杂度实际上确是提升了代码的可读性,可维护性,可扩展性等~~
最后有什么不对之处或者改进之处还望大家多多提醒指正哦!