Spring_4_工廠高階特性
目錄
1. 物件的生命週期
- 什麼是物件的⽣命週期?
⼀個物件 建立、存活、消亡 的⼀個完整過程。 - 為什麼要學習物件的⽣命週期? 由 Spring 負責物件的 建立、存活、銷燬,瞭解⽣命週期,有利於我們使用好 Spring 為我們建立的物件。
- ⽣命週期的 3 個階段: 建立階段 —> 初始化階段 —> 銷燬階段
1.1 建立階段
Spring 工廠何時建立物件?'
- scope="prototype":Spring 工廠在獲取物件 ctx.getBean("xxx") 的同時,建立物件。
- scope="singleton":Spring 工廠建立的同時,建立物件。 通過配置 <bean lazy-init="true"/> 也可以實現工廠獲取物件的同時,建立物件。 通過配置 <bean lazy-init="true"/> 也可以實現工廠獲取物件的同時,建立物件。
1.2 初始化階段
什麼時候? Spring 工廠在建立完物件後,呼叫物件的初始化方法,完成對應的初始化操作。 初始化方法提供:程式設計師根據需求,提供初始化方法,最終完成初始化操作。 初始化方法呼叫:Spring 工廠進行呼叫。
提供初始化方法的兩種方式:
InitializingBean 介面:
public class Product implements InitializingBean { //程序员根据需求实现的方法, 完成初始化操作
物件中提供一個普通的初始化方法,配置檔案種配置 init-method:
public class Product { public void myInit() { System.out.println("Product.myInit"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit"/>
- 初始化操作的細節分析:
- 如果⼀個物件既實現 InitializingBean 同時⼜提供的 普通的初始化方法,執行順序? 先執行 InitializingBean,再執行 普通初始化方法。
- 注入⼀定發⽣在初始化操作的前面。
- 初始化操作到底是什麼? 資源的初始化:資料庫、IO、網路、…
1.3 銷燬階段
Spring 銷燬物件前,會呼叫物件的銷燬方法,完成銷燬操作。
- Spring 什麼時候銷燬所建立的物件?ctx.close(); 銷燬方法提供:程式設計師根據業務需求,定義銷燬方法,完成銷燬操作
銷燬方法呼叫:Spring 工廠進行呼叫。
開發流程與初始化操作一樣,提供銷燬方法的兩種方式:
DisposableBean 介面:
public class Product implements DisposableBean { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
物件中提供一個普通的銷燬方法,配置檔案種配置 destroy-method:
public class Product { // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 public void myDestory() { System.out.println("Product.myDestory"); } }
<bean id="product" class="com.yusael.life.Product" destroy-method="myDestory"/>
- 銷燬階段細節分析: 銷燬方法的操作只適用於 scope="singleton",初始化操作都適用。
- 銷燬操作到底是什麼? 資源的釋放:io.close()、connection.close()、…
1.4 總結
public class Product implements InitializingBean, DisposableBean { private String name; public String getName() { return name; } public void setName(String name) { System.out.println("Product.setName"); this.name = name; } Product() { // 创建 System.out.println("Product.Product"); } // 程序员根据需求实现的方法, 完成初始化操作 public void myInit() { System.out.println("Product.myInit"); } // 程序员根据需求实现的方法, 完成初始化操作 @Override public void afterPropertiesSet() throws Exception { System.out.println("Product.afterPropertiesSet"); } public void myDestory() { System.out.println("Product.myDestory"); } // 程序员根据⾃⼰的需求, 定义销毁方法, 完成销毁操作 @Override public void destroy() throws Exception { System.out.println("Product.destroy"); } }
<bean id="product" class="com.yusael.life.Product" init-method="myInit" destroy-method="myDestory"> <property name="name" value="yusael"/> </bean>
img1
2. 配置檔案引數化
配置檔案引數化:把 Spring 配置檔案中需要經常修改的字串資訊,轉移到⼀個更小的配置檔案中。
- Spring 的配置檔案中是否存在需要經常修改的字串? 存在:以資料庫連線相關的引數…
- 經常變化字串,在 Spring 的配置檔案中,直接修改不利於專案維護(修改)
- 轉移到⼀個小的配置檔案(.properties)利於維護(修改)
優點:利於 Spring 配置檔案的維護(修改)
2.1 配置檔案引數的開發步驟
提供⼀個小的配置檔案(.properities) 名字:沒有要求 放置位置:沒有要求
jdbc.driverClassName = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://localhost:3306/spring?useSSL=false jdbc.username = root jdbc.password = 1234
Spring 的配置檔案與小配置檔案進行整合:
<!--Spring的配置文件与⼩配置文件进行整合--> <!--resources 下的文件在整个程序编译完后会被放到 classpath 目录下,src.main.java中的文件也是--> <context:property-placeholder location="classpath:/db.properties"/>
在 Spring 配置檔案中通過 ${key} 獲取小配置檔案中對應的值:
<bean id="conn" class="com.yusael.factorybean.ConnectionFactoryBean"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean>
3. 自定義型別轉換器
產生原因:當 Spring 內部沒有提供特定型別轉換器時,而程式設計師在應用的過程中還需要使用,那麼 就需要程式設計師⾃⼰定義型別轉換器。
3.1 自定義型別轉換器開發步驟
類 implements Converter 介面
public class MyDateConverter implements Converter<String, Date> { /* convert方法作用: String ---> Date SimpleDateFormat sdf = new SimpleDateFormat(); sdf.parset(String) ---> Date 参数: source : 代表的是配置文件中, 日期字符串 <value>2020-10-11</value> return : 当把转换好的 Date 作为 convert 方法的返回值后, Spring ⾃动的为birthday属性进行注入(赋值) */ @Override 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; } }
在 Spring 的配置檔案中進行配置; 先建立 MyDateConverter 物件,再註冊型別轉換器;
<!--创建 MyDateConverter 对象--> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"/> <!--用于注册类型转换器--> <bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean> <bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012-12-12"/> </bean>
- 自定義型別轉換器細節
MyDateConverter 中的日期的格式,通過 依賴注入 的方式,由配置檔案完成賦值。
public class MyDateConverter implements Converter<String, Date> { private String pattern; @Override 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; } public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } }
<!-- 配置文件完成对日期格式的赋值 --> <bean id="myDateConverter" class="com.yusael.converter.MyDateConverter"> <property name="pattern" value="yyyy-MM-dd"/> </bean>
ConversionSeviceFactoryBean 定義 id屬性,值必須是 conversionService;
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <ref bean="myDateConverter"/> </set> </property> </bean>
Spring 框架其實內建了日期型別的轉換器:日期格式必須是 2020/05/01。
<bean id="good" class="com.yusael.converter.Good"> <property name="name" value="zhenyu"/> <property name="birthday" value="2012/12/12"/> </bean>
4. 後置處理 Bean
BeanPostProcessor 作用:對 Spring 工廠所建立的物件,進行再加工。(AOP 的底層實現)
4.1 後置處理 Bean 原理分析
img2
4.2 實現 BeanPostProcessor 介面中規定的兩個方法
postProcessBeforeInitialization 作用:Spring 建立完物件,並進行注入後,可以執行 Before ⽅法進行加工; 通過方法的引數獲得 Spring 建立好的物件,最終通過返回值交給 Spring 框架。
public Object postProcessBeforeInitialization(Object bean, String beanName) { return bean; }
postProcessAfterInitialization 作⽤:Spring 執行完物件的初始化操作後,可以執行 After ⽅法進行加工; 通過方法的引數獲得 Spring 建立好的物件,最終通過返回值交給 Spring 框架。
public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; }
- 應用 實戰中:很少處理 Spring 的初始化操作,沒有必要區分 Before,After。只需要實現其中一個,建議是 After 方法即可
4.3 BeanPostProcessor 開發步驟
類 實現 BeanPostProcessor 介面
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { Category category = (Category) bean; category.setName("yusael"); return category; } }
Spring 配置檔案中進行配置
<bean id="myBeanPostProcessor" class="com.yusael.beanpost.MyBeanPostProcessor"/>
細節 BeanPostProcessor 會對 Spring 工廠建立的所有物件進行加工。如果工廠建立了多個不同的物件,要注意區別傳入的物件:
@Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof Category) { Category category = (Category) bean; category.setName("yusael"); return category; } return bean; }