?作者簡介:大家好,我是Leo,熱愛Java后端開發者,一個想要與大家共同進步的男人😉😉
🍎個人主頁:Leo的博客
💞當前專欄: Spring專欄
?特色專欄: MySQL學習
🥭本文內容:Spring5學習筆記— 工廠高級特性
🖥?個人小站 :個人博客,歡迎大家訪問
📚個人知識庫: 知識庫,歡迎大家訪問
1. 對象的生命周期
1.1 什么是對象的生命周期
含義:一個對象創建、存活、消亡的一個完整過程。
1.2 為什么要學習對象的生命周期
由 Spring
負責對象的創建、存活、銷毀,了解生命周期,有利于我們使用好Spring為我們創建的對象。
1.3 聲明周期的三個階段
1. 創建階段
-
scope="singleton"
此時會在創建Spring工廠時,創建對象。
注意:如果同時設置了
lazy-init="true"
,那么會在獲取對象ctx.getBean("")
時創建。 -
scope="prototype"
Spring工廠會在獲取對象時,創建對象,即調用
ctx.getBean("")
方法時創建。
注意:如果有屬性需要注入(DI),會在創建完成時立即進行注入。
2. 初始化階段
創建階段完成后,Spring會調用對象的初始化方法,完成對應的初始化操作。
程序員提供初始化方法的途徑:
-
實現
InitializingBean
接口://實現這個方法,完成初始化操作 public void afterProperitesSet(){}
- 對象中提供一個普通的方法同時配置Spring配置文件:
public void myInit(){}
細節分析:
-
如果一個對象即實現InitializingBean,同時又提供的普通的初始化方法 ,順序:
- InitializingBean
- 普通初始化方法
-
注入一定發生在初始化操作的前面
-
什么叫做初始化操作:資源的初始化:數據庫、IO、網絡 …
3. 銷毀階段
Spring銷毀對象ctx.close();
前,會調用對象的銷毀方法,完成銷毀操作
程序員定義銷毀方法的途徑:
-
實現DisposableBean
public void destroy()throws Exception{}
-
定義一個普通的銷毀方法同時配置Spring配置文件:
public void myDestroy()throws Exception{}
<bean id="" class="" init-method="" destroy-method="myDestroy"/>
細節分析:
- 銷毀方法的操作只適用于
scope="singleton"
- 銷毀操作主要指資源的釋放操作,比如
io.close();
connection.close();
2. 配置文件參數化
2.1 什么是配置文件參數化
把Spring配置文件中需要經常修改的字符串信息,轉移到一個更小的配置文件中。
- Spring的配置文件中存在需要經常修改的字符串?
存在 以數據庫連接相關的參數 代表- 經常變化字符串,在Spring的配置文件中,直接修改
不利于項目維護(修改)- 轉移到一個小的配置文件(.properties)
利于維護(修改)配置文件參數化:利于Spring配置文件的維護(修改)
2.2 配置文件參數化的開發步驟:
1. 提供一個小的配置文件
# 名字:隨便
# 放置位置:隨便jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/test?useSSL=false
jdbc.username = root
jdbc.password = root
2. Spring的配置文件與小配置文件進行整合
<context:property-placeholder location="classpath:/db.properties"/>
3. 在Spring配置文件中通過${key}
獲取小配置文件中對應的值
3. 自定義類型轉換器
1. 什么是類型轉換器
Spring 提供了一種 Converter(類型轉換器)的類型轉換工具。在 Spring MVC 中,它的作用是在控制器方法對請求進行處理前,先獲取到請求發送過來的參數,并將其轉換為控制器方法指定的數據類型,然后再將轉換后的參數值傳遞給控制器方法的形參,這樣后臺的控制器方法就可以正確地獲取請求中攜帶的參數了。
Spring MVC 框架默認提供了許多內置的類型轉換器,主要包括以下幾種類型。
1.1 標量轉換器
名稱 | 作用 |
---|---|
StringToBooleanConverter | String 到 boolean 類型轉換 |
ObjectToStringConverter | Object 到 String 轉換,調用 toString 方法轉換 |
StringToNumberConverterFactory | String 到數字轉換(例如 Integer、Long 等) |
NumberToNumberConverterFactory | 數字子類型(基本類型)到數字類型(包裝類型)轉換 |
StringToCharacterConverter | String 到 Character 轉換,取字符串中的第一個字符 |
NumberToCharacterConverter | 數字子類型到 Character 轉換 |
CharacterToNumberFactory | Character 到數字子類型轉換 |
StringToEnumConverterFactory | String 到枚舉類型轉換,通過 Enum.valueOf 將字符串轉換為需要的枚舉類型 |
EnumToStringConverter | 枚舉類型到 String 轉換,返回枚舉對象的 name 值 |
StringToLocaleConverter | String 到 java.util.Locale 轉換 |
PropertiesToStringConverter | java.util.Properties 到 String 轉換,默認通過 ISO-8859-1 解碼 |
StringToPropertiesConverter | String 到 java.util.Properties 轉換,默認使用 ISO-8859-1 編碼 |
1.2 集合、數組相關轉換器
名稱 | 作用 |
---|---|
ArrayToCollectionConverter | 任意數組到任意集合(List、Set)轉換 |
CollectionToArrayConverter | 任意集合到任意數組轉換 |
ArrayToArrayConverter | 任意數組到任意數組轉換 |
CollectionToCollectionConverter | 集合之間的類型轉換 |
MapToMapConverter | Map之間的類型轉換 |
ArrayToStringConverter | 任意數組到 String 轉換 |
StringToArrayConverter | 字符串到數組的轉換,默認通過“,”分割,且去除字符串兩邊的空格(trim) |
ArrayToObjectConverter | 任意數組到 Object 的轉換,如果目標類型和源類型兼容,直接返回源對象;否則返回數組的第一個元素并進行類型轉換 |
ObjectToArrayConverter | Object 到單元素數組轉換 |
CollectionToStringConverter | 任意集合(List、Set)到 String 轉換 |
StringToCollectionConverter | String 到集合(List、Set)轉換,默認通過“,”分割,且去除字符串兩邊的空格(trim) |
CollectionToObjectConverter | 任意集合到任意 Object 的轉換,如果目標類型和源類型兼容,直接返回源對象;否則返回集合的第一個元素并進行類型轉換 |
ObjectToCollectionConverter | Object 到單元素集合的類型轉換 |
Spring MVC 對于基本類型(例如 int、long、float、double、boolean 以及 char 等)已經做好了基本類型轉換。因此,通常情況下 Spring MVC 提供的這些類型轉換器可以滿足開發人員大多數的類型轉換需求的。
注意:在使用內置類型轉換器時,請求參數輸入值需要與接收參數類型相兼容,否則會報 400 錯誤。
2. 類型轉換器的作用
Spring通過類型轉換器把配置文件中字符串類型的數據,轉換成對象中成員變量對應類型的數據,進而完成了注入。
3. 為什么要自定義類型轉換器?
原因:實際應用中需要轉換某種特定的類型,且Spring內部沒有提供這種類型轉換器時,需要程序員自己定義。
4. 自定義類型轉換器的開發步驟
4.1 實現 Converter<>
接口
public class MyDateConverter implements Converter<String, Date> {/*convert方法作用:String ---> DateSimpleDateFormat sdf = new SimpleDateFormat();sdf.parset(String) ---> Dateparam:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>return : 當把轉換好的Date作為convert方法的返回值后,Spring自動的為birthday屬性進行注入(賦值)*/public Date convert(String source) {Date date = null;try {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");date = sdf.parse(source);} catch (ParseException e) {e.printStackTrace();}return date;}
}
4.2 在Spring的配置文件中進行配置
<!-- 創建自定義轉換器對象 -->
<bean id="myDateConverter" class="com.yuziyan.converter.MyDateConverter"/><!-- 在Spring中注冊自定義的轉換器 -->
<!-- 目的:告知Spring框架,我們所創建的MyDateConverter是一個類型轉換器 -->
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><set><ref bean="myDateConverter"/></set></property>
</bean><!-- 上面兩步完成之后就可以直接使用了 -->
5. 細節
5.1 體會依賴注入(DI)
MyDateConverter
中的日期的格式,可以通過依賴注入的方式,由配置文件完成賦值。
public class MyDateConverter implements Converter<String, Date> {private String pattern;public String getPattern() { return pattern; }public void setPattern(String pattern) { this.pattern = pattern; }/*convert方法作用:String ---> DateSimpleDateFormat sdf = new SimpleDateFormat();sdf.parset(String) ---> Dateparam:source 代表的是配置文件中 日期字符串 <value>2020-10-11</value>return : 當把轉換好的Date作為convert方法的返回值后,Spring自動的為birthday屬性進行注入(賦值)*/public Date convert(String source) {Date date = null;try {SimpleDateFormat sdf = new SimpleDateFormat(pattern);date = sdf.parse(source);} catch (ParseException e) {e.printStackTrace();}return date;}
}
<!--創建自定義類型轉換器對象-->
<bean id="myDateConverter" class="com.yuziyan.converter.MyDateConverter"><property name="pattern" value="yyyy-MM-dd"/>
</bean>
ConversionSeviceFactoryBean
定義id屬性,值必須是conversionService
5.2 Spring框架內置日期類型的轉換器
日期格式:2020/05/01 (不支持 :2020-05-01)
6. 后置處理Bean
6.1 BeanPostProcessor的作用:
對Spring工廠所創建的對象,進行再加工。
注意:BeanPostProcessor是接口
1. BeanPostProcessor
都是在目標對象被實例化之后,并且屬性也被設置之后調用的
- postProcessBeforeInitialization
- 在afterPropertiesSet或者自定義的初始化方法(使用@bean注解中的initMethod()屬性或者使用xml配置)之前執行
- postProcessAfterInitialization
- 在afterPropertiesSet或者自定義的初始化方法之后執行(如果是從FactoryBean中獲取的對象,則只有這個方法會起作用,,postProcessBeforeInitialization以及afterPropertiesSet或者自定義的初始化方法都不會有作用)
2. InstantiationAwareBeanPostProcessor
InstantiationAwareBeanPostProcessor接口繼承BeanPostProcessor接口,它內部提供了3個方法,再加上BeanPostProcessor接口內部的2個方法,所以實現這個接口需要實現5個方法。InstantiationAwareBeanPostProcessor接口的主要作用在于目標對象的實例化過程中需要處理的事情,包括實例化對象的前后過程以及實例的屬性設置
在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean()方法的Object bean = resolveBeforeInstantiation(beanName, mbdToUse);方法里面執行了這個后置處理器
- postProcessBeforeInstantiation
- 在目標對象實例化之前調用,方法的返回值類型是Object,我們可以返回任何類型的值。由于這個時候目標對象還未實例化,所以這個返回值可以用來代替原本該生成的目標對象的實例(一般都是代理對象)。如果該方法的返回值代替原本該生成的目標對象,后續只有postProcessAfterInitialization方法會調用,其它方法不再調用;否則按照正常的流程走
- postProcessAfterInstantiation
- 方法在目標對象實例化之后調用,這個時候對象已經被實例化,但是該實例的屬性還未被設置,都是null。如果該方法返回false,會忽略屬性值的設置;如果返回true,會按照正常流程設置屬性值。
- postProcessPropertyValues
- 方法對屬性值進行修改(這個時候屬性值還未被設置,但是我們可以修改原本該設置進去的屬性值)。如果postProcessAfterInstantiation方法返回false,該方法不會被調用。可以在該方法內對屬性值進行修改
- postProcessBeforeInitialization&postProcessAfterInitialization
- 父接口BeanPostProcessor的2個方法
3. SmartInstantiationAwareBeanPostProcessor
智能實例化Bean后置處理器(繼承InstantiationAwareBeanPostProcessor)
- determineCandidateConstructors
- 檢測Bean的構造器,可以檢測出多個候選構造器(Java好像只會確定唯一的備選的構造器)
- getEarlyBeanReference
- 循環引用的后置處理器,這個東西比較復雜, 獲得提前暴露的bean引用。主要用于解決循環引用的問題,只有單例對象才會調用此方法
- predictBeanType
- 預測bean的類型,在最后的類型轉化時會用到
4. MergedBeanDefinitionPostProcessor
- postProcessMergedBeanDefinition
- 緩存bean的注入信息的后置處理器,僅僅是緩存或者干脆叫做查找更加合適,沒有完成注入,注入是另外一個后置處理器的作用(不實現這個方法,也能直接調用postProcessPropertyValues完成屬性值的注入)
5. DestructionAwareBeanPostProcessor
- postProcessBeforeDestruction
- 在bean實例被銷毀之前被調用
6.2 后置處理Bean的運行原理分析
程序員實現BeanPostProcessor規定接口中的方法:
參數一:Spring工廠創建好的對象
參數二:對象名字
返回值:返回的對象會交給Spring框架
Object postProcessBeforeInitiallization(Object bean String beanName)
作用:Spring創建完對象,并進行注入后,會運行Before方法進行加工Object postProcessAfterInitiallization(Object bean String beanName)
作用:Spring執行完對象的初始化操作后,會運行After方法進行加工實戰中:
很少處理Spring的初始化操作:沒有必要區分Before After。只需要實現其中的一個After方法即可
注意:
postProcessBeforeInitiallization(){
return bean對象
}
6.3 BeanPostProcessor的開發步驟
1. 實現 BeanPostProcessor接口
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Categroy categroy = (Categroy) bean;categroy.setName("xiaowb");return categroy;}
}
2. Spring的配置文件中進行配置
<bean id="myBeanPostProcessor" class="xxx.MyBeanPostProcessor"/>
3. BeanPostProcessor細節
BeanPostProcessor會對Spring工廠中所有創建的對象進行加工。