spring源码刨析总结
spring源碼刨析筆記
1.概述
spring就是 spring Framework
Ioc Inversion of Control(控制反轉/反轉控制)
DI Dependancy Injection(依賴注入)
Aop Aspect oriented Programming 面向切面編程(OOP的延續)
2.Ioc與DI
Ioc與Aop的區別
Ioc在對象角度將對象實例化以及管理的權力交給了容器
DI在容器角度將對象依賴注入到其他對象
3.Aop
3.1橫切邏輯代碼
多個縱向流程中出現相同子流程代碼
出現的問題:
橫切代碼重復問題,與業務邏輯代碼混在一起,臃腫,維護不方便
Aop不改變業務邏輯情況下,增強橫切邏輯代碼,根本上解耦合代碼
4.自定義的Spring框架
//將private AccountDao accountDao = new JdbcAccountDaoImpl();轉換為 private AccountDao accountDao;// 構造函數傳值/set方法傳值public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}4.1思路
public class BeanFactory {//集合的定義放在類的最上面來,因為后面的使用時在static靜態代碼塊中,否則在使用時集合對象會為nullstatic List<String> classNames = new ArrayList<>(); // 緩存掃描到的class全限定類名static List<String> fieldsAlreayProcessed = new ArrayList<>(); // 緩存已經進行過依賴注入的信息/*** 任務一:讀取解析xml,通過反射技術實例化對象并且存儲待用(map集合)* 任務二:對外提供獲取實例對象的接口(根據id獲取)*/private static Map<String,Object> map = new HashMap<>(); // 存儲對象 }1.遍歷掃描路徑
//獲取掃描包的全限定類名
<beans><component-scan base-package="com.lagou.edu"></component-scan> </beans> // 加載xmlInputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");// 解析xmlSAXReader saxReader = new SAXReader();Document document = saxReader.read(resourceAsStream);Element rootElement = document.getRootElement();Element scanElement = (Element) rootElement.selectSingleNode("//component-scan");String scanPackage = scanElement.attributeValue("base-package");/*** 掃描指定包下的注解*/private static void doScan(String scanPackage) {String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");File pack = new File(scanPackagePath);File[] files = pack.listFiles();for(File file: files) {if(file.isDirectory()) { // 子package// 遞歸doScan(scanPackage + "." + file.getName()); // com.lagou.demo.controller}else if(file.getName().endsWith(".class")) {String className = scanPackage + "." + file.getName().replaceAll(".class", "");classNames.add(className);}}}2.對象實例化
/*** 通過反射實例化對象,此時暫不維護依賴注入關系*/private static void doInstance() {if (classNames.size() == 0) return;try {for (int i = 0; i < classNames.size(); i++) {String className = classNames.get(i);// 反射Class<?> aClass = Class.forName(className);// 只處理標注了注解@MyService、@MyRepository和@MyComponent的類if (aClass.isAnnotationPresent(MyService.class)|| aClass.isAnnotationPresent(MyRepository.class)|| aClass.isAnnotationPresent(MyComponent.class)) {//獲取注解value值String beanName = null;if (aClass.isAnnotationPresent(MyService.class)) {beanName = aClass.getAnnotation(MyService.class).value();} else if (aClass.isAnnotationPresent(MyRepository.class)) {beanName = aClass.getAnnotation(MyRepository.class).value();} else if (aClass.isAnnotationPresent(MyComponent.class)) {beanName = aClass.getAnnotation(MyComponent.class).value();}// 如果指定了id,就以指定的為準Object o = aClass.newInstance();if ("".equals(beanName.trim())) {beanName = lowerFirst(aClass.getSimpleName());}map.put(beanName,o);// service層往往是有接口的,面向接口開發,此時再以接口名為id,放入一份對象到容器中,便于后期根據接口類型注入Class<?>[] interfaces = aClass.getInterfaces();if(interfaces != null && interfaces.length > 0) {for (int j = 0; j < interfaces.length; j++) {Class<?> anInterface = interfaces[j];// 以接口的全限定類名作為id放入map.put(anInterface.getName(), aClass.newInstance());}}} else {continue;}}} catch (Exception e) {e.printStackTrace();}}3.維護注入關系
/** 實現依賴注入*/private static void doAutoWired(){if(map.isEmpty()) {return;}// 遍歷ioc中所有對象,查看對象中的字段,是否有@LagouAutowired注解,如果有需要維護依賴注入關系for(Map.Entry<String,Object> entry: map.entrySet()) {try {doObjectDependancy(entry.getValue());} catch (IllegalAccessException e) {e.printStackTrace();}}}/*** A 可能依賴于 B ,B 可能依賴于 C ,C 可能又依賴于D,本方法主要維護一下嵌套依賴*/private static void doObjectDependancy(Object object) throws IllegalAccessException {Field[] declaredFields = object.getClass().getDeclaredFields();if(declaredFields == null || declaredFields.length ==0) {return;}// 遍歷判斷處理for (int i = 0; i < declaredFields.length; i++) {Field declaredField = declaredFields[i];if (!declaredField.isAnnotationPresent(MyAutowired.class)) {continue;}// 判斷當前字段是否處理過,如果已經處理過則continue,避免嵌套處理死循環if(fieldsAlreayProcessed.contains(object.getClass().getName() + "." + declaredField.getName())){continue;}Object dependObject = null;dependObject = map.get(declaredField.getType().getName()); // 先按照聲明的是接口去獲取,如果獲取不到再按照首字母小寫if(dependObject == null) {dependObject = map.get(lowerFirst(declaredField.getType().getSimpleName()));}// 記錄下給哪個對象的哪個屬性設置過,避免死循環fieldsAlreayProcessed.add(object.getClass().getName() + "." + declaredField.getName());// 迭代doObjectDependancy(dependObject);declaredField.setAccessible(true);declaredField.set(object,dependObject);}}4.維護事務
/** 實現事務管理,為添加了@MyTransactional注解的對象創建代理對象,并覆蓋原IOC容器中的對象*/private static void doTransactional() {ProxyFactory proxyFactory = (ProxyFactory) map.get("proxyFactory");for(Map.Entry<String,Object> entry: map.entrySet()) {String beanName = entry.getKey();Object o = entry.getValue();Class<?> aClass = entry.getValue().getClass();if(aClass.isAnnotationPresent(MyTransactional.class)) {// 需要進行事務控制// 有實現接口Class<?>[] interfaces = aClass.getInterfaces();if(interfaces != null && interfaces.length > 0) {// 使用jdk動態代理map.put(beanName,proxyFactory.getJdkProxy(o));}else{// 使用cglib動態代理map.put(beanName,proxyFactory.getCglibProxy(o));}}}}4.2代碼
創建connection的時候放到同一個線程中
public class ConnectionUtils {private ThreadLocal<Connection> threadLocal = new ThreadLocal<>(); // 存儲當前線程的連接/*** 從當前線程獲取連接*/public Connection getCurrentThreadConn() throws SQLException {/*** 判斷當前線程中是否已經綁定連接,如果沒有綁定,需要從連接池獲取一個連接綁定到當前線程*/Connection connection = threadLocal.get();if(connection == null) {// 從連接池拿連接并綁定到線程connection = DruidUtils.getInstance().getConnection();// 綁定到當前線程threadLocal.set(connection);}return connection;} }5. FactoryBean 和 BeanFactory區別
BeanFactory是個bean 工廠,是一個工廠類(接口), 它負責生產和管理bean的一個工廠
是ioc 容器最底層的接口,是個ioc容器,是spring用來管理和裝配普通bean的ioc容器(這些bean成為普通bean)。
FactoryBean是個bean,在IOC容器的基礎上給Bean的實現加上了一個簡單工廠模式和裝飾模式,是一個可以生產對象和裝飾對象的工廠bean,由spring管理后,生產的對象是由getObject()方法決定的。
// 可以讓我們?定義Bean的創建過程(完成復雜Bean的定義) public interface FactoryBean<T> {@Nullable// 返回FactoryBean創建的Bean實例,如果isSingleton返回true,則該實例會放到Spring容器的單例對象緩存池中MapT getObject() throws Exception;@Nullable// 返回FactoryBean創建的Bean類型Class<?> getObjectType();// 返回作?域是否單例default boolean isSingleton() {return true;} }public class CompanyFactoryBean implements FactoryBean<Company> {@Overridepublic Company getObject() throws Exception {// 模擬創建復雜對象CompanyCompany company = new Company();String[] strings = companyInfo.split(",");company.setName(strings[0]);company.setAddress(strings[1]);company.setScale(Integer.parseInt(strings[2]));return company;}}6.spring的循環引用
1.構造器注入循環依賴
2.@Autowired的依賴注入
6.1流程
①:構造器的循環依賴。【這個Spring解決不了】
Spring是先將Bean對象實例化【依賴無參構造函數】—>再設置對象屬性的
//一級緩存 //singletonFactories : 單例對象工廠的cache /** Cache of singleton objects: bean name --> bean instance */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256); //二級緩存 // earlySingletonObjects :提前暴光的單例對象的Cache 。【用于檢測循環引用,與singletonFactories互斥】 /** Cache of early singleton objects: bean name --> bean instance */ private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16); //三級緩存//singletonObjects:單例對象的cache /** Cache of singleton factories: bean name --> ObjectFactory */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16); /** Names of beans that are currently in creation. */ // 這個緩存也十分重要:它表示bean創建過程中都會在里面呆著~ // 它在Bean開始創建時放值,創建完成時會將其移出~ private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));/** Names of beans that have already been created at least once. */ // 當這個Bean被創建完成后,會標記為這個 注意:這里是set集合 不會重復 // 至少被創建了一次的 都會放進這里~~~~ private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));6.2為什么不用二級緩存要用三級緩存
可以二級緩存就可以初始化一些內容,
在將三級緩存放入二級緩存的時候,會判斷是否有SmartInstantiationAwareBeanPostProcessor這樣的后置處理器,換句話說這里是給用戶提供接口擴展的,所以采用了三級緩存, 特殊寫法中
6.3源代碼中的特殊寫法
//添加到三級緩存中addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));//====改寫后addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {getEarlyBeanReference(beanName, mbd, bean);} });//ObjectFactory方法public interface ObjectFactory<T> {T getObject() throws BeansException;}//addSingletonFactory方法 protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(singletonFactory, "Singleton factory must not be null");synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {//添加三級緩存this.singletonFactories.put(beanName, singletonFactory);//移除二級this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}}//getEarlyBeanReference方法protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 這么一大段就這句話是核心,也就是當bean要進行提前曝光時,// 給一個機會,通過重寫后置處理器的getEarlyBeanReference方法,來自定義操作bean// 值得注意的是,如果提前曝光了,但是沒有被提前引用,則該后置處理器并不生效!!!// 這也正式三級緩存存在的意義,否則二級緩存就可以解決循環依賴的問題exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;}6.4文字版敘述流程
使用context.getBean(A.class),旨在獲取容器內的單例A(若A不存在,就會走A這個Bean的創建流程),顯然初次獲取A是不存在的,因此走A的創建之路~
實例化A(注意此處僅僅是實例化),并將它放進緩存(此時A已經實例化完成,已經可以被引用了)
初始化A:@Autowired依賴注入B(此時需要去容器內獲取B)為了完成依賴注入B,會通過getBean(B)去容器內找B。但此時B在容器內不存在,就走向B的創建之路~
實例化B,并將其放入緩存。
初始化B,@Autowired依賴注入A(此時需要去容器內獲取A)
此處重要:初始化B時會調用getBean(A)去容器內找到A,上面我們已經說過了此時候因為A已經實例化完成了并且放進了緩存里,所以這個時候去看緩存里是已經存在A的引用了的,所以getBean(A)能夠正常返回(此時B也能夠被引用了)然后調用AOP的后置處理器類:getEarlyBeanReference,拿到代理后的bean(假設此處切面滿足,要創建代理);
經過上面的步驟后,B里面,field已經填充ok,其中,且填充的field是代理后的A,這里命名為proxy A。
B 繼續其他的后續處理。
B初始化成功(此時已經注入A成功了,已成功持有A的引用了),return(注意此處return相當于是返回最上面的getBean(B)這句代碼,回到了初始化A的流程中~)。
因為B實例已經成功返回了,因此最終A也初始化成功
到此,B持有的已經是初始化完成的A,A持有的也是初始化完成的B
6.5 spring的事務
1.四大特征
原子性(Atomicity)
一致性(Consistency)
隔離性(Isolation)
持久性(Durability)
2.事務的隔離級別
Serializable(串?化):可避免臟讀、不可重復讀、虛讀情況的發?。(串?化) 最?
Repeatable read(可重復讀):可避免臟讀、不可重復讀情況的發?。(幻讀有可能發?) 第?
該機制下會對要update的?進?加鎖
Read committed(讀已提交):可避免臟讀情況發?。不可重復讀和幻讀?定會發?。 第三
Read uncommitted(讀未提交):最低級別,以上情況均?法保證。(讀未提交) 最低
注意:級別依次升?,效率依次降低
MySQL的默認隔離級別是:REPEATABLE READ
3.事務的傳播行為
PROPAGATION_REQUIRED 如果當前沒有事務,就新建?個事務,如果已經存在?個事務中,
加?到這個事務中。這是最常?的選擇。
PROPAGATION_SUPPORTS ?持當前事務,如果當前沒有事務,就以?事務?式執?。
PROPAGATION_MANDATORY 使?當前的事務,如果當前沒有事務,就拋出異常。
PROPAGATION_REQUIRES_NEW 新建事務,如果當前存在事務,把當前事務掛起。
PROPAGATION_NOT_SUPPORTED 以?事務?式執?操作,如果當前存在事務,就把當前事務掛起。
PROPAGATION_NEVER 以?事務?式執?,如果當前存在事務,則拋出異常。
PROPAGATION_NESTED 如果當前存在事務,則在嵌套事務內執?。如果當前沒有事務,則
執?與PROPAGATION_REQUIRED類似的操作。
6.6資料
1.https://blog.csdn.net/chaitoudaren/article/details/104833575
2.https://zhuanlan.zhihu.com/p/84267654
3.https://blog.csdn.net/qq_36381855/article/details/79752689
java代理
1.java動態代理
實現了InvocationHandler接口
public Object getJdkProxy(Object obj) {Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = null;try{// 開啟事務(關閉事務的自動提交)transactionManager.beginTransaction();result = method.invoke(obj,args);// 提交事務transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滾事務transactionManager.rollback();// 拋出異常便于上層servlet捕獲throw e;}return result;}}); }2.cglib動態代理
實現MethodInterceptor接口
public Object getCglibProxy(Object obj) { //創建Enhancer對象,類似于JDK動態代理的Proxy類,下一步就是設置幾個參數return Enhancer.create(obj.getClass(), new MethodInterceptor() {@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {Object result = null;try{// 開啟事務(關閉事務的自動提交)transactionManager.beginTransaction();result = method.invoke(obj,objects);// 提交事務transactionManager.commit();}catch (Exception e) {e.printStackTrace();// 回滾事務transactionManager.rollback();// 拋出異常便于上層servlet捕獲throw e;}return result;}});}3.區別
1)JDK動態代理只能對實現了接口的類生成代理,而不能針對類。
2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法,
并覆蓋其中方法實現增強,但是因為采用的是繼承,所以該類或方法最好不要聲明成final,
對于final類或方法,是無法繼承的
鏈接:https://www.jianshu.com/p/46d092bb737d
補充
1.ThreadLocal是什么
// 存儲當前線程的連接 private ThreadLocal<Connection> threadLocal = new ThreadLocal<>();ThreadLocal 內部維護了一個 Map ,這個 Map 是叫做 ThreadLocalMap 里賣弄有個Entey數組的table
ThreadLocal 適用于每個線程需要自己獨立的實例且該實例需要在多個方法中被使用,也即變量在線程間隔離而在方法或類間共享的場景。
ThreadLocal 適用于如下兩種場景
- 每個線程需要有自己單獨的實例
- 實例需要在多個方法中共享,但不希望被多線程共享
1.1源代碼刨析
總結
1.每個線程持有一個ThreadLocalMap對象==》set()方法沒有的時候會去CreateMap()
2.每個線程Thread持有一個ThreadLocalMap類型的實例內含Entry(可以理解為每個線程Thread都持有一個Entry型的數組table)
//set方法public void set(T value) {//獲取當前線程Thread t = Thread.currentThread();//取值ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}//創建一個ThreadLocalMap方法void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}//ThreadLocalMap方法static class ThreadLocalMap {//Entry數組private Entry[] table;//Entry對象 繼承了弱引用的接口static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated(允許) with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}//ThreadLocalMap對象的實例化ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {private static final int INITIAL_CAPACITY = 16;//初始化因子為16table = new Entry[INITIAL_CAPACITY];//位運算,結果與取模相同,計算出需要存放的位int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);//給ThreadLocalMap存值 private void set(ThreadLocal<?> key, Object value) {//獲取ThreadLocalMap的TableEntry[] tab = table;int len = tab.length;//將threadLocalHashCode進行一個位運算(取模)得到索引iint i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();}} }1.2總結
1.不同線程之間訪問時訪問的是不同的table數組的同一位置即都為table[i],只不過這個不同線程之間的table是獨立的。
2.對于同一線程的不同ThreadLocal來講,這些ThreadLocal實例共享一個table數組,然后每個ThreadLocal實例在table中的索引i是不同的。
1.3ThreadLocal和Synchronized
ThreadLocal和Synchronized都是為了解決多線程中相同變量的訪問沖突問題,不同的點是
Synchronized是通過線程等待,犧牲時間來解決訪問沖突
ThreadLocal是通過每個線程單獨一份存儲空間,犧牲空間來解決沖突,并且相比于Synchronized,ThreadLocal具有線程隔離的效果,只有在線程內才能獲取到對應的值,線程外則不能訪問到想要的值。
鏈接:https://www.jianshu.com/p/3c5d7f09dfbd
1.4關于Spring,ioc創建的對象和new創建的對象有啥區別
Spring Bean是反射創建,先創建bean之后,有一系列的步驟(ioc 12個步驟)可以被開發中敢于修改擴展,最終把創建好的對象放在map里
讀源碼
今天我就給大家分享一下路神的Spring源碼學習方法:(源碼的學習方法是通用的)
1、通讀Spring官方文檔
學習Spring源碼之前,首先要把Spring官方網文檔系統的閱讀一遍。哪怕你讀不懂,也會接觸到很多名詞,讀源碼的時候大有幫助。
有人拿自己英語不好當借口,子路笑言自己的英文水平經常被人噴,這個困難要自己克服。
2、如何正確閱讀Spring源碼
讀完源碼就忘,是因為你沒有理解透徹。子路建議:“不要從字面意義上去讀源碼,通過源碼啟動方式閱讀。”
比如讀nacos的源碼,要理解作者做這個設計變量的思路、設計代碼的原則、作者的想法是怎樣的?
比如nacos跟Spring、Spring boot、Spring cloud這四個角色分別完成什么樣的功能?Spring cloud中Spring-cloud-common這個包有什么用?Spring boot主要完成的功能?Spring又完成什么功能?
那么三者結合在一起就可以看出作者寫代碼的意圖,一定要站在作者的角度,結合全局來看源碼。
3、盡情去調試Spring吧
源碼級的知識一定要自己驗證!特別是Spring的擴展點!
在學習過程中,不要怕,盡量多去調試;看一下就去斷點調試一下;多去寫自己的注釋;盡量去把Spring代碼改了,把代碼給刪了!
多思考Spring某些地方預留的接口能干嘛?這個地方是不是可以做擴展?MyBatis是如何擴展Spring的?市面上還有哪些主流框架擴展了Spring?邊看源碼邊思考,這樣你的記憶會加深很多。
學習Spring源碼目的就是為了讓我們能夠去對Spring做二次開發或者擴展。
實話實說,大多數人學Spring,就是為了去面試。很多人在簡歷上寫“讀過Spring源碼”,這么寫你連電話都接不到!
我們讀過Spring源碼之后,簡歷上該怎么寫?給大家做個參考:
系統的閱讀過Spring源碼;
能夠對Spring做二次開發;
并且熟知Spring當中的各種擴展點;
熟知主流框架對Spring源碼的擴展;
單詞
Injection 注射注入
current 現在,流通的
associated關聯的,聯系
necessary必須的,必要的
rehash重復
refresh更新,恢復
obtain獲得流行
triggered 引起 觸發
學習資源
spring循環依賴 https://mp.weixin.qq.com/s/RFxoVMeW5Mx9kzzFGhpyKA
閱讀源碼的方式:https://mp.weixin.qq.com/s/XQ5jl2pUa1W7Ueu0pWFS2Q
什么是ThreadLocal:https://mp.weixin.qq.com/s/bcH2pL06J5udBWedE1tbCA
springBean的生命周期:https://mp.weixin.qq.com/s/rz9cZRLZZnA_kahetEYjaw
總結
以上是生活随笔為你收集整理的spring源码刨析总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springMvc源码刨析笔记
- 下一篇: mybatis源码刨析总结