1. 先回顧下spring 創(chuàng)建bean 的大致流程:
1.1. bean定義注冊到beanDefinationMap 中 ,beanDefinitionNames保存bean 名稱
1.2. 遍歷 beanDefinitionNames 調用AbstractBeanFactory#getBean方法來創(chuàng)建和初始化bean

1.3. getBean 的流程:
1.3.1 從緩存中獲取bean(3級緩存依次獲?。@取到返回
1.3.2 沒有獲取到 ,創(chuàng)建bean, 把beanName標記為正在創(chuàng)建中 ,反射實例化bean
1.3.3 如果bean 在創(chuàng)建中 , bean的對象工廠加入3級緩存
1.3.4 bean 注入屬性(即屬性賦值)同樣根據 AbstractBeanFactory#getBean方法獲取各屬性的實例
1.3.5 bean 初始化 (通過BeanPostProcessor接口生成代理對象)AbstractAutowireCapableBeanFactory#initializeBean


1.3.6 如果允許提前暴露bean, 從一級和二級緩存中獲取對象earlySingletonReference,如果找到, 給exposedObject 賦值 earlySingletonReference

1.3.7 返回exposedObject
2. 存在循環(huán)依賴spring創(chuàng)建Bean的過程
場景:假設AService 中有BService 屬性 , BService 有AService屬性, 此時 AService 和BService 存在循環(huán)依賴。
根據Spring 創(chuàng)建Bean 的流程,創(chuàng)建過程如下:
2.1 反射實例化AService ,放入三級緩存
2.2 注入屬性BService,此時BService未創(chuàng)建 , 則創(chuàng)建BService
2.3 反射實例化BService ,放入三級緩存
2.4 注入屬性AService ,此時AService已經實例化 ,并存在于三級緩存, 從三級緩存中獲取并調用getObject方法獲取AService,并將獲取的AService 移入二級緩存(且刪除三級緩存的值)
2.5 初始化BService ,將BService 放入一級緩存 ,并移除二級緩存和三級緩存的值
2.6 回到步驟2.2 , 注入屬性BService 完成
2.7 完成AService 初始化 ,將AService 放入一級緩存, 并移除二級緩存和三級緩存的值。

3. 存在循環(huán)依賴但只有一級緩存會怎樣
3.1 反射實例化AService ,放入一級緩存
3.2 注入屬性BService ,此時BService 未實例化, 實例化BService
3.3 反射創(chuàng)建BService , 放入一級緩存
3.4 注入Aservice 屬性 ,此時AService 已經實例化,并存在于一級緩存中
3.5 初始化BService
3.6 回到步驟3.2 ,注入BService完成
3.7 完成Aservice初始化 。
有問題?貌似沒有問題?。。。。?br> 代碼論證:
package org.springframework.test.my.cache;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class BeanFactory {
private static ConcurrentHashMap<String , Object> singletonObjs = new ConcurrentHashMap<>();
public static Object getBean(Class clz){
if(null != singletonObjs.get(clz.getCanonicalName())) {
return singletonObjs.get(clz.getCanonicalName());
}
try {
return doCreatBean(clz);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
refresh();
print();
}
private static Set<Class<?>> scan(){
return getClasses("org.springframework.test.my.cache.beans");
}
public static void refresh(){
Set<Class<?>> clzSet = scan();
for(Class clz : clzSet){
try {
doCreatBean(clz);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
private static Object doCreatBean(Class clz) throws InstantiationException, IllegalAccessException {
Object o = clz.newInstance();
singletonObjs.put(clz.getCanonicalName() , o);
Field[] fields = clz.getFields();
for(Field f : fields){
f.setAccessible(true);
f.set(o , getBean(f.getType()));
}
return o;
}
private static void print() {
for(String beanName : singletonObjs.keySet()){
System.out.println("==beanName==");
System.out.println(singletonObjs.get(beanName));
}
}
public static Set<Class<?>> getClasses(String pack) {
// 第一個class類的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循環(huán)迭代
boolean recursive = true;
// 獲取包的名字 并進行替換
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定義一個枚舉的集合 并進行循環(huán)來處理這個目錄下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(
packageDirName);
// 循環(huán)迭代下去
while (dirs.hasMoreElements()) {
// 獲取下一個元素
URL url = dirs.nextElement();
// 得到協(xié)議的名稱
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服務器上
if ("file".equals(protocol)) {
System.err.println("file類型的掃描");
// 獲取包的物理路徑
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式掃描整個包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath,
recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定義一個JarFile
System.err.println("jar類型的掃描");
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection())
.getJarFile();
// 從此jar包 得到一個枚舉類
Enumeration<JarEntry> entries = jar.entries();
// 同樣的進行循環(huán)迭代
while (entries.hasMoreElements()) {
// 獲取jar里的一個實體 可以是目錄 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/開頭的
if (name.charAt(0) == '/') {
// 獲取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定義的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"結尾 是一個包
if (idx != -1) {
// 獲取包名 把"/"替換成"."
packageName = name.substring(0, idx)
.replace('/', '.');
}
// 如果可以迭代下去 并且是一個包
if ((idx != -1) || recursive) {
// 如果是一個.class文件 而且不是目錄
if (name.endsWith(".class")
&& !entry.isDirectory()) {
// 去掉后面的".class" 獲取真正的類名
String className = name.substring(
packageName.length() + 1, name
.length() - 6);
try {
// 添加到classes
classes.add(Class
.forName(packageName + '.'
+ className));
} catch (ClassNotFoundException e) {
// log
// .error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在掃描用戶定義視圖時從jar包獲取文件出錯");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
public static void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 獲取此包的目錄 建立一個File
File dir = new File(packagePath);
// 如果不存在或者 也不是目錄就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用戶定義包名 " + packageName + " 下沒有任何文件");
return;
}
// 如果存在 就獲取包下的所有文件 包括目錄
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定義過濾規(guī)則 如果可以循環(huán)(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 循環(huán)所有文件
for (File file : dirfiles) {
// 如果是目錄 則繼續(xù)掃描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java類文件 去掉后面的.class 只留下類名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
//經過回復同學的提醒,這里用forName有一些不好,會觸發(fā)static方法,沒有使用classLoader的load干凈
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
} catch (ClassNotFoundException e) {
// log.error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");
e.printStackTrace();
}
}
}
}
}

至此,我們僅僅使用一級緩存就解決了循環(huán)依賴?。。?!那么spring 引入3級緩存的真實目的到底是什么???
4. 使用三級緩存是否因為AOP 代理?
先看下Spring 是如何實現(xiàn)aop的。
4.1. spring 在實例化bean后 , 會以beanName 為key , ObejectFactory 為value 放入三級緩存中 。
通過調用ObjectFactory#getObject() 獲取bean對象。

ObjectFactory#getObject() 是一個函數(shù)式接口 ,調用getObject實際調用AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法

方法邏輯就是如果有SmartInstantiationAwareBeanPostProcessor實現(xiàn)類,則返回接口的getEarlyBeanReference方法返回值,否則就是返回入參bean,即反射創(chuàng)建完的bean。
4.2 aop 的實現(xiàn)AbstractAutoProxyCreator



也就是說,spring 通過 ObjectFactory# getObejct方法提前暴露了代理對象 , 對象初始化后通過BeanPostProcessor的后置處理器生成代理對象
5. 假設只有一級緩存 生成代理對象的流程
5.1 反射實例化AService ,提前生成ASerivce的代理對象放入一級緩存
5.2 注入屬性BService ,此時BService 未實例化, 實例化BService
5.3 反射創(chuàng)建BService , 放入一級緩存
5.4 注入Aservice 屬性 ,此時AService 已經實例化,并存在于一級緩存中
5.5 初始化BService
5.6 回到步驟3.2 ,注入BService完成
5.7 完成Aservice初始化 。
有問題么? 沒有問題!??!
驗證: 我們注釋了將對象放入三級緩存的代碼 ,而直接放入一級緩存

通過啟動spring 的代碼 , 發(fā)現(xiàn)任然能解決循環(huán)依賴和 代理對象問題
所以綜上,從代碼的角度,一級緩存完全能解決循環(huán)依賴問題和代理對象問題
那spring 使用3級緩存的目的是什么呢?
這個問題個人的想法是 :
Spring開發(fā)者不希望循環(huán)依賴的處理影響了原本創(chuàng)建bean的流程,原本是在bean初始化之后,再通過后置處理來創(chuàng)建代理對象;如果只使用一級緩存,那勢必要在實例化bean后直接創(chuàng)建代理對象放入緩存,這樣就僅僅因為循環(huán)依賴破壞了原本的流程