java spring 理解
1.spring IOC容器 其實就是 new 了一個 ApplicationContext 類對象。-》應用上下文對象。
2.應用上下文對象 實例化、配置,并管理 bean。
所以上下文對象是 spring 的核心, 創建對象并把他們連接在一起,管理他們的生命周期。
--------------------
spring容器(應用上下文)通過我們提交的 POJO類 和 配置元數據(你懂得) 產生一個充分配置的(Fulled Configured)可使用的(ready for use)系統。
1.spring創建bean的幾個手段(bean就是new好的對象)
--通過@Component, @Service注解的方式 (默認都是通過無參的構造函數來實例化對象)
--通過普通的XML方式(跟@Component注解一個道理) 《順序:通過<bean>標簽的class 屬性得到一個class對象;然后通過class對象獲取到對應的方法名稱的Method對象;最后反射調用Method.invoke(null, args)》
--通過@Configuration注解的方式
--通過@Bean的方式
--通過靜態工廠方法 ????? (因為是靜態方法,方法在執行時,不需要一個對象)
--通過實例工廠方法的方式 ????
2.依賴注入的兩種方式(實例化后的bean對象,注入才能成為spring bean。)
--構造函數注入
--Setter方法注入
通過@Autowire指定使用哪個構造方法(有參無參)或哪個setter方法注入;
首先明確一點,直接添加@Autowire注解到字段上,不需要提供 setter方法 也能完成注入。(spring 會通過反射獲取當前Class中的private定義的字段,然后通過反射包的方法,File.set(Class, Privatekey) 這種方式完成注入)
構造函數注入和setter方法注入可以混用,
對于一些強制依賴,最好使用構造函數注入,對于一些可選依賴,我們可以采用setter方法注入。
spring 團隊推薦使用 構造函數的方式注入,,但是對于一些構造參數過長的構造函數,spring是 不推薦的。
~方法注入vs依賴注入:
為什么有了依賴注入,還需要有方法注入的方式呢,方法注入解決了什么問題? (我也看不懂,不知道)
-依賴注入:什么是依賴? 一個對象的依賴其實就是自身的屬性。spring中的依賴注入其實就是屬性注入。
我們知道一個對象由兩部分組成:屬性 + 行為。 可以說spring通過屬性注入 + 方法注入的方式掌控整個bean。
屬性注入和方法注入都是spring提供給我們用來處理bean之間協作關系的手段。
屬性注入有兩種方式:構造函數注入和 setter方法注入。
方法注入需要依賴動態代理完成。(不是很懂)
方法注入對屬性注入進行了一定程度上的補充,因為屬性注入的情況下,原型可能會失去原型的意義(為什么要使用方法注入)
3. 精確注入vs自動注入vs方法注入 (依賴注入又叫‘精確注入’,對應的還有‘自動注入’)
--自動注入是針對整個對象,或者一整批對象。比如我們如果將autoService這個bean的注入模型設置為byName,spring會為我們去尋找所有符合要求的名字(通過setter方法)bean并注入到autoService 中,
而精確注入這種方式,是我們針對對象中的某個屬性,比如我們在autoService中的dmzService這個屬性字段上添加了@AutoWired注解,代表我們要精確的注入dmzService這個屬性。
而方法注入主要是基于方法對對象進行注入。
--我們通常所說***byName***,***byType***跟我們在前文中提到的注入模型中的byName,byType 完全不一樣。通常我們說的***byName***,***byType***是spring尋找bean的手段。比如,當我們注入模型為constructor時,Spring 會先通過名稱找符合要求的bean,這種通過名稱尋找對應的bean的方式,我們可以成為byName. 我們可以將一次注入分為兩個階段,首先是尋找符合要求的bean,其次再將符合要求的bean注入。
3.1 補充spring1.4小結的剩余部分
-- depends-on:
我們首先要知道,默認情況下,spring在實例化容器中的對象時是按名稱進行自然排序進行實例化的。比如我們現在有A,B,C三個對象,那么spring在實例化時會按照A,B,C這樣的順序進行實例化。
但是,在某些情況下我們可能需要B在A之前完成實例化。這個時候,我們就需要使用depends-on這個屬性了。
-- lazy:
默認情況下,spring會在容器啟動階段完成所有bean的實例化,以及一系列的生命周期回調。
但是,某些情況下我們可能需要讓某一個bean延遲實例化。這種情況下,我們需要用到lazy屬性。
4. BeanDefinition
BD包含了我們對 bean 做的配置,,比如 XML </bean> 標簽的形式進行的配置。
BD是 spring 對 bean 抽象 后的實體。 它是 spring 創建 bean 的一個標準。
BD 包含以下與數據:
-- 一個全限定 類名, 通常來說, 就是對應的 bean 的 全限定類名。
-- bean 的 行為 配置 元素, 這些 元素 展示了 這個 bean 在容器中 是 如何 工作的, 包括 scope, lifecycle, callbacks 等等。
-- 這個bean的 依賴信息。
-- 一些其他配置信息, 比如 我們 配置了一個連接池對象, 那么我們還會配置 其他的池子 大小, 最大連接數等。
4.1 普通創建bean vs BD創建bean
普通: 磁盤上的兩個.class文件 , 通過 ClassLoader 加載 成了 JVM 方法區中的兩個 class 對象, 然后 通過 new 的方式 創建了 兩個對象(ok);
BD: 磁盤上的兩個.class 文件, 通過 ClassLoader 加載 成了 JVM 方法區中的兩個 class 對象,而 spring 對其管理得 bean 沒有直接采用 new 的方式。 而是 先 通過 解析 配置數據 以及 根據對象 本身的一些定義 而 獲取 其 對應的 beanDefinition, 并將這個beanDefinition 作為 之后 創建 這個 bean 的 依據。同時 spring 在這個過程中 提供了 一些 擴展點, 例如 我們在圖中所 提到了 BeanFactoryProcessor. (ok)
4.2 總結:
什么是BeanDefinition? 總結起來就是一句話,Spring 創建 bean 是 的 建模對象。
BeanDefinition 具體使用的子類,以及 Spring 在 哪些地方使用到了 他們。 )(不懂)
4.3 內容補充:
單例:
一個單例的bean 意味著, 這個bean 只會 容器 創建 一次。 在 創建后, 容器中的 每個地方 使用的 都是 同一個 bean 對象。
<bean scope='singleton' />
@Component // 這里配置singleton,默認就是singleton @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class LuBanService{ }
原型:
一個原型的bean 意味著, 每次我們使用時, 都會重新 創建 這個 bean。
<bean scope='ptototype' />
@Component // 這里配置prototype @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class LuBanService{ }
5. BeanDefinition
5.1 什么是 合并?
一個BD 包含了 很多 配置信息。包括構造函數的參數, setter方法 的參數, 還有 容器一些特定的配置信息。 比如 初始方法, 靜態方法等等。
一個 子 BD 可以繼承 父 BD的 配置信息, 不僅如此,還可以覆蓋 其中的 一些值,或者 添加一些自己需要的屬性。
一個父 BD 可以作為 BD 定義的模板。
簡單的總結就是: 子 BD 會從 父 BD 繼承沒有的屬性。子 BD 已經存在的屬性不會被覆蓋。
關于 父 BD 中 abstract 屬性的 說明: 并不是作為 父 BD 就一定要有 abstract 屬性,abstract 只是 代表了 這個 BD 是否要被 Spring 進行 實例化,并 創建 對應的 bean, 如果為 true,代表容器不需要對其進行實例化。
如果一個 父 BD 沒有 class 屬性, 那么 必須要設置 其 abstract 為 true;
一般 作為 父 BD 都設置 abstract 為 true,因為我們只是要繼承這個模板,而不是 實例化父 BD。 要是設置abstract 為 true, 你又不去繼承他,他自己又不能實例化,那沒啥用了,這個。
5.2 Spring 在 哪些階段做了 合并?
-- Spring 掃描 獲取 BD 時。
-- Spring 在 實例化一個 BD 時 也會進行 合并。
在掃描階段之所以要合并,是因為 String 需要 拿到 指定了、實現了 BeanDefinitionRegistryPostProcessor 接口的 BD 的 名稱。
在實例化階段, Spring 需要用到 BD 中的 一些列屬性 做 判斷, 所以進行了一系列合并,。
總結起來就是一個原因: Spring 需要用到 BD 的屬性, 要保證獲取到的BD 的屬性是正確的。
總結: BeanDefinition 這個類很重要, 是整個Spring 的基石。 之所以每次用到 BD 都要進行一次合并,是為了每次拿到都是 最新的,最有效的。 因為利用容器提供 的 一些擴展點 我們可以修改 BD 中的一些屬性。
6. BeanDefinitionRegistryPostProcessor 的 作用。
-- BeanFactoryPostProcessor 可以對 Bean 配置元數據 進行 操作。 也就是說, Spring 允許 BFPP 讀取指定 bean 的 配置 元數據, 并可以在 bean 被實例化之前 修改它。
這里說的 配置元數據 其實就是 我們之前所說的 BeanDefinition。
-- 我們可以配置 多個 BFPP, 并且 只要 我們 配置的 BFPP 同時實現了 Ordered 接口的話,我么 還可以控制這些 BFPP 的 執行順序。
6.1. 
-- Ordered 接口 用于 決定 執行順序。
-- PriorityOrdered 這個接口 直接 繼承了 Ordered 接口,并沒有做 任何 擴展。 只是作為 一個 標記 接口, 也 用于 決定 BFPP 的執行順序。
-- Aware 先不管。
-- FunctionalInterface ,這是 Java 8 新增 的一個接口,也是只起標記作用,標記該接口是一個函數式接口。
-- BeanFactoryPostProcessor 代表這個類是一個 Bean 工廠的后置處理器。
-- PropertiesLoaderSupport, 這個類主要 包含 定義了 屬性的 加載方法。包含屬性如下:
@Nullable
Protected Properties [] localProperties; // 本地屬性,可以直接在XML 中配置。
Protected boolean localOverirde = false; // 是否本地的屬性覆蓋提供的文件中的屬性, 默認不會。
@NUllable
Privated Resource [] locations; // 根據地址找到對應的文件。
privated boolean ignoreResourceNotFound = false; // 沒有找到文件是否拋出異常; false 不拋出;
@Nullable
private String fileEncoding; // 對應文件資源的編碼
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister(); // 文件解析器
-- PropertyResourceConfigurer , 這個類可以對讀取到的一些 屬性 進行 轉換;
-- PlaceholderConfigurerSupport , 主要負責對占位符進行 解析。其中的幾個屬性如下:
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${"; // 默認解析的前綴
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}"; // 默認解析的后綴
public static final String DEFAULT_VALUE_SEPARATOR = ":"; // 屬性名 跟 屬性值 的分隔符
-- PropertyPlaceholderConfigurer 繼承了上面這些類的所有功能, 同時 可以配置屬性的解析順序。如:
public static final int SYSTEM_PROPERTIES_MODE_NEVER = 0; // 不在系統屬性中查找
public static final int SYSTEM_PROPERTIES_MODE_FALLBACK = 1; // 如果沒有在配置文件中找到,再去系統屬性中查找
public static final int SYSTEM_PROPERTIES_MODE_OVERRIDE = 2; // 先查找系統中的屬性,沒有再去查找配置文件中的屬性
總結: BFPP 執行的順序總結如下:
-- 先執行直接實現了 BeanFactoryRegestryPostProcessor 接口的后置 處理器, 所有實現了BeanDefinitionRegistryPostProcessor 接口的類有 兩個 方法: 一個是 PostProcessBeanDefinitionRegistry 方法, 一個是繼承父接口的 PostProcessBeanFactory 方法。
-- PostProcessBeanDefinitoinRegistry 方法 早于 PostProcessBeanFactory 方法執行, 對于 PostProcessBeanDefinitionRegistry 的執行順序 又 遵循 如下 原子:
先執行 實現了 PriorityOrdered 接口的類中 的 PostProcessBeanDefinitionRegistry 方法; 再執行實現了 Ordered 接口的 類中 的 PostProcessBeanDefinitionRegistry 的 方法; 最后執行沒有實現上面兩個接口 的 類 中 的 PostProcessBeanDefinitionRegistry 方法;
-- 執行 完成 所有 的 PostProcessBeanDefinitionRegistry 方法后, 再執行 實現了 BeanFactoryPostProcessor 接口的后置處理器:
先執行實現了 PriorityOrdered 接口的類中的 PostProcessBeanFactory 方法; 再執行實現了 Ordered 接口中 的 PostProcessBeanFactory 方法; 最后 執行沒有實現上面兩個接口類中 PostProcessBeanFactory 的 方法。
7. FactoryBean: (Spring 中的 一個 特殊的 bean, Spring 利用它 提供 了 一個 創建 bean 的方式)
-- FactoryBean 主要用來 定制化 Bean 的 創建 邏輯
-- 當我們實例化一個Bean 的邏輯很復雜的 時候, 使用 FactoryBean 是很必要的, 這樣 可以 規避我們 去使用 過長 的 XML 配置。
-- FactoryBean 提供了以下 三個方法:
Object getObject() // 返回這個 FactoryBean 所創建的 對象。
boolean isSingleton() // 返回 FactoryBean 所創建的對象是否為 單例。默認返回true;
Class GetObjectType() // 返回這個 FactoryBean 所創建的 對象的 類型, 如果我們能確認返回的對象的 類型的 話,我們應該正常 對這個方法 做出實現,而不是 返回 null。
7.1 Spring 自身大量使用了 FactoryBean 這個概念。至少有 50 個 BeanFactory 實現類 存在 于 Spring 容器中。 、、、;
;假設我們定義了一個 FactoryBaen,名為 MyfactoryBean, 當我們調用 getBean('MyFactoryBean‘’) 方法時,返回的并不是這個FactoryBean, 而是這個 FactoryBean 所創建的 Bean, 如果我們想要獲取到 這個 FactoryBean, 需要在 名字前面 拼接 ‘&‘’’ 如:‘getBean("myFactoryBean")’;
7.2 SmartFactoryBean ( 繼承了 FactoryBean 的 唯一子接口,并且默認這個接口的實例是 懶加載的,就是 Spring 在啟動時, 不會立即將這個 bean 創建出來)
為了讓這個 bean 在 spring 啟動的時候就創建 出來, 我們在實現 SmartFactoryBean 時, 需要重寫 isEagerInit 方法,將返回值 設為 true;
我們也可以在 一個不是懶加載的 Bean 中 注入 這個 SmartFactoryBean 所創建的 bean, Spring 為了解決依賴關系,也會將這個 bean 創建 出來。
7.3 BeanDefinition vs FactoryBean
@Configuration public class Config { @Bean public B b(){ return new B(); } }
我們通過 @Bean 的方式 來 創建一個Bean, 那么 在 B 的 BeanDefinition 會 記錄 factoryBeanName 這個屬性, 同時 還會記錄 是 這個 Bean 中 哪個 方法 創建 B 的。如: factoryBeanName = config, factoryMethodName = b;
7.4 FactoryBean 相關的面試題:
FactoryBean 和 BeanFactory 的 區別: FB 是 Spring 提供的 一個 擴展點,適用于 復雜 的 bean 的 創建。 Mybatis 在 和 Spring 整合 時 就用 到了這個擴展點。 并且 FB 創建的 Bean 和 普通的 Bean 不一樣。FB 是Spring 創建Bean 的另一種手段。

浙公網安備 33010602011771號